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/TooltipTextMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') 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; } -- 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 From 2262d7562b57c37c238fc8a7c373304984a5fe3b Mon Sep 17 00:00:00 2001 From: bob Date: Wed, 4 Sep 2019 17:49:19 -0400 Subject: prosemirror restructuring. --- src/client/util/ProsemirrorExampleTransfer.ts | 71 +++++----------------- src/client/util/RichTextSchema.tsx | 71 +++++++++++----------- src/client/util/TooltipTextMenu.tsx | 37 ++++++----- src/client/util/prosemirrorPatches.js | 30 +++++++++ src/client/views/OverlayView.tsx | 2 +- src/client/views/nodes/FormattedTextBox.tsx | 6 +- src/client/views/nodes/FormattedTextBoxComment.tsx | 4 +- src/client/views/pdf/Page.tsx | 2 +- 8 files changed, 107 insertions(+), 116 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/ProsemirrorExampleTransfer.ts b/src/client/util/ProsemirrorExampleTransfer.ts index 5016e72c6..9c4b2d3d1 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, } from "./prosemirrorPatches.js"; -import { splitListItem, wrapInList, sinkListItem } from "prosemirror-schema-list"; +import { liftListItem, sinkListItem } from "./prosemirrorPatches.js"; +import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection, NodeSelection } from "prosemirror-state"; import { TooltipTextMenu } from "./TooltipTextMenu"; @@ -62,7 +62,7 @@ export default function buildKeymap>(schema: S, mapKeys?: return true; } return false; - }) + }); let cmd = chainCommands(exitCode, (state, dispatch) => { @@ -93,54 +93,16 @@ export default function buildKeymap>(schema: S, mapKeys?: bind("Mod-s", TooltipTextMenu.insertStar); - let updateOrderedList = (start: number, rangeStart: any, delta: number, tx2: Transaction, forward: boolean) => { - let bs = rangeStart.attrs.bulletStyle; - bs + delta > 0 && tx2.setNodeMarkup(start, rangeStart.type, { mapStyle: rangeStart.attrs.mapStyle, bulletStyle: rangeStart.attrs.bulletStyle + delta }, rangeStart.marks); - - let brk = false; - rangeStart.descendants((node: any, offset: any, index: any) => { + let updateBullets = (tx2: Transaction) => { + tx2.doc.descendants((node: any, offset: any, index: any) => { if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) { - if (!brk && (bs !== node.attrs.bulletStyle || delta > 0 || (forward && bs > 1))) { - tx2.setNodeMarkup(start + offset + 1, node.type, { mapStyle: node.attrs.mapStyle, bulletStyle: node.attrs.bulletStyle + delta }, node.marks); - } else { - brk = true; - } + let path = (tx2.doc.resolve(offset) as any).path; + let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && (c as any).type === schema.nodes.ordered_list ? 1 : 0), 0); + if (node.type === schema.nodes.ordered_list) depth++; + tx2.setNodeMarkup(offset, node.type, { mapStyle: node.attrs.mapStyle, bulletStyle: depth }, node.marks); } }); - } - - let updateBullets = (tx2: Transaction, refStart: number, delta: number) => { - let i = refStart; - for (let i = refStart; i >= 0; i--) { - let testPos = tx2.doc.nodeAt(i); - if (!testPos) { - for (let i = refStart + 1; i <= tx2.doc.nodeSize; i++) { - try { - let testPos = tx2.doc.nodeAt(i); - if (testPos && testPos.type === schema.nodes.ordered_list) { - updateOrderedList(i, testPos, delta, tx2, true); - break; - } - } catch (e) { - break; - } - } - break; - } - if ((testPos.type === schema.nodes.list_item || testPos.type === schema.nodes.ordered_list)) { - 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) { - updateOrderedList(start, rangeStart, delta, tx2, false); - } - break; - } - } - } + }; bind("Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { @@ -148,8 +110,7 @@ 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()); if (!sinkListItem(schema.nodes.list_item)(state, (tx2: Transaction) => { - var range = state.selection.$from.blockRange(state.selection.$to); - updateBullets(tx2, range!.start - 1, 1); + updateBullets(tx2); marks && tx2.ensureMarks([...marks]); marks && tx2.setStoredMarks([...marks]); dispatch(tx2); @@ -157,7 +118,7 @@ export default function buildKeymap>(schema: S, mapKeys?: 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) => { - updateBullets(tx2, range!.start, 1); + updateBullets(tx2); // 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]); @@ -169,14 +130,10 @@ export default function buildKeymap>(schema: S, mapKeys?: }); bind("Shift-Tab", (state: EditorState, dispatch: (tx: Transaction) => void) => { - var range = state.selection.$from.blockRange(state.selection.$to); var marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks()); - let tr = state.tr; - - if (!liftListItem(schema.nodes.list_item)(tr, (tx2: Transaction) => { - var range = tx2.selection.$from.blockRange(tx2.selection.$to); - updateBullets(tx2, range!.start, -1); + if (!liftListItem(schema.nodes.list_item)(state.tr, (tx2: Transaction) => { + updateBullets(tx2); marks && tx2.ensureMarks([...marks]); marks && tx2.setStoredMarks([...marks]); dispatch(tx2); diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 75e982872..3174d668c 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -597,8 +597,6 @@ export class ImageResizeView { } export class OrderedListView { - constructor(node: any, view: any, getPos: any) { } - update(node: any) { return false; // if attr's of an ordered_list (e.g., bulletStyle) change, return false forces the dom node to be recreated which is necessary for the bullet labels to update } @@ -613,33 +611,33 @@ export class FootnoteView { constructor(node: any, view: any, getPos: any) { // We'll need these later - this.node = node - this.outerView = view - this.getPos = getPos + this.node = node; + this.outerView = view; + this.getPos = getPos; // The node's representation in the editor (empty, for now) this.dom = document.createElement("footnote"); this.dom.addEventListener("pointerup", this.toggle, true); // These are used when the footnote is selected - this.innerView = null + this.innerView = null; } selectNode() { const attrs = { ...this.node.attrs }; attrs.visibility = true; - this.dom.classList.add("ProseMirror-selectednode") - if (!this.innerView) this.open() + this.dom.classList.add("ProseMirror-selectednode"); + if (!this.innerView) this.open(); } deselectNode() { const attrs = { ...this.node.attrs }; attrs.visibility = false; - this.dom.classList.remove("ProseMirror-selectednode") - if (this.innerView) this.close() + this.dom.classList.remove("ProseMirror-selectednode"); + if (this.innerView) this.close(); } open() { - if (!(this.outerView as any).isOverlay) return; + if (!this.outerView.isOverlay) return; // Append a tooltip to the outer node - let tooltip = this.dom.appendChild(document.createElement("div")) + let tooltip = this.dom.appendChild(document.createElement("div")); tooltip.className = "footnote-tooltip"; // And put a sub-ProseMirror into that this.innerView = new EditorView(tooltip, { @@ -679,56 +677,55 @@ export class FootnoteView { if (this.innerView) this.close(); else { this.open(); - } } close() { - this.innerView && this.innerView.destroy() - this.innerView = null - this.dom.textContent = "" + this.innerView && this.innerView.destroy(); + this.innerView = null; + this.dom.textContent = ""; } dispatchInner(tr: any) { - let { state, transactions } = this.innerView.state.applyTransaction(tr) - this.innerView.updateState(state) + let { state, transactions } = this.innerView.state.applyTransaction(tr); + this.innerView.updateState(state); if (!tr.getMeta("fromOutside")) { - let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1) - for (let i = 0; i < transactions.length; i++) { - let steps = transactions[i].steps - for (let j = 0; j < steps.length; j++) - outerTr.step(steps[j].map(offsetMap)) + let outerTr = this.outerView.state.tr, offsetMap = StepMap.offset(this.getPos() + 1); + for (let steps of transactions) { + for (let step of steps) { + outerTr.step(step.map(offsetMap)); + } } - if (outerTr.docChanged) this.outerView.dispatch(outerTr) + if (outerTr.docChanged) this.outerView.dispatch(outerTr); } } update(node: any) { - if (!node.sameMarkup(this.node)) return false - this.node = node + if (!node.sameMarkup(this.node)) return false; + this.node = node; if (this.innerView) { - let state = this.innerView.state - let start = node.content.findDiffStart(state.doc.content) - if (start != null) { - let { a: endA, b: endB } = node.content.findDiffEnd(state.doc.content) - let overlap = start - Math.min(endA, endB) - if (overlap > 0) { endA += overlap; endB += overlap } + let state = this.innerView.state; + let start = node.content.findDiffStart(state.doc.content); + if (start !== null) { + let { a: endA, b: endB } = node.content.findDiffEnd(state.doc.content); + let overlap = start - Math.min(endA, endB); + if (overlap > 0) { endA += overlap; endB += overlap; } this.innerView.dispatch( state.tr .replace(start, endB, node.slice(start, endA)) - .setMeta("fromOutside", true)) + .setMeta("fromOutside", true)); } } - return true + return true; } destroy() { - if (this.innerView) this.close() + if (this.innerView) this.close(); } stopEvent(event: any) { - return this.innerView && this.innerView.dom.contains(event.target) + return this.innerView && this.innerView.dom.contains(event.target); } - ignoreMutation() { return true } + ignoreMutation() { return true; } } export class SummarizedView { diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index ce7b04e31..6d375fc1d 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -4,7 +4,7 @@ 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 { wrapInList } from 'prosemirror-schema-list'; -import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; +import { EditorState, NodeSelection, TextSelection, Transaction } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc, Field, Opt } from "../../new_fields/Doc"; import { Id } from "../../new_fields/FieldSymbols"; @@ -509,30 +509,37 @@ export class TooltipTextMenu { } } + updateBullets = (tx2: Transaction, style: string) => { + tx2.doc.descendants((node: any, offset: any, index: any) => { + if (node.type === schema.nodes.ordered_list || node.type === schema.nodes.list_item) { + let path = (tx2.doc.resolve(offset) as any).path; + let depth = Array.from(path).reduce((p: number, c: any) => p + (c.hasOwnProperty("type") && (c as any).type === schema.nodes.ordered_list ? 1 : 0), 0); + if (node.type === schema.nodes.ordered_list) depth++; + tx2.setNodeMarkup(offset, node.type, { mapStyle: style, bulletStyle: depth }, node.marks); + } + }); + }; //remove all node typeand apply the passed-in one to the selected text - changeToNodeType(nodeType: NodeType | undefined, view: EditorView) { + changeToNodeType = (nodeType: NodeType | undefined, view: EditorView) => { //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 = 1; - path[i].attrs.mapStyle = (nodeType as any).attrs.mapStyle; - break; - } - } + if (!wrapInList(schema.nodes.ordered_list)(view.state, (tx2: any) => { + this.updateBullets(tx2, (nodeType as any).attrs.mapStyle); marks && tx2.ensureMarks([...marks]); marks && tx2.setStoredMarks([...marks]); view.dispatch(tx2); - }); + })) { + let tx2 = view.state.tr; + this.updateBullets(tx2, (nodeType as any).attrs.mapStyle); + marks && tx2.ensureMarks([...marks]); + marks && tx2.setStoredMarks([...marks]); + + view.dispatch(tx2); + } } } diff --git a/src/client/util/prosemirrorPatches.js b/src/client/util/prosemirrorPatches.js index c273c2323..6bf4395ad 100644 --- a/src/client/util/prosemirrorPatches.js +++ b/src/client/util/prosemirrorPatches.js @@ -6,6 +6,7 @@ var prosemirrorTransform = require('prosemirror-transform'); var prosemirrorModel = require('prosemirror-model'); exports.liftListItem = liftListItem; +exports.sinkListItem = sinkListItem; // :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool // Create a command to lift the list item around the selection up into // a wrapping list. @@ -59,4 +60,33 @@ function liftOutOfList(tr, dispatch, range) { atStart ? 0 : 1, atEnd ? 0 : 1), atStart ? 0 : 1)); dispatch(tr.scrollIntoView()); return true +} + +// :: (NodeType) → (state: EditorState, dispatch: ?(tr: Transaction)) → bool +// Create a command to sink the list item around the selection down +// into an inner list. +function sinkListItem(itemType) { + return function (state, dispatch) { + var ref = state.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 } + var startIndex = range.startIndex; + if (startIndex == 0) { return false } + var parent = range.parent, nodeBefore = parent.child(startIndex - 1); + if (nodeBefore.type != itemType) { return false; } + + if (dispatch) { + var nestedBefore = nodeBefore.lastChild && nodeBefore.lastChild.type == parent.type; + var inner = prosemirrorModel.Fragment.from(nestedBefore ? itemType.create() : null); + let slice = new prosemirrorModel.Slice(prosemirrorModel.Fragment.from(itemType.create(null, prosemirrorModel.Fragment.from(parent.type.create(parent.attrs, inner)))), + nestedBefore ? 3 : 1, 0); + var before = range.start, after = range.end; + dispatch(state.tr.step(new prosemirrorTransform.ReplaceAroundStep(before - (nestedBefore ? 3 : 1), after, + before, after, slice, 1, true)) + .scrollIntoView()); + } + return true + } } \ No newline at end of file diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 54ab8696e..fe06e4440 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -191,7 +191,7 @@ export class OverlayView extends React.Component { zoomToScale={emptyFunction} getScale={returnOne} /> ; - }) + }); } render() { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 4cb52ec78..f9ad040a2 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -174,7 +174,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe syncNodeSelection(view: any, sel: any) { if (sel instanceof NodeSelection) { var desc = view.docView.descAt(sel.from); - if (desc != view.lastSelectedViewDesc) { + if (desc !== view.lastSelectedViewDesc) { if (view.lastSelectedViewDesc) { view.lastSelectedViewDesc.deselectNode(); view.lastSelectedViewDesc = null; @@ -635,8 +635,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe nodeViews: { image(node, view, getPos) { return new ImageResizeView(node, view, getPos); }, star(node, view, getPos) { return new SummarizedView(node, view, getPos); }, - ordered_list(node, view, getPos) { return new OrderedListView(node, view, getPos); }, - footnote(node, view, getPos) { return new FootnoteView(node, view, getPos) } + ordered_list(node, view, getPos) { return new OrderedListView(); }, + footnote(node, view, getPos) { return new FootnoteView(node, view, getPos); } }, clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, diff --git a/src/client/views/nodes/FormattedTextBoxComment.tsx b/src/client/views/nodes/FormattedTextBoxComment.tsx index 255000936..2d2fff06d 100644 --- a/src/client/views/nodes/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/FormattedTextBoxComment.tsx @@ -92,7 +92,7 @@ export class FormattedTextBoxComment { if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) return; - let set = "none" + let set = "none"; if (state.selection.$from) { let nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); let naft = findEndOfMark(state.selection.$from, view, findOtherUserMark); @@ -130,7 +130,7 @@ export class FormattedTextBoxComment { 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) && (FormattedTextBoxComment.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); diff --git a/src/client/views/pdf/Page.tsx b/src/client/views/pdf/Page.tsx index 8df2dce29..856e883e7 100644 --- a/src/client/views/pdf/Page.tsx +++ b/src/client/views/pdf/Page.tsx @@ -64,7 +64,7 @@ export default class Page extends React.Component { // lower scale = easier to read at small sizes, higher scale = easier to read at large sizes if (this._state !== "rendering" && !this._page && this._canvas.current && this._textLayer.current) { this._state = "rendering"; - let viewport = page.getViewport({ scale: scale }); + let viewport = page.getViewport(scale); this._canvas.current.width = this._width = viewport.width; this._canvas.current.height = this._height = viewport.height; this.props.pageLoaded(viewport); -- cgit v1.2.3-70-g09d2 From e5b52b1083c86b946a2758983e397826579d2282 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 6 Sep 2019 01:41:06 -0400 Subject: fixed summarizing in text notes. --- src/Utils.ts | 14 +++ src/client/util/RichTextSchema.tsx | 157 ++++++++++++--------------- src/client/util/TooltipTextMenu.tsx | 18 ++- src/client/views/nodes/FormattedTextBox.scss | 20 ++++ src/client/views/nodes/FormattedTextBox.tsx | 41 ++----- src/new_fields/Doc.ts | 2 +- 6 files changed, 120 insertions(+), 132 deletions(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 959b89fe5..f805ae872 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -133,6 +133,20 @@ export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => vo return dup; } +export function timenow() { + var now = new Date(); + let ampm = 'am'; + let h = now.getHours(); + let m: any = now.getMinutes(); + let s: any = now.getSeconds(); + if (h >= 12) { + if (h > 12) h -= 12; + ampm = 'pm'; + } + if (m < 10) m = '0' + m; + return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm; +} + export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); } export function returnTrue() { return true; } diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 2b689b7ef..a8ba0a4be 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -1,4 +1,4 @@ -import { DOMOutputSpecArray, MarkSpec, Node, NodeSpec, Schema, Slice } from "prosemirror-model"; +import { DOMOutputSpecArray, MarkSpec, Node, NodeSpec, Schema, Slice, Fragment } from "prosemirror-model"; import { bulletList, listItem, orderedList } from 'prosemirror-schema-list'; import { TextSelection, EditorState } from "prosemirror-state"; import { Doc } from "../../new_fields/Doc"; @@ -7,6 +7,8 @@ import { EditorView } from "prosemirror-view"; import { keymap } from "prosemirror-keymap"; import { undo, redo } from "prosemirror-history"; import { toggleMark, splitBlock, selectAll, baseKeymap } from "prosemirror-commands"; +import { Domain } from "domain"; +import { DOM } from "@fortawesome/fontawesome-svg-core"; const pDOM: DOMOutputSpecArray = ["p", 0], blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -42,14 +44,6 @@ export const nodes: { [index: string]: NodeSpec } = { toDOM() { return pDOM; } }, - // starmine: { - // inline: true, - // attrs: { oldtext: { default: "" } }, - // group: "inline", - // toDOM() { return ["star", "㊉"]; }, - // parseDOM: [{ tag: "star" }] - // }, - // :: NodeSpec A blockquote (`
`) wrapping one or more blocks. blockquote: { content: "block+", @@ -107,10 +101,9 @@ export const nodes: { [index: string]: NodeSpec } = { visibility: { default: false }, text: { default: undefined }, textslice: { default: undefined }, - textlen: { default: 0 } - }, group: "inline", + inclusive: false, toDOM(node) { const attrs = { style: `width: 40px` }; return ["span", { ...node.attrs, ...attrs }]; @@ -205,12 +198,10 @@ export const nodes: { [index: string]: NodeSpec } = { const multiMap = bs === 1 ? "decimal1" : bs === 2 ? "upper-alpha" : bs === 3 ? "lower-roman" : bs === 4 ? "lower-alpha" : ""; let map = node.attrs.mapStyle === "decimal" ? decMap : multiMap; return node.attrs.visibility ? ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0] : - ['ol', { class: `${map}-ol`, style: `list-style: none;` }]; - //return node.attrs.bulletStyle < 2 ? ['ol', { class: `${map}-ol`, style: `list-style: none;` }, 0] : - // ['ol', { class: `${node.attrs.bulletStyle}`, style: `list-style: ${node.attrs.bulletStyle}; font-size: 5px` }, "hello"]; + ['ol', { class: `${map}-ol`, style: `list-style: none;` }]; } }, - //this doesn't currently work for some reason + bullet_list: { ...bulletList, content: 'list_item+', @@ -221,14 +212,6 @@ export const nodes: { [index: string]: NodeSpec } = { } }, - //bullet_list: { - // content: 'list_item+', - // group: 'block', - //active: blockActive(schema.nodes.bullet_list), - //enable: wrapInList(schema.nodes.bullet_list), - //run: wrapInList(schema.nodes.bullet_list), - //select: state => true, - // }, list_item: { attrs: { bulletStyle: { default: 0 }, @@ -251,7 +234,6 @@ export const nodes: { [index: string]: NodeSpec } = { const emDOM: DOMOutputSpecArray = ["em", 0]; const strongDOM: DOMOutputSpecArray = ["strong", 0]; const codeDOM: DOMOutputSpecArray = ["code", 0]; -const underlineDOM: DOMOutputSpecArray = ["underline", 0]; // :: Object [Specs](#model.MarkSpec) for the marks in the schema. export const marks: { [index: string]: MarkSpec } = { @@ -289,16 +271,6 @@ export const marks: { [index: string]: MarkSpec } = { toDOM() { return strongDOM; } }, - underline: { - parseDOM: [ - { tag: 'u' }, - { style: 'text-decoration=underline' } - ], - toDOM: () => ['span', { - style: 'text-decoration:underline' - }] - }, - strikethrough: { parseDOM: [ { tag: 'strike' }, @@ -340,14 +312,52 @@ export const marks: { [index: string]: MarkSpec } = { }, highlight: { - parseDOM: [{ style: 'text-decoration: underline' }], + parseDOM: [ + { + tag: "span", + getAttrs: (p: any) => { + if (typeof (p) !== "string") { + let style = getComputedStyle(p); + if (style.textDecoration === "underline") return null; + if (p.parentElement.outerHTML.indexOf("text-decoration: underline") !== -1 && + p.parentElement.outerHTML.indexOf("text-decoration-style: dotted") !== -1) + return null; + } + return false; + } + }, + ], + inclusive: false, + priority: 100, toDOM() { return ['span', { - style: 'text-decoration: underline; text-decoration-color: rgba(204, 206, 210, 0.92)' + style: 'text-decoration: underline; text-decoration-style: dotted; text-decoration-color: rgba(204, 206, 210, 0.92)' }]; } }, + underline: { + parseDOM: [ + { + tag: "span", + getAttrs: (p: any) => { + if (typeof (p) !== "string") { + let style = getComputedStyle(p); + if (style.textDecoration === "underline") + return null; + if (p.parentElement.outerHTML.indexOf("text-decoration-style:line") !== -1) + return null; + } + return false; + } + } + // { style: "text-decoration=underline" } + ], + toDOM: () => ['span', { + style: 'text-decoration:underline;text-decoration-style:line' + }] + }, + search_highlight: { parseDOM: [{ style: 'background: yellow' }], toDOM() { @@ -530,9 +540,6 @@ export const marks: { [index: string]: MarkSpec } = { }] }, }; -function getFontSize(element: any) { - return parseFloat((getComputedStyle(element) as any).fontSize); -} export class ImageResizeView { _handle: HTMLElement; @@ -733,73 +740,52 @@ export class FootnoteView { } export class SummarizedView { - // TODO: highlight text that is summarized. to find end of region, walk along mark _collapsed: HTMLElement; _view: any; constructor(node: any, view: any, getPos: any) { this._collapsed = document.createElement("span"); - this._collapsed.textContent = node.attrs.visibility ? "㊀" : "㊉"; - this._collapsed.style.opacity = "0.5"; - this._collapsed.style.position = "relative"; - this._collapsed.style.width = "40px"; - this._collapsed.style.height = "20px"; - let self = this; + this._collapsed.className = this.className(node.attrs.visibility); this._view = view; const js = node.toJSON; node.toJSON = function () { - return js.apply(this, arguments); }; - this._collapsed.onpointerdown = function (e: any) { - if (node.attrs.visibility) { - // node.attrs.visibility = !node.attrs.visibility; - let y = getPos(); - const attrs = { ...node.attrs }; - attrs.visibility = !attrs.visibility; - let { from, to } = self.updateSummarizedText(y + 1, view.state.schema.marks.highlight); - let length = to - from; - let newSelection = TextSelection.create(view.state.doc, y + 1, y + 1 + length); - // update attrs of node - attrs.text = newSelection.content(); - attrs.textslice = newSelection.content().toJSON(); - view.dispatch(view.state.tr.setNodeMarkup(y, undefined, attrs)); - view.dispatch(view.state.tr.setSelection(newSelection).deleteSelection(view.state, () => { })); - let marks = view.state.storedMarks.filter((m: any) => m.type !== view.state.schema.marks.highlight); - view.state.storedMarks = marks; - self._collapsed.textContent = "㊉"; - } else { - // node.attrs.visibility = !node.attrs.visibility; - let y = getPos(); - const attrs = { ...node.attrs }; - attrs.visibility = !attrs.visibility; - view.dispatch(view.state.tr.setNodeMarkup(y, undefined, attrs)); - let mark = view.state.schema.mark(view.state.schema.marks.highlight); - view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, y + 1, y + 1))); - const from = view.state.selection.from; - let size = node.attrs.text.size; - view.dispatch(view.state.tr.replaceSelection(node.attrs.text).addMark(from, from + size, mark).removeStoredMark(mark)); - self._collapsed.textContent = "㊀"; + + this._collapsed.onpointerdown = (e: any) => { + const visible = !node.attrs.visibility; + const attrs = { ...node.attrs, visibility: visible }; + let textSelection = TextSelection.create(view.state.doc, getPos() + 1, getPos() + 1); + if (!visible) { // update summarized text and save in attrs + textSelection = this.updateSummarizedText(getPos() + 1); + attrs.text = textSelection.content(); + attrs.textslice = attrs.text.toJSON(); } + view.dispatch(view.state.tr. + setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed) + replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it + setNodeMarkup(getPos(), undefined, attrs)); // update the attrs e.preventDefault(); e.stopPropagation(); + this._collapsed.className = this.className(visible); }; (this as any).dom = this._collapsed; - - } - selectNode() { } + selectNode() { } + + deselectNode() { } - updateSummarizedText(start?: any, mark?: any) { - let $start = this._view.state.doc.resolve(start); + className = (visible: boolean) => "formattedTextBox-summarizer" + (visible ? "" : "-collapsed"); + + updateSummarizedText(start?: any) { + let mark = this._view.state.schema.marks.highlight.create(); let endPos = start; - let _mark = this._view.state.schema.mark(this._view.state.schema.marks.highlight); let visited = new Set(); for (let i: number = start + 1; i < this._view.state.doc.nodeSize - 1; i++) { let skip = false; this._view.state.doc.nodesBetween(start, i, (node: Node, pos: number, parent: Node, index: number) => { if (node.isLeaf && !visited.has(node) && !skip) { - if (node.marks.find((m: any) => m.type === _mark.type)) { + if (node.marks.find((m: any) => m.type === mark.type)) { visited.add(node); endPos = i + node.nodeSize - 1; } @@ -807,10 +793,7 @@ export class SummarizedView { } }); } - return { from: start, to: endPos }; - } - - deselectNode() { + return TextSelection.create(this._view.state.doc, start, endPos); } } // :: Schema diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 6d375fc1d..020c51c36 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -307,10 +307,9 @@ export class TooltipTextMenu { { handlers: { dragComplete: action(() => { - // let m = dragData.droppedDocuments; let linkDoc = dragData.linkDocument; let proto = Doc.GetProto(linkDoc); - if (docView && docView.props.ContainingCollectionView) { + if (proto && docView && docView.props.ContainingCollectionView) { proto.sourceContext = docView.props.ContainingCollectionView.props.Document; } linkDoc instanceof Doc && this.makeLink(Utils.prepend("/doc/" + linkDoc[Id]), ctrlKey ? "onRight" : "inTab"); @@ -322,8 +321,6 @@ export class TooltipTextMenu { e.preventDefault(); }; this.linkEditor.appendChild(this.linkDrag); - // this.linkEditor.appendChild(this.linkText); - // this.linkEditor.appendChild(linkBtn); this.tooltip.appendChild(this.linkEditor); } @@ -432,11 +429,13 @@ export class TooltipTextMenu { } public static insertStar(state: EditorState, dispatch: any) { - let newNode = schema.nodes.star.create({ visibility: false, text: state.selection.content(), textslice: state.selection.content().toJSON(), textlen: state.selection.to - state.selection.from }); - if (dispatch) { - //console.log(newNode.attrs.text.toString()); - dispatch(state.tr.replaceSelectionWith(newNode)); - } + if (state.selection.empty) return false; + let mark = state.schema.marks.highlight.create(); + let tr = state.tr; + tr.addMark(state.selection.from, state.selection.to, mark); + let content = tr.selection.content(); + let newNode = schema.nodes.star.create({ visibility: false, text: content, textslice: content.toJSON() }); + dispatch && dispatch(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); return true; } @@ -637,7 +636,6 @@ export class TooltipTextMenu { Array.from(this._brushMarks).filter(m => m.type !== schema.marks.user_mark).forEach((mark: Mark) => { const markType = mark.type; this.changeToMarkInGroup(markType, this.view, []); - }); } } diff --git a/src/client/views/nodes/FormattedTextBox.scss b/src/client/views/nodes/FormattedTextBox.scss index 8f47402c4..0de050e79 100644 --- a/src/client/views/nodes/FormattedTextBox.scss +++ b/src/client/views/nodes/FormattedTextBox.scss @@ -131,6 +131,26 @@ footnote::after { width: 0; } +.formattedTextBox-summarizer { + opacity :0.5; + position: relative; + width:40px; + height:20px; +} +.formattedTextBox-summarizer::after{ + content: "←" ; +} + +.formattedTextBox-summarizer-collapsed { + opacity :0.5; + position: relative; + width:40px; + height:20px; +} +.formattedTextBox-summarizer-collapsed::after { + content: "..."; +} + ol { counter-reset: deci1 0;} .decimal1-ol {counter-reset: deci1; p { display: inline }; font-size: 24 } .decimal2-ol {counter-reset: deci2; p { display: inline }; font-size: 18 } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index d811273e8..006a33011 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -15,7 +15,7 @@ import { List } from '../../../new_fields/List'; import { RichTextField, ToPlainText, FromPlainText } from "../../../new_fields/RichTextField"; import { BoolCast, Cast, NumCast, StrCast, DateCast } from "../../../new_fields/Types"; import { createSchema, makeInterface } from "../../../new_fields/Schema"; -import { Utils, numberRange } from '../../../Utils'; +import { Utils, numberRange, timenow } from '../../../Utils'; import { DocServer } from "../../DocServer"; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; @@ -117,6 +117,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe @undoBatch public setFontColor(color: string) { + this._editorView!.state.storedMarks 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; @@ -654,10 +655,8 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe this.props.select(false); } else if (this.props.isOverlay) this._editorView!.focus(); - 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 })]; - this._editorView!.state.storedMarks = newMarks; - + this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark). + addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() }))); } componentWillUnmount() { @@ -681,10 +680,9 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } if (e.button === 0 && this.props.isSelected() && !e.altKey && !e.ctrlKey && !e.metaKey) { e.stopPropagation(); - if (FormattedTextBox._toolTipTextMenu && FormattedTextBox._toolTipTextMenu.tooltip) { - //this._toolTipTextMenu.tooltip.style.opacity = "0"; - } } + this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark). + addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })); let ctrlKey = e.ctrlKey; if (e.button === 0 && ((!this.props.isSelected() && !e.ctrlKey) || (this.props.isSelected() && e.ctrlKey)) && !e.metaKey && e.target) { let href = (e.target as any).href; @@ -744,13 +742,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe } } - setAnnotation = (start: number, end: number, mark: Mark, opened: boolean, keep: boolean = false) => { - let view = this._editorView!; - let mid = view.state.doc.resolve(Math.round((start + end) / 2)); - let nmark = view.state.schema.marks.user_mark.create({ ...mark.attrs, userid: keep ? Doc.CurrentUserEmail : mark.attrs.userid, opened: opened }); - view.dispatch(view.state.tr.removeMark(start, end, nmark).addMark(start, end, nmark).setSelection(new TextSelection(mid, mid))); - } - @action onFocused = (e: React.FocusEvent): void => { document.removeEventListener("keypress", this.recordKeyHandler); @@ -774,7 +765,7 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe onClick = (e: React.MouseEvent): void => { if (this.props.isSelected() && e.nativeEvent.offsetX < 40) { let pos = this._editorView!.posAtCoords({ left: e.clientX, top: e.clientY }); - if (pos) { + if (pos && pos.pos > 0) { let node = this._editorView!.state.doc.nodeAt(pos.pos); let node2 = node && node.type === schema.nodes.paragraph ? this._editorView!.state.doc.nodeAt(pos.pos - 1) : undefined; if (node === this._nodeClicked && node2 && (node2.type === schema.nodes.ordered_list || node2.type === schema.nodes.list_item)) { @@ -833,24 +824,6 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe // SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(this.props.Document, this.props.ContainingCollectionView)!, false); // }, 0); } - function timenow() { - var now = new Date(); - let ampm = 'am'; - let h = now.getHours(); - let m: any = now.getMinutes(); - let s: any = now.getSeconds(); - if (h >= 12) { - if (h > 12) h -= 12; - ampm = 'pm'; - } - - if (m < 10) m = '0' + m; - 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() })]; - this._editorView!.state.storedMarks = newMarks; - // stop propagation doesn't seem to stop propagation of native keyboard events. // so we set a flag on the native event that marks that the event's been handled. (e.nativeEvent as any).DASHFormattedTextBoxHandled = true; diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index a703f1cef..1462cdfad 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -308,7 +308,7 @@ export namespace Doc { // gets the document's prototype or returns the document if it is a prototype export function GetProto(doc: Doc) { - return Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc); + return doc && (Doc.GetT(doc, "isPrototype", "boolean", true) ? doc : (doc.proto || doc)); } export function GetDataDoc(doc: Doc): Doc { let proto = Doc.GetProto(doc); -- cgit v1.2.3-70-g09d2 From 2d21fff15510d6eeb8975cc2459f69ca28d86d1d Mon Sep 17 00:00:00 2001 From: bob Date: Mon, 9 Sep 2019 17:26:27 -0400 Subject: added stand-in spatial parser. --- src/client/util/TooltipTextMenu.tsx | 11 +++++++++++ src/client/views/DocumentDecorations.tsx | 6 ++++++ src/client/views/InkingControl.tsx | 7 ++++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 13 +++++++++++++ .../views/collections/collectionFreeForm/MarqueeView.tsx | 1 + src/client/views/nodes/FormattedTextBox.tsx | 10 ++++++++++ 6 files changed, 47 insertions(+), 1 deletion(-) (limited to 'src/client/util/TooltipTextMenu.tsx') diff --git a/src/client/util/TooltipTextMenu.tsx b/src/client/util/TooltipTextMenu.tsx index 020c51c36..c376b6f86 100644 --- a/src/client/util/TooltipTextMenu.tsx +++ b/src/client/util/TooltipTextMenu.tsx @@ -18,6 +18,7 @@ import { DragManager } from "./DragManager"; import { LinkManager } from "./LinkManager"; import { schema } from "./RichTextSchema"; import "./TooltipTextMenu.scss"; +import { Cast, NumCast } from '../../new_fields/Types'; const { toggleMark, setBlockType } = require("prosemirror-commands"); const { openPrompt, TextField } = require("./ProsemirrorCopy/prompt.js"); @@ -495,10 +496,20 @@ export class TooltipTextMenu { if (markType.name[0] === 'p') { let size = this.fontSizeToNum.get(markType); if (size) { this.updateFontSizeDropdown(String(size) + " pt"); } + let ruleProvider = Cast(this.editorProps.Document.ruleProvider, Doc) as Doc; + let heading = NumCast(this.editorProps.Document.heading); + if (ruleProvider && heading) { + ruleProvider["ruleSize_" + heading] = size; + } } else { let fontName = this.fontStylesToName.get(markType); if (fontName) { this.updateFontStyleDropdown(fontName); } + let ruleProvider = Cast(this.editorProps.Document.ruleProvider, Doc) as Doc; + let heading = NumCast(this.editorProps.Document.heading); + if (ruleProvider && heading) { + ruleProvider["ruleFont_" + heading] = fontName; + } } //actually apply font return toggleMark(markType)(view.state, view.dispatch, view); diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 700a4b49d..7cdb16f52 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -428,6 +428,12 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let dist = Math.sqrt((e.clientX - this._radiusDown[0]) * (e.clientX - this._radiusDown[0]) + (e.clientY - this._radiusDown[1]) * (e.clientY - this._radiusDown[1])); SelectionManager.SelectedDocuments().map(dv => dv.props.Document.layout instanceof Doc ? dv.props.Document.layout : dv.props.Document.isTemplate ? dv.props.Document : Doc.GetProto(dv.props.Document)). map(d => d.borderRounding = `${Math.min(100, dist)}%`); + SelectionManager.SelectedDocuments().map(dv => { + let cv = dv.props.ContainingCollectionView; + let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc); + let heading = NumCast(dv.props.Document.heading); + cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleRounding_" + heading] = StrCast(dv.props.Document.borderRounding)); + }) e.stopPropagation(); e.preventDefault(); } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 3f40642b5..eb6312e78 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -9,7 +9,7 @@ import { SelectionManager } from "../util/SelectionManager"; import { InkTool } from "../../new_fields/InkField"; import { Doc } from "../../new_fields/Doc"; import { undoBatch, UndoManager } from "../util/UndoManager"; -import { StrCast } from "../../new_fields/Types"; +import { StrCast, NumCast, Cast } from "../../new_fields/Types"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { MainOverlayTextBox } from "./MainOverlayTextBox"; @@ -50,6 +50,11 @@ export class InkingControl extends React.Component { let targetDoc = view.props.Document.layout instanceof Doc ? view.props.Document.layout : view.props.Document.isTemplate ? view.props.Document : Doc.GetProto(view.props.Document); let oldColor = StrCast(targetDoc.backgroundColor); targetDoc.backgroundColor = this._selectedColor; + if (view.props.Document.heading) { + let cv = view.props.ContainingCollectionView; + let ruleProvider = cv && (Cast(cv.props.Document.ruleProvider, Doc) as Doc); + cv && ((ruleProvider ? ruleProvider : cv.props.Document)["ruleColor_" + NumCast(view.props.Document.heading)] = this._selectedColor); + } return { target: targetDoc, previous: oldColor diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2ec2b0671..f7c1bedbb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -256,6 +256,19 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private getLocalTransform = (): Transform => Transform.Identity().scale(1 / this.zoomScaling()).translate(this.panX(), this.panY()); private addLiveTextBox = (newBox: Doc) => { FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed + newBox.heading = 1; + for (let i = 0; i < this.childDocs.length; i++) { + if (this.childDocs[i].heading == 1) { + newBox.heading = 2; + } + } + let ruleProvider = Cast(this.props.Document.ruleProvider, Doc); + if (!(ruleProvider instanceof Doc)) ruleProvider = this.props.Document; + let col = StrCast(ruleProvider["ruleColor_" + NumCast(newBox.heading)]); + let round = StrCast(ruleProvider["ruleRounding_" + NumCast(newBox.heading)]); + round && (newBox.borderRounding = round); + col && (newBox.backgroundColor = col); + newBox.ruleProvider = ruleProvider; this.addDocument(newBox, false); } private addDocument = (newBox: Doc, allowDuplicates: boolean) => { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 5015ee39a..100e6d817 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -329,6 +329,7 @@ export class MarqueeView extends React.Component this.props.addLiveTextDocument(summary); } else { + newCollection.ruleProvider = this.props.container.props.Document; this.props.addDocument(newCollection, false); this.props.selectDocuments([newCollection]); } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index ae1393fc4..93c97fa23 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -665,6 +665,16 @@ export class FormattedTextBox extends DocComponent<(FieldViewProps & FormattedTe else if (this.props.isOverlay) this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. this._editorView!.state.storedMarks = [...(this._editorView!.state.storedMarks ? this._editorView!.state.storedMarks : []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: timenow() })]; + let heading = this.props.Document.heading; + if (heading) { + let ruleProvider = Cast(this.props.Document.ruleProvider, Doc); + if (ruleProvider instanceof Doc) { + let font = StrCast(ruleProvider["ruleFont_" + heading]); + let size = NumCast(ruleProvider["ruleSize_" + heading]); + size && (this._editorView!.state.storedMarks = [...this._editorView!.state.storedMarks, schema.marks.pFontSize.create({ fontSize: size })]); + font && (this._editorView!.state.storedMarks = [...this._editorView!.state.storedMarks, font === "Arial" ? schema.marks.arial.create() : schema.marks.comicSans.create()]); + } + } } componentWillUnmount() { -- cgit v1.2.3-70-g09d2