diff options
Diffstat (limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextMenu.tsx | 100 |
1 files changed, 54 insertions, 46 deletions
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 712be91c8..4881070fd 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,27 +1,28 @@ -import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { Tooltip } from '@mui/material'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { lift, wrapIn } from 'prosemirror-commands'; import { Mark, MarkType, Node as ProsNode, ResolvedPos } from 'prosemirror-model'; import { wrapInList } from 'prosemirror-schema-list'; import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; +import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; -import { Cast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; +import { numberRange } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { EquationBox } from '../EquationBox'; import { FieldViewProps } from '../FieldView'; import { FormattedTextBox } from './FormattedTextBox'; import { updateBullets } from './ProsemirrorExampleTransfer'; import './RichTextMenu.scss'; import { schema } from './schema_rts'; -import { EquationBox } from '../EquationBox'; -import { numberRange } from '../../../../Utils'; const { toggleMark } = require('prosemirror-commands'); @observer @@ -30,6 +31,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable private _linkToRef = React.createRef<HTMLInputElement>(); + layoutDoc: Doc | undefined; @observable public view?: EditorView; public editorProps: FieldViewProps | undefined; @@ -62,14 +64,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { @observable private showLinkDropdown: boolean = false; _reaction: IReactionDisposer | undefined; - constructor(props: Readonly<{}>) { + constructor(props: AntimodeMenuProps) { super(props); - runInAction(() => { - RichTextMenu.Instance = this; - this.updateMenu(undefined, undefined, props); - this._canFade = false; - this.Pinned = true; - }); + makeObservable(this); + RichTextMenu.Instance = this; + this.updateMenu(undefined, undefined, props, this.layoutDoc); + this._canFade = false; + this.Pinned = true; } @computed get noAutoLink() { @@ -102,11 +103,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { @computed get textAlign() { return this._activeAlignment; } + @computed get textVcenter() { + return BoolCast(this.layoutDoc?.layout_centered); + } _disposer: IReactionDisposer | undefined; componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views().slice(), - views => this.updateMenu(undefined, undefined, undefined) + () => SelectionManager.Views.slice(), + views => this.updateMenu(undefined, undefined, undefined, undefined) ); } componentWillUnmount() { @@ -114,11 +118,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } @action - public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any) { + public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: any, layoutDoc: Doc | undefined) { if (this._linkToRef.current?.getBoundingClientRect().width) { return; } this.view = view; + this.layoutDoc = layoutDoc; props && (this.editorProps = props); // Don't do anything if the document/selection didn't change @@ -181,7 +186,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveAlignment() { - if (this.view && this.TextView?.props.isSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const path = (this.view.state.selection.$from as any).path; for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) { @@ -194,7 +199,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // finds font sizes and families in selection getActiveListStyle() { - if (this.view && this.TextView?.props.isSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const path = (this.view.state.selection.$from as any).path; for (let i = 0; i < path.length; i += 3) { if (path[i].type === this.view.state.schema.nodes.ordered_list) { @@ -214,7 +219,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const activeSizes = new Set<string>(); const activeColors = new Set<string>(); const activeHighlights = new Set<string>(); - if (this.view && this.TextView?.props.isSelected()) { + if (this.view && this.TextView?.props.rootSelected?.()) { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; @@ -233,8 +238,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize); m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight)); }); - } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views().forEach(dv => StrCast(dv.rootDoc._text_fontSize) && activeSizes.add(StrCast(dv.rootDoc._text_fontSize))); + } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) { + SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); } return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) }; } @@ -249,7 +254,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { //finds all active marks on selection in given group getActiveMarksOnSelection() { let activeMarks: MarkType[] = []; - if (!this.view || !this.TextView?.props.isSelected()) return activeMarks; + if (!this.view || !this.TextView?.props.rootSelected?.()) return activeMarks; const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); @@ -285,10 +290,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { return activeMarks; } - destroy() { - !this.TextView?.props.isSelected() && this.fadeOut(true); - } - @action setActiveMarkButtons(activeMarks: MarkType[] | undefined) { if (!activeMarks) return; @@ -357,10 +358,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); this.view.focus(); } - } else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize)); + } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) { + SelectionManager.Views.forEach(dv => (dv.Document._text_fontSize = fontSize)); } else Doc.UserDoc().fontSize = fontSize; - this.updateMenu(this.view, undefined, this.props); + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); }; setFontFamily = (family: string) => { @@ -369,7 +370,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); this.view.focus(); } else Doc.UserDoc().fontFamily = family; - this.updateMenu(this.view, undefined, this.props); + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); }; setHighlight(color: string) { @@ -378,7 +379,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.setMark(highlightMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(highlightMark)), true); this.view.focus(); } else Doc.UserDoc()._fontHighlight = color; - this.updateMenu(this.view, undefined, this.props); + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); } setColor(color: string) { @@ -387,7 +388,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true); this.view.focus(); } else Doc.UserDoc().fontColor = color; - this.updateMenu(this.view, undefined, this.props); + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); } // TODO: remove doesn't work @@ -428,7 +429,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } } this.view.focus(); - this.updateMenu(this.view, undefined, this.props); + this.updateMenu(this.view, undefined, this.props, this.layoutDoc); }; insertSummarizer(state: EditorState, dispatch: any) { @@ -442,8 +443,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { return true; } + vcenterToggle = (view: EditorView, dispatch: any) => { + this.layoutDoc && (this.layoutDoc.layout_centered = !this.layoutDoc.layout_centered); + }; align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => { - if (this.TextView?.props.isSelected()) { + if (this.TextView?.props.rootSelected?.()) { var tr = view.state.tr; view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) { @@ -638,7 +642,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (linkDoc instanceof Doc) { const link_anchor_1 = await Cast(linkDoc.link_anchor_1, Doc); const link_anchor_2 = await Cast(linkDoc.link_anchor_2, Doc); - const currentDoc = SelectionManager.Docs().lastElement(); + const currentDoc = SelectionManager.Docs.lastElement(); if (currentDoc && link_anchor_1 && link_anchor_2) { if (Doc.AreProtosEqual(currentDoc, link_anchor_1)) { return StrCast(link_anchor_2.title); @@ -665,7 +669,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { }; @undoBatch - @action deleteLink = () => { if (this.view) { const linkAnchor = this.view.state.selection.$from.nodeAfter?.marks.find(m => m.type === this.view!.state.schema.marks.linkAnchor); @@ -771,11 +774,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { // <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._text_fontSize = val); + // SelectionManager.Views.map(dv => dv.props.Document._text_fontSize = val); // })), // this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { // this.activeFontFamily = val; - // SelectionManager.Views().map(dv => dv.props.Document._text_fontFamily = val); + // SelectionManager.Views.map(dv => dv.props.Document._text_fontFamily = val); // })), // <div className="collectionMenu-divider" key="divider 4" />, // this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), @@ -808,10 +811,15 @@ interface ButtonDropdownProps { } @observer -export class ButtonDropdown extends React.Component<ButtonDropdownProps> { +export class ButtonDropdown extends ObservableReactComponent<ButtonDropdownProps> { @observable private showDropdown: boolean = false; private ref: HTMLDivElement | null = null; + constructor(props: any) { + super(props); + makeObservable(this); + } + componentDidMount() { document.addEventListener('pointerdown', this.onBlur); } @@ -846,22 +854,22 @@ export class ButtonDropdown extends React.Component<ButtonDropdownProps> { render() { return ( <div className="button-dropdown-wrapper" ref={node => (this.ref = node)}> - {!this.props.pdf ? ( - <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this.props.openDropdownOnButton ? this.onDropdownClick : undefined}> - {this.props.button} - <div style={{ marginTop: '-8.5', position: 'relative' }} onPointerDown={!this.props.openDropdownOnButton ? this.onDropdownClick : undefined}> + {!this._props.pdf ? ( + <div className="antimodeMenu-button dropdown-button-combined" onPointerDown={this._props.openDropdownOnButton ? this.onDropdownClick : undefined}> + {this._props.button} + <div style={{ marginTop: '-8.5', position: 'relative' }} onPointerDown={!this._props.openDropdownOnButton ? this.onDropdownClick : undefined}> <FontAwesomeIcon icon="caret-down" size="sm" /> </div> </div> ) : ( <> - {this.props.button} + {this._props.button} <button className="dropdown-button antimodeMenu-button" key="antimodebutton" onPointerDown={this.onDropdownClick}> <FontAwesomeIcon icon="caret-down" size="sm" /> </button> </> )} - {this.showDropdown ? this.props.dropdownContent : null} + {this.showDropdown ? this._props.dropdownContent : null} </div> ); } @@ -875,6 +883,6 @@ export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> return null; } update(view: EditorView, lastState: EditorState | undefined) { - RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps); + RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, (view as any).TextView?.layoutDoc); } } |