- {`wiki:phrase`}
+ {`(@wiki:phrase)`}
{` display wikipedia page for entered text (terminate with carriage return)`}
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 36bd037ca..3a2509c3d 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -48,9 +48,9 @@ export class LinkBox extends ViewBoxBaseComponent() {
componentDidMount() {
this._props.setContentViewBox?.(this);
this._disposers.deleting = reaction(
- () => (!this.anchor1 || !this.anchor2) && this.DocumentView?.() && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView!())),
+ () => !this.anchor1 && !this.anchor2 && this.DocumentView?.() && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView!())),
empty => empty && ((this._hackToSeeIfDeleted = setTimeout(() =>
- (!this.anchor1 || !this.anchor2) && this._props.removeDocument?.(this.Document)
+ (!this.anchor1 && !this.anchor2) && this._props.removeDocument?.(this.Document)
)), 1000) // prettier-ignore
);
this._disposers.dragging = reaction(
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 4b6ee7d72..c9c8f9260 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -185,8 +185,9 @@ export class LinkDocPreview extends ObservableReactComponent doc.type === DocumentType.WEB)
+ .lastElement() ?? Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true });
DocumentManager.Instance.showDocument(webDoc, {
openLocation: OpenWhere.lightbox,
willPan: true,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 5c779734d..66df1eaf2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1607,7 +1607,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
- } else {
+ } else if (node) {
try {
editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos))));
} catch (e) {
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 483035e86..5e53a019e 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -295,7 +295,7 @@ export class RichTextRules {
// create a hyperlink to a titled document
// @()
- new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@:\.\? \-0-9]+)\)/), (state, match, start, end) => {
+ new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@\.\? \-0-9]+)\)/), (state, match, start, end) => {
const docTitle = match[2];
const prefixLength = '@('.length;
if (docTitle) {
@@ -380,17 +380,17 @@ export class RichTextRules {
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
- // wiki:title
- new InputRule(new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), (state, match, start, end) => {
- const title = match[1];
+ // @(wiki:title)
+ new InputRule(new RegExp(/@\(wiki:([a-zA-Z_@:\.\?\-0-9 ]+)\)$/), (state, match, start, end) => {
+ const title = match[1].trim().replace(/ /g, '_');
this.TextBox.EditorView?.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))));
this.TextBox.makeLinkAnchor(undefined, 'add:right', `https://en.wikipedia.org/wiki/${title.trim()}`, 'wikipedia reference');
const fstate = this.TextBox.EditorView?.state;
if (fstate) {
- const tr = fstate?.tr.deleteRange(start, start + 5);
- return tr.setSelection(new TextSelection(tr.doc.resolve(end - 5))).insertText(' ');
+ const tr = fstate?.tr.deleteRange(start, start + '@(wiki:'.length);
+ return tr.setSelection(new TextSelection(tr.doc.resolve(end - '@(wiki:'.length))).insertText(' ');
}
return state.tr;
}),
--
cgit v1.2.3-70-g09d2
From 47ae05389d7ce564efde19b7a639b38842759cc1 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sun, 31 Mar 2024 00:09:33 -0400
Subject: fixed dropdown in title bar location. moved rotate button up to not
interfere with button bar. don't blur title when editing it. fix drawgging
rotate center on templates. support setting alternate colors for text docs
-- pretty hacky. fixed tabbing through dashFieldViews.
---
src/client/views/DocumentDecorations.scss | 2 +-
src/client/views/DocumentDecorations.tsx | 21 +++++++++++++++------
src/client/views/StyleProvider.tsx | 5 ++++-
.../collectionFreeForm/CollectionFreeFormView.tsx | 2 +-
src/client/views/global/globalScripts.ts | 4 +++-
src/client/views/nodes/DocumentView.scss | 6 ++++++
src/client/views/nodes/DocumentView.tsx | 6 +++++-
.../views/nodes/formattedText/DashFieldView.tsx | 17 ++++++++++-------
.../views/nodes/formattedText/FormattedTextBox.tsx | 7 +++++--
9 files changed, 50 insertions(+), 20 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss
index ac0ef054c..239c0a977 100644
--- a/src/client/views/DocumentDecorations.scss
+++ b/src/client/views/DocumentDecorations.scss
@@ -14,7 +14,7 @@ $resizeHandler: 8px;
height: 30;
width: 30;
right: -40;
- bottom: -40;
+ bottom: -20;
//top: calc(50% - 15px);
position: absolute;
pointer-events: all;
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 6698cd5bc..951e0912c 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -73,11 +73,18 @@ export class DocumentDecorations extends ObservableReactComponent {
+ let inputting = false;
+ if (this._titleControlString.startsWith('$')) {
+ const titleFieldKey = this._titleControlString.substring(1);
+ if (SelectionManager.Views[0]?.Document[titleFieldKey] !== this._accumulatedTitle) {
+ inputting = true;
+ }
+ }
const center = {x: (this.Bounds.x+this.Bounds.r)/2, y: (this.Bounds.y+this.Bounds.b)/2};
const {x,y} = Utils.rotPt(e.clientX - center.x,
e.clientY - center.y,
NumCast(SelectionManager.Views.lastElement()?.screenToViewTransform().Rotate));
- (this._showNothing = !DocumentButtonBar.Instance?._tooltipOpen && !(this.Bounds.x !== Number.MAX_VALUE && //
+ (this._showNothing = !inputting && !DocumentButtonBar.Instance?._tooltipOpen && !(this.Bounds.x !== Number.MAX_VALUE && //
(this.Bounds.x > center.x+x || this.Bounds.r < center.x+x ||
this.Bounds.y > center.y+y || this.Bounds.b < center.y+y )));
})); // prettier-ignore
@@ -336,8 +343,8 @@ export class DocumentDecorations extends ObservableReactComponent // return false to keep getting events
this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean,
action(e => (this._isRotating = false)), // upEvent
- action(e => (seldocview.Document.rotation_centerX = seldocview.Document.rotation_centerY = 0))
+ action(e => (seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0)),
+ true
); // prettier-ignore
+ e.stopPropagation();
};
@action
@@ -617,7 +626,7 @@ export class DocumentDecorations extends ObservableReactComponent {
this._editingTitle = false;
- !hideTitle && this.titleBlur();
+ this.titleBlur();
})}
onChange={action(e => !hideTitle && (this._accumulatedTitle = e.target.value))}
onKeyDown={hideTitle ? emptyFunction : this.titleEntered}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 749198fe6..dcec2fe3d 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -200,7 +200,10 @@ export function DefaultStyleProvider(doc: Opt, props: Opt = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?.backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''));
+ const dataKey = doc ? Doc.LayoutFieldKey(doc) : "";
+ const usePath = StrCast(doc?.[dataKey + "_usePath"]);
+ const alternate = usePath.includes(":hover") ? ( doc?.isHovering ? '_' + usePath.replace(":hover","") : "") : usePath ? "_" +usePath:usePath;
+ let docColor: Opt = StrCast(doc?.[fieldKey+alternate], StrCast(doc?.['backgroundColor' +alternate], isCaption ? 'rgba(0,0,0,0.4)' : ''));
if (doc?.[StrCast(doc?.layout_fieldKey)] instanceof Doc) docColor = StrCast(doc._backgroundColor,docColor)
// prettier-ignore
switch (layoutDoc?.type) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d435173f3..791124f50 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1288,7 +1288,7 @@ export class CollectionFreeFormView extends CollectionSubView div {
+ transform-origin: top left !important;
+ }
+}
+
.contentFittingDocumentView {
position: relative;
display: flex;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index a04030a5f..fc2da18d9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -843,7 +843,11 @@ export class DocumentViewInternal extends DocComponent
- {!dropdownWidth ? null : {this.fieldsDropdown(showTitle)}
}
+ {!dropdownWidth ? null : (
+
+ {this.fieldsDropdown(showTitle)}
+
+ )}
this._nodeSelected;
@@ -38,6 +39,7 @@ export class DashFieldView {
const self = this;
this.node = node;
this.tbox = tbox;
+ this.getpos = getPos;
this.dom = document.createElement('div');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
@@ -54,13 +56,13 @@ export class DashFieldView {
const editor = tbox.EditorView;
if (editor) {
const state = editor.state;
- for (var i = state.selection.to; i < state.doc.content.size; i++) {
+ for (var i = self.getpos() + 1; i < state.doc.content.size; i++) {
if (state.doc.nodeAt(i)?.type.name === state.schema.nodes.dashField.name) {
editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(i))));
return;
}
}
- tBox.setFocus(state.selection.to);
+ // tBox.setFocus(state.selection.to);
}
}
};
@@ -137,7 +139,7 @@ export class DashFieldViewInternal extends ObservableReactComponent (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc);
+ const setDoc = action((doc: Doc) => (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc));
if (this._props.docId) {
DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
@@ -255,7 +257,7 @@ export class DashFieldViewInternal extends ObservableReactComponent | undefined) => {
- event && this._dashDoc && (this._dashDoc[this._fieldKey] = event.target.value);
+ event && this._dashDoc && (this._dashDoc[this._fieldKey] = event.target.value === '-unset-' ? undefined : event.target.value);
};
@computed get values() {
@@ -281,13 +283,14 @@ export class DashFieldViewInternal extends ObservableReactComponent
)}
{this._props.fieldKey.startsWith('#') || this._hideValue ? null : this.fieldValueContent}
- {/* {!this.values.length ? null : (
-
+ {!this.values.length ? null : (
+
+ -unset-
{this.values.map(val => (
{val.label}
))}
- )} */}
+ )}
);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 66df1eaf2..3700b08d6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2064,8 +2064,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._isHovering = true))}
- onPointerLeave={action(() => (this._isHovering = false))}
+ onPointerEnter={action(() => {
+ this._isHovering = true;
+ this.layoutDoc[`_${this._props.fieldKey}_usePath`] && (this.Document.isHovering = true);
+ })}
+ onPointerLeave={action(() => (this.Document.isHovering = this._isHovering = false))}
ref={r => {
this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
this._oldWheel = r;
--
cgit v1.2.3-70-g09d2
From 1e37adacfe5b44a71ed7d7dfd785c6c2d0808eae Mon Sep 17 00:00:00 2001
From: bobzel
Date: Sun, 31 Mar 2024 08:43:38 -0400
Subject: fixed divider column for notetaking view. fixed scrolling of text
docs. fixed keeping user docs in system button bars.
---
src/client/documents/Documents.ts | 2 +-
.../views/collections/CollectionNoteTakingViewDivider.tsx | 12 ++++++++++++
src/client/views/nodes/formattedText/FormattedTextBox.scss | 1 +
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 +
4 files changed, 15 insertions(+), 1 deletion(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 8dccdeba9..6a0d45543 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1591,7 +1591,7 @@ export namespace DocUtils {
}
});
items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item));
- items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
+ items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
}
return doc;
}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 5e4bce19d..50a97b978 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -51,6 +51,18 @@ export class CollectionNoteTakingViewDivider extends ObservableReactComponent
+ this.registerResizing(e)}
+ style={{
+ height: '95%',
+ width: 12,
+ borderRight: '4px solid #282828',
+ borderLeft: '4px solid #282828',
+ position: 'fixed',
+ pointerEvents: 'none',
+ }}
+ />
this.registerResizing(e)}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 03ff0436b..00d890860 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -156,6 +156,7 @@ audiotag:hover {
.formattedTextBox-inner,
.formattedTextBox-inner-minimal {
height: 100%;
+ overflow: auto;
white-space: pre-wrap;
.ProseMirror:hover {
background: rgba(200, 200, 200, 0.2);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3700b08d6..2deecb6ad 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2032,6 +2032,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
Date: Sun, 31 Mar 2024 23:09:53 -0400
Subject: fixed scrolling to targets in text views. fixed referencing fields
on another doc from text box. fixed '@name' technique for publishing
documents to also remove documents and work anywhere a title is set.
---
src/client/documents/Documents.ts | 5 ++++
src/client/util/Scripting.ts | 2 +-
src/client/views/DocumentButtonBar.tsx | 2 +-
src/client/views/DocumentDecorations.tsx | 6 ----
.../views/collections/CollectionDockingView.tsx | 25 ++++++++++++++--
.../collections/collectionFreeForm/MarqueeView.tsx | 2 +-
.../views/nodes/formattedText/DashFieldView.tsx | 8 ++---
.../views/nodes/formattedText/FormattedTextBox.tsx | 34 ++++++++++------------
.../views/nodes/formattedText/RichTextRules.ts | 13 +++------
src/client/views/nodes/formattedText/nodes_rts.ts | 1 -
src/fields/Doc.ts | 13 +++++++--
src/fields/util.ts | 16 +++++++++-
12 files changed, 78 insertions(+), 49 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6a0d45543..eb15c332f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1516,6 +1516,9 @@ export namespace DocUtils {
return linkDoc;
});
+ const a = source.layout_unrendered ? 'link_anchor_1.annotationOn' : 'link_anchor_1';
+ const b = target.layout_unrendered ? 'link_anchor_2.annotationOn' : 'link_anchor_2';
+
return makeLink(
Docs.Create.LinkDocument(
source,
@@ -1529,6 +1532,8 @@ export namespace DocUtils {
link_displayLine: linkSettings.link_displayLine,
link_relationship: linkSettings.link_relationship,
link_description: linkSettings.link_description,
+ x: ComputedField.MakeFunction(`(this.${a}.x+this.${b}.x)/2`) as any,
+ y: ComputedField.MakeFunction(`(this.${a}.y+this.${b}.y)/2`) as any,
link_autoMoveAnchors: true,
_lockedPosition: true,
_layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box)
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 31222aa50..422e708bc 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -250,7 +250,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
const outputText = host.readFile('file.js');
const diagnostics = ts.getPreEmitDiagnostics(program).concat(testResult.diagnostics);
-
+ if (script.startsWith('@')) options.typecheck = true; // need the compilation to fail so that the script will return itself as a string (instead of nothing)
const result = Run(outputText, paramNames, diagnostics, script, options);
if (options.globals) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index d65e0b406..15ce4c15f 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -416,7 +416,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
)}
{DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ? {this.endLinkButton}
: null}
- {Doc.noviceMode ? null : {this.templateButton}
}
+ {this.templateButton}
{!SelectionManager.Views?.some(v => v.allLinks.length) ? null : {this.followLinkButton}
}
{this.pinButton}
{this.recordButton}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 951e0912c..9e469ed1f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -131,12 +131,6 @@ export class DocumentDecorations extends ObservableReactComponent {
if (titleFieldKey === 'title') {
d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-');
- if (StrCast(d.Document.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
- Doc.RemFromMyPublished(d.Document);
- }
- if (!StrCast(d.Document.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
- Doc.AddToMyPublished(d.Document);
- }
}
KeyValueBox.SetField(d.Document, titleFieldKey, this._accumulatedTitle);
}),
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 25bfdb588..b2897a9b7 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -3,14 +3,14 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import * as GoldenLayout from '../../../client/goldenLayout';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
-import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { FieldValue, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
+import { GetEffectiveAcl, inheritParentAcls, SetPropSetterCb } from '../../../fields/util';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, emptyFunction, incrementTitleCopy } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
@@ -32,6 +32,7 @@ import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
import { TabDocView } from './TabDocView';
+import { ComputedField } from '../../../fields/ScriptField';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -313,8 +314,26 @@ export class CollectionDockingView extends CollectionSubView() {
}
};
+ /**
+ * This publishes Docs having titles starting with '@' to Doc.myPublishedDocs
+ * Once published, any text that uses the 'title' in its body will automatically
+ * be linked to this published document.
+ * @param target
+ * @param title
+ */
+ titleChanged = (target: any, value: any) => {
+ const title = Field.toString(value);
+ if (title.startsWith('@') && !title.substring(1).match(/[\(\)\[\]@]/) && title.length > 1) {
+ const embedding = DocListCast(target.proto_embeddings).lastElement();
+ embedding && Doc.AddToMyPublished(embedding);
+ } else if (!title.startsWith('@')) {
+ DocListCast(target.proto_embeddings).forEach(doc => Doc.RemFromMyPublished(doc));
+ }
+ };
+
componentDidMount: () => void = async () => {
this._unmounting = false;
+ SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 6b3a56b0b..6eca91e9d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -499,7 +499,7 @@ export class MarqueeView extends ObservableReactComponent
);
@@ -119,7 +118,6 @@ interface IDashFieldViewInternal {
height: number;
editable: boolean;
nodeSelected: () => boolean;
- dataDoc: boolean;
node: any;
getPos: any;
unclickable: () => boolean;
@@ -139,7 +137,7 @@ export class DashFieldViewInternal extends ObservableReactComponent (this._dashDoc = this._props.dataDoc ? doc[DocData] : doc));
+ const setDoc = action((doc: Doc) => (this._dashDoc = doc));
if (this._props.docId) {
DocServer.GetRefField(this._props.docId).then(dashDoc => dashDoc instanceof Doc && setDoc(dashDoc));
@@ -168,7 +166,7 @@ export class DashFieldViewInternal extends ObservableReactComponent !this._props.tbox.ProseRef?.contains(document.activeElement) && this._props.tbox._props.onBlur?.());
}
});
- selectedCell = (): [Doc, number] => [this._dashDoc!, 0];
+ selectedCell = (): [Doc, number] | undefined => (this._dashDoc ? [this._dashDoc, 0] : undefined);
columnWidth = () => Math.min(this._props.tbox._props.PanelWidth(), Math.max(50, this._props.tbox._props.PanelWidth() - 100)); // try to leave room for the fieldKey
// set the display of the field's value (checkbox for booleans, span of text for strings)
@@ -284,7 +282,7 @@ export class DashFieldViewInternal extends ObservableReactComponent
+
-unset-
{this.values.map(val => (
{val.label}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2deecb6ad..80e56efe0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -478,7 +478,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent FieldValue(this.dataDoc.title));
if (!(cfield instanceof ComputedField)) {
this.dataDoc.title = (prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '')).trim();
- if (str.startsWith('@') && str.length > 1) {
- Doc.AddToMyPublished(this.Document);
- }
}
}
};
@@ -510,7 +507,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent) => {
const editorView = this._editorView;
if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.Document)) {
- const autoLinkTerm = StrCast(target.title).replace(/^@/, '');
+ const autoLinkTerm = Field.toString(target.title as Field).replace(/^@/, '');
var alink: Doc | undefined;
this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => {
if (
@@ -1312,15 +1309,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent NumCast(this.layoutDoc._layout_scrollTop),
pos => {
- if (!this._ignoreScroll && this._scrollRef.current && !this._props.dontSelectOnLoad) {
+ if (!this._ignoreScroll && this.ProseRef && !this._props.dontSelectOnLoad) {
const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
if (duration) {
- this._scrollStopper = smoothScroll(duration, this._scrollRef.current, Math.abs(pos || 0), 'ease', this._scrollStopper);
+ this._scrollStopper = smoothScroll(duration, this.ProseRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
} else {
- this._scrollRef.current.scrollTo({ top: pos });
+ this.ProseRef.scrollTo({ top: pos });
}
}
},
@@ -1418,7 +1415,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
const docPos = editorView.coordsAtPos(editorView.state.selection.to);
const viewRect = self._ref.current!.getBoundingClientRect();
- const scrollRef = self._scrollRef.current;
+ const scrollRef = self.ProseRef;
const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
const botOff = docPos.bottom > viewRect.bottom ? docPos.bottom - viewRect.bottom : undefined;
if (((topOff && Math.abs(Math.trunc(topOff)) > 0) || (botOff && Math.abs(Math.trunc(botOff)) > 0)) && scrollRef) {
@@ -1750,20 +1747,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent document publishing to Doc.myPublishedDocs
+ const match = RTFCast(this.Document[this.fieldKey]).Text.match(/^(@[a-zA-Z][a-zA-Z_0-9 -]*[a-zA-Z_0-9-]+)/);
+ if (match) {
+ this.dataDoc.title_custom = true;
+ this.dataDoc.title = match[1]; // this triggers the collectionDockingView to publish this Doc
+ this.EditorView?.dispatch(this.EditorView?.state.tr.deleteRange(0, match[1].length + 1));
+ }
+
this.endUndoTypingBatch();
FormattedTextBox.LiveTextUndo?.end();
FormattedTextBox.LiveTextUndo = undefined;
const state = this._editorView!.state;
- if (StrCast(this.Document.title).startsWith('@') && !this.dataDoc.title_custom) {
- UndoManager.RunInBatch(() => {
- this.dataDoc.title_custom = true;
- this.dataDoc.layout_showTitle = 'title';
- const tr = this._editorView!.state.tr;
- this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.Document.title).length + 2))).deleteSelection());
- }, 'titler');
- }
// if the text box blurs and none of its contents are focused(), then pass the blur along
setTimeout(() => !this.ProseRef?.contains(document.activeElement) && this._props.onBlur?.());
};
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 5e53a019e..e8cf9e992 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -295,8 +295,8 @@ export class RichTextRules {
// create a hyperlink to a titled document
// @()
- new InputRule(new RegExp(/(^|\s)@\(([a-zA-Z_@\.\? \-0-9]+)\)/), (state, match, start, end) => {
- const docTitle = match[2];
+ new InputRule(new RegExp(/@\(([a-zA-Z_@\.\? \-0-9]+)\)/), (state, match, start, end) => {
+ const docTitle = match[1];
const prefixLength = '@('.length;
if (docTitle) {
const linkToDoc = (target: Doc) => {
@@ -342,12 +342,7 @@ export class RichTextRules {
const assign = match[4] === ':' ? (match[4] = '') : match[4];
const value = match[5];
const dataDoc = value === undefined ? !fieldKey.startsWith('_') : !assign?.startsWith('=');
- const getTitledDoc = (docTitle: string) => {
- if (!DocServer.FindDocByTitle(docTitle)) {
- Doc.AddToMyPublished(Docs.Create.TextDocument('', { title: docTitle, _width: 400, _layout_autoHeight: true }));
- }
- return DocServer.FindDocByTitle(docTitle);
- };
+ const getTitledDoc = (docTitle: string) => DocServer.FindDocByTitle(docTitle);
// if the value has commas assume its an array (unless it's part of a chat gpt call indicated by '((' )
if (value?.includes(',') && !value.startsWith('((')) {
const values = value.split(',');
@@ -359,7 +354,7 @@ export class RichTextRules {
if (fieldKey === this.TextBox.fieldKey) return this.TextBox.EditorView!.state.tr;
}
const target = docTitle ? getTitledDoc(docTitle) : undefined;
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, hideValue: false, dataDoc });
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target?.[Id], hideKey: false, hideValue: false });
return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
},
{ inCode: true }
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index e335044ea..cab3a6ef5 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -266,7 +266,6 @@ export const nodes: { [index: string]: NodeSpec } = {
hideKey: { default: false },
hideValue: { default: false },
editable: { default: true },
- dataDoc: { default: false },
},
leafText: node => Field.toString((DocServer.GetCachedRefField(node.attrs.docId as string) as Doc)?.[node.attrs.fieldKey as string] as Field),
group: 'inline',
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 200896e25..9973232bf 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -86,7 +86,8 @@ export namespace Field {
// this is a bit hacky, but we treat '^@' references to a published document
// as a kind of macro to include the content of those documents
Doc.MyPublishedDocs.forEach(doc => {
- const regex = new RegExp(`^\\^${doc.title}\\s`, 'm');
+ const regexMultilineFlag = 'm';
+ const regex = new RegExp(`^\\^${StrCast(doc.title).replace(/[\(\)]*/g, '')}\\s`, regexMultilineFlag); // need to remove characters that can cause the regular expression to be invalid
const sections = (Cast(doc.text, RichTextField, null)?.Text ?? '').split('--DOCDATA--');
if (script.match(regex)) {
script = script.replace(regex, sections[0]) + (sections.length > 1 ? sections[1] : '');
@@ -218,8 +219,14 @@ export class Doc extends RefField {
public static IsInMyOverlay(doc: Doc) { return Doc.MyOverlayDocs.includes(doc); } // prettier-ignore
public static AddToMyOverlay(doc: Doc) { Doc.ActiveDashboard?.myOverlayDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myOverlayDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
public static RemFromMyOverlay(doc: Doc) { Doc.ActiveDashboard?.myOverlayDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myOverlayDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myOverlayDocs), undefined, doc); } // prettier-ignore
- public static AddToMyPublished(doc: Doc) { Doc.ActiveDashboard?.myPublishedDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
- public static RemFromMyPublished(doc: Doc){ Doc.ActiveDashboard?.myPublishedDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ public static AddToMyPublished(doc: Doc) {
+ doc[DocData].title_custom = true;
+ doc[DocData].layout_showTitle = 'title';
+ Doc.ActiveDashboard?.myPublishedDocs ? Doc.AddDocToList(Doc.ActiveDashboard, 'myPublishedDocs', doc) : Doc.AddDocToList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
+ public static RemFromMyPublished(doc: Doc){
+ doc[DocData].title_custom = false;
+ doc[DocData].layout_showTitle = undefined;
+ Doc.ActiveDashboard?.myPublishedDocs ? Doc.RemoveDocFromList(Doc.ActiveDashboard,'myPublishedDocs', doc) : Doc.RemoveDocFromList(DocCast(Doc.UserDoc().myPublishedDocs), undefined, doc); } // prettier-ignore
public static IsComicStyle(doc?: Doc) { return doc && Doc.ActiveDashboard && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic' ; } // prettier-ignore
constructor(id?: FieldId, forceSave?: boolean) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c2ec3f13a..ad592391e 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,4 +1,4 @@
-import { $mobx, action, observable, runInAction, trace } from 'mobx';
+import { $mobx, action, observable, runInAction, trace, values } from 'mobx';
import { computedFn } from 'mobx-utils';
import { returnZero } from '../Utils';
import { DocServer } from '../client/DocServer';
@@ -16,6 +16,7 @@ import { RichTextField } from './RichTextField';
import { SchemaHeaderField } from './SchemaHeaderField';
import { ComputedField } from './ScriptField';
import { DocCast, ScriptCast, StrCast } from './Types';
+import { BaseException } from 'pdfjs-dist/types/src/shared/util';
function _readOnlySetter(): never {
throw new Error("Documents can't be modified in read-only mode");
@@ -56,6 +57,8 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
delete curValue[FieldChanged];
}
+ if (typeof prop === 'string' && _propSetterCB.has(prop)) _propSetterCB.get(prop)!(target[SelfProxy], value);
+
const effectiveAcl = GetEffectiveAcl(target);
const writeMode = DocServer.getFieldWriteMode(prop as string);
@@ -282,6 +285,17 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
dataDocChanged && updateCachedAcls(dataDoc);
}
+export var _propSetterCB = new Map void) | undefined>();
+/**
+ * sets a callback function to be called whenever a value is assigned to the specified field key.
+ * For example, this is used to "publish" documents with titles that start with '@'
+ * @param prop
+ * @param setter
+ */
+export function SetPropSetterCb(prop: string, setter: ((target: any, value: any) => void) | undefined) {
+ _propSetterCB.set(prop, setter);
+}
+
//
// target should be either a Doc or ListImpl. receiver should be a Proxy Or List.
//
--
cgit v1.2.3-70-g09d2
From e4854bdb361f1d9e0ae85ee46327e2919117fc48 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Mon, 1 Apr 2024 11:14:24 -0400
Subject: revert back to old style of forced text scrolling - doesn't work for
pres trails.
---
src/Utils.ts | 5 +++--
src/client/views/nodes/formattedText/FormattedTextBox.scss | 1 -
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 14 +++++++-------
3 files changed, 10 insertions(+), 10 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/Utils.ts b/src/Utils.ts
index 38325a463..21c91278f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -653,13 +653,14 @@ export function smoothScroll(duration: number, element: HTMLElement | HTMLElemen
const animateScroll = () => {
const currentDate = new Date().getTime();
const currentTime = currentDate - startDate;
- elements.map((element, i) => (element.scrollTop = easeFunc(transition, currentTime, starts[i], to - starts[i], duration)));
+ const setScrollTop = (element: HTMLElement, value: number) => (element.scrollTop = value);
+ elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration)));
if (!_stop) {
if (currentTime < duration) {
requestAnimationFrame(animateScroll);
} else {
- elements.forEach(element => (element.scrollTop = to));
+ elements.forEach(element => setScrollTop(element, to));
}
}
};
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 00d890860..03ff0436b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -156,7 +156,6 @@ audiotag:hover {
.formattedTextBox-inner,
.formattedTextBox-inner-minimal {
height: 100%;
- overflow: auto;
white-space: pre-wrap;
.ProseMirror:hover {
background: rgba(200, 200, 200, 0.2);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 80e56efe0..a78d8363e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -91,7 +91,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent();
private _sidebarTagRef = React.createRef();
private _ref: React.RefObject = React.createRef();
- private _scrollRef: React.RefObject = React.createRef();
+ private _scrollRef: HTMLDivElement | null = null;
private _editorView: Opt;
public _applyingChange: string = '';
private _inDrop = false;
@@ -1415,14 +1415,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
const docPos = editorView.coordsAtPos(editorView.state.selection.to);
const viewRect = self._ref.current!.getBoundingClientRect();
- const scrollRef = self.ProseRef;
+ const scrollRef = self._scrollRef;
const topOff = docPos.top < viewRect.top ? docPos.top - viewRect.top : undefined;
const botOff = docPos.bottom > 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.ScreenToLocalBoxXf().Scale;
if (this._focusSpeed !== undefined) {
- scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed, scrollRef, scrollPos, 'ease', this._scrollStopper));
+ setTimeout(() => scrollPos && (this._scrollStopper = smoothScroll(this._focusSpeed || 0, scrollRef, scrollPos, 'ease', this._scrollStopper)));
} else {
scrollRef.scrollTo({ top: scrollPos });
}
@@ -1823,10 +1823,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- if (!LinkInfo.Instance?.LinkInfo && this._scrollRef.current) {
+ if (!LinkInfo.Instance?.LinkInfo && this._scrollRef) {
if (!this._props.dontSelectOnLoad) {
this._ignoreScroll = true;
- this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
+ this.layoutDoc._layout_scrollTop = this._scrollRef.scrollTop;
this._ignoreScroll = false;
e.stopPropagation();
e.preventDefault();
@@ -2031,7 +2031,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
(this._scrollRef = r)}
style={{
width: this._props.dontSelectOnLoad || this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
--
cgit v1.2.3-70-g09d2
From 5e865b0a96c7b13e538d159baea0c10416bef921 Mon Sep 17 00:00:00 2001
From: bobzel
Date: Mon, 1 Apr 2024 11:21:05 -0400
Subject: forgot these from last - fixes forced text scroll from pres trail.
---
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a78d8363e..269a114bb 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1309,15 +1309,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent NumCast(this.layoutDoc._layout_scrollTop),
pos => {
- if (!this._ignoreScroll && this.ProseRef && !this._props.dontSelectOnLoad) {
+ if (!this._ignoreScroll && this._scrollRef && !this._props.dontSelectOnLoad) {
const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
const durationSecStr = viewTrans.match(/([0-9.]*)s/);
const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
if (duration) {
- this._scrollStopper = smoothScroll(duration, this.ProseRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
+ this._scrollStopper = smoothScroll(duration, this._scrollRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
} else {
- this.ProseRef.scrollTo({ top: pos });
+ this._scrollRef.scrollTo({ top: pos });
}
}
},
--
cgit v1.2.3-70-g09d2
From 83deb55fa4e99e25cebd4a9d4eb882c2fddee28b Mon Sep 17 00:00:00 2001
From: bobzel
Date: Mon, 1 Apr 2024 13:44:16 -0400
Subject: fixed header template to be marginally functional.
---
src/Utils.ts | 3 +-
src/client/documents/Documents.ts | 7 ++--
src/client/util/CurrentUserUtils.ts | 25 +++++++------
.../views/nodes/formattedText/FormattedTextBox.tsx | 42 +++++++++-------------
src/fields/Doc.ts | 3 +-
5 files changed, 35 insertions(+), 45 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/Utils.ts b/src/Utils.ts
index 21c91278f..a64c7c8a7 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -654,10 +654,9 @@ export function smoothScroll(duration: number, element: HTMLElement | HTMLElemen
const currentDate = new Date().getTime();
const currentTime = currentDate - startDate;
const setScrollTop = (element: HTMLElement, value: number) => (element.scrollTop = value);
- elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration)));
-
if (!_stop) {
if (currentTime < duration) {
+ elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration)));
requestAnimationFrame(animateScroll);
} else {
elements.forEach(element => setScrollTop(element, to));
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index eb15c332f..20e74a3e1 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -254,9 +254,9 @@ export class DocumentOptions {
dontRegisterView?: BOOLt = new BoolInfo('are views of this document registered so that they can be found when following links, etc', false);
_undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
undoIgnoreFields?: List; //'fields that should not be added to the undo stack (opacity for Undo/Redo/and sidebar) AND whether modifications to document are undoable (true for linearview menu buttons to prevent open/close from entering undo stack)'
- _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title', false);
- _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes', false);
- _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
+ _header_height?: NUMt = new NumInfo('height of document header used for displaying title', false);
+ _header_fontSize?: NUMt = new NumInfo('font size of header of custom notes', false);
+ _header_pointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
_lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged");
_lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed');
@@ -284,6 +284,7 @@ export class DocumentOptions {
layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow
layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)');
_layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+ _layout_autoHeightMargins?: NUMt = new NumInfo('Margin heights to be added to the computed auto height of a Doc');
_layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false);
_layout_currentTimecode?: NUMt = new NumInfo('the current timecode of a time-based document (e.g., current time of a video) value is in seconds', false);
_layout_centered?: BOOLt = new BoolInfo('whether text should be vertically centered in Doc');
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b06801066..16e0cb14a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -222,20 +222,19 @@ export class CurrentUserUtils {
};
const headerBtnHgt = 10;
const headerTemplate = (opts:DocumentOptions) => {
- const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "Untitled Header",
+ const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "Header Template",
layout:
- "" +
- ` ` +
- " " +
- ` Metadata ` +
- " "
+ `
+
+
+ Metadata
+ `
}, "header");
-
// "" +
- // " " +
- // " " +
+ // " " +
+ // " " +
// "
";
- MakeTemplate(Doc.GetProto(header));
+ MakeTemplate(header);
return header;
}
const slideView = (opts:DocumentOptions) => {
@@ -243,9 +242,9 @@ export class CurrentUserUtils {
[
Docs.Create.MulticolumnDocument([], { title: "hero", _height: 200, isSystem: true }),
Docs.Create.TextDocument("", { title: "text", _layout_fitWidth:true, _height: 100, isSystem: true, _text_fontFamily: StrCast(Doc.UserDoc().fontFamily), _text_fontSize: StrCast(Doc.UserDoc().fontSize) })
- ], {...opts, title: "Untitled Slide View"});
+ ], {...opts, title: "Slide View Template"});
- MakeTemplate(Doc.GetProto(slide));
+ MakeTemplate(slide);
return slide;
}
const plotlyApi = () => {
@@ -364,7 +363,7 @@ pie title Minerals in my tap water
{key: "Button", creator: Docs.Create.ButtonDocument, opts: { _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title_custom: true, waitForDoubleClickToClick: 'never'}, scripts: {onClick: FollowLinkScript()?.script.originalScript ?? ""}},
{key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }},
{key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }},
- {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _layout_autoHeight: true, treeView_HideUnrendered: true}},
+ {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: true}},
{key: "ViewSlide", creator: slideView, opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}},
{key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }},
{key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }},
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 269a114bb..2e8444379 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -884,7 +884,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
this.Document.layout_fieldKey = 'layout_meta';
- setTimeout(() => (this.layoutDoc._headerHeight = this.layoutDoc._layout_autoHeightMargins = 50), 50);
+ setTimeout(() => (this.layoutDoc._header_height = this.layoutDoc._layout_autoHeightMargins = 50), 50);
}),
icon: 'eye',
});
@@ -1305,25 +1305,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent NumCast(this.layoutDoc._layout_scrollTop),
pos => {
- if (!this._ignoreScroll && this._scrollRef && !this._props.dontSelectOnLoad) {
- const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
- const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
- const durationSecStr = viewTrans.match(/([0-9.]*)s/);
- const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
- if (duration) {
- this._scrollStopper = smoothScroll(duration, this._scrollRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
- } else {
- this._scrollRef.scrollTo({ top: pos });
- }
+ if (!this._ignoreScroll && this._scrollRef) {
+ const durationStr = StrCast(this.Document._viewTransition).match(/([0-9]+)(m?)s/);
+ const duration = Number(durationStr?.[1]) * (durationStr?.[2] ? 1 : 1000);
+ this._scrollStopper = smoothScroll(duration || 0, this._scrollRef, Math.abs(pos || 0), 'ease', this._scrollStopper);
}
},
{ fireImmediately: true }
);
- quickScroll = undefined;
this.tryUpdateScrollHeight();
setTimeout(this.tryUpdateScrollHeight, 250);
}
@@ -1749,7 +1741,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent document publishing to Doc.myPublishedDocs
- const match = RTFCast(this.Document[this.fieldKey]).Text.match(/^(@[a-zA-Z][a-zA-Z_0-9 -]*[a-zA-Z_0-9-]+)/);
+ const match = RTFCast(this.Document[this.fieldKey])?.Text.match(/^(@[a-zA-Z][a-zA-Z_0-9 -]*[a-zA-Z_0-9-]+)/);
if (match) {
this.dataDoc.title_custom = true;
this.dataDoc.title = match[1]; // this triggers the collectionDockingView to publish this Doc
@@ -1824,13 +1816,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
if (!LinkInfo.Instance?.LinkInfo && this._scrollRef) {
- if (!this._props.dontSelectOnLoad) {
- this._ignoreScroll = true;
- this.layoutDoc._layout_scrollTop = this._scrollRef.scrollTop;
- this._ignoreScroll = false;
- e.stopPropagation();
- e.preventDefault();
- }
+ this._ignoreScroll = true;
+ this.layoutDoc._layout_scrollTop = this._scrollRef.scrollTop;
+ this._ignoreScroll = false;
+ e.stopPropagation();
+ e.preventDefault();
}
};
tryUpdateScrollHeight = () => {
@@ -2026,7 +2016,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
+ 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 (
@@ -2059,7 +2049,7 @@ 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.,
+ 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._scrollRef = r)}
style={{
- width: this._props.dontSelectOnLoad || this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
+ width: this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
@@ -2128,8 +2118,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
- {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.SidebarShown || this.layout_sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+ {this.noSidebar || this.Document._layout_noSidebar || 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/fields/Doc.ts b/src/fields/Doc.ts
index 9973232bf..48214cf25 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1162,7 +1162,8 @@ export namespace Doc {
return doc[StrCast(doc.layout_fieldKey, 'layout')];
}
export function LayoutFieldKey(doc: Doc, templateLayoutString?: string): string {
- return StrCast(templateLayoutString || Doc.Layout(doc).layout).split("'")[1]; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
+ const match = StrCast(templateLayoutString || Doc.Layout(doc).layout).match(/fieldKey={'([^']+)'}/);
+ return match?.[1] || ''; // bcz: TODO check on this . used to always reference 'layout', now it uses the layout speicfied by the current layout_fieldKey
}
export function NativeAspect(doc: Doc, dataDoc?: Doc, useDim?: boolean) {
return Doc.NativeWidth(doc, dataDoc, useDim) / (Doc.NativeHeight(doc, dataDoc, useDim) || 1);
--
cgit v1.2.3-70-g09d2
From 75cbf3ffc1893d02b48ef65ec834e9ae597398bf Mon Sep 17 00:00:00 2001
From: bobzel
Date: Mon, 1 Apr 2024 20:23:24 -0400
Subject: fixed text toggle buttons to highlight based on selection. enabled
background colors for text lists. cleaned up text insertion point setting on
pointer up. fixed autoHeight for text boxes with 'auto' nodes like ordered
lists.
---
src/client/util/CurrentUserUtils.ts | 2 +-
src/client/views/global/globalScripts.ts | 50 +++---
.../views/nodes/formattedText/DashFieldView.scss | 1 +
.../nodes/formattedText/FormattedTextBox.scss | 14 ++
.../views/nodes/formattedText/FormattedTextBox.tsx | 47 ++---
.../formattedText/ProsemirrorExampleTransfer.ts | 2 +-
.../views/nodes/formattedText/RichTextMenu.tsx | 199 ++++-----------------
.../views/nodes/formattedText/RichTextRules.ts | 4 +-
src/client/views/nodes/formattedText/nodes_rts.ts | 30 ++--
9 files changed, 113 insertions(+), 236 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b73111062..38ebc86e7 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -725,7 +725,7 @@ pie title Minerals in my tap water
btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) },
{ title: "Font Size",toolTip: "Font size (%size)", btnType: ButtonType.NumberDropdownButton, toolType:"fontSize", ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 6 },
{ title: "Color", toolTip: "Font color (%color)", btnType: ButtonType.ColorButton, icon: "font", toolType:"fontColor",ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'}},
- { title: "Highlight",toolTip: "Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'},funcs: {hidden: "IsNoviceMode()"} },
+ { title: "Highlight",toolTip: "Font highlight", btnType: ButtonType.ColorButton, icon: "highlighter", toolType:"highlight",ignoreClick: true, scripts: {script: '{ return setFontAttr(this.toolType, value, _readOnly_);}'}},
{ title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", toolType:"bold", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", toolType:"italics", ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
{ title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", toolType:"underline",ignoreClick: true, scripts: {onClick: '{ return toggleCharStyle(this.toolType, _readOnly_);}'} },
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index dab642499..497ab98d8 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -151,26 +151,26 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
const map: Map<'font'|'fontColor'|'highlight'|'fontSize'|'alignment', { checkResult: () => any; setDoc: () => void;}> = new Map([
['font', {
checkResult: () => RichTextMenu.Instance?.fontFamily,
- setDoc: () => value && RichTextMenu.Instance.setFontFamily(value),
+ setDoc: () => value && RichTextMenu.Instance?.setFontFamily(value),
}],
['highlight', {
- checkResult: () =>(selected ?? Doc.UserDoc())._fontHighlight,
- setDoc: () => value && RichTextMenu.Instance.setHighlight(value),
+ checkResult: () => RichTextMenu.Instance?.fontHighlight,
+ setDoc: () => value && RichTextMenu.Instance?.setHighlight(value),
}],
['fontColor', {
checkResult: () => RichTextMenu.Instance?.fontColor,
- setDoc: () => value && RichTextMenu.Instance.setColor(value),
+ setDoc: () => value && RichTextMenu.Instance?.setColor(value),
}],
['alignment', {
- checkResult: () => RichTextMenu.Instance.textAlign,
- setDoc: () => value && editorView?.state ? RichTextMenu.Instance.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
+ checkResult: () => RichTextMenu.Instance?.textAlign,
+ setDoc: () => value && editorView?.state ? RichTextMenu.Instance?.align(editorView, editorView.dispatch, value):(Doc.UserDoc().textAlign = value),
}],
['fontSize', {
checkResult: () => RichTextMenu.Instance?.fontSize.replace('px', ''),
setDoc: () => {
if (typeof value === 'number') value = value.toString();
if (value && Number(value).toString() === value) value += 'px';
- RichTextMenu.Instance.setFontSize(value);
+ RichTextMenu.Instance?.setFontSize(value);
},
}],
]);
@@ -182,45 +182,45 @@ ScriptingGlobals.add(function setFontAttr(attr: 'font' | 'fontColor' | 'highligh
});
type attrname = 'noAutoLink' | 'dictation' | 'bold' | 'italics' | 'elide' | 'underline' | 'left' | 'center' | 'right' | 'vcent' | 'bullet' | 'decimal';
-type attrfuncs = [attrname, { checkResult: () => boolean; toggle: () => any }];
+type attrfuncs = [attrname, { checkResult: () => boolean; toggle?: () => any }];
ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: boolean) {
const textView = RichTextMenu.Instance?.TextView;
const editorView = textView?.EditorView;
// prettier-ignore
const alignments:attrfuncs[] = (['left','right','center','vcent'] as ("left"|"center"|"right"|"vcent")[]).map((where) =>
- [ where, { checkResult: () =>(editorView ? (where === 'vcent' ? RichTextMenu.Instance.textVcenter:
- (RichTextMenu.Instance.textAlign === where)):
+ [ where, { checkResult: () =>(editorView ? (where === 'vcent' ? RichTextMenu.Instance?.textVcenter ?? false:
+ (RichTextMenu.Instance?.textAlign === where)):
where === 'vcent' ? BoolCast(Doc.UserDoc()._layout_centered):
(Doc.UserDoc().textAlign ===where) ? true:false),
- toggle: () => (editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance.vcenterToggle(editorView, editorView.dispatch):
- RichTextMenu.Instance.align(editorView, editorView.dispatch, where)):
+ toggle: () => (editorView?.state ? (where === 'vcent' ? RichTextMenu.Instance?.vcenterToggle(editorView, editorView.dispatch):
+ RichTextMenu.Instance?.align(editorView, editorView.dispatch, where)):
where === 'vcent' ? Doc.UserDoc()._layout_centered = !Doc.UserDoc()._layout_centered:
(Doc.UserDoc().textAlign = where))}]); // prettier-ignore
// prettier-ignore
const listings:attrfuncs[] = (['bullet','decimal'] as attrname[]).map(list =>
- [ list, { checkResult: () => (editorView ? RichTextMenu.Instance.getActiveListStyle() === list:false),
- toggle: () => editorView?.state && RichTextMenu.Instance.changeListType(list) }]);
+ [ list, { checkResult: () => (editorView ? RichTextMenu.Instance?.getActiveListStyle() === list:false),
+ toggle: () => editorView?.state && RichTextMenu.Instance?.changeListType(list) }]);
// prettier-ignore
const attrs:attrfuncs[] = [
['dictation', { checkResult: () => textView?._recordingDictation ? true:false,
toggle: () => textView && runInAction(() => (textView._recordingDictation = !textView._recordingDictation)) }],
['elide', { checkResult: () => false,
toggle: () => editorView ? RichTextMenu.Instance?.elideSelection(): 0}],
- ['noAutoLink',{ checkResult: () => (editorView ? RichTextMenu.Instance.noAutoLink : false),
+ ['noAutoLink',{ checkResult: () => ((editorView && RichTextMenu.Instance?.noAutoLink) ?? false),
toggle: () => editorView && RichTextMenu.Instance?.toggleNoAutoLinkAnchor()}],
- ['bold', { checkResult: () => (editorView ? RichTextMenu.Instance.bold : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
- toggle: editorView ? RichTextMenu.Instance.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
- ['italics', { checkResult: () => (editorView ? RichTextMenu.Instance.italics : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
- toggle: editorView ? RichTextMenu.Instance.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
- ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance.underline : (Doc.UserDoc().textDecoration === 'underline') ? true:false),
- toggle: editorView ? RichTextMenu.Instance.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
+ ['bold', { checkResult: () => (editorView ? RichTextMenu.Instance?.bold??false : (Doc.UserDoc().fontWeight === 'bold') ? true:false),
+ toggle: editorView ? RichTextMenu.Instance?.toggleBold : () => (Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold')}],
+ ['italics', { checkResult: () => (editorView ? RichTextMenu.Instance?.italics ?? false : (Doc.UserDoc().fontStyle === 'italics') ? true:false),
+ toggle: editorView ? RichTextMenu.Instance?.toggleItalics : () => (Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics')}],
+ ['underline', { checkResult: () => (editorView ? RichTextMenu.Instance?.underline ?? false: (Doc.UserDoc().textDecoration === 'underline') ? true:false),
+ toggle: editorView ? RichTextMenu.Instance?.toggleUnderline : () => (Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline') }]]
const map = new Map(attrs.concat(alignments).concat(listings));
if (checkResult) {
return map.get(charStyle)?.checkResult();
}
- undoable(() => map.get(charStyle)?.toggle(), 'toggle ' + charStyle)();
+ undoable(() => map.get(charStyle)?.toggle?.(), 'toggle ' + charStyle)();
});
export function checkInksToGroup() {
@@ -448,10 +448,10 @@ ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) {
*/
ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
SelectionManager.Docs.map(doc => (doc._text_fontFamily = key));
- const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ const editorView = RichTextMenu.Instance?.TextView?.EditorView;
if (checkResult) {
- return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+ return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc())?.fontFamily);
}
- if (editorView) RichTextMenu.Instance.setFontFamily(key);
+ if (editorView) RichTextMenu.Instance?.setFontFamily(key);
else Doc.UserDoc().fontFamily = key;
});
diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss
index 74eeb014c..d79df4272 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.scss
+++ b/src/client/views/nodes/formattedText/DashFieldView.scss
@@ -26,6 +26,7 @@
display: inline-block;
font-weight: normal;
background: rgba(0, 0, 0, 0.1);
+ cursor: default;
}
.dashFieldView-fieldSpan {
min-width: 8px;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 03ff0436b..3dcc45c96 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -273,6 +273,7 @@ footnote::before {
height: 20px;
&::before {
content: '→';
+ cursor: default;
}
&:hover {
background: orange;
@@ -348,6 +349,7 @@ footnote::before {
touch-action: none;
span {
font-family: inherit;
+ background-color: inherit;
}
blockquote {
@@ -397,6 +399,7 @@ footnote::before {
font-family: inherit;
}
margin-left: 0;
+ background-color: inherit;
}
.decimal2-ol {
counter-reset: deci2;
@@ -406,6 +409,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 2.1em;
+ background-color: inherit;
}
.decimal3-ol {
counter-reset: deci3;
@@ -415,6 +419,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 2.85em;
+ background-color: inherit;
}
.decimal4-ol {
counter-reset: deci4;
@@ -424,6 +429,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 3.85em;
+ background-color: inherit;
}
.decimal5-ol {
counter-reset: deci5;
@@ -432,6 +438,7 @@ footnote::before {
font-family: inherit;
}
font-size: smaller;
+ background-color: inherit;
}
.decimal6-ol {
counter-reset: deci6;
@@ -440,6 +447,7 @@ footnote::before {
font-family: inherit;
}
font-size: smaller;
+ background-color: inherit;
}
.decimal7-ol {
counter-reset: deci7;
@@ -448,6 +456,7 @@ footnote::before {
font-family: inherit;
}
font-size: smaller;
+ background-color: inherit;
}
.multi1-ol {
@@ -458,6 +467,7 @@ footnote::before {
}
margin-left: 0;
padding-left: 1.2em;
+ background-color: inherit;
}
.multi2-ol {
counter-reset: multi2;
@@ -467,6 +477,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 2em;
+ background-color: inherit;
}
.multi3-ol {
counter-reset: multi3;
@@ -476,6 +487,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 2.85em;
+ background-color: inherit;
}
.multi4-ol {
counter-reset: multi4;
@@ -485,6 +497,7 @@ footnote::before {
}
font-size: smaller;
padding-left: 3.85em;
+ background-color: inherit;
}
//.bullet:before, .bullet1:before, .bullet2:before, .bullet3:before, .bullet4:before, .bullet5:before { transition: 0.5s; display: inline-block; vertical-align: top; margin-left: -1em; width: 1em; content:" " }
@@ -788,6 +801,7 @@ footnote::before {
height: 20px;
&::before {
content: '→';
+ cursor: default;
}
&:hover {
background: orange;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 2e8444379..c2f3a6e4b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -291,7 +291,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- this._editorView?.state && RichTextMenu.Instance.setHighlight(color);
+ this._editorView?.state && RichTextMenu.Instance?.setHighlight(color);
return undefined;
}, 'highlght text');
AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true);
@@ -1577,32 +1577,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- const editor = this._editorView!;
- const state = editor?.state;
- if (!state || !editor || !this.ProseRef?.children[0].className.includes('-focused')) return;
- if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
- else if (this._props.isContentActive() && !e.button) {
- const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- let xpos = pcords?.pos || 0;
- while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
- xpos = xpos - 1;
- }
- let node: any;
- try {
- node = state.doc.nodeAt(xpos);
- } catch (e) {}
- if (node?.type !== schema.nodes.dashFieldView) {
- editor.dispatch(state.tr.setSelection(TextSelection.near(state.doc.resolve(xpos))));
- let target = e.target as any; // hrefs are stored on the dataset of the node that wraps the hyerlink
- while (target && !target.dataset?.targethrefs) target = target.parentElement;
- FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
- } else if (node) {
- try {
- editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos))));
- } catch (e) {
- editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(xpos - 1))));
- }
- }
+ const state = this.EditorView?.state;
+ if (state && this.ProseRef?.children[0].className.includes('-focused') && this._props.isContentActive() && !e.button) {
+ if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
+ let target = e.target as any; // hrefs are stored on the dataset of the node that wraps the hyerlink
+ for (let target = e.target as any; target && !target.dataset?.targethrefs; target = target.parentElement);
+ while (target && !target.dataset?.targethrefs) target = target.parentElement;
+ FormattedTextBoxComment.update(this, this.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
}
};
@action
@@ -1788,7 +1769,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Number(val.replace('px', '').replace('auto', '0'));
- const toHgt = (node: Element) => {
+ const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
+ const toNum = (val: string) => Number(val.replace('px', ''));
+ const toHgt = (node: Element): number => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
- return toNum(height) + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
+ const childHeight = height === 'auto' ? getChildrenHeights(Array.from(node.children)) : toNum(height);
+ return childHeight + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
};
- const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
+ const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children);
const scrollHeight = this.ProseRef && proseHeight;
if (this._props.setHeight && !this._props.suppressSetHeight && 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
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 92cc65fc5..ec8879487 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -104,7 +104,7 @@ export function buildKeymap>(schema: S, props: any, mapKey
!wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
const tx25 = updateBullets(tx2, schema);
const ol_node = tx25.doc.nodeAt(range!.start)!;
- const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, { ...ol_node.attrs, ...(marks?.[0]?.type === schema.marks.pFontSize ? { fontSize: marks[0].attrs.fontSize } : {}) });
+ const tx3 = tx25.setNodeMarkup(range!.start, ol_node.type, ol_node.attrs, marks);
// when promoting to a list, assume list will format things so don't copy the stored marks.
marks && tx3.ensureMarks([...marks]);
marks && tx3.setStoredMarks([...marks]);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9b6eb8b8d..9c282a1d2 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -27,7 +27,10 @@ const { toggleMark } = require('prosemirror-commands');
@observer
export class RichTextMenu extends AntimodeMenu {
- @observable static Instance: RichTextMenu;
+ static _instance: { menu: RichTextMenu | undefined } = observable({ menu: undefined });
+ static get Instance() {
+ return RichTextMenu._instance?.menu;
+ }
public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable
private _linkToRef = React.createRef();
@@ -67,7 +70,7 @@ export class RichTextMenu extends AntimodeMenu {
constructor(props: AntimodeMenuProps) {
super(props);
makeObservable(this);
- RichTextMenu.Instance = this;
+ RichTextMenu._instance.menu = this;
this.updateMenu(undefined, undefined, props, this.layoutDoc);
this._canFade = false;
this.Pinned = true;
@@ -131,11 +134,7 @@ export class RichTextMenu extends AntimodeMenu {
if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return;
}
- // update active marks
- const activeMarks = this.getActiveMarksOnSelection();
- this.setActiveMarkButtons(activeMarks);
-
- // update active font family and size
+ this.setActiveMarkButtons(this.getActiveMarksOnSelection());
const active = this.getActiveFontStylesOnSelection();
const activeFamilies = active.activeFamilies;
const activeSizes = active.activeSizes;
@@ -164,15 +163,13 @@ export class RichTextMenu extends AntimodeMenu {
const fromRange = numberRange(state.selection.from).reverse();
const newPos = nodeOl ? fromRange.find(i => state.doc.nodeAt(i)?.type === state.schema.nodes.ordered_list) ?? state.selection.from : state.selection.from;
const node = (state.selection as NodeSelection).node ?? (newPos >= 0 ? state.doc.nodeAt(newPos) : undefined);
- 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 };
- if (mark.type === schema.marks.pFontColor) attrs = { ...attrs, fontColor: mark.attrs.color };
- const tr = updateBullets(state.tr.setNodeMarkup(newPos, node.type, attrs), state.schema);
- dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to))));
- }
- {
+ if (node?.type === schema.nodes.ordered_list || node?.type === schema.nodes.list_item) {
+ const hasMark = node.marks.some(m => m.type === mark.type);
+ const otherMarks = node.marks.filter(m => m.type !== mark.type);
+ const addAnyway = node.marks.filter(m => m.type === mark.type && Object.keys(m.attrs).some(akey => m.attrs[akey] !== mark.attrs[akey]));
+ const markup = state.tr.setNodeMarkup(newPos, node.type, node.attrs, hasMark && !addAnyway ? otherMarks : [...otherMarks, mark]);
+ dispatch(updateBullets(markup, state.schema));
+ } else {
const state = this.view?.state;
const tr = this.view?.state.tr;
if (tr && state) {
@@ -225,11 +222,12 @@ export class RichTextMenu extends AntimodeMenu {
if (this.view && this.TextView?._props.rootSelected?.()) {
const state = this.view.state;
const pos = this.view.state.selection.$from;
- const marks: Mark[] = [...(state.storedMarks ?? [])];
+ var marks: Mark[] = [...(state.storedMarks ?? [])];
if (state.storedMarks !== null) {
} else 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) : []));
+ for (let i = 0; i <= pos.depth; i++) {
+ marks = [...Array.from(pos.node(i).marks), ...this.view.state.selection.$anchor.marks(), ...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));
@@ -256,41 +254,26 @@ export class RichTextMenu extends AntimodeMenu {
//finds all active marks on selection in given group
getActiveMarksOnSelection() {
- let activeMarks: MarkType[] = [];
- if (!this.view || !this.TextView?._props.rootSelected?.()) return activeMarks;
+ if (!this.view || !this.TextView?._props.rootSelected?.()) return [] as MarkType[];
- const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
- if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
- //current selection
- const { empty, ranges, $to } = this.view.state.selection as TextSelection;
const state = this.view.state;
- if (!empty) {
- activeMarks = markGroup.filter(mark => {
- const has = false;
- for (let i = 0; !has && i < ranges.length; i++) {
- return state.doc.rangeHasMark(ranges[i].$from.pos, ranges[i].$to.pos, mark);
- }
- return false;
- });
- } else {
- const pos = this.view.state.selection.$from;
- const ref_node: ProsNode | null = this.reference_node(pos);
- if (ref_node !== null && ref_node !== this.view.state.doc) {
- if (ref_node.isText) {
- } else {
- return [];
- }
- activeMarks = markGroup.filter(mark_type => {
- // 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 mark.isInSet(ref_node.marks);
- });
+ var marks: Mark[] = [...(state.storedMarks ?? [])];
+ const pos = this.view.state.selection.$from;
+ if (state.storedMarks !== null) {
+ } else if (state.selection.empty) {
+ for (let i = 0; i <= pos.depth; i++) {
+ marks = [...Array.from(pos.node(i).marks), ...this.view.state.selection.$anchor.marks(), ...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));
+ });
}
- return activeMarks;
+ const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
+ return markGroup.filter(mark_type => {
+ const mark = state.schema.mark(mark_type);
+ return mark.isInSet(marks);
+ });
}
@action
@@ -418,7 +401,7 @@ export class RichTextMenu extends AntimodeMenu {
// TODO: remove doesn't work
// remove all node type and apply the passed-in one to the selected text
changeListType = (mapStyle: string) => {
- const active = this.view?.state && RichTextMenu.Instance.getActiveListStyle();
+ const active = this.view?.state && RichTextMenu.Instance?.getActiveListStyle();
const nodeType = this.view?.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? '' : mapStyle });
if (!this.view || nodeType?.attrs.mapStyle === '') return;
@@ -566,7 +549,7 @@ export class RichTextMenu extends AntimodeMenu {
// todo: add brushes to brushMap to save with a style name
onBrushNameKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
- RichTextMenu.Instance.brushMarks && RichTextMenu.Instance._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks);
+ RichTextMenu.Instance?.brushMarks && RichTextMenu.Instance?._brushMap.set(this._brushNameRef.current!.value, RichTextMenu.Instance.brushMarks);
this._brushNameRef.current!.style.background = 'lightGray';
}
};
@@ -574,7 +557,7 @@ export class RichTextMenu extends AntimodeMenu {
@action
clearBrush() {
- RichTextMenu.Instance.brushMarks = new Set();
+ RichTextMenu.Instance && (RichTextMenu.Instance.brushMarks = new Set());
}
@action
@@ -710,118 +693,8 @@ export class RichTextMenu extends AntimodeMenu {
}
};
- linkExtend($start: ResolvedPos, href: string) {
- const mark = this.view!.state.schema.marks.linkAnchor;
-
- let startIndex = $start.index();
- let endIndex = $start.indexAfter();
-
- while (startIndex > 0 && $start.parent.child(startIndex - 1).marks.filter(m => m.type === mark && m.attrs.allAnchors.find((item: { href: string }) => item.href === href)).length) startIndex--;
- while (endIndex < $start.parent.childCount && $start.parent.child(endIndex).marks.filter(m => m.type === mark && m.attrs.allAnchors.find((item: { href: string }) => item.href === href)).length) endIndex++;
-
- let startPos = $start.start();
- let endPos = startPos;
- for (let i = 0; i < endIndex; i++) {
- const size = $start.parent.child(i).nodeSize;
- if (i < startIndex) startPos += size;
- endPos += size;
- }
- return { from: startPos, to: endPos };
- }
-
- reference_node(pos: ResolvedPos): ProsNode | null {
- if (!this.view) return null;
-
- let ref_node: ProsNode = this.view.state.doc;
- if (pos.nodeBefore !== null && pos.nodeBefore !== undefined) {
- ref_node = pos.nodeBefore;
- }
- if (pos.nodeAfter !== null && pos.nodeAfter !== undefined) {
- if (!pos.nodeBefore || this.view.state.selection.$from.pos !== this.view.state.selection.$to.pos) {
- ref_node = pos.nodeAfter;
- }
- }
- if (!ref_node && pos.pos > 0) {
- let skip = false;
- for (let i: number = pos.pos - 1; i > 0; i--) {
- this.view.state.doc.nodesBetween(i, pos.pos, (node: ProsNode) => {
- if (node.isLeaf && !skip) {
- ref_node = node;
- skip = true;
- }
- });
- }
- }
- if (!ref_node.isLeaf && ref_node.childCount > 0) {
- ref_node = ref_node.child(0);
- }
- return ref_node;
- }
-
render() {
return null;
- // TraceMobx();
- // const row1 = {[
- // //!this.collapsed ? this.getDragger() : (null),
- // // !this.Pinned ? (null) :
{[
- // // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
- // // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
- // // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
- // // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
- // // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
- // // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
- // //
- // // ]}
,
- // this.createButton("bold", "Bold", this.boldActive, toggleMark(schema.marks.strong)),
- // this.createButton("italic", "Italic", this.italicsActive, toggleMark(schema.marks.em)),
- // this.createButton("underline", "Underline", this.underlineActive, toggleMark(schema.marks.underline)),
- // this.createButton("strikethrough", "Strikethrough", this.strikethroughActive, toggleMark(schema.marks.strikethrough)),
- // this.createButton("superscript", "Superscript", this.superscriptActive, toggleMark(schema.marks.superscript)),
- // this.createButton("subscript", "Subscript", this.subscriptActive, toggleMark(schema.marks.subscript)),
- // this.createColorButton(),
- // this.createHighlighterButton(),
- // this.createLinkButton(),
- // this.createBrushButton(),
- //
,
- // this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft),
- // this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter),
- // this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight),
- // this.createButton("indent", "Inset More", undefined, this.insetParagraph),
- // this.createButton("outdent", "Inset Less", undefined, this.outsetParagraph),
- // this.createButton("hand-point-left", "Hanging Indent", undefined, this.hangingIndentParagraph),
- // this.createButton("hand-point-right", "Indent", undefined, this.indentParagraph),
- // ]}
;
-
- // const row2 =
- // {this.collapsed ? this.getDragger() : (null)}
- //
- //
- // {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
- // this.activeFontSize = val;
- // SelectionManager.Views.map(dv => dv.Document._text_fontSize = val);
- // })),
- // this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
- // this.activeFontFamily = val;
- // SelectionManager.Views.map(dv => dv.Document._text_fontFamily = val);
- // })),
- //
,
- // this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
- // this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer),
- // this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote),
- // this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule)
- // ]}
- //
- // {/*
- // {
- //
- //
- //
- //
}
- //
- //
- //
- //
*/}
- //
;
}
}
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index e8cf9e992..42665830f 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -246,7 +246,7 @@ export class RichTextRules {
// activate a style by name using prefix '%'
new InputRule(new RegExp(/%[a-zA-Z_]+$/), (state, match, start, end) => {
const color = match[0].substring(1, match[0].length);
- const marks = RichTextMenu.Instance._brushMap.get(color);
+ const marks = RichTextMenu.Instance?._brushMap.get(color);
if (
DocListCast((Doc.UserDoc().template_notes as Doc).data)
@@ -367,7 +367,7 @@ export class RichTextRules {
if (count) {
const tr = this.TextBox.EditorView?.state.tr.insertText(' ' + (gptval as string));
tr && this.TextBox.EditorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(end + 2), tr.doc.resolve(end + 2 + (gptval as string).length))));
- RichTextMenu.Instance.elideSelection(this.TextBox.EditorView?.state, true);
+ RichTextMenu.Instance?.elideSelection(this.TextBox.EditorView?.state, true);
}
count++;
});
diff --git a/src/client/views/nodes/formattedText/nodes_rts.ts b/src/client/views/nodes/formattedText/nodes_rts.ts
index cab3a6ef5..ceafc3ba1 100644
--- a/src/client/views/nodes/formattedText/nodes_rts.ts
+++ b/src/client/views/nodes/formattedText/nodes_rts.ts
@@ -24,6 +24,7 @@ export const nodes: { [index: string]: NodeSpec } = {
// :: NodeSpec The top level document node.
doc: {
content: 'block+',
+ marks: '_',
},
paragraph: ParagraphNodeSpec,
@@ -331,12 +332,10 @@ export const nodes: { [index: string]: NodeSpec } = {
...orderedList,
content: 'list_item+',
group: 'block',
+ marks: '_',
attrs: {
bulletStyle: { default: 0 },
- mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet"
- fontColor: { default: 'inherit' },
- fontSize: { default: undefined },
- fontFamily: { default: undefined },
+ mapStyle: { default: 'decimal' }, // "decimal", "multi", "bullet",
visibility: { default: true },
indent: { default: undefined },
},
@@ -376,9 +375,10 @@ export const nodes: { [index: string]: NodeSpec } = {
],
toDOM(node: Node) {
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
- const fsize = node.attrs.fontSize ? `font-size: ${node.attrs.fontSize};` : '';
- const ffam = node.attrs.fontFamily ? `font-family:${node.attrs.fontFamily};` : '';
- const fcol = node.attrs.fontColor ? `color: ${node.attrs.fontColor};` : '';
+ const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
+ const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
+ const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
+ const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
const marg = node.attrs.indent ? `margin-left: ${node.attrs.indent};` : '';
if (node.attrs.mapStyle === 'bullet') {
return [
@@ -386,7 +386,7 @@ export const nodes: { [index: string]: NodeSpec } = {
{
'data-mapStyle': node.attrs.mapStyle,
'data-bulletStyle': node.attrs.bulletStyle,
- style: `${fsize} ${ffam} ${fcol} ${marg}`,
+ style: `${fhigh} ${fsize} ${ffam} ${fcol} ${marg}`,
},
0,
];
@@ -398,7 +398,7 @@ export const nodes: { [index: string]: NodeSpec } = {
class: `${map}-ol`,
'data-mapStyle': node.attrs.mapStyle,
'data-bulletStyle': node.attrs.bulletStyle,
- style: `list-style: none; ${fsize} ${ffam} ${fcol} ${marg}`,
+ style: `list-style: none; ${fhigh} ${fsize} ${ffam} ${fcol} ${marg}`,
},
0,
]
@@ -422,16 +422,22 @@ export const nodes: { [index: string]: NodeSpec } = {
},
},
],
- toDOM(node: any) {
+ toDOM(node: Node) {
+ const fhigh = (found => (found ? `background-color: ${found};` : ''))(node.marks.find(m => m.type.name === 'marker')?.attrs.highlight);
+ const fsize = (found => (found ? `font-size: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontSize')?.attrs.fontSize);
+ const ffam = (found => (found ? `font-family: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontFamily')?.attrs.family);
+ const fcol = (found => (found ? `color: ${found};` : ''))(node.marks.find(m => m.type.name === 'pFontColor')?.attrs.color);
const map = node.attrs.bulletStyle ? node.attrs.mapStyle + node.attrs.bulletStyle : '';
return [
'li',
- { class: `${map}`, 'data-mapStyle': node.attrs.mapStyle, 'data-bulletStyle': node.attrs.bulletStyle },
+ { class: `${map}`, style: `${fhigh} ${fsize} ${ffam} ${fcol} `, 'data-mapStyle': node.attrs.mapStyle, 'data-bulletStyle': node.attrs.bulletStyle },
node.attrs.visibility
? 0
: [
'span',
- { style: `position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== 'bullet' ? 'inline-block' : 'list-item'}; text-overflow: ellipsis; white-space: pre` },
+ {
+ style: `${fhigh} ${fsize} ${ffam} ${fcol} position: relative; width: 100%; height: 1.5em; overflow: hidden; display: ${node.attrs.mapStyle !== 'bullet' ? 'inline-block' : 'list-item'}; text-overflow: ellipsis; white-space: pre`,
+ },
`${node.firstChild?.textContent}...`,
],
];
--
cgit v1.2.3-70-g09d2
From 53fbe74037f03456a678d592d0ae5660c2f0d55e Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 10 Apr 2024 21:02:05 -0400
Subject: more notetaking fixes - turn off autoSize, unobserve children. fixed
typing a new note to delete placeholder text. added autoCreate for columns.
added pivot column selector for notetaking view.
---
.../collections/CollectionNoteTakingView.scss | 9 +++
.../views/collections/CollectionNoteTakingView.tsx | 80 +++++++++++++++++-----
.../collections/CollectionNoteTakingViewColumn.tsx | 4 +-
.../views/nodes/formattedText/FormattedTextBox.tsx | 2 +-
4 files changed, 73 insertions(+), 22 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 81000e7a5..95fda7b0a 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -494,6 +494,15 @@
.rc-switch-checked .rc-switch-inner {
left: 8px;
}
+
+ .collectionNoteTaking-pivotField {
+ display: none;
+ }
+ &:hover {
+ .collectionNoteTaking-pivotField {
+ display: unset;
+ }
+ }
}
.collectionNoteTakingViewLight {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 5c52d2398..d8a0aebb1 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -14,9 +14,10 @@ import { Docs, DocUtils } from '../../documents/Documents';
import { DragManager, dropActionType } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoable, undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { Colors } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView';
@@ -26,7 +27,7 @@ import './CollectionNoteTakingView.scss';
import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn';
import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
import { CollectionSubView } from './CollectionSubView';
-import { Colors } from '../global/globalEnums';
+import { FieldsDropdown } from '../FieldsDropdown';
const _global = (window /* browser */ || global) /* node */ as any;
/**
@@ -42,7 +43,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
_masonryGridRef: HTMLDivElement | null = null;
_draggerRef = React.createRef();
@computed get notetakingCategoryField() {
- return StrCast(this.dataDoc.notetaking_category, 'NotetakingCategory');
+ return StrCast(this.dataDoc.notetaking_column, StrCast(this.layoutDoc.pivotField, 'notetaking_column'));
}
public DividerWidth = 16;
@observable docsDraggedRowCol: number[] = [];
@@ -155,25 +156,34 @@ export class CollectionNoteTakingView extends CollectionSubView() {
);
};
+ @computed get allFieldValues() {
+ return new Set(this.childDocs.map(doc => StrCast(doc[this.notetakingCategoryField])));
+ }
+
componentDidMount() {
super.componentDidMount?.();
document.addEventListener('pointerup', this.removeDocDragHighlight, true);
- this._disposers.layout_autoHeight = reaction(
- () => this.layoutDoc._layout_autoHeight,
- layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight)))
+
+ this._disposers.autoColumns = reaction(
+ () => (this.layoutDoc._notetaking_columns_autoCreate ? Array.from(this.allFieldValues) : undefined),
+ columns => undoable(() => columns?.filter(col => !this.colHeaderData.some(h => h.heading === col)).forEach(col => this.addColumn(col)), 'adding columns')(),
+ { fireImmediately: true }
);
this._disposers.refList = reaction(
() => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
- if (autoHeight) refList.forEach(r => this.observer.observe(r));
- else this.observer.disconnect();
+ if (autoHeight) {
+ refList.forEach(r => this.observer.observe(r));
+ this._props.setHeight?.(this.headerMargin + Math.max(...this._refList.map(DivHeight)));
+ } else this.observer.disconnect();
},
{ fireImmediately: true }
);
}
componentWillUnmount() {
+ this.observer.disconnect();
document.removeEventListener('pointerup', this.removeDocDragHighlight, true);
super.componentWillUnmount();
Object.keys(this._disposers).forEach(key => this._disposers[key]());
@@ -300,7 +310,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
getDocWidth(d: Doc) {
const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
- const existingWidth = existingHeader?.width ? existingHeader.width : 0;
+ const existingWidth = this.layoutDoc._notetaking_columns_autoSize ? 1 / (this.colHeaderData.length ?? 1) : existingHeader?.width ? existingHeader.width : 0;
const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
const width = d.layout_fitWidth ? maxWidth : NumCast(d._width);
return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
@@ -502,7 +512,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
editableViewProps = () => ({
GetValue: () => '',
- SetValue: this.addGroup,
+ SetValue: this.addColumn,
contents: '+ Column',
});
@@ -543,18 +553,26 @@ export class CollectionNoteTakingView extends CollectionSubView() {
/>
);
+ @undoBatch
+ remColumn = (value: SchemaHeaderField) => {
+ const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ if (value) {
+ const index = colHdrData.indexOf(value);
+ index !== -1 && colHdrData.splice(index, 1);
+ this.resizeColumns(colHdrData);
+ }
+ };
+
// addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of
// columnHeaders and resizing the existing columns to make room for our new one.
@undoBatch
- addGroup = (value: string) => {
- if (this.colHeaderData) {
- for (const header of this.colHeaderData) {
- if (header.heading === value) {
- alert('You cannot use an existing column name. Please try a new column name');
- return value;
- }
+ addColumn = (value: string) => {
+ this.colHeaderData.forEach(header => {
+ if (header.heading === value) {
+ alert('You cannot use an existing column name. Please try a new column name');
+ return value;
}
- }
+ });
const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
const newColWidth = 1 / (this.numGroupColumns + 1);
columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
@@ -562,11 +580,25 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return true;
};
+ removeEmptyColumns = undoable(() => {
+ this.colHeaderData.filter(h => !this.allFieldValues.has(h.heading)).forEach(this.remColumn);
+ }, 'remove empty Columns');
+
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!e.isPropagationStopped()) {
const subItems: ContextMenuProps[] = [];
- subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+ subItems.push({
+ description: `${this.layoutDoc._notetaking_columns_autoCreate ? 'Manually' : 'Automatically'} Create columns`,
+ event: () => (this.layoutDoc._notetaking_columns_autoCreate = !this.layoutDoc._notetaking_columns_autoCreate),
+ icon: 'computer',
+ });
+ subItems.push({ description: 'Remove Empty Columns', event: this.removeEmptyColumns, icon: 'computer' });
+ subItems.push({
+ description: `${this.layoutDoc._notetaking_columns_autoSize ? 'Variable Size' : 'Autosize'} Columns`,
+ event: () => (this.layoutDoc._notetaking_columns_autoSize = !this.layoutDoc._notetaking_columns_autoSize),
+ icon: 'plus',
+ });
subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
@@ -634,6 +666,16 @@ export class CollectionNoteTakingView extends CollectionSubView() {
onContextMenu={this.onContextMenu}
onWheel={e => this._props.isContentActive() && e.stopPropagation()}>
<>{this.renderedSections}>
+
+ {
+ this.layoutDoc._pivotField = fieldKey;
+ this.removeEmptyColumns();
+ }, 'change pivot field')}
+ placeholder={StrCast(this.layoutDoc._pivotField)}
+ />
+
);
}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 788490b82..448b11b05 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -4,7 +4,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { lightOrDark, returnEmptyString } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -68,6 +67,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent
hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
@@ -154,7 +154,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent {
const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
if (this._props.headingObject) {
- this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
+ // this._props.docList.forEach(d => (d[DocData][this._props.pivotField] = undefined));
colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
this._props.resizeColumns(colHdrData);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c2f3a6e4b..5c59f7f60 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1255,7 +1255,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- if (this._editorView && this._applyingChange !== this.fieldKey) {
+ if (this._editorView && this._applyingChange !== this.fieldKey && incomingValue?.data !== this.dataDoc[this.fieldKey]) {
if (incomingValue?.data) {
const updatedState = JSON.parse(incomingValue.data.Data);
if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
--
cgit v1.2.3-70-g09d2
From d444d05ebe5eb0d72070ba21e1ae5935930b749f Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 17 Apr 2024 11:56:15 -0400
Subject: reverted fix for replacing placeholder text in a new typed note --
breaks switching from one fieldKey to another
---
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 5c59f7f60..c2f3a6e4b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1255,7 +1255,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- if (this._editorView && this._applyingChange !== this.fieldKey && incomingValue?.data !== this.dataDoc[this.fieldKey]) {
+ if (this._editorView && this._applyingChange !== this.fieldKey) {
if (incomingValue?.data) {
const updatedState = JSON.parse(incomingValue.data.Data);
if (JSON.stringify(this._editorView.state.toJSON()) !== JSON.stringify(updatedState)) {
--
cgit v1.2.3-70-g09d2
From 62937027183dc8acf14e489fbb4590aff6fce2cd Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 17 Apr 2024 12:25:17 -0400
Subject: fix problem where placeholder text of note wasn't being overwritten.
---
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 +
1 file changed, 1 insertion(+)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c2f3a6e4b..a2db2a1cc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1484,6 +1484,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent
Date: Wed, 24 Apr 2024 18:36:52 -0400
Subject: minor tweaks for merging
---
src/client/apis/gpt/GPT.ts | 6 +++---
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 9 +++------
2 files changed, 6 insertions(+), 9 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index c97d29d6f..30194f9f8 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -43,7 +43,7 @@ const gptAPICall = async (inputText: string, callType: GPTCallType, prompt?: any
};
const openai = new OpenAI(configuration);
- let usePrompt = prompt? opts.prompt+prompt: opts.prompt;
+ let usePrompt = prompt ? opts.prompt + prompt : opts.prompt;
let messages: ChatCompletionMessageParam[] = [
{ role: 'system', content: usePrompt },
{ role: 'user', content: inputText },
@@ -51,9 +51,9 @@ const gptAPICall = async (inputText: string, callType: GPTCallType, prompt?: any
const response = await openai.chat.completions.create({
model: opts.model,
- messages: messages,
- temperature: opts.temp,
max_tokens: opts.maxTokens,
+ temperature: opts.temp,
+ messages,
});
const content = response.choices[0].message.content;
return content;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index bb910737b..36c1de841 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -984,13 +984,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {
- if (!this._editorView) return;
if (resIndex < newText.length) {
- const marks = this._editorView.state.storedMarks ?? [];
- this._editorView.dispatch(this._editorView.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
- setTimeout(() => {
- this.animateRes(resIndex + 1, newText);
- }, 20);
+ const marks = this._editorView?.state.storedMarks ?? [];
+ this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(marks));
+ setTimeout(() => this.animateRes(resIndex + 1, newText), 20);
}
};
--
cgit v1.2.3-70-g09d2
From ae9404e2c80555875f2fd69f4ae55105ae51168b Mon Sep 17 00:00:00 2001
From: bobzel
Date: Wed, 24 Apr 2024 18:44:28 -0400
Subject: from last
---
src/client/views/nodes/formattedText/FormattedTextBox.tsx | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
(limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 36c1de841..a46b19a85 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -996,15 +996,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent