From 939074601016a674d6a01922bab1383684fce63f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 25 Aug 2019 22:55:51 -0400 Subject: added sub-bullet types to prosemirror --- src/client/util/TooltipTextMenu.tsx | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 4672dd246..7a0c6f8c5 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -172,6 +172,9 @@ export class TooltipTextMenu { this.listTypeToIcon = new Map(); this.listTypeToIcon.set(schema.nodes.bullet_list, ":"); this.listTypeToIcon.set(schema.nodes.ordered_list, "1)"); + this.listTypeToIcon.set(schema.nodes.alphabet_list, "a)"); + this.listTypeToIcon.set(schema.nodes.cap_alphabet_list, "A)"); + this.listTypeToIcon.set(schema.nodes.roman_list, "i."); // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜"); this.listTypes = Array.from(this.listTypeToIcon.keys()); -- cgit v1.2.3-70-g09d2 From 094bbd52f2b55f501357a9f6b057042ad7684f27 Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 26 Aug 2019 16:58:24 -0400 Subject: playing more with bulleted lists. --- src/client/util/ProsemirrorExampleTransfer.ts | 70 +++++++++++++++++++++------ src/client/util/RichTextRules.ts | 3 +- src/client/util/RichTextSchema.tsx | 10 +++- src/client/util/TooltipTextMenu.tsx | 10 +++- src/client/views/nodes/FormattedTextBox.tsx | 12 ++--- 5 files changed, 78 insertions(+), 27 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index b928532d6..3cdfba59a 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -2,8 +2,8 @@ import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, setB import { redo, undo } from "prosemirror-history"; import { undoInputRule } from "prosemirror-inputrules"; import { Schema } from "prosemirror-model"; -import { liftListItem, splitListItem, wrapInList } from "prosemirror-schema-list"; -import { EditorState, Transaction } from "prosemirror-state"; +import { liftListItem, splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; +import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { TooltipTextMenu } from "./TooltipTextMenu"; const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false; @@ -79,32 +79,72 @@ export default function buildKeymap>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - liftListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - marks && tx2.ensureMarks(marks); - marks && tx2.setStoredMarks(marks); - dispatch(tx2); - }); - }); + let levelMark = (depth: number) => { + let p10 = schema.marks.pFontSize.create({ fontSize: 10 }); + let p14 = schema.marks.pFontSize.create({ fontSize: 14 }); + let p18 = schema.marks.pFontSize.create({ fontSize: 18 }); + let p24 = schema.marks.pFontSize.create({ fontSize: 24 }); + return depth == 0 ? p24 : depth == 2 ? p18 : depth == 4 ? p14 : p10; + } let bulletFunc = (state: EditorState, dispatch: (tx: Transaction) => void) => { var ref = state.selection; var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); let depth = range && range.depth ? range.depth : 0; let nodeType = depth == 2 ? schema.nodes.cap_alphabet_list : depth == 4 ? schema.nodes.roman_list : depth == 6 ? schema.nodes.alphabet_list : schema.nodes.ordered_list; + let nodeTypeMark = depth == 2 ? schema.marks.mcap_alphabet_list : depth == 4 ? schema.marks.mroman_list : depth == 6 ? schema.marks.malphabet_list : schema.marks.mordered_list; + let created = levelMark(depth); + if (!sinkListItem(nodeType /*schema.nodes.list_item */)(state, (tx2: Transaction) => { + const resolvedPos = tx2.doc.resolve(range!.start); + + let ns = new NodeSelection(resolvedPos); + let tx3 = tx2.addMark(ns.from - 1, ns.to, created).addMark(ns.from - 1, ns.to, nodeTypeMark as any).setSelection(TextSelection.create(tx2.doc, ns.to - (depth == 0 ? 3 : 1))); + marks && tx3.ensureMarks([...marks, created]); + marks && tx3.setStoredMarks([...marks, created]); - if (!wrapInList(nodeType)(state, (tx2: Transaction) => { - marks && tx2.ensureMarks(marks); - marks && tx2.setStoredMarks(marks); - dispatch(tx2); + dispatch(tx3); })) { - console.log("bullet fail"); + let sxf = state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)); + let newstate = state.applyTransaction(sxf); + if (!wrapInList(nodeType)(newstate.state, (tx2: Transaction) => { + const resolvedPos = tx2.doc.resolve(range!.start); + let ns = new TextSelection(resolvedPos, tx2.doc.resolve(range!.end + 1)); // new NodeSelection(resolvedPos); + let tx3 = tx2.setSelection(ns).removeMark(ns.from, ns.to, created).addMark(ns.from, ns.to, created).setSelection(TextSelection.create(tx2.doc, ns.to)); + let tx4 = depth > 0 ? tx3.insertText(" ").setSelection(TextSelection.create(tx2.doc, ns.to - 2, ns.to + 2)).deleteSelection() : tx3; + marks && tx4.ensureMarks([...marks, created]); + marks && tx4.setStoredMarks([...marks, created]); + + dispatch(tx4); + })) { + console.log("bullet fail"); + } } } bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => bulletFunc(state, dispatch)); + bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { + var ref = state.selection; + var range = ref.$from.blockRange(ref.$to); + var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); + let created = levelMark(range && range.depth ? range.depth - 4 : 0); + liftListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { + try { + const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); + let nodeIndex = resolvedPos.pos - (resolvedPos.nodeBefore && resolvedPos.nodeBefore.type.name === "text" ? resolvedPos.nodeBefore!.nodeSize : 0); + let ns = new NodeSelection(tx2.doc.resolve(nodeIndex)); + if (resolvedPos.nodeAfter && resolvedPos.nodeAfter.type.name === "list_item") + ns = new NodeSelection(tx2.doc.resolve(nodeIndex + 1)); + let tx3 = tx2.setSelection(ns).removeMark(ns.from, ns.to, created).addMark(ns.from, ns.to, created).setSelection(TextSelection.create(tx2.doc, ns.to)); + marks && tx3.ensureMarks([...marks, created]); + marks && tx3.setStoredMarks([...marks, created]); + dispatch(tx3); + } catch (e) { + dispatch(tx2); + } + }); + }); + bind("Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => { var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); if (!splitListItem(schema.nodes.list_item)(state, (tx3: Transaction) => { diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 89933650b..8c4c76027 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -26,7 +26,7 @@ export const inpRules = { match => ({ order: +match[1] }), (match, node) => node.childCount + node.attrs.order === +match[1] ), - // 1. ordered list + // a. alphabbetical list wrappingInputRule( /^([a-z]+)\.\s$/, schema.nodes.alphabet_list, @@ -34,7 +34,6 @@ export const inpRules = { (match, node) => node.childCount + node.attrs.order === +match[1] ), - // * bullet list wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.bullet_list), diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a8ce4731c..f128162c2 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -319,6 +319,15 @@ export const marks: { [index: string]: MarkSpec } = { toDOM: () => ['sup'] }, + malphabet_list: { + }, + mcap_alphabet_list: { + }, + mroman_list: { + }, + mo_list: { + }, + highlight: { parseDOM: [{ style: 'color: blue' }], toDOM() { @@ -412,7 +421,6 @@ export const marks: { [index: string]: MarkSpec } = { attrs: { fontSize: { default: 10 } }, - inclusive: false, parseDOM: [{ style: 'font-size: 10px;' }], toDOM: (node) => ['span', { style: `font-size: ${node.attrs.fontSize}px;` diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 7a0c6f8c5..77396c829 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -165,7 +165,15 @@ export class TooltipTextMenu { this.fontSizeToNum.set(schema.marks.p48, 48); this.fontSizeToNum.set(schema.marks.p72, 72); this.fontSizeToNum.set(schema.marks.pFontSize, 10); - this.fontSizeToNum.set(schema.marks.pFontSize, 10); + // this.fontSizeToNum.set(schema.marks.pFontSize, 12); + // this.fontSizeToNum.set(schema.marks.pFontSize, 14); + // this.fontSizeToNum.set(schema.marks.pFontSize, 16); + // this.fontSizeToNum.set(schema.marks.pFontSize, 18); + // this.fontSizeToNum.set(schema.marks.pFontSize, 20); + // this.fontSizeToNum.set(schema.marks.pFontSize, 24); + // this.fontSizeToNum.set(schema.marks.pFontSize, 32); + // this.fontSizeToNum.set(schema.marks.pFontSize, 48); + // this.fontSizeToNum.set(schema.marks.pFontSize, 72); this.fontSizes = Array.from(this.fontSizeToNum.keys()); //list types diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d6ba1700a..acfd2a3b8 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -115,7 +115,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @undoBatch public setFontColor(color: string) { - let self = this; if (this._editorView!.state.selection.from === this._editorView!.state.selection.to) return false; if (this._editorView!.state.selection.to - this._editorView!.state.selection.from > this._editorView!.state.doc.nodeSize - 3) { this.props.Document.color = color; @@ -176,15 +175,13 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = true); this._editorView.updateState(state); FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false); - if (state.selection.empty && FormattedTextBox._toolTipTextMenu) { - const marks = tx.storedMarks; - if (marks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(marks); } + if (state.selection.empty && FormattedTextBox._toolTipTextMenu && tx.storedMarks) { + FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks); } this._applyingChange = true; - const fieldkey = "preview"; - if (this.extensionDoc) this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n"); - if (this.extensionDoc) this.extensionDoc.lastModified = new DateField(new Date(Date.now())); + this.extensionDoc && (this.extensionDoc.text = state.doc.textBetween(0, state.doc.content.size, "\n\n")); + this.extensionDoc && (this.extensionDoc.lastModified = new DateField(new Date(Date.now()))); this.dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(state.toJSON())); this._applyingChange = false; let title = StrCast(this.dataDoc.title); @@ -198,7 +195,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe public highlightSearchTerms = (terms: String[]) => { if (this._editorView && (this._editorView as any).docView) { - const fieldkey = "preview"; const doc = this._editorView.state.doc; const mark = this._editorView.state.schema.mark(this._editorView.state.schema.marks.search_highlight); doc.nodesBetween(0, doc.content.size, (node: ProsNode, pos: number, parent: ProsNode, index: number) => { -- cgit v1.2.3-70-g09d2 From e13dc44532ea5f94bdc13a2103bc4a00438ee617 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 27 Aug 2019 01:29:35 -0400 Subject: simplified things. --- src/client/util/ProsemirrorExampleTransfer.ts | 43 ++++++++++----------------- src/client/util/RichTextSchema.tsx | 33 -------------------- src/client/util/TooltipTextMenu.tsx | 3 -- 3 files changed, 15 insertions(+), 64 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 052fb0c6d..d602ce4a1 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -84,35 +84,28 @@ export default function buildKeymap>(schema: S, mapKeys?: var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); let depth = range && range.depth ? range.depth : 0; - let nodeType = depth == 2 ? schema.nodes.cap_alphabet_list : depth == 4 ? schema.nodes.roman_list : depth == 6 ? schema.nodes.alphabet_list : schema.nodes.ordered_list; - let nodeTypeMark = schema.marks.mbulletType.create({ bulletType: depth == 2 ? "upper-alpha" : depth == 4 ? "lower-roman" : depth == 6 ? "lower-alpha" : "decimal" }); + let nodeTypeMark = depth == 2 ? "upper-alpha" : depth == 4 ? "lower-roman" : depth == 6 ? "lower-alpha" : "decimal"; if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { const resolvedPos = tx2.doc.resolve(range!.start); let path = (resolvedPos as any).path as any; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = (nodeTypeMark as any).attrs.bulletType; + path[i].attrs.bulletStyle = nodeTypeMark; break; } } - let ns = new NodeSelection(resolvedPos); - let tx3 = tx2.setSelection(TextSelection.create(tx2.doc, ns.to - (depth == 0 ? 3 : 1))); - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - dispatch(tx3); + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + dispatch(tx2); })) { let sxf = state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)); let newstate = state.applyTransaction(sxf); - if (!wrapInList(nodeType)(newstate.state, (tx2: Transaction) => { - const resolvedPos = tx2.doc.resolve(range!.start); - let ns = new TextSelection(resolvedPos, tx2.doc.resolve(range!.end + 1)); // new NodeSelection(resolvedPos); - let tx3 = tx2.setSelection(ns).setSelection(TextSelection.create(tx2.doc, ns.to)); - let tx4 = tx3; - marks && tx4.ensureMarks([...marks]); - marks && tx4.setStoredMarks([...marks]); - - dispatch(tx4); + if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + + dispatch(tx2); })) { console.log("bullet fail"); } @@ -125,28 +118,22 @@ export default function buildKeymap>(schema: S, mapKeys?: var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); let depth = range && range.depth > 3 ? range.depth - 4 : 0; - let nodeTypeMark = schema.marks.mbulletType.create({ bulletType: depth == 2 ? "upper-alpha" : depth == 4 ? "lower-roman" : depth == 6 ? "lower-alpha" : "decimal" }); + let nodeTypeMark = depth == 2 ? "upper-alpha" : depth == 4 ? "lower-roman" : depth == 6 ? "lower-alpha" : "decimal"; liftListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { try { const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - let nodeIndex = resolvedPos.pos - (resolvedPos.nodeBefore && resolvedPos.nodeBefore.type.name === "text" ? resolvedPos.nodeBefore!.nodeSize : 0); let path = (resolvedPos as any).path as any; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = (nodeTypeMark as any).attrs.bulletType; + path[i].attrs.bulletStyle = nodeTypeMark; break; } } - let ns = new NodeSelection(tx2.doc.resolve(nodeIndex)); - if (resolvedPos.nodeAfter && resolvedPos.nodeAfter.type.name === "list_item") - ns = new NodeSelection(tx2.doc.resolve(nodeIndex + 1)); - let tx3 = tx2.setSelection(ns).setSelection(TextSelection.create(tx2.doc, ns.to)); - - marks && tx3.ensureMarks([...marks]); - marks && tx3.setStoredMarks([...marks]); - dispatch(tx3); + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + dispatch(tx2); } catch (e) { dispatch(tx2); } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 26960e889..255f4a60d 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -182,39 +182,6 @@ export const nodes: { [index: string]: NodeSpec } = { return ['ol', { style: `list-style: ${node.attrs.bulletStyle}; font-size: ${fsize}` }, 0] } }, - alphabet_list: { - ...orderedList, - content: 'list_item+', - group: 'block', - attrs: { - bulletStyle: { default: "lower-alpha" }, - }, - toDOM(node: Node) { - return ['ol', { style: `list-style: ${node.attrs.bulletStyle}` }, 0] - } - }, - cap_alphabet_list: { - ...orderedList, - content: 'list_item+', - group: 'block', - attrs: { - bulletStyle: { default: "upper-alpha" }, - }, - toDOM(node: Node) { - return ['ol', { style: `list-style: ${node.attrs.bulletStyle}` }, 0] - } - }, - roman_list: { - ...orderedList, - content: 'list_item+', - group: 'block', - attrs: { - bulletStyle: { default: "lower-roman" }, - }, - toDOM(node: Node) { - return ['ol', { style: `list-style: ${node.attrs.bulletStyle}` }, 0] - } - }, //this doesn't currently work for some reason bullet_list: { ...bulletList, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 77396c829..7f6ba3aac 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -180,9 +180,6 @@ export class TooltipTextMenu { this.listTypeToIcon = new Map(); this.listTypeToIcon.set(schema.nodes.bullet_list, ":"); this.listTypeToIcon.set(schema.nodes.ordered_list, "1)"); - this.listTypeToIcon.set(schema.nodes.alphabet_list, "a)"); - this.listTypeToIcon.set(schema.nodes.cap_alphabet_list, "A)"); - this.listTypeToIcon.set(schema.nodes.roman_list, "i."); // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜"); this.listTypes = Array.from(this.listTypeToIcon.keys()); -- cgit v1.2.3-70-g09d2 From 1fbf7d7e10bb4dfa7e3a323ee0641d7bbf97b6a8 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 27 Aug 2019 23:24:18 -0400 Subject: fixed several lint errors, and minor issues with bullets --- src/client/util/DocumentManager.ts | 2 +- src/client/util/ProsemirrorExampleTransfer.ts | 17 ++++----- src/client/util/RichTextSchema.tsx | 13 ++++--- src/client/util/TooltipTextMenu.tsx | 31 ++++++++++++---- src/client/views/DocumentDecorations.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 42 +++++++++++----------- .../views/collections/CollectionViewChromes.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 6 ++-- 8 files changed, 71 insertions(+), 44 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 124faf266..ec731da84 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -204,4 +204,4 @@ export class DocumentManager { } } } -Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)) }) \ No newline at end of file +Scripting.addGlobal(function focus(doc: any) { DocumentManager.Instance.getDocumentViews(Doc.GetProto(doc)).map(view => view.props.focus(doc, true)); }); \ No newline at end of file diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 8b6936748..12ad28199 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -79,8 +79,7 @@ export default function buildKeymap>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - // let nodeTypeMark = depth == 2 ? "upper-alpha" : depth == 4 ? "lower-roman" : depth == 6 ? "lower-alpha" : "decimal"; - let nodeTypeMark = (depth: number) => { return depth == 2 ? "decimal2" : depth == 4 ? "decimal3" : depth == 6 ? "decimal4" : "decimal" } + let nodeTypeMark = (depth: number) => depth === 2 ? "indent2" : depth === 4 ? "indent3" : depth === 6 ? "indent4" : "indent1"; let bulletFunc = (state: EditorState, dispatch: (tx: Transaction) => void) => { var ref = state.selection; @@ -90,7 +89,7 @@ export default function buildKeymap>(schema: S, mapKeys?: if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { const resolvedPos = tx2.doc.resolve(range!.start); - let path = (resolvedPos as any).path as any; + let path = (resolvedPos as any).path; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { path[i].attrs.bulletStyle = nodeTypeMark(depth); @@ -105,7 +104,7 @@ export default function buildKeymap>(schema: S, mapKeys?: let newstate = state.applyTransaction(sxf); if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - let path = (resolvedPos as any).path as any; + let path = (resolvedPos as any).path; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { path[i].attrs.bulletStyle = nodeTypeMark(depth); @@ -120,8 +119,9 @@ export default function buildKeymap>(schema: S, mapKeys?: console.log("bullet fail"); } } - } - bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => bulletFunc(state, dispatch)); + }; + + bind("Tab", bulletFunc); bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { var ref = state.selection; @@ -132,7 +132,7 @@ export default function buildKeymap>(schema: S, mapKeys?: try { const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - let path = (resolvedPos as any).path as any; + let path = (resolvedPos as any).path; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { path[i].attrs.bulletStyle = nodeTypeMark(depth); @@ -159,7 +159,8 @@ export default function buildKeymap>(schema: S, mapKeys?: if (!splitBlockKeepMarks(state, (tx3: Transaction) => { marks && tx3.ensureMarks(marks); marks && tx3.setStoredMarks(marks); - if (!liftListItem(schema.nodes.list_item)(state, (tx4: Transaction) => dispatch(tx4))) { + if (!liftListItem(schema.nodes.list_item)(state, dispatch as ((tx: Transaction>) => void)) + ) { dispatch(tx3); } })) { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 76c45e6c1..4e18f410d 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -177,10 +177,15 @@ export const nodes: { [index: string]: NodeSpec } = { group: 'block', attrs: { bulletStyle: { default: "" }, + mapStyle: { default: "decimal" } }, toDOM(node: Node) { - for (let i = 0; i < node.childCount; i++) node.child(i).attrs.className = node.attrs.bulletStyle; - return ['ol', { class: `${node.attrs.bulletStyle}-ol`, style: `list-style: none;` }, 0] + const bs = node.attrs.bulletStyle; + const decMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "decimal2" : bs === "indent3" ? "decimal3" : bs === "indent4" ? "decimal4" : ""; + const multiMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "upper-alpha" : bs === "indent3" ? "lower-roman" : bs === "indent4" ? "lower-alpha" : ""; + let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap; + for (let i = 0; i < node.childCount; i++) node.child(i).attrs.className = map; + return ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0]; //return ['ol', { class: `${node.attrs.bulletStyle}`, style: `list-style: ${node.attrs.bulletStyle};`, 0] } }, @@ -192,7 +197,7 @@ export const nodes: { [index: string]: NodeSpec } = { // parseDOM: [{ tag: "ul" }, { style: 'list-style-type=disc' }], toDOM(node: Node) { for (let i = 0; i < node.childCount; i++) node.child(i).attrs.className = ""; - return ['ul', 0] + return ['ul', 0]; } }, @@ -302,7 +307,7 @@ export const marks: { [index: string]: MarkSpec } = { }, toDOM(node: any) { return ['span', { - style: `background: ${node.attrs.bulletType == "decimal" ? "yellow" : node.attrs.bulletType === "upper-alpha" ? "blue" : "green"}` + style: `background: ${node.attrs.bulletType === "decimal" ? "yellow" : node.attrs.bulletType === "upper-alpha" ? "blue" : "green"}` }]; } }, diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 7f6ba3aac..e979e6cde 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -28,11 +28,11 @@ export class TooltipTextMenu { private view: EditorView; private fontStyles: MarkType[]; private fontSizes: MarkType[]; - private listTypes: NodeType[]; + private listTypes: (NodeType | any)[]; private editorProps: FieldViewProps & FormattedTextBoxProps; private fontSizeToNum: Map; private fontStylesToName: Map; - private listTypeToIcon: Map; + private listTypeToIcon: Map; //private link: HTMLAnchorElement; private wrapper: HTMLDivElement; private extras: HTMLDivElement; @@ -179,7 +179,8 @@ export class TooltipTextMenu { //list types this.listTypeToIcon = new Map(); this.listTypeToIcon.set(schema.nodes.bullet_list, ":"); - this.listTypeToIcon.set(schema.nodes.ordered_list, "1)"); + this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "decimal" }), "1.1"); + this.listTypeToIcon.set(schema.nodes.ordered_list.create({ mapStyle: "multi" }), "1.A"); // this.listTypeToIcon.set(schema.nodes.bullet_list, "⬜"); this.listTypes = Array.from(this.listTypeToIcon.keys()); @@ -512,10 +513,28 @@ export class TooltipTextMenu { //remove all node typeand apply the passed-in one to the selected text changeToNodeType(nodeType: NodeType | undefined, view: EditorView) { - //remove old - liftListItem(schema.nodes.list_item)(view.state, view.dispatch); - if (nodeType) { //add new + //remove oldif (nodeType) { //add new + if (nodeType === schema.nodes.bullet_list) { wrapInList(nodeType)(view.state, view.dispatch); + } else { + var ref = view.state.selection; + var range = ref.$from.blockRange(ref.$to); + var marks = view.state.storedMarks || (view.state.selection.$to.parentOffset && view.state.selection.$from.marks()); + wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { + const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); + let path = resolvedPos.path; + for (let i = path.length - 1; i > 0; i--) { + if (path[i].type === schema.nodes.ordered_list) { + path[i].attrs.bulletStyle = "indent1"; + path[i].attrs.mapStyle = (nodeType as any).attrs.mapStyle; + break; + } + } + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + + view.dispatch(tx2); + }); } } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e93893586..203227241 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -201,7 +201,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } @observable _forceUpdate = 0; - _lastBox = { x: 0, y: 0, r: 0, b: 0 } + _lastBox = { x: 0, y: 0, r: 0, b: 0 }; @computed get Bounds(): { x: number, y: number, b: number, r: number } { let x = this._forceUpdate; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 04133fb5b..50f03005c 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -432,26 +432,28 @@ class TreeView extends React.Component { } let ascending = Cast(containingCollection.sortAscending, "boolean", null); - if (ascending !== undefined) docs.sort(function (a, b): 1 | -1 { - let descA = ascending ? b : a; - let descB = ascending ? a : b; - let first = descA.title; - let second = descB.title; - // TODO find better way to sort how to sort.................. - if (typeof first === 'number' && typeof second === 'number') { - return (first - second) > 0 ? 1 : -1; - } - if (typeof first === 'string' && typeof second === 'string') { - return first > second ? 1 : -1; - } - if (typeof first === 'boolean' && typeof second === 'boolean') { - // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load - // return Number(descA.x) > Number(descB.x) ? 1 : -1; - // } - return first > second ? 1 : -1; - } - return ascending ? 1 : -1; - }); + if (ascending !== undefined) { + docs.sort(function (a, b): 1 | -1 { + let descA = ascending ? b : a; + let descB = ascending ? a : b; + let first = descA.title; + let second = descB.title; + // TODO find better way to sort how to sort.................. + if (typeof first === 'number' && typeof second === 'number') { + return (first - second) > 0 ? 1 : -1; + } + if (typeof first === 'string' && typeof second === 'string') { + return first > second ? 1 : -1; + } + if (typeof first === 'boolean' && typeof second === 'boolean') { + // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load + // return Number(descA.x) > Number(descB.x) ? 1 : -1; + // } + return first > second ? 1 : -1; + } + return ascending ? 1 : -1; + }); + } let rowWidth = () => panelWidth() - 20; return docs.map((child, i) => { diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 4b3f7c87e..c897af17e 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -53,7 +53,7 @@ export class CollectionViewBaseChrome extends React.Component { this.props.CollectionView.props.Document.panX = 0; this.props.CollectionView.props.Document.panY = 0; this.props.CollectionView.props.Document.scale = 1 }, + immediate: (draggedDocs: Doc[]) => { this.props.CollectionView.props.Document.panX = 0; this.props.CollectionView.props.Document.panY = 0; this.props.CollectionView.props.Document.scale = 1; }, initialize: (button: Doc) => { button.restoredPanX = this.props.CollectionView.props.Document.panX; button.restoredPanY = this.props.CollectionView.props.Document.panY; button.restoredScale = this.props.CollectionView.props.Document.scale; } }; _freeform_commands = [this._contentCommand, this._templateCommand, this._viewCommand]; diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ba558a0b2..36740fc66 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -331,8 +331,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe keymap(buildKeymap(schema)), keymap(baseKeymap), ] - } - }; + }; + } @action rebuildEditor() { @@ -776,7 +776,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe SelectionManager.DeselectAll(); } e.stopPropagation(); - if (e.key === "Tab") { + if (e.key === "Tab" || e.key === "Enter") { // bullets typically change "levels" when tab or enter is used. sometimes backspcae, so maybe that should be added. e.preventDefault(); setTimeout(() => { // force re-rendering of bullet numbers that may have had their bullet labels change. (Our prosemirrior code re-"marks" the changed bullets, but nothing causes the Dom to be re-rendered which is where the nubering takes place) SelectionManager.DeselectAll(); -- cgit v1.2.3-70-g09d2 From 5f39a01998e5ee60eb6b796a5cd6b11fc680ceee Mon Sep 17 00:00:00 2001 From: bob Date: Fri, 30 Aug 2019 13:04:40 -0400 Subject: cleanup of outline mode text --- src/client/util/ProsemirrorExampleTransfer.ts | 96 +++++++++++++-------------- src/client/util/RichTextSchema.tsx | 6 +- src/client/util/TooltipLinkingMenu.tsx | 20 +----- src/client/util/TooltipTextMenu.tsx | 4 +- src/client/util/prosemirrorPatches.js | 62 +++++++++++++++++ 5 files changed, 117 insertions(+), 71 deletions(-) create mode 100644 src/client/util/prosemirrorPatches.js (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 3979d8a49..4af2cf58f 100644 --- a/src/client/util/ProsemirrorExampleTransfer.ts +++ b/src/client/util/ProsemirrorExampleTransfer.ts @@ -2,7 +2,8 @@ import { chainCommands, exitCode, joinDown, joinUp, lift, selectParentNode, setB import { redo, undo } from "prosemirror-history"; import { undoInputRule } from "prosemirror-inputrules"; import { Schema } from "prosemirror-model"; -import { liftListItem, splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; +import { liftListItem, } from "./prosemirrorPatches.js"; +import { splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { TooltipTextMenu } from "./TooltipTextMenu"; @@ -79,75 +80,73 @@ export default function buildKeymap>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - let nodeTypeMark = (depth: number) => depth === 2 ? "indent2" : depth === 4 ? "indent3" : depth === 6 ? "indent4" : "indent1"; + let updateBullets = (tx2: Transaction, refStart: number, delta: number) => { + for (let i = refStart; i > 0; i--) { + let testPos = tx2.doc.nodeAt(i); + if (testPos && testPos.type === schema.nodes.list_item) { + let start = i; + let preve = i > 0 && tx2.doc.nodeAt(start - 1); + if (preve && preve.type === schema.nodes.ordered_list) { + start = start - 1; + } + let rangeStart = tx2.doc.nodeAt(start); + if (rangeStart && rangeStart.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(start, rangeStart.type, { ...rangeStart.attrs, bulletStyle: rangeStart.attrs.bulletStyle + delta }, rangeStart.marks); + } + rangeStart && rangeStart.descendants((node: any, offset: any, index: any) => { + if (node.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(start + offset + 1, node.type, { ...node.attrs, bulletStyle: node.attrs.bulletStyle + delta }, node.marks); + } + }); + break; + } + } + } - let bulletFunc = (state: EditorState, dispatch: (tx: Transaction) => void) => { + bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { var ref = state.selection; var range = ref.$from.blockRange(ref.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - let depth = range && range.depth ? range.depth : 0; if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - const resolvedPos = tx2.doc.resolve(range!.start); - - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); - break; - } - } + updateBullets(tx2, range!.start, 1); marks && tx2.ensureMarks([...marks]); marks && tx2.setStoredMarks([...marks]); dispatch(tx2); - })) { + })) { // couldn't sink into an existing list, so wrap in a new one let sxf = state.tr.setSelection(TextSelection.create(state.doc, range!.start, range!.end)); let newstate = state.applyTransaction(sxf); if (!wrapInList(schema.nodes.ordered_list)(newstate.state, (tx2: Transaction) => { - const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); + for (let i = range!.start; i >= 0; i--) { + let rangeStart = tx2.doc.nodeAt(i); + if (rangeStart && rangeStart.type === schema.nodes.ordered_list) { + tx2.setNodeMarkup(i, rangeStart.type, { ...rangeStart.attrs, bulletStyle: 1 }, rangeStart.marks); break; } } // when promoting to a list, assume list will format things so don't copy the stored marks. - // marks && tx2.ensureMarks([...marks]); - // marks && tx2.setStoredMarks([...marks]); - + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); dispatch(tx2); })) { - console.log("bullet fail"); + console.log("bullet promote fail"); } } - }; - - bind("Tab", bulletFunc); + }); bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - var ref = state.selection; - var range = ref.$from.blockRange(ref.$to); + var range = state.selection.$from.blockRange(state.selection.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - let depth = range && range.depth > 3 ? range.depth - 4 : 0; - liftListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - try { - const resolvedPos = tx2.doc.resolve(Math.round((range!.start + range!.end) / 2)); - - let path = (resolvedPos as any).path; - for (let i = path.length - 1; i > 0; i--) { - if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = nodeTypeMark(depth); - break; - } - } - marks && tx2.ensureMarks([...marks]); - marks && tx2.setStoredMarks([...marks]); - dispatch(tx2); - } catch (e) { - dispatch(tx2); - } - }); + let tr = state.tr; + updateBullets(tr, range!.start, -1); + + if (!liftListItem(schema.nodes.list_item)(tr, (tx2: Transaction) => { + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + dispatch(tx2); + })) { + console.log("bullet demote fail"); + } }); bind("Enter", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -160,8 +159,7 @@ export default function buildKeymap>(schema: S, mapKeys?: if (!splitBlockKeepMarks(state, (tx3: Transaction) => { marks && tx3.ensureMarks(marks); marks && tx3.setStoredMarks(marks); - if (!liftListItem(schema.nodes.list_item)(state, dispatch as ((tx: Transaction>) => void)) - ) { + if (!liftListItem(schema.nodes.list_item)(tx3, dispatch as ((tx: Transaction>) => void))) { dispatch(tx3); } })) { diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index a642ee46c..0994f5f6b 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -176,13 +176,13 @@ export const nodes: { [index: string]: NodeSpec } = { content: 'list_item+', group: 'block', attrs: { - bulletStyle: { default: "" }, + bulletStyle: { default: 0 }, mapStyle: { default: "decimal" } }, toDOM(node: Node) { const bs = node.attrs.bulletStyle; - const decMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "decimal2" : bs === "indent3" ? "decimal3" : bs === "indent4" ? "decimal4" : ""; - const multiMap = bs === "indent1" ? "decimal" : bs === "indent2" ? "upper-alpha" : bs === "indent3" ? "lower-roman" : bs === "indent4" ? "lower-alpha" : ""; + const decMap = bs === 1 ? "decimal" : bs === 2 ? "decimal2" : bs === 3 ? "decimal3" : bs === 4 ? "decimal4" : ""; + const multiMap = bs === 1 ? "decimal" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : ""; let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap; for (let i = 0; i < node.childCount; i++) node.child(i).attrs.className = map; return ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0]; diff --git a/src/client/util/TooltipLinkingMenu.tsx b/src/client/util/TooltipLinkingMenu.tsx index 55e0eb909..e6d6c471f 100644 --- a/src/client/util/TooltipLinkingMenu.tsx +++ b/src/client/util/TooltipLinkingMenu.tsx @@ -1,23 +1,9 @@ -import { action, IReactionDisposer, reaction } from "mobx"; -import { Dropdown, DropdownSubmenu, MenuItem, MenuItemSpec, renderGrouped, icons, } from "prosemirror-menu"; //no import css -import { baseKeymap, lift } from "prosemirror-commands"; -import { history, redo, undo } from "prosemirror-history"; -import { keymap } from "prosemirror-keymap"; -import { EditorState, Transaction, NodeSelection, TextSelection } from "prosemirror-state"; +import { EditorState } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { schema } from "./RichTextSchema"; -import { Schema, NodeType, MarkType } from "prosemirror-model"; -import React = require("react"); +import { FieldViewProps } from "../views/nodes/FieldView"; import "./TooltipTextMenu.scss"; +import React = require("react"); const { toggleMark, setBlockType, wrapIn } = require("prosemirror-commands"); -import { library } from '@fortawesome/fontawesome-svg-core'; -import { wrapInList, bulletList, liftListItem, listItem } from 'prosemirror-schema-list'; -import { - faListUl, -} from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { FieldViewProps } from "../views/nodes/FieldView"; -import { throwStatement } from "babel-types"; const SVG = "http://www.w3.org/2000/svg"; diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index e979e6cde..3b4b7f05a 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -3,7 +3,7 @@ import { faListUl } from '@fortawesome/free-solid-svg-icons'; import { action, observable } from "mobx"; import { Dropdown, icons, MenuItem } from "prosemirror-menu"; //no import css import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos, Schema } from "prosemirror-model"; -import { liftListItem, wrapInList } from 'prosemirror-schema-list'; +import { wrapInList } from 'prosemirror-schema-list'; import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Field, Opt } from "../../new_fields/Doc"; @@ -525,7 +525,7 @@ export class TooltipTextMenu { let path = resolvedPos.path; for (let i = path.length - 1; i > 0; i--) { if (path[i].type === schema.nodes.ordered_list) { - path[i].attrs.bulletStyle = "indent1"; + path[i].attrs.bulletStyle = 1; path[i].attrs.mapStyle = (nodeType as any).attrs.mapStyle; break; } diff --git a/src/client/util/prosemirrorPatches.js b/src/client/util/prosemirrorPatches.js new file mode 100644 index 000000000..c273c2323 --- /dev/null +++ b/src/client/util/prosemirrorPatches.js @@ -0,0 +1,62 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var prosemirrorTransform = require('prosemirror-transform'); +var prosemirrorModel = require('prosemirror-model'); + +exports.liftListItem = liftListItem; +// :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool +// Create a command to lift the list item around the selection up into +// a wrapping list. +function liftListItem(itemType) { + return function (tx, dispatch) { + var ref = tx.selection; + var $from = ref.$from; + var $to = ref.$to; + var range = $from.blockRange($to, function (node) { return node.childCount && node.firstChild.type == itemType; }); + if (!range) { return false } + if (!dispatch) { return true } + if ($from.node(range.depth - 1).type == itemType) // Inside a parent list + { return liftToOuterList(tx, dispatch, itemType, range) } + else // Outer list node + { return liftOutOfList(tx, dispatch, range) } + } +} + +function liftToOuterList(tr, dispatch, itemType, range) { + var end = range.end, endOfList = range.$to.end(range.depth); + if (end < endOfList) { + // There are siblings after the lifted items, which must become + // children of the last item + tr.step(new prosemirrorTransform.ReplaceAroundStep(end - 1, endOfList, end, endOfList, + new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, range.parent.copy())), 1, 0), 1, true)); + range = new prosemirrorModel.NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth); + } + dispatch(tr.lift(range, prosemirrorTransform.liftTarget(range)).scrollIntoView()); + return true +} + +function liftOutOfList(tr, dispatch, range) { + var list = range.parent; + // Merge the list items into a single big item + for (var pos = range.end, i = range.endIndex - 1, e = range.startIndex; i > e; i--) { + pos -= list.child(i).nodeSize; + tr.delete(pos - 1, pos + 1); + } + var $start = tr.doc.resolve(range.start), item = $start.nodeAfter; + var atStart = range.startIndex == 0, atEnd = range.endIndex == list.childCount; + var parent = $start.node(-1), indexBefore = $start.index(-1); + if (!parent.canReplace(indexBefore + (atStart ? 0 : 1), indexBefore + 1, + item.content.append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list)))) { return false } + var start = $start.pos, end = start + item.nodeSize; + // Strip off the surrounding list. At the sides where we're not at + // the end of the list, the existing list is closed. At sides where + // this is the end, it is overwritten to its end. + tr.step(new prosemirrorTransform.ReplaceAroundStep(start - (atStart ? 1 : 0), end + (atEnd ? 1 : 0), start + 1, end - 1, + new prosemirrorModel.Slice((atStart ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))) + .append(atEnd ? prosemirrorModel.Fragment.empty : prosemirrorModel.Fragment.from(list.copy(prosemirrorModel.Fragment.empty))), + atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1)); + dispatch(tr.scrollIntoView()); + return true +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 4a1d94325d5d1b5641cd280e89c442c114074e8d Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 3 Sep 2019 08:03:43 -0400 Subject: cleanup names for formattedTextBoxComment stuff. fixed brushing bug with user_mark. --- src/client/util/RichTextSchema.tsx | 2 +- src/client/util/TooltipTextMenu.tsx | 6 +- src/client/views/nodes/FormattedTextBox.tsx | 12 ++-- src/client/views/nodes/FormattedTextBoxComment.tsx | 70 +++++++++++----------- 4 files changed, 41 insertions(+), 49 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 3161467b8..7911cf629 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -346,7 +346,7 @@ export const marks: { [index: string]: MarkSpec } = { return hidden ? (node.attrs.opened ? ['span', { class: "userMarkOpen" }, 0] : - ['span', { class: "userMark" }, ['span', { style: "font-size:2" }, 0]] + ['span', { class: "userMark" }, ['span', 0]] ) : ['span', 0]; } diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 3b4b7f05a..ce7b04e31 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -60,8 +60,6 @@ export class TooltipTextMenu { @observable private _storedMarks: Mark[] | null | undefined; - public HackToFixTextSelectionGlitch: boolean = false; - constructor(view: EditorView, editorProps: FieldViewProps & FormattedTextBoxProps) { this.view = view; @@ -629,7 +627,7 @@ export class TooltipTextMenu { if (!this.view.state.selection.empty && $from && $from.nodeAfter) { if (this._brushMarks && to - from > 0) { this.view.dispatch(this.view.state.tr.removeMark(from, to)); - this._brushMarks.forEach((mark: Mark) => { + Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { const markType = mark.type; this.changeToMarkInGroup(markType, this.view, []); @@ -876,8 +874,6 @@ export class TooltipTextMenu { this.updateFontSizeDropdown("Various"); } } - !this.HackToFixTextSelectionGlitch && - this.view.dispatch(this.view.state.tr.setStoredMarks(this._activeMarks)); // bcz: what's the purpose of this line? It messes up text selection without the Hack. this.update_mark_doms(); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 93ac2f06f..6232dd3ab 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -37,8 +37,7 @@ import { DocumentDecorations } from '../DocumentDecorations'; import { DictationManager } from '../../util/DictationManager'; import { ReplaceStep } from 'prosemirror-transform'; import { DocumentType } from '../../documents/DocumentTypes'; -import { selectionSizePlugin, findStartOfMark, findUserMark, findEndOfMark, findOtherUserMark, SelectionSizeTooltip } from './FormattedTextBoxComment'; -import { date } from 'serializr'; +import { formattedTextBoxCommentPlugin, FormattedTextBoxComment } from './FormattedTextBoxComment'; library.add(faEdit); library.add(faSmile, faTextHeight, faUpload); @@ -172,9 +171,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe dispatchTransaction = (tx: Transaction) => { if (this._editorView) { const state = this._editorView.state.apply(tx); - FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = true); this._editorView.updateState(state); - FormattedTextBox._toolTipTextMenu && (FormattedTextBox._toolTipTextMenu.HackToFixTextSelectionGlitch = false); if (state.selection.empty && FormattedTextBox._toolTipTextMenu && tx.storedMarks) { FormattedTextBox._toolTipTextMenu.mark_key_pressed(tx.storedMarks); } @@ -327,7 +324,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe attributes: { class: "ProseMirror-example-setup-style" } } }), - selectionSizePlugin + formattedTextBoxCommentPlugin ] : [ history(), keymap(buildKeymap(schema)), @@ -713,7 +710,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } onPointerUp = (e: React.PointerEvent): void => { - SelectionSizeTooltip.textBox = this; + FormattedTextBoxComment.textBox = this; if (e.buttons === 1 && this.props.isSelected() && !e.altKey) { e.stopPropagation(); } @@ -805,8 +802,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } if (m < 10) m = '0' + m; - if (s < 10) s = '0' + s; - return now.toLocaleDateString() + ' ' + h + ':' + m + ':' + s + ' ' + ampm; + return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm; } var markerss = this._editorView!.state.storedMarks || (this._editorView!.state.selection.$to.parentOffset && this._editorView!.state.selection.$from.marks()); let newMarks = [...(markerss ? markerss.filter(m => m.type !== schema.marks.user_mark) : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })]; diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx index c368a3f34..255000936 100644 --- a/src/client/views/nodes/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/FormattedTextBoxComment.tsx @@ -8,8 +8,8 @@ import { DocServer } from "../../DocServer"; import { Utils } from "../../../Utils"; import { StrCast } from "../../../new_fields/Types"; -export let selectionSizePlugin = new Plugin({ - view(editorView) { return new SelectionSizeTooltip(editorView); } +export let formattedTextBoxCommentPlugin = new Plugin({ + view(editorView) { return new FormattedTextBoxComment(editorView); } }); export function findOtherUserMark(marks: Mark[]): Mark | undefined { return marks.find(m => m.attrs.userid && m.attrs.userid !== Doc.CurrentUserEmail); @@ -42,7 +42,7 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark } -export class SelectionSizeTooltip { +export class FormattedTextBoxComment { static tooltip: HTMLElement; static tooltipText: HTMLElement; static start: number; @@ -51,39 +51,39 @@ export class SelectionSizeTooltip { static opened: boolean; static textBox: any; constructor(view: any) { - if (!SelectionSizeTooltip.tooltip) { + if (!FormattedTextBoxComment.tooltip) { const root = document.getElementById("root"); let input = document.createElement("input"); input.type = "checkbox"; - SelectionSizeTooltip.tooltip = document.createElement("div"); - SelectionSizeTooltip.tooltipText = document.createElement("div"); - SelectionSizeTooltip.tooltip.appendChild(SelectionSizeTooltip.tooltipText); - SelectionSizeTooltip.tooltip.className = "FormattedTextBox-tooltip"; - SelectionSizeTooltip.tooltip.style.pointerEvents = "all"; - SelectionSizeTooltip.tooltip.appendChild(input); - SelectionSizeTooltip.tooltip.onpointerdown = (e: PointerEvent) => { + FormattedTextBoxComment.tooltip = document.createElement("div"); + FormattedTextBoxComment.tooltipText = document.createElement("div"); + FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip"; + FormattedTextBoxComment.tooltip.style.pointerEvents = "all"; + FormattedTextBoxComment.tooltip.appendChild(input); + FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => { let keep = e.target && (e.target as any).type === "checkbox"; - SelectionSizeTooltip.opened = keep || !SelectionSizeTooltip.opened; - SelectionSizeTooltip.textBox && SelectionSizeTooltip.textBox.setAnnotation( - SelectionSizeTooltip.start, SelectionSizeTooltip.end, SelectionSizeTooltip.mark, - SelectionSizeTooltip.opened, keep); + FormattedTextBoxComment.opened = keep || !FormattedTextBoxComment.opened; + FormattedTextBoxComment.textBox && FormattedTextBoxComment.textBox.setAnnotation( + FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark, + FormattedTextBoxComment.opened, keep); }; - root && root.appendChild(SelectionSizeTooltip.tooltip); + root && root.appendChild(FormattedTextBoxComment.tooltip); } this.update(view, undefined); } public static Hide() { - SelectionSizeTooltip.textBox = undefined; - SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = "none"); + FormattedTextBoxComment.textBox = undefined; + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); } public static SetState(textBox: any, opened: boolean, start: number, end: number, mark: Mark) { - SelectionSizeTooltip.textBox = textBox; - SelectionSizeTooltip.start = start; - SelectionSizeTooltip.end = end; - SelectionSizeTooltip.mark = mark; - SelectionSizeTooltip.opened = opened; - SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = ""); + FormattedTextBoxComment.textBox = textBox; + FormattedTextBoxComment.start = start; + FormattedTextBoxComment.end = end; + FormattedTextBoxComment.mark = mark; + FormattedTextBoxComment.opened = opened; + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = ""); } update(view: EditorView, lastState?: EditorState) { @@ -102,10 +102,10 @@ export class SelectionSizeTooltip { let mark = child && findOtherUserMark(child.marks); let noselection = view.state.selection.$from === view.state.selection.$to; if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) { - SelectionSizeTooltip.SetState(this, mark.attrs.opened, spos, epos, mark); + FormattedTextBoxComment.SetState(this, mark.attrs.opened, spos, epos, mark); } if (mark && child && nbef && naft) { - SelectionSizeTooltip.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified; + FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " " + mark.attrs.modified; // These are in screen coordinates // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to); let start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); @@ -114,23 +114,23 @@ export class SelectionSizeTooltip { // Find a center-ish x position from the selection endpoints (when // crossing lines, end may be more to the left) let left = Math.max((start.left + end.left) / 2, start.left + 3); - SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px"; - SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px"; + FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; + FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; set = ""; } } if (set === "none" && state.selection.$from) { - SelectionSizeTooltip.textBox = undefined; + FormattedTextBoxComment.textBox = undefined; let nbef = findStartOfMark(state.selection.$from, view, findLinkMark); let naft = findEndOfMark(state.selection.$from, view, findLinkMark); let child = state.selection.$from.nodeBefore; let mark = child && findLinkMark(child.marks); if (mark && child && nbef && naft) { - SelectionSizeTooltip.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href); + FormattedTextBoxComment.tooltipText.textContent = "link : " + (mark.attrs.title || mark.attrs.href); if (mark.attrs.href.indexOf(Utils.prepend("/doc/")) === 0) { let docTarget = mark.attrs.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; docTarget && DocServer.GetRefField(docTarget).then(linkDoc => - (linkDoc as Doc) && (SelectionSizeTooltip.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc)!.title))); + (linkDoc as Doc) && (FormattedTextBoxComment.tooltipText.textContent = "link :" + StrCast((linkDoc as Doc)!.title))); } // These are in screen coordinates // let start = view.coordsAtPos(state.selection.from), end = view.coordsAtPos(state.selection.to); @@ -140,13 +140,13 @@ export class SelectionSizeTooltip { // Find a center-ish x position from the selection endpoints (when // crossing lines, end may be more to the left) let left = Math.max((start.left + end.left) / 2, start.left + 3); - SelectionSizeTooltip.tooltip.style.left = (left - box.left) + "px"; - SelectionSizeTooltip.tooltip.style.bottom = (box.bottom - start.top) + "px"; + FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; + FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; set = ""; } } - SelectionSizeTooltip.tooltip && (SelectionSizeTooltip.tooltip.style.display = set); + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = set); } - destroy() { SelectionSizeTooltip.tooltip.style.display = "none"; } + destroy() { FormattedTextBoxComment.tooltip.style.display = "none"; } } -- cgit v1.2.3-70-g09d2