@firecms/core
Version:
Awesome Firebase/Firestore-based headless open-source CMS
109 lines (99 loc) • 4.12 kB
text/typescript
import { EditorState } from "prosemirror-state";
export function isMarkActive(state: EditorState, type: any) {
if (!state || !type) return false;
const { from, $from, to, empty } = state.selection;
if (empty) return !!type.isInSet(state.storedMarks || $from.marks());
return state.doc.rangeHasMark(from, to, type);
}
export function isNodeActive(state: EditorState, type: any, attrs: any = {}) {
if (!state || !type) return false;
const { $from, to, node } = state.selection as any;
if (node) {
return node.type === type && (!attrs || Object.keys(attrs).every((key) => node.attrs[key] === attrs[key]));
}
return to <= $from.end() && $from.parent.type === type && (!attrs || Object.keys(attrs).every((key) => $from.parent.attrs[key] === attrs[key]));
}
export function getMarkAttributes(state: EditorState, type: any) {
if (!state || !type) return {};
const { from, $from, to, empty } = state.selection;
let mark;
if (empty) {
mark = type.isInSet(state.storedMarks || $from.marks());
} else {
let found = false;
state.doc.nodesBetween(from, to, (node) => {
if (found) return false;
const m = type.isInSet(node.marks);
if (m) {
mark = m;
found = true;
}
return true;
});
}
return mark ? mark.attrs : {};
}
export function setMark(type: any, attrs?: any) {
return (state: EditorState, dispatch?: (tr: any) => void) => {
const { empty, $cursor, ranges } = state.selection as any;
if ((empty && !$cursor) || !type) return false;
if (dispatch) {
if ($cursor) {
dispatch(state.tr.addStoredMark(type.create(attrs)));
} else {
let tr = state.tr;
for (let i = 0; i < ranges.length; i++) {
let { $from, $to } = ranges[i];
tr.addMark($from.pos, $to.pos, type.create(attrs));
}
dispatch(tr.scrollIntoView());
}
}
return true;
};
}
export function unsetMark(type: any) {
return (state: EditorState, dispatch?: (tr: any) => void) => {
const { empty, $cursor, ranges } = state.selection as any;
if ((empty && !$cursor) || !type) return false;
if (dispatch) {
let tr = state.tr;
if ($cursor) {
const parent = $cursor.parent;
let markStart = -1;
let markEnd = -1;
let currentMarkStart = -1;
parent.forEach((child: any, offset: number) => {
const childStart = $cursor.start() + offset;
const childEnd = childStart + child.nodeSize;
if (type.isInSet(child.marks)) {
if (currentMarkStart === -1) currentMarkStart = childStart;
if ($cursor.pos >= childStart && $cursor.pos <= childEnd) {
markStart = currentMarkStart;
}
} else {
if (currentMarkStart !== -1) {
if (markStart !== -1 && markEnd === -1) markEnd = childStart;
currentMarkStart = -1;
}
}
});
if (markStart !== -1 && markEnd === -1) {
markEnd = $cursor.end();
}
if (markStart !== -1 && markEnd !== -1) {
tr.removeMark(markStart, markEnd, type);
}
tr.removeStoredMark(type);
dispatch(tr.scrollIntoView());
} else {
for (let i = 0; i < ranges.length; i++) {
let { $from, $to } = ranges[i];
tr.removeMark($from.pos, $to.pos, type);
}
dispatch(tr.scrollIntoView());
}
}
return true;
};
}