diff options
Diffstat (limited to 'src/client/views/nodes/formattedText')
4 files changed, 118 insertions, 107 deletions
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index e519de1c5..149836e93 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -12,6 +12,7 @@ import { FormattedTextBox } from "./FormattedTextBox"; import React = require("react"); import * as ReactDOM from 'react-dom'; import { observer } from "mobx-react"; +import { ColorScheme } from "../../../util/SettingsManager"; export class DashDocView { _fieldWrapper: HTMLSpanElement; // container for label and value @@ -20,7 +21,7 @@ export class DashDocView { this._fieldWrapper = document.createElement("span"); this._fieldWrapper.style.position = "relative"; this._fieldWrapper.style.textIndent = "0"; - this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard.darkScheme ? "dimGray" : "lightGray")); + this._fieldWrapper.style.border = "1px solid " + StrCast(tbox.layoutDoc.color, (CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimGray" : "lightGray")); this._fieldWrapper.style.width = node.attrs.width; this._fieldWrapper.style.height = node.attrs.height; this._fieldWrapper.style.display = node.attrs.hidden ? "none" : "inline-block"; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ebd509669..7afa54d5f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -279,8 +279,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp (curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text) && (this.dataDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()))); if ((!curTemp && !curProto) || curText || json.includes("dash")) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) if (removeSelection(json) !== removeSelection(curLayout?.Data)) { - !curText && tx.storedMarks?.filter(m => m.type.name === "pFontSize").map(m => Doc.UserDoc().fontSize = this.layoutDoc._fontSize = (m.attrs.fontSize + "px")); - !curText && tx.storedMarks?.filter(m => m.type.name === "pFontFamily").map(m => Doc.UserDoc().fontFamily = this.layoutDoc._fontFamily = m.attrs.fontFamily); this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); this.dataDoc[this.props.fieldKey + "-noTemplate"] = true;//(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); @@ -833,7 +831,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._disposers.componentHeights = reaction( // set the document height when one of the component heights changes and autoHeight is on () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }), ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => { - autoHeight && this.props.setHeight(marginsHeight + Math.max(sidebarHeight, textHeight)); + autoHeight && this.props.setHeight?.(marginsHeight + Math.max(sidebarHeight, textHeight)); }, { fireImmediately: true }); this._disposers.links = reaction(() => DocListCast(this.Document.links), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { @@ -894,11 +892,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp { fireImmediately: Doc.IsSearchMatchUnmemoized(this.rootDoc) ? true : false }); this._disposers.selected = reaction(() => this.props.isSelected(), - action((selected) => { + action(selected => { if (RichTextMenu.Instance?.view === this._editorView && !selected) { RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined); } - })); + if (this._editorView && selected) { + RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); + } + }), { fireImmediately: true }); if (!this.props.dontRegisterView) { this._disposers.record = reaction(() => this._recording, @@ -1168,7 +1169,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp selectOnLoad && 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. if (this._editorView && !this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark)) { - this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })]; + this._editorView.state.storedMarks = [...(this._editorView.state.storedMarks ?? []), + schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }), + ...(Doc.UserDoc().fontColor !== "transparent" && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []), + ...(Doc.UserDoc().fontStyle === "italics" ? [schema.mark(schema.marks.em)] : []), + ...(Doc.UserDoc().textDecoration === "underline" ? [schema.mark(schema.marks.underline)] : []), + ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: StrCast(Doc.UserDoc().fontFamily) })] : []), + ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: StrCast(Doc.UserDoc().fontSize, "") })] : []), + ...(Doc.UserDoc().fontWeight === "bold" ? [schema.mark(schema.marks.strong)] : [])]; } } @@ -1596,7 +1604,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp background: this.props.background ? this.props.background : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), color: this.props.color ? this.props.color : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), fontSize: this.props.fontSize ? this.props.fontSize : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), - fontWeight: Cast(this.layoutDoc._fontWeight, "number", null), + fontWeight: Cast(this.layoutDoc._fontWeight, "string", null) as any, fontFamily: StrCast(this.layoutDoc._fontFamily, "inherit"), pointerEvents: interactive ? undefined : "none", }} diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 9904a7939..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>(); @@ -39,22 +35,22 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { public _brushMap: Map<string, Set<Mark>> = new Map(); @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"; @@ -67,10 +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); + runInAction(() => { + RichTextMenu.Instance = this; + this._canFade = false; + //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); + this.Pinned = true; + }); } componentDidMount() { @@ -81,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 @@ -110,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)); @@ -125,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)))); @@ -142,17 +148,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } } - getBoldStatus() { - if (this.view && this.TextView.props.isSelected(true)) { - 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) { - return path[i].attrs.strong; - } - } - } - } - // finds font sizes and families in selection getActiveAlignment() { if (this.view && this.TextView.props.isSelected(true)) { @@ -193,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 }; } @@ -251,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); }); } } @@ -270,56 +263,66 @@ 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; } }); } - toggleBold = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.strong, { strong: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + 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(); + } } - toggleUnderline = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.underline, { underline: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + 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(); + } } - toggleItalic = (view: EditorView, forceBool?: boolean) => { - const mark = view.state.schema.mark(view.state.schema.marks.em, { em: forceBool }); - this.setMark(mark, view.state, view.dispatch, false); - view.focus(); + 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(); + } } - setFontSize = (size: number, view: EditorView) => { - const fmark = view.state.schema.marks.pFontSize.create({ fontSize: size }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); - view.focus(); - this.updateMenu(view, undefined, this.props); + 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); + } } - setFontFamily = (family: string, view: EditorView) => { - const fmark = view.state.schema.marks.pFontFamily.create({ family: family }); - this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); - view.focus(); - this.updateMenu(view, undefined, this.props); + 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); + } } setHighlight(color: String, view: EditorView, dispatch: any) { @@ -330,13 +333,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { } setColor(color: String, view: EditorView, dispatch: any) { - const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color: color }); - if (view.state.selection.empty) { - dispatch(view.state.tr.addStoredMark(colorMark)); - return false; + 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); } - this.setMark(colorMark, view.state, dispatch, true); - view.focus(); } // TODO: remove doesn't work diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 655ee7e44..6103a28d6 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -61,13 +61,13 @@ export const marks: { [index: string]: MarkSpec } = { /** FONT SIZES */ pFontSize: { - attrs: { fontSize: { default: 10 } }, + attrs: { fontSize: { default: "10px" } }, parseDOM: [{ tag: "span", getAttrs(dom: any) { - return { fontSize: dom.style.fontSize ? Number(dom.style.fontSize.replace("px", "")) : "" }; + return { fontSize: dom.style.fontSize ? dom.style.fontSize.toString() : "" }; } }], - toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize}px;` }] : ['span', 0] + toDOM: (node) => node.attrs.fontSize ? ['span', { style: `font-size: ${node.attrs.fontSize};` }] : ['span', 0] }, /* FONTS */ |
