import { action, computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; import './EquationBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; import EquationEditor from './formattedText/EquationEditor'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { Doc } from '../../../fields/Doc'; @observer export class EquationBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); } _ref: React.RefObject = React.createRef(); _liveTextUndo: UndoManager.Batch | undefined; // captured undo batch when typing a new text note into a collection constructor(props: FieldViewProps) { super(props); makeObservable(this); } componentDidMount() { this._props.setContentViewBox?.(this); if (DocumentView.SelectOnLoad === this.rootDoc && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) { this._liveTextUndo = FormattedTextBox.LiveTextUndo; FormattedTextBox.LiveTextUndo = undefined; this._props.select(false); this.dataDoc[Doc.LayoutDataKey(this.Document)] = FormattedTextBox.SelectOnLoadChar ?? ''; this._ref.current?.mathField.focus(); this.dataDoc[Doc.LayoutDataKey(this.Document)] === 'x' && this._ref.current?.mathField.select(); DocumentView.SetSelectOnLoad(undefined); FormattedTextBox.SelectOnLoadChar = ''; } reaction( () => this._props.isSelected(), selected => { if (this._ref.current) { if (selected) (this._ref.current.element.current?.children[0] as HTMLElement).addEventListener('keydown', this.keyPressed, true); else (this._ref.current.element.current?.children[0] as HTMLElement).removeEventListener('keydown', this.keyPressed); } }, { fireImmediately: true } ); } @computed get fontSize() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontSize) as string; } // prettier-ignore @computed get fontColor() { return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.FontColor) as string; } // prettier-ignore @action keyPressed = (e: KeyboardEvent) => { if (e.key === 'Enter') { const nextEq = Docs.Create.EquationDocument(e.shiftKey ? StrCast(this.dataDoc[Doc.LayoutDataKey(this.Document)]) : '', { title: '# math', _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) + NumCast(this.Document._height) + 10, backgroundColor: StrCast(this.Document.backgroundColor), color: StrCast(this.Document.color), fontSize: this.fontSize, }); DocumentView.SetSelectOnLoad(nextEq); this._props.addDocument?.(nextEq); e.stopPropagation(); } if (e.key === 'Tab') { const target = this.Document.isTemplateDoc ? this.rootDoc : this.Document; const graph = Docs.Create.FunctionPlotDocument([this.Document], { x: NumCast(target.x) + NumCast(this.layoutDoc._width), y: NumCast(target.y), _width: 400, _height: 300, backgroundColor: 'white', }); const link = DocUtils.MakeLink(this.Document, graph, { layout_isSvg: true, link_relationship: 'function', link_description: 'input' }); this._props.addDocument?.(graph); link && this._props.addDocument?.(link); e.stopPropagation(); } if (e.key === 'Backspace' && !this.dataDoc[Doc.LayoutDataKey(this.Document)]) this._props.removeDocument?.(this.Document); }; @undoBatch onChange = (str: string) => { this.dataDoc[Doc.LayoutDataKey(this.Document)] = str; }; 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; if (this.layoutDoc._nativeWidth) { this.layoutDoc._nativeWidth = mathWidth; this.layoutDoc._nativeHeight = mathHeight; } }; setRef = (r: HTMLDivElement) => r && this._ref.current?.element.current && this.updateSize(this._ref.current?.element.current); render() { TraceMobx(); const scale = this._props.NativeDimScaling?.() || 1; return (
e.stopPropagation()} onPointerDown={e => !e.ctrlKey && e.stopPropagation()} onBlur={() => this._liveTextUndo?.end()} style={{ transform: `scale(${scale})`, minWidth: `${100 / scale}%`, height: `${100 / scale}%`, 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), }}>
); } } Docs.Prototypes.TemplateMap.set(DocumentType.EQUATION, { layout: { view: EquationBox, dataField: 'text' }, options: { acl: '', _xMargin: 10, _yMargin: 10, fontSize: '14px', _nativeWidth: 40, _nativeHeight: 40, _layout_reflowHorizontal: false, _layout_reflowVertical: false, _layout_nativeDimEditable: false, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill', }, // systemIcon: 'BsSuperscript' + BsSubscript });