diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/EquationBox.tsx | 65 | ||||
-rw-r--r-- | src/client/views/nodes/LinkBox.tsx | 38 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/EquationEditor.tsx | 11 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextMenu.tsx | 10 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 2 |
5 files changed, 72 insertions, 54 deletions
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index f98b622e8..576b5bbe0 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -1,7 +1,6 @@ import { action, computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DivHeight, DivWidth } from '../../../ClientUtils'; import { Doc } from '../../../fields/Doc'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; @@ -39,15 +38,6 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() { Doc.SetSelectOnLoad(undefined); } reaction( - () => StrCast(this.dataDoc.text), - text => { - if (text && text !== this._ref.current!.mathField.latex()) { - this._ref.current!.mathField.latex(text); - } - } - // { fireImmediately: true } - ); - reaction( () => this._props.isSelected(), selected => { if (this._ref.current) { @@ -63,15 +53,15 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() { @action keyPressed = (e: KeyboardEvent) => { - const _height = DivHeight(this._ref.current!.element?.current); - const _width = DivWidth(this._ref.current!.element?.current); if (e.key === 'Enter') { - const nextEq = Docs.Create.EquationDocument(e.shiftKey ? StrCast(this.dataDoc.text) : 'x', { + const nextEq = Docs.Create.EquationDocument(e.shiftKey ? StrCast(this.dataDoc.text) : '', { title: '# math', - _width, - _height: 20, + _width: NumCast(this.layoutDoc._width), + _height: NumCast(this.layoutDoc._height), + nativeHeight: NumCast(this.dataDoc.nativeHeight), + nativeWidth: NumCast(this.dataDoc.nativeWidth), x: NumCast(this.layoutDoc.x), - y: NumCast(this.layoutDoc.y) + _height + 10, + y: NumCast(this.layoutDoc.y) + NumCast(this.Document._height) + 10, backgroundColor: StrCast(this.Document.backgroundColor), color: StrCast(this.Document.color), fontSize: this.fontSize, @@ -100,49 +90,46 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() { this.dataDoc.text = str; }; - updateSize = () => { - const style = this._ref.current?.element.current && getComputedStyle(this._ref.current.element.current); - if (style?.width.endsWith('px') && style?.height.endsWith('px')) { - const mathWidth = Math.max(35, NumCast(this.layoutDoc.xMargin) * 2 + Number(style.width.replace('px', ''))); - const mathHeight = Math.max(20, NumCast(this.layoutDoc.yMargin) * 2 + Number(style.height.replace('px', ''))); - if (this.layoutDoc._nativeWidth) { - // if equation has been scaled then editing the expression must also edit the native dimensions to keep the aspect ratio - const prevNwidth = NumCast(this.layoutDoc._nativeWidth); - this.layoutDoc._nativeWidth = mathWidth; - this.layoutDoc._nativeHeight = mathHeight; - this.layoutDoc._width = mathWidth * (NumCast(this.layoutDoc._width) / prevNwidth); - this.layoutDoc._height = mathHeight * (NumCast(this.layoutDoc._width) / mathWidth); - } else { - this.layoutDoc._width = mathWidth; - this.layoutDoc._height = mathHeight; - } - } + updateSize = (mathSpan: HTMLSpanElement) => { + const style = getComputedStyle(mathSpan); + const styleWidth = Number(style.width.replace('px', '') || 0); + const styleHeight = Number(style.height.replace('px', '') || 0); + const mathWidth = Math.max(35, NumCast(this.layoutDoc.xMargin) * 2 + styleWidth); + const mathHeight = Math.max(20, NumCast(this.layoutDoc.yMargin) * 2 + styleHeight); + const nScale = !this.dataDoc.nativeWidth ? 1 + : (prevNwidth => { // if equation has been scaled then editing the expression must also edit the native dimensions to keep the aspect ratio + [this.dataDoc.nativeWidth, this.dataDoc.nativeHeight] = [mathWidth, mathHeight]; + return NumCast(this.layoutDoc._width) / prevNwidth; + })(NumCast(this.dataDoc.nativeWidth)); // prettier-ignore + + this.layoutDoc._width = mathWidth * nScale; + this.layoutDoc._height = mathHeight * nScale; }; render() { TraceMobx(); const scale = this._props.NativeDimScaling?.() || 1; return ( <div - ref={() => this.updateSize()} + ref={r => r && this._ref.current?.element.current && this.updateSize(this._ref.current?.element.current)} className="equationBox-cont" + onKeyDown={e => e.stopPropagation()} onPointerDown={e => !e.ctrlKey && e.stopPropagation()} onBlur={() => { FormattedTextBox.LiveTextUndo?.end(); }} style={{ - minWidth: `${100 / scale}%`, transform: `scale(${scale})`, + minWidth: `${100 / scale}%`, height: `${100 / scale}%`, - pointerEvents: !this._props.isSelected() ? 'none' : undefined, + pointerEvents: !this._props.isContentActive() ? 'none' : undefined, fontSize: this.fontSize, color: this.fontColor, paddingLeft: NumCast(this.layoutDoc.xMargin), paddingRight: NumCast(this.layoutDoc.xMargin), paddingTop: NumCast(this.layoutDoc.yMargin), paddingBottom: NumCast(this.layoutDoc.yMargin), - }} - onKeyDown={e => e.stopPropagation()}> - <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, 'x')} spaceBehavesLikeTab onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" /> + }}> + <EquationEditor ref={this._ref} value={StrCast(this.dataDoc.text, '')} spaceBehavesLikeTab onChange={this.onChange} autoCommands="pi theta sqrt sum prod alpha beta gamma rho" autoOperatorNames="sin cos tan" /> </div> ); } diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index c62a8afb1..d5dc256d9 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -20,6 +20,7 @@ import { StyleProp } from '../StyleProp'; import { ComparisonBox } from './ComparisonBox'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { RichTextMenu } from './formattedText/RichTextMenu'; import './LinkBox.scss'; @observer @@ -29,6 +30,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { } _hackToSeeIfDeleted: NodeJS.Timeout | undefined; _disposers: { [name: string]: IReactionDisposer } = {}; + _divRef: HTMLDivElement | null = null; @observable _forceAnimate: number = 0; // forces xArrow to animate when a transition animation is detected on something that affects an anchor @observable _hide = false; // don't render if anchor is not visible since that breaks xAnchor @@ -78,6 +80,24 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { })) // prettier-ignore ); } + /** + * When an IconButton is clicked, it will receive focus. However, we don't want that since we want or need that since we really want + * to maintain focus in the label's editing div (and cursor position). so this relies on IconButton's having a tabindex set to -1 so that + * we can march up the tree from the 'relatedTarget' to determine if the loss of focus was caused by a fonticonbox. If it is, we then + * restore focus + * @param e focusout event on the editing div + */ + keepFocus = (e: FocusEvent) => { + if (e.relatedTarget instanceof HTMLElement && e.relatedTarget.tabIndex === -1) { + for (let ele: HTMLElement | null = e.relatedTarget; ele; ele = (ele as HTMLElement)?.parentElement) { + if (['listItem-container', 'fonticonbox'].includes((ele as HTMLElement)?.className ?? '')) { + console.log('RESTORE :', document.activeElement, this._divRef); + this._divRef?.focus(); + break; + } + } + } + }; render() { TraceMobx(); @@ -98,7 +118,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { a.Document[DocCss]; b.Document[DocCss]; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const axf = a.screenToViewTransform(); // these force re-render when a or b moves (so do NOT remove) const bxf = b.screenToViewTransform(); const scale = docView?.screenToViewTransform().Scale ?? 1; @@ -157,7 +176,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { const fontFamily = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontFamily) as string; const fontSize = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) as number; const fontColor = (c => (c !== 'transparent' ? c : undefined))(StrCast(this.layoutDoc.link_fontColor)); - // eslint-disable-next-line camelcase const { stroke_markerScale: strokeMarkerScale, stroke_width: strokeRawWidth, stroke_startMarker: strokeStartMarker, stroke_endMarker: strokeEndMarker, stroke_dash: strokeDash } = this.Document; const strokeWidth = NumCast(strokeRawWidth, 1); @@ -197,8 +215,23 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { <div id={this.DocumentView?.().DocUniqueId} className="linkBox-label" + tabIndex={-1} + ref={r => (this._divRef = r)} + onPointerDown={e => e.stopPropagation()} + onFocus={() => { + RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, this.dataDoc); + this._divRef?.removeEventListener('focusout', this.keepFocus); + this._divRef?.addEventListener('focusout', this.keepFocus); + }} + onBlur={() => { + if (document.activeElement !== this._divRef && document.activeElement?.parentElement !== this._divRef) { + this._divRef?.removeEventListener('focusout', this.keepFocus); + RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined); + } + }} style={{ borderRadius: '8px', + transform: `scale(${1 / scale})`, pointerEvents: this._props.isDocumentActive?.() ? 'all' : undefined, fontSize, fontFamily /* , fontStyle: 'italic' */, @@ -250,7 +283,6 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() { return ( <div className={`linkBox-container${this._props.isContentActive() ? '-interactive' : ''}`} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string }}> <ComparisonBox - // eslint-disable-next-line react/jsx-props-no-spreading {...this.props} // fieldKey="link_anchor" setHeight={emptyFunction} diff --git a/src/client/views/nodes/formattedText/EquationEditor.tsx b/src/client/views/nodes/formattedText/EquationEditor.tsx index 8bb4a0a26..48efa6e63 100644 --- a/src/client/views/nodes/formattedText/EquationEditor.tsx +++ b/src/client/views/nodes/formattedText/EquationEditor.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/require-default-props */ import React, { Component, createRef } from 'react'; // Import JQuery, required for the functioning of the equation editor @@ -7,6 +6,7 @@ import './EquationEditor.scss'; // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).jQuery = $; +// eslint-disable-next-line @typescript-eslint/no-require-imports require('mathquill/build/mathquill'); // eslint-disable-next-line @typescript-eslint/no-explicit-any (window as any).MathQuill = (window as any).MathQuill.getInterface(1); @@ -57,13 +57,8 @@ class EquationEditor extends Component<EquationEditorProps> { const config = { handlers: { edit: () => { - if (this.ignoreEditEvents > 0) { - this.ignoreEditEvents -= 1; - return; - } - if (this.mathField.latex() !== value) { - onChange(this.mathField.latex()); - } + if (this.ignoreEditEvents <= 0) onChange(this.mathField.latex()); + else this.ignoreEditEvents -= 1; }, enter: onEnter, }, diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 62fbd23ea..c0acbe36f 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -147,8 +147,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this._activeListType = this.getActiveListStyle(); this._activeAlignment = this.getActiveAlignment(); this._activeFitBox = BoolCast(refDoc[refField + 'fitBox'], BoolCast(Doc.UserDoc().fitBox)); - 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._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]) : '...'; @@ -374,7 +378,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.setMark(fmark, this.view.state, (tx: Transaction) => this.view!.dispatch(tx.addStoredMark(fmark)), true); this.view.focus(); } else if (this.dataDoc) { - this.dataDoc[`text_${fontField}`] = value; + this.dataDoc[`${Doc.LayoutFieldKey(this.dataDoc)}_${fontField}`] = value; this.updateMenu(undefined, undefined, undefined, this.dataDoc); } else { Doc.UserDoc()[fontField] = value; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 3d14ce4c0..c332c592b 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -390,7 +390,7 @@ export class RichTextRules { // %eq new InputRule(/%eq/, (state, match, start, end) => { const fieldKey = 'math' + Utils.GenerateGuid(); - this.TextBox.dataDoc[fieldKey] = 'y='; + this.TextBox.dataDoc[fieldKey] = ''; const tr = state.tr.setSelection(new TextSelection(state.tr.doc.resolve(end - 3), state.tr.doc.resolve(end))).replaceSelectionWith(schema.nodes.equation.create({ fieldKey })); return tr.setSelection(new NodeSelection(tr.doc.resolve(tr.selection.$from.pos - 1))); }), |