aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/EquationBox.tsx65
-rw-r--r--src/client/views/nodes/LinkBox.tsx38
-rw-r--r--src/client/views/nodes/formattedText/EquationEditor.tsx11
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx10
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts2
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)));
}),