diff options
Diffstat (limited to 'src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts')
-rw-r--r-- | src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 129 |
1 files changed, 69 insertions, 60 deletions
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 03c902580..7a8b72be0 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -1,29 +1,29 @@ import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from 'prosemirror-commands'; import { redo, undo } from 'prosemirror-history'; import { Schema } from 'prosemirror-model'; -import { splitListItem, wrapInList, sinkListItem, liftListItem } from 'prosemirror-schema-list'; +import { liftListItem, sinkListItem, splitListItem, wrapInList } from 'prosemirror-schema-list'; import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state'; import { liftTarget } from 'prosemirror-transform'; -import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols'; -import { GetEffectiveAcl } from '../../../../fields/util'; +import { EditorView } from 'prosemirror-view'; +import { ClientUtils } from '../../../../ClientUtils'; import { Utils } from '../../../../Utils'; +import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols'; +import { GetEffectiveAcl } from '../../../../fields/util'; import { Docs } from '../../../documents/Documents'; import { RTFMarkup } from '../../../util/RTFMarkup'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { OpenWhere } from '../DocumentView'; -import { Doc } from '../../../../fields/Doc'; -import { EditorView } from 'prosemirror-view'; +import { DocumentView } from '../DocumentView'; +import { OpenWhere } from '../OpenWhere'; const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false; export type KeyMap = { [key: string]: any }; -export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) => { +export const updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: string, from?: number, to?: number) => { let mapStyle = assignedMapStyle; - tx2.doc.descendants((node: any, offset: any, index: any) => { + tx2.doc.descendants((node: any, offset: any /* , index: any */) => { if ((from === undefined || to === undefined || (from <= offset + node.nodeSize && to >= offset)) && (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item)) { - const path = (tx2.doc.resolve(offset) as any).path; - let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty('type') && c.type === schema.nodes.ordered_list ? 1 : 0), 0); + const { path } = tx2.doc.resolve(offset) as any; + let depth = Array.from(path).reduce((p: number, c: any) => p + (c.type === schema.nodes.ordered_list ? 1 : 0), 0); if (node.type === schema.nodes.ordered_list) { if (depth === 0 && !assignedMapStyle) mapStyle = node.attrs.mapStyle; depth++; @@ -34,38 +34,44 @@ export let updateBullets = (tx2: Transaction, schema: Schema, assignedMapStyle?: return tx2; }; -export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKeys?: KeyMap): KeyMap { +export function buildKeymap<S extends Schema<any>>(schema: S, props: any): KeyMap { const keys: { [key: string]: any } = {}; function bind(key: string, cmd: any) { - if (mapKeys) { - const mapped = mapKeys[key]; - if (mapped === false) return; - if (mapped) key = mapped; - } keys[key] = cmd; } + function onKey(): boolean | undefined { + // bcz: this is pretty hacky -- prosemirror doesn't send us the keyboard event, but the 'event' variable is in scope.. so we access it anyway + // eslint-disable-next-line no-restricted-globals + return props.onKey?.(event, props); + } + const canEdit = (state: any) => { - switch (GetEffectiveAcl(props.TemplateDataDocument)) { + const permissions = GetEffectiveAcl(props.TemplateDataDocument ?? props.Document[DocData]); + switch (permissions) { case AclAugment: - const prevNode = state.selection.$cursor.nodeBefore; - const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid; - if (prevUser != Doc.CurrentUserEmail) { - return false; + { + const prevNode = state.selection.$cursor.nodeBefore; + const prevUser = !prevNode ? ClientUtils.CurrentUserEmail() : prevNode.marks.lastElement()?.attrs.userid; + if (prevUser !== ClientUtils.CurrentUserEmail()) { + return false; + } } + break; + default: } return true; }; const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch); - //History commands + // History commands bind('Mod-z', undo); bind('Shift-Mod-z', redo); !mac && bind('Mod-y', redo); - //Commands to modify Mark + // Commands to modify Mark bind('Mod-b', toggleEditableMark(schema.marks.strong)); bind('Mod-B', toggleEditableMark(schema.marks.strong)); @@ -77,15 +83,15 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind('Mod-u', toggleEditableMark(schema.marks.underline)); bind('Mod-U', toggleEditableMark(schema.marks.underline)); - //Commands for lists + // Commands for lists bind('Ctrl-i', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any)); - bind('Ctrl-Tab', () => (props.onKey?.(event, props) ? true : true)); - bind('Alt-Tab', () => (props.onKey?.(event, props) ? true : true)); - bind('Meta-Tab', () => (props.onKey?.(event, props) ? true : true)); - bind('Meta-Enter', () => (props.onKey?.(event, props) ? true : true)); + bind('Ctrl-Tab', () => onKey() || true); + bind('Alt-Tab', () => onKey() || true); + bind('Meta-Tab', () => onKey() || true); + bind('Meta-Enter', () => onKey() || true); bind('Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.onKey?.(event, props)) return true; + if (onKey()) return true; if (!canEdit(state)) return true; const ref = state.selection; const range = ref.$from.blockRange(ref.$to); @@ -103,8 +109,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey if ( !wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => { const tx25 = updateBullets(tx2, schema); - const ol_node = tx25.doc.nodeAt(range!.start)!; - const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, ol_node.attrs, marks); + const olNode = tx25.doc.nodeAt(range!.start)!; + const tx3 = tx25.setNodeMarkup(range!.start, olNode.type, olNode.attrs, marks); // when promoting to a list, assume list will format things so don't copy the stored marks. marks && tx3.ensureMarks([...marks]); marks && tx3.setStoredMarks([...marks]); @@ -115,10 +121,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey console.log('bullet promote fail'); } } + return undefined; }); bind('Shift-Tab', (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.onKey?.(event, props)) return true; + if (onKey()) return true; if (!canEdit(state)) return true; const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); @@ -132,15 +139,16 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey ) { console.log('bullet demote fail'); } + return undefined; }); - //Command to create a new Tab with a PDF of all the command shortcuts - bind('Mod-/', (state: EditorState, dispatch: (tx: Transaction) => void) => { - const newDoc = Docs.Create.PdfDocument(Utils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 }); + // Command to create a new Tab with a PDF of all the command shortcuts + bind('Mod-/', () => { + const newDoc = Docs.Create.PdfDocument(ClientUtils.prepend('/assets/cheat-sheet.pdf'), { _width: 300, _height: 300 }); props.addDocTab(newDoc, OpenWhere.addRight); }); - //Commands to modify BlockType + // Commands to modify BlockType bind('Ctrl->', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any))); bind('Alt-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any)); bind('Shift-Ctrl-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any)); @@ -156,25 +164,25 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind('Shift-Ctrl-' + i, (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any)); } - //Command to create a horizontal break line + // Command to create a horizontal break line const hr = schema.nodes.horizontal_rule; bind('Mod-_', (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(hr.create()).scrollIntoView())); - //Command to unselect all + // Command to unselect all bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => { dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); (document.activeElement as any).blur?.(); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); }); - bind('Alt-Enter', () => (props.onKey?.(event, props) ? true : true)); - bind('Ctrl-Enter', () => (props.onKey?.(event, props) ? true : true)); + bind('Alt-Enter', () => onKey() || true); + bind('Ctrl-Enter', () => onKey() || true); bind('Cmd-a', (state: EditorState, dispatch: (tx: Transaction) => void) => { dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1)))); return true; }); - bind('Cmd-?', (state: EditorState, dispatch: (tx: Transaction) => void) => { - RTFMarkup.Instance.open(); + bind('Cmd-?', () => { + RTFMarkup.Instance.setOpen(true); return true; }); bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -189,14 +197,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }); bind('Cmd-]', (state: EditorState, dispatch: (tx: Transaction) => void) => { const resolved = state.doc.resolve(state.selection.from) as any; - const tr = state.tr; + const { tr } = state; if (resolved?.parent.type.name === 'paragraph') { tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'right' }, resolved.parent.marks); } else { const node = resolved.nodeAfter; const sm = state.storedMarks || undefined; if (node) { - tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'right' })).setStoredMarks([...node.marks, ...(sm || [])]); } } dispatch(tr); @@ -204,14 +212,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }); bind('Cmd-\\', (state: EditorState, dispatch: (tx: Transaction) => void) => { const resolved = state.doc.resolve(state.selection.from) as any; - const tr = state.tr; + const { tr } = state; if (resolved?.parent.type.name === 'paragraph') { tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'center' }, resolved.parent.marks); } else { const node = resolved.nodeAfter; const sm = state.storedMarks || undefined; if (node) { - tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'center' })).setStoredMarks([...node.marks, ...(sm || [])]); } } dispatch(tr); @@ -219,14 +227,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }); bind('Cmd-[', (state: EditorState, dispatch: (tx: Transaction) => void) => { const resolved = state.doc.resolve(state.selection.from) as any; - const tr = state.tr; + const { tr } = state; if (resolved?.parent.type.name === 'paragraph') { tr.setNodeMarkup(resolved.path[resolved.path.length - 4], schema.nodes.paragraph, { ...resolved.parent.attrs, align: 'left' }, resolved.parent.marks); } else { const node = resolved.nodeAfter; const sm = state.storedMarks || undefined; if (node) { - tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm ? sm : [])]); + tr.replaceRangeWith(state.selection.from, state.selection.from, schema.nodes.paragraph.create({ align: 'left' })).setStoredMarks([...node.marks, ...(sm || [])]); } } dispatch(tr); @@ -236,7 +244,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey bind('Cmd-f', (state: EditorState, dispatch: (tx: Transaction) => void) => { const content = state.tr.selection.empty ? undefined : state.tr.selection.content().content.textBetween(0, state.tr.selection.content().size + 1); const newNode = schema.nodes.footnote.create({}, content ? state.schema.text(content) : undefined); - const tr = state.tr; + const { tr } = state; tr.replaceSelectionWith(newNode); // replace insertion with a footnote. dispatch( tr.setSelection( @@ -258,7 +266,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); const backspace = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView) => { - if (props.onKey?.(event, props)) return true; + if (onKey()) return true; if (!canEdit(state)) return true; if ( @@ -271,7 +279,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey dispatch(updateBullets(tx, schema)); if (view.state.selection.$anchor.node(-1)?.type === schema.nodes.list_item) { // gets rid of an extra paragraph when joining two list items together. - joinBackward(view.state, (tx: Transaction) => view.dispatch(tx)); + joinBackward(view.state, (tx2: Transaction) => view.dispatch(tx2)); } }) ) { @@ -288,11 +296,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }; bind('Backspace', backspace); - //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock - //command to break line + // newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock + // command to break line const enter = (state: EditorState, dispatch: (tx: Transaction) => void, view: EditorView, once = true) => { - if (props.onKey?.(event, props)) return true; + if (onKey()) return true; if (!canEdit(state)) return true; const trange = state.selection.$from.blockRange(state.selection.$to); @@ -337,7 +345,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey !splitBlockKeepMarks(state, (tx3: Transaction) => { const tonode = tx3.selection.$to.node(); if (tx3.selection.to && tx3.doc.nodeAt(tx3.selection.to - 1)) { - const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks); + const tx4 = tx3.setNodeMarkup(tx3.selection.to - 1, tonode.type, fromattrs, tonode.marks).setStoredMarks(marks || []); dispatch(tx4); } @@ -356,9 +364,10 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey }; bind('Enter', enter); - //Command to create a blank space - bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => { - if (props.TemplateDataDocument && GetEffectiveAcl(props.TemplateDataDocument) != AclEdit && GetEffectiveAcl(props.TemplateDataDocument) != AclAugment && GetEffectiveAcl(props.TemplateDataDocument) != AclAdmin) return true; + // Command to create a blank space + bind('Space', () => { + const editDoc = props.TemplateDataDocument ?? props.Document[DocData]; + if (editDoc && ![AclAdmin, AclAugment, AclEdit].includes(GetEffectiveAcl(editDoc))) return true; return false; }); |