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.tsx123
1 files changed, 74 insertions, 49 deletions
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 88e2e4248..758b4035e 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -1,6 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { action, computed, IReactionDisposer, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { lift, toggleMark, wrapIn } from 'prosemirror-commands';
import { Mark, MarkType } from 'prosemirror-model';
@@ -32,7 +32,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;
+ dataDoc: Doc | undefined;
@observable public view?: EditorView & { TextView?: FormattedTextBox } = undefined;
public editorProps: FieldViewProps | AntimodeMenuProps | undefined;
@@ -41,7 +41,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private collapsed: boolean = false;
@observable private _noLinkActive: boolean = false;
@observable private _boldActive: boolean = false;
- @observable private _italicsActive: boolean = false;
+ @observable private _italicActive: boolean = false;
@observable private _underlineActive: boolean = false;
@observable private _strikethroughActive: boolean = false;
@observable private _subscriptActive: boolean = false;
@@ -49,6 +49,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private _activeFontSize: string = '13px';
@observable private _activeFontFamily: string = '';
+ @observable private _activeFitBox: boolean = false;
@observable private _activeListType: string = '';
@observable private _activeAlignment: string = 'left';
@@ -64,18 +65,21 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@observable private currentLink: string | undefined = '';
@observable private showLinkDropdown: boolean = false;
- _reaction: IReactionDisposer | undefined;
constructor(props: AntimodeMenuProps) {
super(props);
makeObservable(this);
runInAction(() => {
RichTextMenu._instance.menu = this;
- this.updateMenu(undefined, undefined, props, this.layoutDoc);
+ this.updateMenu(undefined, undefined, props, this.dataDoc);
this._canFade = false;
this.Pinned = true;
});
}
+ @computed get RootSelected() {
+ return this.TextView?._props.rootSelected?.() || this.TextView?._props.isContentActive();
+ }
+
@computed get noAutoLink() {
return this._noLinkActive;
}
@@ -85,8 +89,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get underline() {
return this._underlineActive;
}
- @computed get italics() {
- return this._italicsActive;
+ @computed get italic() {
+ return this._italicActive;
}
@computed get strikeThrough() {
return this._strikethroughActive;
@@ -97,6 +101,9 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
@computed get fontHighlight() {
return this._activeHighlightColor;
}
+ @computed get fitBox() {
+ return this._activeFitBox;
+ }
@computed get fontFamily() {
return this._activeFontFamily;
}
@@ -110,26 +117,16 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
return this._activeAlignment;
}
@computed get textVcenter() {
- return BoolCast(this.layoutDoc?._layout_centered);
- }
- _disposer: IReactionDisposer | undefined;
- componentDidMount() {
- // this._disposer = reaction(
- // () => DocumentView.Selected().slice(),
- // () => this.updateMenu(undefined, undefined, undefined, undefined)
- // );
- }
- componentWillUnmount() {
- this._disposer?.();
+ return BoolCast(this.dataDoc?._layout_centered, BoolCast(Doc.UserDoc().layout_centered));
}
@action
- public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, layoutDoc: Doc | undefined) {
+ public updateMenu(view: EditorView | undefined, lastState: EditorState | undefined, props: FormattedTextBoxProps | AntimodeMenuProps | undefined, dataDoc: Doc | undefined) {
if (this._linkToRef.current?.getBoundingClientRect().width) {
return;
}
this.view = view;
- this.layoutDoc = layoutDoc;
+ this.dataDoc = dataDoc;
props && (this.editorProps = props);
// Don't do anything if the document/selection didn't change
@@ -143,14 +140,19 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const { activeSizes } = active;
const { activeColors } = active;
const { activeHighlights } = active;
- const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc();
+ const refDoc = DocumentView.Selected().lastElement()?.dataDoc ?? Doc.UserDoc();
const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey);
const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt));
this._activeListType = this.getActiveListStyle();
this._activeAlignment = this.getActiveAlignment();
- this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, refVal('fontFamily', 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
- this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, refVal('fontSize', '10px')) : activeSizes[0];
+ this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox));
+ this._activeFontFamily = !activeFamilies.length
+ ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontFamily'], refVal('fontFamily', 'Arial')))
+ : activeFamilies.length === 1
+ ? String(activeFamilies[0])
+ : 'various';
+ this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(this.dataDoc?.[Doc.LayoutFieldKey(this.dataDoc) + '_fontSize'], refVal('fontSize', '10px'))) : activeSizes[0];
this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, refVal('fontColor', 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
@@ -177,13 +179,13 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
toggleMark(mark.type, mark.attrs)(state, dispatch);
}
}
- // this.updateMenu(this.view, undefined, undefined, this.layoutDoc);
}
+ this.setActiveMarkButtons(this.getActiveMarksOnSelection());
};
// finds font sizes and families in selection
getActiveAlignment = () => {
- if (this.view && this.TextView?._props.rootSelected?.()) {
+ if (this.view && this.RootSelected) {
const from = this.view.state.selection.$from;
for (let i = from.depth; i >= 0; i--) {
const node = from.node(i);
@@ -191,8 +193,10 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
return node.attrs.align || 'left';
}
}
+ } else if (this.dataDoc) {
+ return StrCast(this.dataDoc.text_align) || 'left';
}
- return 'left';
+ return StrCast(Doc.UserDoc().textAlign) || 'left';
};
// finds font sizes and families in selection
@@ -216,7 +220,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.rootSelected?.()) {
+ if (this.view && this.RootSelected) {
const { state } = this.view;
const pos = this.view.state.selection.$from;
let marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -252,7 +256,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// finds all active marks on selection in given group
getActiveMarksOnSelection() {
- if (!this.view || !this.TextView?._props.rootSelected?.()) return [] as MarkType[];
+ if (!this.view || !this.RootSelected) return [] as MarkType[];
const { state } = this.view;
let marks: Mark[] = [...(state.storedMarks ?? [])];
@@ -281,7 +285,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this._noLinkActive = false;
this._boldActive = false;
- this._italicsActive = false;
+ this._italicActive = false;
this._underlineActive = false;
this._strikethroughActive = false;
this._subscriptActive = false;
@@ -291,7 +295,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
switch (mark.name) {
case 'noAutoLinkAnchor': this._noLinkActive = true; break;
case 'strong': this._boldActive = true; break;
- case 'em': this._italicsActive = true; break;
+ case 'em': this._italicActive = true; break;
case 'underline': this._underlineActive = true; break;
case 'strikethrough': this._strikethroughActive = true; break;
case 'subscript': this._subscriptActive = true; break;
@@ -326,6 +330,17 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.view.focus();
}
};
+ toggleFitBox = () => {
+ if (this.dataDoc) {
+ const doc = this.dataDoc;
+ (document.activeElement as HTMLElement)?.blur();
+ doc.text_fitBox = !doc.text_fitBox;
+ } else {
+ Doc.UserDoc().fitBox = !Doc.UserDoc().fitBox;
+ Doc.UserDoc().textAlign = Doc.UserDoc().fitBox ? 'center' : undefined;
+ }
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
+ };
toggleBold = () => {
if (this.view) {
const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
@@ -342,7 +357,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
- toggleItalics = () => {
+ toggleItalic = () => {
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);
@@ -350,22 +365,27 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
- setFontField = (value: string, fontField: 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
- if (this.TextView && this.view) {
- const { text, paragraph } = this.view.state.schema.nodes;
- const selNode = this.view.state.selection.$anchor.node();
- if (this.view.state.selection.from === 1 && this.view.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
- this.TextView.dataDoc[this.TextView.fieldKey + `_${fontField}`] = value;
- this.view.focus();
+ setFontField = (value: string, fontField: 'fitBox' | 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => {
+ if (this.TextView && this.view && fontField !== 'fitBox') {
+ if (this.view.hasFocus()) {
+ const attrs: { [key: string]: string } = {};
+ attrs[fontField] = value;
+ const fmark = this.view.state.schema.marks['pF' + fontField.substring(1)].create(attrs);
+ this.setMark(fmark, this.view.state, (tx: Transaction) => this.view?.dispatch(tx.addStoredMark(fmark)), true);
+ } else {
+ Array.from(new Set([...DocumentView.Selected(), this.TextView.DocumentView?.()]))
+ .filter(v => v?.ComponentView instanceof FormattedTextBox && v.ComponentView.EditorView?.TextView)
+ .map(v => v!.ComponentView as FormattedTextBox)
+ .forEach(view => {
+ view.EditorView!.TextView!.dataDoc[(view.EditorView!.TextView!.fieldKey ?? 'text') + `_${fontField}`] = value;
+ });
}
- const attrs: { [key: string]: string } = {};
- attrs[fontField] = value;
- const fmark = this.view?.state.schema.marks['pF' + fontField.substring(1)].create(attrs);
- this.setMark(fmark, this.view.state, (tx: Transaction) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
- this.view.focus();
+ } else if (this.dataDoc) {
+ this.dataDoc[`${Doc.LayoutFieldKey(this.dataDoc)}_${fontField}`] = value;
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
} else {
Doc.UserDoc()[fontField] = value;
- // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
}
};
@@ -391,7 +411,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.view!.dispatch(tx3);
});
this.view.focus();
- // this.updateMenu(this.view, undefined, this.props, this.layoutDoc);
};
insertSummarizer(state: EditorState, dispatch: (tr: Transaction) => void) {
@@ -406,10 +425,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
}
vcenterToggle = () => {
- this.layoutDoc && (this.layoutDoc._layout_centered = !this.layoutDoc._layout_centered);
+ if (this.dataDoc) this.dataDoc._layout_centered = !this.dataDoc._layout_centered;
+ else Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered;
};
- align = (view: EditorView, dispatch: (tr: Transaction) => void, alignment: 'left' | 'right' | 'center') => {
- if (this.TextView?._props.rootSelected?.()) {
+ align = (view: EditorView | undefined, dispatch: undefined | ((tr: Transaction) => void), alignment: 'left' | 'right' | 'center') => {
+ if (view && dispatch && this.RootSelected) {
let { tr } = view.state;
view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos) => {
if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) {
@@ -421,6 +441,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
});
view.focus();
dispatch?.(tr);
+ } else {
+ if (this.dataDoc) {
+ this.dataDoc.text_align = alignment;
+ } else Doc.UserDoc().textAlign = alignment;
+ this.updateMenu(undefined, undefined, undefined, this.dataDoc);
}
};
@@ -698,7 +723,7 @@ interface RichTextMenuPluginProps {
}
export class RichTextMenuPlugin extends React.Component<RichTextMenuPluginProps> {
update(view: EditorView & { TextView?: FormattedTextBox }, lastState: EditorState | undefined) {
- RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.layoutDoc);
+ RichTextMenu.Instance?.updateMenu(view, lastState, this.props.editorProps, view.TextView?.dataDoc);
}
render() {
return null;