aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText/RichTextMenu.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/formattedText/RichTextMenu.tsx')
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx100
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);
}
}