diff options
author | Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> | 2021-09-23 15:09:32 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-23 15:09:32 -0400 |
commit | 281ea90236adb8cb3ff8db7ddc76a466a8641bcd (patch) | |
tree | be9a5086596e33c269857c9ead0293719786035e /src/client/views/nodes/formattedText/RichTextMenu.tsx | |
parent | 26e265c6fc4950b859724aa2c0fbe6a028a56bfc (diff) | |
parent | d5f9533d153e11e24d2ab7c03b4561170f0768fe (diff) |
Merge branch 'master' into linking-anh
Diffstat (limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextMenu.tsx | 634 |
1 files changed, 179 insertions, 455 deletions
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 82ad2b7db..bd05af977 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,8 +1,7 @@ import React = require("react"); -import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; -import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, IReactionDisposer, observable, reaction, runInAction, computed } from "mobx"; import { observer } from "mobx-react"; import { lift, wrapIn } from "prosemirror-commands"; import { Mark, MarkType, Node as ProsNode, NodeType, ResolvedPos } from "prosemirror-model"; @@ -10,10 +9,7 @@ import { wrapInList } from "prosemirror-schema-list"; import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc } from "../../../../fields/Doc"; -import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField'; import { Cast, StrCast } from "../../../../fields/Types"; -import { TraceMobx } from "../../../../fields/util"; -import { unimplementedFunction, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { LinkManager } from "../../../util/LinkManager"; import { SelectionManager } from "../../../util/SelectionManager"; @@ -29,7 +25,7 @@ const { toggleMark } = require("prosemirror-commands"); @observer export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { - static Instance: RichTextMenu; + @observable static Instance: RichTextMenu; public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable private _linkToRef = React.createRef<HTMLInputElement>(); @@ -37,29 +33,24 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { public editorProps: FieldViewProps & FormattedTextBoxProps | undefined; public _brushMap: Map<string, Set<Mark>> = new Map(); - private fontSizeOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; - private fontFamilyOptions: { mark: Mark | null, title: string, label: string, command: any, hidden?: boolean, style?: {} }[]; - private listTypeOptions: { node: NodeType | any | null, title: string, label: string, command: any, style?: {} }[]; - private fontColors: (string | undefined)[]; - private highlightColors: (string | undefined)[]; @observable private collapsed: boolean = false; - @observable private boldActive: boolean = false; - @observable private italicsActive: boolean = false; - @observable private underlineActive: boolean = false; - @observable private strikethroughActive: boolean = false; - @observable private subscriptActive: boolean = false; - @observable private superscriptActive: boolean = false; - - @observable private activeFontSize: string = ""; - @observable private activeFontFamily: string = ""; + @observable private _boldActive: boolean = false; + @observable private _italicsActive: boolean = false; + @observable private _underlineActive: boolean = false; + @observable private _strikethroughActive: boolean = false; + @observable private _subscriptActive: boolean = false; + @observable private _superscriptActive: boolean = false; + + @observable private _activeFontSize: string = "13px"; + @observable private _activeFontFamily: string = ""; @observable private activeListType: string = ""; @observable private activeAlignment: string = "left"; @observable private brushMarks: Set<Mark> = new Set(); @observable private showBrushDropdown: boolean = false; - @observable private activeFontColor: string = "black"; + @observable private _activeFontColor: string = "black"; @observable private showColorDropdown: boolean = false; @observable private activeHighlightColor: string = "transparent"; @@ -72,74 +63,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { _delayHide = false; constructor(props: Readonly<{}>) { super(props); - RichTextMenu.Instance = this; - this._canFade = false; - //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); - runInAction(() => this.Pinned = true); - - this.fontSizeOptions = [ - { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 8 }), title: "Set font size", label: "8px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 9 }), title: "Set font size", label: "9px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 10 }), title: "Set font size", label: "10px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 12 }), title: "Set font size", label: "12px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 14 }), title: "Set font size", label: "14px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 16 }), title: "Set font size", label: "16px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 18 }), title: "Set font size", label: "18px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 20 }), title: "Set font size", label: "20px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 24 }), title: "Set font size", label: "24px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 32 }), title: "Set font size", label: "32px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 48 }), title: "Set font size", label: "48px", command: this.changeFontSize }, - { mark: schema.marks.pFontSize.create({ fontSize: 72 }), title: "Set font size", label: "72px", command: this.changeFontSize }, - { mark: null, title: "", label: "...", command: unimplementedFunction, hidden: true }, - { mark: null, title: "", label: "13px", command: unimplementedFunction, hidden: true }, // this is here because the default size is 13, but there is no actual 13pt option - ]; - - this.fontFamilyOptions = [ - { mark: schema.marks.pFontFamily.create({ family: "Times New Roman" }), title: "Set font family", label: "Times New Roman", command: this.changeFontFamily, style: { fontFamily: "Times New Roman" } }, - { mark: schema.marks.pFontFamily.create({ family: "Arial" }), title: "Set font family", label: "Arial", command: this.changeFontFamily, style: { fontFamily: "Arial" } }, - { mark: schema.marks.pFontFamily.create({ family: "Georgia" }), title: "Set font family", label: "Georgia", command: this.changeFontFamily, style: { fontFamily: "Georgia" } }, - { mark: schema.marks.pFontFamily.create({ family: "Comic Sans MS" }), title: "Set font family", label: "Comic Sans MS", command: this.changeFontFamily, style: { fontFamily: "Comic Sans MS" } }, - { mark: schema.marks.pFontFamily.create({ family: "Tahoma" }), title: "Set font family", label: "Tahoma", command: this.changeFontFamily, style: { fontFamily: "Tahoma" } }, - { mark: schema.marks.pFontFamily.create({ family: "Impact" }), title: "Set font family", label: "Impact", command: this.changeFontFamily, style: { fontFamily: "Impact" } }, - { mark: schema.marks.pFontFamily.create({ family: "Crimson Text" }), title: "Set font family", label: "Crimson Text", command: this.changeFontFamily, style: { fontFamily: "Crimson Text" } }, - { mark: null, title: "", label: "various", command: unimplementedFunction, hidden: true }, - // { mark: null, title: "", label: "default", command: unimplementedFunction, hidden: true }, - ]; - - this.listTypeOptions = [ - { node: schema.nodes.ordered_list.create({ mapStyle: "bullet" }), title: "Set list type", label: ":", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "decimal" }), title: "Set list type", label: "1.1", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "multi" }), title: "Set list type", label: "A.1", command: this.changeListType }, - { node: schema.nodes.ordered_list.create({ mapStyle: "" }), title: "Set list type", label: "<none>", command: this.changeListType }, - //{ node: undefined, title: "Set list type", label: "Remove", command: this.changeListType }, - ]; - - this.fontColors = [ - DarkPastelSchemaPalette.get("pink2"), - DarkPastelSchemaPalette.get("purple4"), - DarkPastelSchemaPalette.get("bluegreen1"), - DarkPastelSchemaPalette.get("yellow4"), - DarkPastelSchemaPalette.get("red2"), - DarkPastelSchemaPalette.get("bluegreen7"), - DarkPastelSchemaPalette.get("bluegreen5"), - DarkPastelSchemaPalette.get("orange1"), - "#757472", - "#000" - ]; - - this.highlightColors = [ - PastelSchemaPalette.get("pink2"), - PastelSchemaPalette.get("purple4"), - PastelSchemaPalette.get("bluegreen1"), - PastelSchemaPalette.get("yellow4"), - PastelSchemaPalette.get("red2"), - PastelSchemaPalette.get("bluegreen7"), - PastelSchemaPalette.get("bluegreen5"), - PastelSchemaPalette.get("orange1"), - "white", - "transparent" - ]; + runInAction(() => { + RichTextMenu.Instance = this; + this._canFade = false; + //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + this.Pinned = true; + }); } componentDidMount() { @@ -150,6 +79,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this._reaction?.(); } + @computed get bold() { return this._boldActive; } + @computed get underline() { return this._underlineActive; } + @computed get italics() { return this._italicsActive; } + @computed get strikeThrough() { return this._strikethroughActive; } + @computed get fontColor() { return this._activeFontColor; } + @computed get fontFamily() { return this._activeFontFamily; } + @computed get fontSize() { return this._activeFontSize; } + public delayHide = () => this._delayHide = true; @action @@ -179,10 +116,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.activeListType = this.getActiveListStyle(); this.activeAlignment = this.getActiveAlignment(); - this.activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various"; - this.activeFontSize = !activeSizes.length ? "13px" : activeSizes.length === 1 ? String(activeSizes[0]) : "..."; - this.activeFontColor = !activeColors.length ? "black" : activeColors.length === 1 ? String(activeColors[0]) : "..."; - this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length === 1 ? String(activeHighlights[0]) : "..."; + this._activeFontFamily = !activeFamilies.length ? "Arial" : activeFamilies.length === 1 ? String(activeFamilies[0]) : "various"; + this._activeFontSize = !activeSizes.length ? "13px" : activeSizes[0]; + this._activeFontColor = !activeColors.length ? "black" : activeColors.length > 0 ? String(activeColors[0]) : "..."; + this.activeHighlightColor = !activeHighlights.length ? "" : activeHighlights.length > 0 ? String(activeHighlights[0]) : "..."; // update link in current selection this.getTextLinkTargetTitle().then(targetTitle => this.setCurrentLink(targetTitle)); @@ -194,7 +131,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (node?.type === schema.nodes.ordered_list) { let attrs = node.attrs; if (mark.type === schema.marks.pFontFamily) attrs = { ...attrs, fontFamily: mark.attrs.family }; - if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: `${mark.attrs.fontSize}px` }; + if (mark.type === schema.marks.pFontSize) attrs = { ...attrs, fontSize: mark.attrs.fontSize }; if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color }; const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema); dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from)))); @@ -251,25 +188,22 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (this.TextView.props.isSelected(true)) { const state = this.view.state; const pos = this.view.state.selection.$from; - const ref_node = this.reference_node(pos); - if (ref_node && ref_node !== this.view.state.doc && ref_node.isText) { - const marks = Array.from(ref_node.marks); - marks.push(...(this.view.state.storedMarks as any)); - marks.forEach(m => { - m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); - m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); - m.type === state.schema.marks.pFontSize && activeSizes.push(String(m.attrs.fontSize) + "px"); - m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); + const marks: Mark<any>[] = [...(state.storedMarks ?? [])]; + if (state.selection.empty) { + const ref_node = this.reference_node(pos); + marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : [])); + } else { + state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + node.marks?.filter(mark => !mark.isInSet(marks)).map(mark => marks.push(mark)); }); } - !activeFamilies.length && (activeFamilies.push(StrCast(this.TextView.layoutDoc._fontFamily, StrCast(Doc.UserDoc().fontFamily)))); - !activeSizes.length && (activeSizes.push(StrCast(this.TextView.layoutDoc._fontSize, StrCast(Doc.UserDoc().fontSize)))); - !activeColors.length && (activeColors.push(StrCast(this.TextView.layoutDoc.color, StrCast(Doc.UserDoc().fontColor)))); + marks.forEach(m => { + m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); + m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); + m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize); + m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); + }); } - !activeFamilies.length && (activeFamilies.push(StrCast(Doc.UserDoc().fontFamily))); - !activeSizes.length && (activeSizes.push(StrCast(Doc.UserDoc().fontSize))); - !activeColors.length && (activeColors.push(StrCast(Doc.UserDoc().fontColor, "black"))); - !activeHighlights.length && (activeHighlights.push(StrCast(Doc.UserDoc().fontHighlight, ""))); return { activeFamilies, activeSizes, activeColors, activeHighlights }; } @@ -309,11 +243,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { return []; } activeMarks = markGroup.filter(mark_type => { - if (mark_type === state.schema.marks.pFontSize) { - return ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); - } + // if (mark_type === state.schema.marks.pFontSize) { + // return mark.isINSet + // ref_node.marks.some(m => m.type.name === state.schema.marks.pFontSize.name); + // } const mark = state.schema.mark(mark_type); - return ref_node.marks.includes(mark); + return mark.isInSet(ref_node.marks); }); } } @@ -328,123 +263,86 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { setActiveMarkButtons(activeMarks: MarkType[] | undefined) { if (!activeMarks) return; - this.boldActive = false; - this.italicsActive = false; - this.underlineActive = false; - this.strikethroughActive = false; - this.subscriptActive = false; - this.superscriptActive = false; + this._boldActive = false; + this._italicsActive = false; + this._underlineActive = false; + this._strikethroughActive = false; + this._subscriptActive = false; + this._superscriptActive = false; activeMarks.forEach(mark => { switch (mark.name) { - case "strong": this.boldActive = true; break; - case "em": this.italicsActive = true; break; - case "underline": this.underlineActive = true; break; - case "strikethrough": this.strikethroughActive = true; break; - case "subscript": this.subscriptActive = true; break; - case "superscript": this.superscriptActive = true; break; + case "strong": this._boldActive = true; break; + case "em": this._italicsActive = true; break; + case "underline": this._underlineActive = true; break; + case "strikethrough": this._strikethroughActive = true; break; + case "subscript": this._subscriptActive = true; break; + case "superscript": this._superscriptActive = true; break; } }); } - createButton(faIcon: string, title: string, isActive: boolean = false, command?: any, onclick?: any) { - const self = this; - function onClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - self.TextView?.endUndoTypingBatch(); - UndoManager.RunInBatch(() => { - self.view && command && command(self.view.state, self.view.dispatch, self.view); - self.view && onclick && onclick(self.view.state, self.view.dispatch, self.view); - }, "rich text menu command"); - self.setActiveMarkButtons(self.getActiveMarksOnSelection()); + toggleBold = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); } - - return ( - <Tooltip title={<div className="dash-tooltip">{title}</div>} key={title} placement="bottom"> - <button className={"antimodeMenu-button" + (isActive ? " active" : "")} onPointerDown={onClick}> - <FontAwesomeIcon icon={faIcon as IconProp} size="lg" /> - </button> - </Tooltip> - ); } - createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => void): JSX.Element { - const items = options.map(({ title, label, hidden, style }) => { - if (hidden) { - return <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>; - } - return <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>; - }); - - const self = this; - function onChange(e: React.ChangeEvent<HTMLSelectElement>) { - e.stopPropagation(); - e.preventDefault(); - self.TextView?.endUndoTypingBatch(); - UndoManager.RunInBatch(() => { - options.forEach(({ label, mark, command }) => { - if (e.target.value === label && mark) { - if (!self.TextView?.props.isSelected(true)) { - switch (mark.type) { - case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break; - case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break; - } - } - else self.view && mark && command(mark, self.view); - } - }); - }, "text mark dropdown"); + toggleUnderline = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.underline); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); } + } - return <Tooltip key={key} title={<div className="dash-tooltip">{key}</div>} placement="bottom"> - <select onChange={onChange} value={activeOption}>{items}</select> - </Tooltip>; + toggleItalics = () => { + if (this.view) { + const mark = this.view.state.schema.mark(this.view.state.schema.marks.em); + this.setMark(mark, this.view.state, this.view.dispatch, false); + this.view.focus(); + } } - createNodesDropdown(activeMap: string, options: { node: NodeType | any | null, title: string, label: string, command: (node: NodeType | any) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => {}): JSX.Element { - const activeOption = activeMap === "bullet" ? ":" : activeMap === "decimal" ? "1.1" : activeMap === "multi" ? "A.1" : "<none>"; - const items = options.map(({ title, label, hidden, style }) => { - if (hidden) { - return <option value={label} title={title} key={label} style={style ? style : {}} hidden>{label}</option>; - } - return <option value={label} title={title} key={label} style={style ? style : {}}>{label}</option>; - }); - const self = this; - function onChange(val: string) { - self.TextView.endUndoTypingBatch(); - options.forEach(({ label, node, command }) => { - if (val === label && node) { - if (self.TextView.props.isSelected(true)) { - UndoManager.RunInBatch(() => self.view && node && command(node), "nodes dropdown"); - setter(val); - } - } - }); + setFontSize = (fontSize: string) => { + if (this.view) { + const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize }); + this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); + this.view.focus(); + this.updateMenu(this.view, undefined, this.props); } + } - return <Tooltip key={key} title={<div className="dash-tooltip">{key}</div>} placement="bottom"> - <select value={activeOption} onChange={e => onChange(e.target.value)}>{items}</select> - </Tooltip>; + setFontFamily = (family: string) => { + if (this.view) { + const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family }); + this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); + this.view.focus(); + this.updateMenu(this.view, undefined, this.props); + } } - changeFontSize = (mark: Mark, view: EditorView) => { - const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); + setHighlight(color: String, view: EditorView, dispatch: any) { + const highlightMark = view.state.schema.mark(view.state.schema.marks.marker, { highlight: color }); + if (view.state.selection.empty) return false; view.focus(); - this.updateMenu(view, undefined, this.props); + this.setMark(highlightMark, view.state, dispatch, false); } - changeFontFamily = (mark: Mark, view: EditorView) => { - const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); - view.focus(); - this.updateMenu(view, undefined, this.props); + setColor(color: String, view: EditorView, dispatch: any) { + if (this.view) { + const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color }); + this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true); + view.focus(); + this.updateMenu(this.view, undefined, this.props); + } } // TODO: remove doesn't work - //remove all node type and apply the passed-in one to the selected text + // remove all node type and apply the passed-in one to the selected text changeListType = (nodeType: Node | undefined) => { if (!this.view || (nodeType as any)?.attrs.mapStyle === "") return; @@ -490,25 +388,27 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { dispatch?.(tr.replaceSelectionWith(newNode).removeMark(tr.selection.from - 1, tr.selection.from, mark)); return true; } - alignCenter = (state: EditorState<any>, dispatch: any) => { - return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "center", dispatch); + alignCenter = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "center", dispatch); } - alignLeft = (state: EditorState<any>, dispatch: any) => { - return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "left", dispatch); + alignLeft = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "left", dispatch); } - alignRight = (state: EditorState<any>, dispatch: any) => { - return this.TextView.props.isSelected(true) && this.alignParagraphs(state, "right", dispatch); + alignRight = (view: EditorView, dispatch: any) => { + return this.TextView.props.isSelected(true) && this.alignParagraphs(view, "right", dispatch); } - alignParagraphs(state: EditorState<any>, align: "left" | "right" | "center", dispatch: any) { - var tr = state.tr; - state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos, parent, index) => { + alignParagraphs(view: EditorView, align: "left" | "right" | "center", dispatch: any) { + var tr = view.state.tr; + view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if (node.type === schema.nodes.paragraph || node.type === schema.nodes.heading) { tr = tr.setNodeMarkup(pos, node.type, { ...node.attrs, align }, node.marks); return false; } + view.focus(); return true; }); + view.focus(); dispatch?.(tr); return true; } @@ -597,47 +497,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } _brushNameRef = React.createRef<HTMLInputElement>(); - createBrushButton() { - const self = this; - const onBrushClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - self.TextView.endUndoTypingBatch(); - UndoManager.RunInBatch(() => self.view && self.fillBrush(self.view.state, self.view.dispatch), "rt brush"); - }; - - let label = "Stored marks: "; - if (this.brushMarks && this.brushMarks.size > 0) { - this.brushMarks.forEach((mark: Mark) => { - const markType = mark.type; - label += markType.name; - label += ", "; - }); - label = label.substring(0, label.length - 2); - } else { - label = "No marks are currently stored"; - } - - //onPointerDown={onBrushClick} - - const button = <Tooltip title={<div className="dash-tooltip">style brush</div>} placement="bottom"> - <button className="antimodeMenu-button" onClick={onBrushClick} style={this.brushMarks?.size > 0 ? { backgroundColor: "121212" } : {}}> - <FontAwesomeIcon icon="paint-roller" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.brushMarks?.size > 0 ? 45 : 0}deg)` }} /> - </button> - </Tooltip>; - - const dropdownContent = - <div className="dropdown"> - <p>{label}</p> - <button onPointerDown={this.clearBrush}>Clear brush</button> - <input placeholder="-brush name-" ref={this._brushNameRef} onKeyPress={this.onBrushNameKeyPress} /> - </div>; - - return ( - <ButtonDropdown view={this.view} key={"brush dropdown"} button={button} openDropdownOnButton={false} dropdownContent={dropdownContent} /> - ); - } - @action clearBrush() { RichTextMenu.Instance.brushMarks = new Set(); @@ -666,123 +525,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } } - @action toggleColorDropdown() { this.showColorDropdown = !this.showColorDropdown; } - @action setActiveColor(color: string) { this.activeFontColor = color; } get TextView() { return (this.view as any)?.TextView as FormattedTextBox; } get TextViewFieldKey() { return this.TextView?.props.fieldKey; } - createColorButton() { - const self = this; - function onColorClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - self.TextView.endUndoTypingBatch(); - if (self.view) { - UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.view.focus(); - self.updateMenu(self.view, undefined, self.props); - } - } - function changeColor(e: React.PointerEvent, color: string) { - e.preventDefault(); - e.stopPropagation(); - self.setActiveColor(color); - self.TextView.endUndoTypingBatch(); - if (self.view) { - UndoManager.RunInBatch(() => self.view && self.insertColor(self.activeFontColor, self.view.state, self.view.dispatch), "rt menu color"); - self.view.focus(); - self.updateMenu(self.view, undefined, self.props); - } - } - // onPointerDown={onColorClick} - const button = <Tooltip title={<div className="dash-tooltip">set font color</div>} placement="bottom"> - <button className="antimodeMenu-button color-preview-button"> - <FontAwesomeIcon icon="palette" size="lg" /> - <div className="color-preview" style={{ backgroundColor: this.activeFontColor }}></div> - </button> - </Tooltip>; - const dropdownContent = - <div className="dropdown" > - <p>Change font color:</p> - <div className="color-wrapper"> - {this.fontColors.map(color => { - if (color) { - return this.activeFontColor === color ? - <button className="color-button active" key={"active" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button> : - <button className="color-button" key={"other" + color} style={{ backgroundColor: color }} onPointerDown={e => changeColor(e, color)}></button>; - } - })} - </div> - </div>; - - return ( - <ButtonDropdown view={this.view} key={"color dropdown"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} /> - ); - } - - public insertColor(color: String, state: EditorState<any>, dispatch: any) { - const colorMark = state.schema.mark(state.schema.marks.pFontColor, { color: color }); - if (state.selection.empty) { - dispatch(state.tr.addStoredMark(colorMark)); - return false; - } - this.setMark(colorMark, state, dispatch, true); - } - - @action toggleHighlightDropdown() { this.showHighlightDropdown = !this.showHighlightDropdown; } @action setActiveHighlight(color: string) { this.activeHighlightColor = color; } - createHighlighterButton() { - const self = this; - function onHighlightClick(e: React.PointerEvent) { - e.preventDefault(); - e.stopPropagation(); - self.TextView.endUndoTypingBatch(); - UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highligher"); - } - function changeHighlight(e: React.PointerEvent, color: string) { - e.preventDefault(); - e.stopPropagation(); - self.setActiveHighlight(color); - self.TextView.endUndoTypingBatch(); - UndoManager.RunInBatch(() => self.view && self.insertHighlight(self.activeHighlightColor, self.view.state, self.view.dispatch), "rt highlighter"); - } - - //onPointerDown={onHighlightClick} - const button = <Tooltip title={<div className="dash-tooltip">set highlight color</div>} placement="bottom"> - <button className="antimodeMenu-button color-preview-button" key="highilghter-button" > - <FontAwesomeIcon icon="highlighter" size="lg" /> - <div className="color-preview" style={{ backgroundColor: this.activeHighlightColor }}></div> - </button> - </Tooltip>; - - const dropdownContent = - <div className="dropdown"> - <p>Change highlight color:</p> - <div className="color-wrapper"> - {this.highlightColors.map(color => { - if (color) { - return this.activeHighlightColor === color ? - <button className="color-button active" key={`active ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button> : - <button className="color-button" key={`inactive ${color}`} style={{ backgroundColor: color }} onPointerDown={e => changeHighlight(e, color)}>{color === "transparent" ? "X" : ""}</button>; - } - })} - </div> - </div>; - - return ( - <ButtonDropdown view={this.view} key={"highlighter"} button={button} dropdownContent={dropdownContent} openDropdownOnButton={true} /> - ); - } - - insertHighlight(color: String, state: EditorState<any>, dispatch: any) { - if (state.selection.empty) return false; - toggleMark(state.schema.marks.marker, { highlight: color })(state, dispatch); - } - @action toggleLinkDropdown() { this.showLinkDropdown = !this.showLinkDropdown; } @action setCurrentLink(link: string) { this.currentLink = link; } createLinkButton() { @@ -828,7 +578,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (linkDoc instanceof Doc) { const anchor1 = await Cast(linkDoc.anchor1, Doc); const anchor2 = await Cast(linkDoc.anchor2, Doc); - const currentDoc = SelectionManager.Views().length && SelectionManager.Views()[0].props.Document; + const currentDoc = SelectionManager.Docs().lastElement(); if (currentDoc && anchor1 && anchor2) { if (Doc.AreProtosEqual(currentDoc, anchor1)) { return StrCast(anchor2.title); @@ -852,7 +602,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { @undoBatch makeLinkToURL = (target: string, lcoation: string) => { ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); - console.log((this.view as any)?.TextView); } @undoBatch @@ -921,95 +670,70 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { return ref_node; } - @action onPointerEnter(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } - @action onPointerLeave(e: React.PointerEvent) { RichTextMenu.Instance.overMenu = false; } - - @action - toggleMenuPin = (e: React.MouseEvent) => { - Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned; - if (!this.Pinned) { - this.fadeOut(true); - } - } - - @action - protected toggleCollapse = (e: React.MouseEvent) => { - this.collapsed = !this.collapsed; - setTimeout(() => { - const x = Math.min(this._left, window.innerWidth - RichTextMenu.Instance.width); - RichTextMenu.Instance.jumpTo(x, this._top, true); - }, 0); - } - render() { - TraceMobx(); - const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[ - //!this.collapsed ? this.getDragger() : (null), - // !this.Pinned ? (null) : <div key="frag1"> {[ - // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), - // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), - // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), - // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), - // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), - // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), - // <div className="richTextMenu-divider" key="divider" /> - // ]}</div>, - this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), - this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), - this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), - this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), - this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), - this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), - this.createColorButton(), - this.createHighlighterButton(), - this.createLinkButton(), - this.createBrushButton(), - <div className="collectionMenu-divider" key="divider 2" />, - this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), - this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), - this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight), - this.createButton("indent", "Inset More", undefined, this.insetParagraph), - this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph), - this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph), - this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph), - ]}</div>; - - const row2 = <div className="antimodeMenu-row row-2" key="row2"> - {this.collapsed ? this.getDragger() : (null)} - <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}> - <div className="collectionMenu-divider" key="divider 3" /> - {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { - this.activeFontSize = val; - SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); - })), - this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { - this.activeFontFamily = val; - SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); - })), - <div className="collectionMenu-divider" key="divider 4" />, - this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), - this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), - this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), - this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule) - ]} - </div> - {/* <div key="collapser"> - {<div key="collapser"> - <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}> - <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} /> - </button> - </div> } - <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}> - <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} /> - </button> - </div> */} - </div>; - - return ( - <div className="richTextMenu" onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} > - {this.getElementWithRows([row1, row2], 2, false)} - </div> - ); + return null; + // TraceMobx(); + // const row1 = <div className="antimodeMenu-row" key="row 1" style={{ display: this.collapsed ? "none" : undefined }}>{[ + // //!this.collapsed ? this.getDragger() : (null), + // // !this.Pinned ? (null) : <div key="frag1"> {[ + // // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + // // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + // // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + // // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + // // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + // // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + // // <div className="richTextMenu-divider" key="divider" /> + // // ]}</div>, + // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)), + // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)), + // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)), + // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)), + // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)), + // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)), + // this.createColorButton(), + // this.createHighlighterButton(), + // this.createLinkButton(), + // this.createBrushButton(), + // <div className="collectionMenu-divider" key="divider 2" />, + // this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), + // this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), + // this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight), + // this.createButton("indent", "Inset More", undefined, this.insetParagraph), + // this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph), + // this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph), + // this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph), + // ]}</div>; + + // const row2 = <div className="antimodeMenu-row row-2" key="row2"> + // {this.collapsed ? this.getDragger() : (null)} + // <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}> + // <div className="collectionMenu-divider" key="divider 3" /> + // {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { + // this.activeFontSize = val; + // SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); + // })), + // this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { + // this.activeFontFamily = val; + // SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); + // })), + // <div className="collectionMenu-divider" key="divider 4" />, + // this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), + // this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), + // this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), + // this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule) + // ]} + // </div> + // {/* <div key="collapser"> + // {<div key="collapser"> + // <button className="antimodeMenu-button" key="collapse menu" title="Collapse menu" onClick={this.toggleCollapse} style={{ backgroundColor: this.collapsed ? "#121212" : "", width: 25 }}> + // <FontAwesomeIcon icon="chevron-left" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.3s", transform: `rotate(${this.collapsed ? 180 : 0}deg)` }} /> + // </button> + // </div> } + // <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "", display: this.collapsed ? "none" : undefined }}> + // <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} /> + // </button> + // </div> */} + // </div>; } } |