From 380ee1acac1c0b7972d7d423cf804af146dc0edf Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 10 Dec 2023 20:19:27 -0500 Subject: massive changes to use mobx 6 which means not accessing props directly in @computed functions. --- .../views/nodes/formattedText/DashDocView.tsx | 2 +- .../views/nodes/formattedText/DashFieldView.tsx | 14 +- .../views/nodes/formattedText/EquationEditor.scss | 468 +++++++++++++++++++++ .../views/nodes/formattedText/EquationEditor.tsx | 8 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 213 +++++----- .../views/nodes/formattedText/RichTextMenu.tsx | 48 ++- 6 files changed, 622 insertions(+), 131 deletions(-) create mode 100644 src/client/views/nodes/formattedText/EquationEditor.scss (limited to 'src/client/views/nodes/formattedText') diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index 4384c8958..6332b200d 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -82,7 +82,7 @@ export class DashDocViewInternal extends React.Component { _spanRef = React.createRef(); _disposers: { [name: string]: IReactionDisposer } = {}; _textBox: FormattedTextBox; - @observable _dashDoc: Doc | undefined; + @observable _dashDoc: Doc | undefined = undefined; @observable _finalLayout: any; @observable _width: number = 0; @observable _height: number = 0; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index a395296d0..19e14d5a7 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -25,7 +25,7 @@ export class DashFieldView { node: any; tbox: FormattedTextBox; - unclickable = () => !this.tbox.props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview); + unclickable = () => !this.tbox._props.isSelected() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview); constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { this.node = node; this.tbox = tbox; @@ -97,13 +97,13 @@ export class DashFieldViewInternal extends React.Component(); - @observable _dashDoc: Doc | undefined; + @observable _dashDoc: Doc | undefined = undefined; @observable _expanded = false; constructor(props: IDashFieldViewInternal) { super(props); this._fieldKey = this.props.fieldKey; - this._textBoxDoc = this.props.tbox.props.Document; + this._textBoxDoc = this.props.tbox.Document; if (this.props.docId) { DocServer.GetRefField(this.props.docId).then(action(dashDoc => dashDoc instanceof Doc && (this._dashDoc = dashDoc))); @@ -126,7 +126,7 @@ export class DashFieldViewInternal extends React.Component this.props.tbox.props.PanelWidth() - 20 : returnZero} + columnWidth={this.props.hideKey ? () => this.props.tbox._props.PanelWidth() - 20 : returnZero} selectedCell={() => [this._dashDoc!, 0]} fieldKey={this._fieldKey} rowHeight={returnZero} @@ -145,7 +145,7 @@ export class DashFieldViewInternal extends React.Component { - let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement(); + let container = this.props.tbox._props.DocumentView?.()._props.docViewPath().lastElement(); if (container) { const embedding = Doc.MakeEmbedding(container.Document); embedding._type_collection = CollectionViewType.Time; @@ -157,7 +157,7 @@ export class DashFieldViewInternal extends React.Component c.heading).indexOf(this._fieldKey) === -1 && list.push(new SchemaHeaderField(this._fieldKey, '#f1efeb')); list.map(c => c.heading).indexOf('text') === -1 && list.push(new SchemaHeaderField('text', '#f1efeb')); embedding._pivotField = this._fieldKey.startsWith('#') ? 'tags' : this._fieldKey; - this.props.tbox.props.addDocTab(embedding, OpenWhere.addRight); + this.props.tbox._props.addDocTab(embedding, OpenWhere.addRight); } }; @@ -177,7 +177,7 @@ export class DashFieldViewInternal extends React.Component {this.props.hideKey ? null : ( diff --git a/src/client/views/nodes/formattedText/EquationEditor.scss b/src/client/views/nodes/formattedText/EquationEditor.scss new file mode 100644 index 000000000..b0c17e56e --- /dev/null +++ b/src/client/views/nodes/formattedText/EquationEditor.scss @@ -0,0 +1,468 @@ +// using this import, we get runtime errors when trying to load the specified font-faces +// so we copy the .css and remove the @font-face imports + +// @import 'mathquill/build/mathquill.css' +/* + * MathQuill v0.10.1 http://mathquill.com + * by Han, Jeanine, and Mary maintainers@mathquill.com + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License, v. 2.0. If a copy of the MPL + * was not distributed with this file, You can obtain + * one at http://mozilla.org/MPL/2.0/. + */ +// @font-face { +// font-family: Symbola; +// src: url(font/Symbola.eot); +// src: +// local('Symbola Regular'), +// local('Symbola'), +// url(font/Symbola.woff2) format('woff2'), +// url(font/Symbola.woff) format('woff'), +// url(font/Symbola.ttf) format('truetype'), +// url(font/Symbola.otf) format('opentype'), +// url(font/Symbola.svg#Symbola) format('svg'); +// } +.mq-editable-field { + display: -moz-inline-box; + display: inline-block; +} +.mq-editable-field .mq-cursor { + border-left: 1px solid black; + margin-left: -1px; + position: relative; + z-index: 1; + padding: 0; + display: -moz-inline-box; + display: inline-block; +} +.mq-editable-field .mq-cursor.mq-blink { + visibility: hidden; +} +.mq-editable-field, +.mq-math-mode .mq-editable-field { + border: 1px solid gray; +} +.mq-editable-field.mq-focused, +.mq-math-mode .mq-editable-field.mq-focused { + -webkit-box-shadow: + #8bd 0 0 1px 2px, + inset #6ae 0 0 2px 0; + -moz-box-shadow: + #8bd 0 0 1px 2px, + inset #6ae 0 0 2px 0; + box-shadow: + #8bd 0 0 1px 2px, + inset #6ae 0 0 2px 0; + border-color: #709ac0; + border-radius: 1px; +} +.mq-math-mode .mq-editable-field { + margin: 1px; +} +.mq-editable-field .mq-latex-command-input { + color: inherit; + font-family: 'Courier New', monospace; + border: 1px solid gray; + padding-right: 1px; + margin-right: 1px; + margin-left: 2px; +} +.mq-editable-field .mq-latex-command-input.mq-empty { + background: transparent; +} +.mq-editable-field .mq-latex-command-input.mq-hasCursor { + border-color: ActiveBorder; +} +.mq-editable-field.mq-empty:after, +.mq-editable-field.mq-text-mode:after, +.mq-math-mode .mq-empty:after { + visibility: hidden; + content: 'c'; +} +.mq-editable-field .mq-cursor:only-child:after, +.mq-editable-field .mq-textarea + .mq-cursor:last-child:after { + visibility: hidden; + content: 'c'; +} +.mq-editable-field .mq-text-mode .mq-cursor:only-child:after { + content: ''; +} +.mq-editable-field.mq-text-mode { + overflow-x: auto; + overflow-y: hidden; +} +.mq-root-block, +.mq-math-mode .mq-root-block { + display: -moz-inline-box; + display: inline-block; + width: 100%; + padding: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + vertical-align: middle; +} +.mq-math-mode { + font-variant: normal; + font-weight: normal; + font-style: normal; + font-size: 115%; + line-height: 1; + display: -moz-inline-box; + display: inline-block; +} +.mq-math-mode .mq-non-leaf, +.mq-math-mode .mq-scaled { + display: -moz-inline-box; + display: inline-block; +} +.mq-math-mode var, +.mq-math-mode .mq-text-mode, +.mq-math-mode .mq-nonSymbola { + font-family: 'Times New Roman', Symbola, serif; + line-height: 0.9; +} +.mq-math-mode * { + font-size: inherit; + line-height: inherit; + margin: 0; + padding: 0; + border-color: black; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + box-sizing: border-box; +} +.mq-math-mode .mq-empty { + background: #ccc; +} +.mq-math-mode .mq-empty.mq-root-block { + background: transparent; +} +.mq-math-mode.mq-empty { + background: transparent; +} +.mq-math-mode .mq-text-mode { + display: inline-block; +} +.mq-math-mode .mq-text-mode.mq-hasCursor { + box-shadow: inset darkgray 0 0.1em 0.2em; + padding: 0 0.1em; + margin: 0 -0.1em; + min-width: 1ex; +} +.mq-math-mode .mq-font { + font: + 1em 'Times New Roman', + Symbola, + serif; +} +.mq-math-mode .mq-font * { + font-family: inherit; + font-style: inherit; +} +.mq-math-mode b, +.mq-math-mode b.mq-font { + font-weight: bolder; +} +.mq-math-mode var, +.mq-math-mode i, +.mq-math-mode i.mq-font { + font-style: italic; +} +.mq-math-mode var.mq-f { + margin-right: 0.2em; + margin-left: 0.1em; +} +.mq-math-mode .mq-roman var.mq-f { + margin: 0; +} +.mq-math-mode big { + font-size: 200%; +} +.mq-math-mode .mq-int > big { + display: inline-block; + -webkit-transform: scaleX(0.7); + -moz-transform: scaleX(0.7); + -ms-transform: scaleX(0.7); + -o-transform: scaleX(0.7); + transform: scaleX(0.7); + vertical-align: -0.16em; +} +.mq-math-mode .mq-int > .mq-supsub { + font-size: 80%; + vertical-align: -1.1em; + padding-right: 0.2em; +} +.mq-math-mode .mq-int > .mq-supsub > .mq-sup > .mq-sup-inner { + vertical-align: 1.3em; +} +.mq-math-mode .mq-int > .mq-supsub > .mq-sub { + margin-left: -0.35em; +} +.mq-math-mode .mq-roman { + font-style: normal; +} +.mq-math-mode .mq-sans-serif { + font-family: sans-serif, Symbola, serif; +} +.mq-math-mode .mq-monospace { + font-family: monospace, Symbola, serif; +} +.mq-math-mode .mq-overline { + border-top: 1px solid black; + margin-top: 1px; +} +.mq-math-mode .mq-underline { + border-bottom: 1px solid black; + margin-bottom: 1px; +} +.mq-math-mode .mq-binary-operator { + padding: 0 0.2em; + display: -moz-inline-box; + display: inline-block; +} +.mq-math-mode .mq-supsub { + text-align: left; + font-size: 90%; + vertical-align: -0.5em; +} +.mq-math-mode .mq-supsub.mq-sup-only { + vertical-align: 0.5em; +} +.mq-math-mode .mq-supsub.mq-sup-only .mq-sup { + display: inline-block; + vertical-align: text-bottom; +} +.mq-math-mode .mq-supsub .mq-sup { + display: block; +} +.mq-math-mode .mq-supsub .mq-sub { + display: block; + float: left; +} +.mq-math-mode .mq-supsub .mq-binary-operator { + padding: 0 0.1em; +} +.mq-math-mode .mq-supsub .mq-fraction { + font-size: 70%; +} +.mq-math-mode sup.mq-nthroot { + font-size: 80%; + vertical-align: 0.8em; + margin-right: -0.6em; + margin-left: 0.2em; + min-width: 0.5em; +} +.mq-math-mode .mq-paren { + padding: 0 0.1em; + vertical-align: top; + -webkit-transform-origin: center 0.06em; + -moz-transform-origin: center 0.06em; + -ms-transform-origin: center 0.06em; + -o-transform-origin: center 0.06em; + transform-origin: center 0.06em; +} +.mq-math-mode .mq-paren.mq-ghost { + color: silver; +} +.mq-math-mode .mq-paren + span { + margin-top: 0.1em; + margin-bottom: 0.1em; +} +.mq-math-mode .mq-array { + vertical-align: middle; + text-align: center; +} +.mq-math-mode .mq-array > span { + display: block; +} +.mq-math-mode .mq-operator-name { + font-family: Symbola, 'Times New Roman', serif; + line-height: 0.9; + font-style: normal; +} +.mq-math-mode var.mq-operator-name.mq-first { + padding-left: 0.2em; +} +.mq-math-mode var.mq-operator-name.mq-last, +.mq-math-mode .mq-supsub.mq-after-operator-name { + padding-right: 0.2em; +} +.mq-math-mode .mq-fraction { + font-size: 90%; + text-align: center; + vertical-align: -0.4em; + padding: 0 0.2em; +} +.mq-math-mode .mq-fraction, +.mq-math-mode .mq-large-operator, +.mq-math-mode x:-moz-any-link { + display: -moz-groupbox; +} +.mq-math-mode .mq-fraction, +.mq-math-mode .mq-large-operator, +.mq-math-mode x:-moz-any-link, +.mq-math-mode x:default { + display: inline-block; +} +.mq-math-mode .mq-numerator, +.mq-math-mode .mq-denominator { + display: block; +} +.mq-math-mode .mq-numerator { + padding: 0 0.1em; +} +.mq-math-mode .mq-denominator { + border-top: 1px solid; + float: right; + width: 100%; + padding: 0.1em; +} +.mq-math-mode .mq-sqrt-prefix { + padding-top: 0; + position: relative; + top: 0.1em; + vertical-align: top; + -webkit-transform-origin: top; + -moz-transform-origin: top; + -ms-transform-origin: top; + -o-transform-origin: top; + transform-origin: top; +} +.mq-math-mode .mq-sqrt-stem { + border-top: 1px solid; + margin-top: 1px; + padding-left: 0.15em; + padding-right: 0.2em; + margin-right: 0.1em; + padding-top: 1px; +} +.mq-math-mode .mq-vector-prefix { + display: block; + text-align: center; + line-height: 0.25em; + margin-bottom: -0.1em; + font-size: 0.75em; +} +.mq-math-mode .mq-vector-stem { + display: block; +} +.mq-math-mode .mq-large-operator { + vertical-align: -0.2em; + padding: 0.2em; + text-align: center; +} +.mq-math-mode .mq-large-operator .mq-from, +.mq-math-mode .mq-large-operator big, +.mq-math-mode .mq-large-operator .mq-to { + display: block; +} +.mq-math-mode .mq-large-operator .mq-from, +.mq-math-mode .mq-large-operator .mq-to { + font-size: 80%; +} +.mq-math-mode .mq-large-operator .mq-from { + float: right; + /* take out of normal flow to manipulate baseline */ + width: 100%; +} +.mq-math-mode, +.mq-math-mode .mq-editable-field { + cursor: text; + font-family: Symbola, 'Times New Roman', serif; +} +.mq-math-mode .mq-overarrow { + border-top: 1px solid black; + margin-top: 1px; + padding-top: 0.2em; +} +.mq-math-mode .mq-overarrow:before { + display: block; + position: relative; + top: -0.34em; + font-size: 0.5em; + line-height: 0em; + content: '\27A4'; + text-align: right; +} +.mq-math-mode .mq-overarrow.mq-arrow-left:before { + -moz-transform: scaleX(-1); + -o-transform: scaleX(-1); + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + filter: FlipH; + -ms-filter: 'FlipH'; +} +.mq-math-mode .mq-selection, +.mq-editable-field .mq-selection, +.mq-math-mode .mq-selection .mq-non-leaf, +.mq-editable-field .mq-selection .mq-non-leaf, +.mq-math-mode .mq-selection .mq-scaled, +.mq-editable-field .mq-selection .mq-scaled { + background: #b4d5fe !important; + background: Highlight !important; + color: HighlightText; + border-color: HighlightText; +} +.mq-math-mode .mq-selection .mq-matrixed, +.mq-editable-field .mq-selection .mq-matrixed { + background: #39f !important; +} +.mq-math-mode .mq-selection .mq-matrixed-container, +.mq-editable-field .mq-selection .mq-matrixed-container { + filter: progid:DXImageTransform.Microsoft.Chroma(color='#3399FF') !important; +} +.mq-math-mode .mq-selection.mq-blur, +.mq-editable-field .mq-selection.mq-blur, +.mq-math-mode .mq-selection.mq-blur .mq-non-leaf, +.mq-editable-field .mq-selection.mq-blur .mq-non-leaf, +.mq-math-mode .mq-selection.mq-blur .mq-scaled, +.mq-editable-field .mq-selection.mq-blur .mq-scaled, +.mq-math-mode .mq-selection.mq-blur .mq-matrixed, +.mq-editable-field .mq-selection.mq-blur .mq-matrixed { + background: #d4d4d4 !important; + color: black; + border-color: black; +} +.mq-math-mode .mq-selection.mq-blur .mq-matrixed-container, +.mq-editable-field .mq-selection.mq-blur .mq-matrixed-container { + filter: progid:DXImageTransform.Microsoft.Chroma(color='#D4D4D4') !important; +} +.mq-editable-field .mq-textarea, +.mq-math-mode .mq-textarea { + position: relative; + -webkit-user-select: text; + -moz-user-select: text; + user-select: text; +} +.mq-editable-field .mq-textarea *, +.mq-math-mode .mq-textarea *, +.mq-editable-field .mq-selectable, +.mq-math-mode .mq-selectable { + -webkit-user-select: text; + -moz-user-select: text; + user-select: text; + position: absolute; + clip: rect(1em 1em 1em 1em); + -webkit-transform: scale(0); + -moz-transform: scale(0); + -ms-transform: scale(0); + -o-transform: scale(0); + transform: scale(0); + resize: none; + width: 1px; + height: 1px; +} +.mq-math-mode .mq-matrixed { + background: white; + display: -moz-inline-box; + display: inline-block; +} +.mq-math-mode .mq-matrixed-container { + filter: progid:DXImageTransform.Microsoft.Chroma(color='white'); + margin-top: -0.1em; +} diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx index bde6c1315..07c70af77 100644 --- a/src/client/views/nodes/formattedText/EquationEditor.tsx +++ b/src/client/views/nodes/formattedText/EquationEditor.tsx @@ -3,8 +3,7 @@ import React, { Component, createRef } from 'react'; // Import JQuery, required for the functioning of the equation editor import $ from 'jquery'; -// Import the styles from the Mathquill editor -import 'mathquill/build/mathquill.css'; +import './EquationEditor.scss'; // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore @@ -14,6 +13,8 @@ window.jQuery = $; // @ts-ignore require('mathquill/build/mathquill'); +(window as any).MathQuill = (window as any).MathQuill.getInterface(1); + type EquationEditorProps = { onChange(latex: string): void; value: string; @@ -74,8 +75,7 @@ class EquationEditor extends Component { autoOperatorNames, }; - // @ts-ignore - this.mathField = (MathQuill as any).MathField(this.element.current, config); + this.mathField = (window as any).MathQuill.MathField(this.element.current, config); this.mathField.latex(value || ''); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 4f8e8769a..244de7849 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { isEqual } from 'lodash'; -import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, ObservableSet, override, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { baseKeymap, selectAll } from 'prosemirror-commands'; import { history } from 'prosemirror-history'; @@ -24,7 +24,7 @@ import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { ComputedField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, copyProps, emptyFunction, numberRange, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { DocServer } from '../../../DocServer'; @@ -129,7 +129,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent; + @override _props: React.PropsWithChildren; + constructor(props: React.PropsWithChildren) { super(props); + this._props = this._prevProps = props; + makeObservable(this); FormattedTextBox.Instance = this; this._recordingStart = Date.now(); } + componentDidUpdate() { + copyProps(this); + } + // removes all hyperlink anchors for the removed linkDoc // TODO: bcz: Argh... if a section of text has multiple anchors, this should just remove the intended one. // but since removing one anchor from the list of attr anchors isn't implemented, this will end up removing nothing. @@ -315,10 +323,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.getAnchor(true), targetCreator), e.pageX, e.pageY); + DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this._props.docViewPath().lastElement(), () => this.getAnchor(true), targetCreator), e.pageX, e.pageY); }); const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); - this.props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); + this._props.rootSelected?.() && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); let ele: Opt = undefined; try { const contents = window.getSelection()?.getRangeAt(0).cloneContents(); @@ -361,7 +369,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const newAutoLinks = new Set(); - const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords); + const oldAutoLinks = LinkManager.Links(this._props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords); if (this._editorView?.state.doc.textContent) { const isNodeSel = this._editorView.state.selection instanceof NodeSelection; const f = this._editorView.state.selection.from; @@ -456,7 +464,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const title = StrCast(this.dataDoc.title, Cast(this.dataDoc.title, RichTextField, null)?.Text); if ( - !this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing + !this._props.dontRegisterView && // (this._props.Document.isTemplateForField === "text" || !this._props.Document.isTemplateForField) && // only update the title if the data document's data field is changing (title.startsWith('-') || title.startsWith('@')) && this._editorView && !this.dataDoc.title_custom && @@ -499,7 +507,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term' }); tr = tr.addMark(pos, pos + node.nodeSize, link); @@ -568,7 +576,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (de.complete.annoDragData) { de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true); @@ -584,7 +591,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const localDelta = this.props + const localDelta = this._props .ScreenToLocalTransform() - .scale(this.props.NativeDimScaling?.() || 1) + .scale(this._props.NativeDimScaling?.() || 1) .transformDirection(delta[0], delta[1]); const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100; const width = NumCast(this.layoutDoc._width) + localDelta[0]; @@ -758,15 +765,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const batch = UndoManager.StartBatch('delete link'); LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); - // const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]); - // this.props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); + // const docAnnotations = DocListCast(this._props.dataDoc[this.fieldKey]); + // this._props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); - this.props.select(false); + this._props.select(false); setTimeout(batch.end); // wait for reaction to remove link from document }; @undoBatch - pinToPres = (anchor: Doc) => this.props.pinToPres(anchor, {}); + pinToPres = (anchor: Doc) => this._props.pinToPres(anchor, {}); @undoBatch makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle); @@ -776,7 +783,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.generateImage(), icon: 'star' }); optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' }); - this.props.renderDepth && + this._props.renderDepth && optionItems.push({ description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns', event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR), @@ -958,7 +965,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { GPTPopup.Instance?.setTextAnchor(this.getAnchor(false)); GPTPopup.Instance?.setImgTargetDoc(this.Document); - GPTPopup.Instance.addToCollection = this.props.addDocument; + GPTPopup.Instance.addToCollection = this._props.addDocument; GPTPopup.Instance.setImgDesc((this.dataDoc.text as RichTextField)?.Text); GPTPopup.Instance.generateImage(); }; @@ -1127,7 +1134,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); return focusSpeed; } else { - return this.props.focus(this.Document, options); + return this._props.focus(this.Document, options); } } }; @@ -1141,10 +1148,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Doc.RecordingEvent, this.breakupDictation); this._disposers.layout_autoHeight = reaction( @@ -1157,7 +1164,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.PanelWidth(), + () => this._props.PanelWidth(), width => this.tryUpdateScrollHeight() ); this._disposers.scrollHeight = reaction( @@ -1171,13 +1178,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)); if ( - (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this.props.isSelected()) && // + (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this._props.isSelected()) && // layout_autoHeight && newHeight && newHeight !== this.layoutDoc.height && - !this.props.dontRegisterView + !this._props.dontRegisterView ) { - this.props.setHeight?.(newHeight); + this._props.setHeight?.(newHeight); } }, { fireImmediately: !Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') } @@ -1219,7 +1226,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.Document[Pulls], + () => this._props.Document[Pulls], () => { if (!DocumentButtonBar.hasPulledHack) { DocumentButtonBar.hasPulledHack = true; @@ -1228,7 +1235,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.Document[Pushes], + () => this._props.Document[Pushes], () => { if (!DocumentButtonBar.hasPushedHack) { DocumentButtonBar.hasPushedHack = true; @@ -1244,7 +1251,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.rootSelected?.(), + () => this._props.rootSelected?.(), action(selected => { //selected && setTimeout(() => this.prepareForTyping()); if (FormattedTextBox._globalHighlights.has('Bold Text')) { @@ -1254,14 +1261,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._recordingDictation, () => { @@ -1276,7 +1283,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent NumCast(this.layoutDoc._layout_scrollTop), pos => { - if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) { + if (!this._ignoreScroll && this._scrollRef.current && !this._props.dontSelectOnLoad) { const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition); const durationMiliStr = viewTrans.match(/([0-9]*)ms/); const durationSecStr = viewTrans.match(/([0-9.]*)s/); @@ -1439,8 +1446,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent self.props.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); - return new RichTextMenuPlugin({ editorProps: this.props }); + runInAction(() => self._props.rootSelected?.() && RichTextMenu.Instance && (RichTextMenu.Instance.view = newView)); + return new RichTextMenuPlugin({ editorProps: this._props }); }, }); } @@ -1462,7 +1469,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined; if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) { const shift = Math.min(topOff ?? Number.MAX_VALUE, botOff ?? Number.MAX_VALUE); - const scrollPos = scrollRef.scrollTop + shift * self.props.ScreenToLocalTransform().Scale; + const scrollPos = scrollRef.scrollTop + shift * self._props.ScreenToLocalTransform().Scale; if (this._focusSpeed !== undefined) { scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed, scrollRef, scrollPos, 'ease', this._scrollStopper)); } else { @@ -1514,11 +1521,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const docView = DocumentManager.Instance.getDocumentView(audiodoc); if (!docView) { - this.props.addDocTab(audiodoc, OpenWhere.addBottom); + this._props.addDocTab(audiodoc, OpenWhere.addBottom); setTimeout(func); } else docView.ComponentView?.playFrom?.(timecode, Cast(anchor.timecodeToHide, 'number', null)); // bcz: would be nice to find the next audio tag in the doc and play until that }; @@ -1614,7 +1620,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 0 && !state.doc.resolve(xpos).node()?.isTextblock) { @@ -1651,7 +1657,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { FormattedTextBoxComment.textBox = this; - if (e.button === 0 && this.props.rootSelected?.() && !e.altKey && !e.ctrlKey && !e.metaKey) { + if (e.button === 0 && this._props.rootSelected?.() && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { // stop propagation if not in sidebar e.stopPropagation(); // if the text box is selected, then it consumes all click events @@ -1662,7 +1668,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - console.log('FOCUSED = ' + this.layoutDoc.title + ' ' + this.props.rootSelected?.()); //applyDevTools.applyDevTools(this._editorView); - this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props, this.layoutDoc); + this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this._props, this.layoutDoc); e.stopPropagation(); }; onClick = (e: React.MouseEvent): void => { - if (!this.props.isContentActive()) return; + if (!this._props.isContentActive()) return; if ((e.nativeEvent as any).handledByInnerReactInstance) { e.stopPropagation(); return; @@ -1709,7 +1714,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (!LinkDocPreview.LinkInfo && this._scrollRef.current) { - if (!this.props.dontSelectOnLoad) { + if (!this._props.dontSelectOnLoad) { this._ignoreScroll = true; this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop; this._ignoreScroll = false; @@ -1861,7 +1866,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); + const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; if (children && !SnappingManager.GetIsDragging()) { const toNum = (val: string) => Number(val.replace('px', '').replace('auto', '0')); @@ -1871,7 +1876,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent p + toHgt(child), margins); const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight); - if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) { + if (this._props.setHeight && scrollHeight && !this._props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => (this.dataDoc[this.fieldKey + '_scrollHeight'] = scrollHeight); @@ -1883,8 +1888,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent BoolCast(this.props.Document._freeform_fitContentsToBox); - sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); + fitContentsToBox = () => BoolCast(this._props.Document._freeform_fitContentsToBox); + sidebarContentScaling = () => (this._props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => { if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar(); return this.addDocument(doc, sidebarKey); @@ -1892,12 +1897,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey); sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey); setSidebarHeight = (height: number) => (this.dataDoc[this.SidebarKey + '_height'] = height); - sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth(); + sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this._props.PanelWidth(); sidebarScreenToLocal = () => - this.props + this._props .ScreenToLocalTransform() - .translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0) - .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1)); + .translate(-(this._props.PanelWidth() - this.sidebarWidth()) / (this._props.NativeDimScaling?.() || 1), 0) + .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this._props.NativeDimScaling?.() || 1)); @computed get audioHandle() { return !this._recordingDictation ? null : ( @@ -1920,9 +1925,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent d?.author).length; const color = !annotated ? Colors.WHITE : Colors.BLACK; - const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.WidgetColor + (annotated ? ':annotated' : '')); + const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.WidgetColor + (annotated ? ':annotated' : '')); - return !annotated && (!this.props.isContentActive() || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? null : ( + return !annotated && (!this._props.isContentActive() || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None) ? null : (
) : ( -
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> +
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this._props.DocumentView?.()!, false), true)}> { if (this.layoutDoc._layout_enableAltContentUI) { - const usePath = this.layoutDoc[`_${this.props.fieldKey}_usePath`]; - this.layoutDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined; + const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`]; + this.layoutDoc[`_${this._props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined; } }; @computed get overlayAlternateIcon() { - const usePath = this.layoutDoc[`_${this.props.fieldKey}_usePath`]; + const usePath = this.layoutDoc[`_${this._props.fieldKey}_usePath`]; return ( setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => this.cycleAlternateText())} style={{ - display: this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'flex' : 'none', + display: this._props.isContentActive() && !SnappingManager.GetIsDragging() ? 'flex' : 'none', background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray', color: usePath === undefined ? 'black' : 'white', }}> @@ -2033,9 +2038,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ); } - @computed get fieldKey() { - const usePath = StrCast(this.layoutDoc[`${this.props.fieldKey}_usePath`]); - return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this.props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : ''); + get fieldKey() { + const usePath = StrCast(this.layoutDoc[`${this._props.fieldKey}_usePath`]); + return this._props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering || this._props.isContentActive()) ? `_${usePath.replace(':hover', '')}` : ''); } @observable _isHovering = false; onPassiveWheel = (e: WheelEvent) => { @@ -2049,14 +2054,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent + if (this._props.isContentActive()) { + const scale = this._props.NativeDimScaling?.() || 1; + const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., const height = Number(styleFromLayoutString.height?.replace('px', '')); // prevent default if selected || child is active but this doc isn't scrollable if ( - (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this.props.PanelHeight()) / scale) && // - (this.props.rootSelected?.() || this.isAnyChildContentActive()) + (this._scrollRef.current?.scrollHeight ?? 0) <= Math.ceil((height ? height : this._props.PanelHeight()) / scale) && // + (this._props.rootSelected?.() || this.isAnyChildContentActive()) ) { e.preventDefault(); } @@ -2065,25 +2070,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); - const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0); - const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this.props, scale); // this converts any expressions in the format string to style props. e.g., + setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); + const paddingX = NumCast(this.layoutDoc._xMargin, this._props.xPadding || 0); + const paddingY = NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0); + const styleFromLayoutString = Doc.styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., return styleFromLayoutString?.height === '0px' ? null : (
- {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection} - {this.noSidebar || this.Document._layout_noSidebar || this.props.dontSelectOnLoad || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle} + {this.noSidebar || this._props.dontSelectOnLoad || !this.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection} + {this.noSidebar || this.Document._layout_noSidebar || this._props.dontSelectOnLoad || this.Document._createDocOnCR || this.layoutDoc._chromeHidden ? null : this.sidebarHandle} {this.audioHandle} {this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 7ce06cf7f..d7e799161 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, override, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { lift, wrapIn } from 'prosemirror-commands'; import { Mark, MarkType, Node as ProsNode, ResolvedPos } from 'prosemirror-model'; @@ -10,7 +10,7 @@ import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { Doc } from '../../../../fields/Doc'; import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; -import { numberRange } from '../../../../Utils'; +import { copyProps, numberRange } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -63,8 +63,12 @@ export class RichTextMenu extends AntimodeMenu { @observable private showLinkDropdown: boolean = false; _reaction: IReactionDisposer | undefined; - constructor(props: Readonly<{}>) { + _prevProps: AntimodeMenuProps; + @override _props: AntimodeMenuProps; + constructor(props: AntimodeMenuProps) { super(props); + this._props = this._prevProps = props; + makeObservable(this); runInAction(() => { RichTextMenu.Instance = this; this.updateMenu(undefined, undefined, props, this.layoutDoc); @@ -107,6 +111,9 @@ export class RichTextMenu extends AntimodeMenu { return BoolCast(this.layoutDoc?.layout_centered); } _disposer: IReactionDisposer | undefined; + componentDidUpdate() { + copyProps(this); + } componentDidMount() { this._disposer = reaction( () => SelectionManager.Views().slice(), @@ -361,7 +368,7 @@ export class RichTextMenu extends AntimodeMenu { } 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.layoutDoc); + this.updateMenu(this.view, undefined, this._props, this.layoutDoc); }; setFontFamily = (family: string) => { @@ -370,7 +377,7 @@ export class RichTextMenu extends AntimodeMenu { 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.layoutDoc); + this.updateMenu(this.view, undefined, this._props, this.layoutDoc); }; setHighlight(color: string) { @@ -379,7 +386,7 @@ export class RichTextMenu extends AntimodeMenu { 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.layoutDoc); + this.updateMenu(this.view, undefined, this._props, this.layoutDoc); } setColor(color: string) { @@ -388,7 +395,7 @@ export class RichTextMenu extends AntimodeMenu { 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.layoutDoc); + this.updateMenu(this.view, undefined, this._props, this.layoutDoc); } // TODO: remove doesn't work @@ -429,7 +436,7 @@ export class RichTextMenu extends AntimodeMenu { } } this.view.focus(); - this.updateMenu(this.view, undefined, this.props, this.layoutDoc); + this.updateMenu(this.view, undefined, this._props, this.layoutDoc); }; insertSummarizer(state: EditorState, dispatch: any) { @@ -669,7 +676,6 @@ export class RichTextMenu extends AntimodeMenu { }; @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); @@ -816,6 +822,18 @@ export class ButtonDropdown extends React.Component { @observable private showDropdown: boolean = false; private ref: HTMLDivElement | null = null; + _prevProps: React.PropsWithChildren; + @observable _props: React.PropsWithChildren; + constructor(props: React.PropsWithChildren) { + super(props); + this._props = this._prevProps = props; + makeObservable(this); + } + + componentDidUpdate() { + copyProps(this); + } + componentDidMount() { document.addEventListener('pointerdown', this.onBlur); } @@ -850,22 +868,22 @@ export class ButtonDropdown extends React.Component { render() { return (
(this.ref = node)}> - {!this.props.pdf ? ( -
- {this.props.button} -
+ {!this._props.pdf ? ( +
+ {this._props.button} +
) : ( <> - {this.props.button} + {this._props.button} )} - {this.showDropdown ? this.props.dropdownContent : null} + {this.showDropdown ? this._props.dropdownContent : null}
); } -- cgit v1.2.3-70-g09d2