aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/formattedText')
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx13
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx10
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss20
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx360
-rw-r--r--src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts17
-rw-r--r--src/client/views/nodes/formattedText/RichTextMenu.tsx14
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts53
7 files changed, 269 insertions, 218 deletions
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index c929b7ff3..48f4c2afd 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -2,8 +2,9 @@ import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NodeSelection } from 'prosemirror-state';
import * as ReactDOM from 'react-dom/client';
-import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Doc } from '../../../../fields/Doc';
+import { Height, Width } from '../../../../fields/DocSymbols';
+import { NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -210,14 +211,14 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
addDocTab={this._textBox.props.addDocTab}
pinToPres={returnFalse}
renderDepth={this._textBox.props.renderDepth + 1}
- PanelWidth={this._finalLayout[WidthSym]}
- PanelHeight={this._finalLayout[HeightSym]}
+ PanelWidth={this._finalLayout[Width]}
+ PanelHeight={this._finalLayout[Height]}
focus={this.outerFocus}
whenChildContentsActiveChanged={returnFalse}
bringToFront={emptyFunction}
dontRegisterView={false}
- docFilters={this.props.tbox?.props.docFilters}
- docRangeFilters={this.props.tbox?.props.docRangeFilters}
+ childFilters={this.props.tbox?.props.childFilters}
+ childFiltersByRanges={this.props.tbox?.props.childFiltersByRanges}
searchFilterDocs={this.props.tbox?.props.searchFilterDocs}
/>
</div>
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 6c61f6709..d5ad128fe 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core';
import { action, computed, IReactionDisposer, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom/client';
-import { DataSym, Doc, Field } from '../../../../fields/Doc';
+import { Doc } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
@@ -17,6 +17,7 @@ import { OpenWhere } from '../DocumentView';
import './DashFieldView.scss';
import { FormattedTextBox } from './FormattedTextBox';
import React = require('react');
+import { Transform } from '../../../util/Transform';
export class DashFieldView {
dom: HTMLDivElement; // container for label and value
@@ -113,6 +114,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
componentWillUnmount() {
this._reactionDisposer?.();
}
+ return100 = () => 100;
// set the display of the field's value (checkbox for booleans, span of text for strings)
@computed get fieldValueContent() {
@@ -123,7 +125,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
col={0}
deselectCell={emptyFunction}
selectCell={emptyFunction}
- maxWidth={this.props.hideKey ? undefined : () => 100}
+ maxWidth={this.props.hideKey ? undefined : this.return100}
columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero}
selectedCell={() => [this._dashDoc!, 0]}
fieldKey={this._fieldKey}
@@ -135,6 +137,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
allowCRs={true}
oneLine={!this._expanded}
finishEdit={action(() => (this._expanded = false))}
+ transform={Transform.Identity}
+ menuTarget={null}
/>
</div>
);
@@ -144,7 +148,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
let container = this.props.tbox.props.DocumentView?.().props.docViewPath().lastElement();
if (container) {
const embedding = Doc.MakeEmbedding(container.rootDoc);
- embedding._viewType = CollectionViewType.Time;
+ embedding._type_collection = CollectionViewType.Time;
const colHdrKey = '_' + container.LayoutFieldKey + '_columnHeaders';
let list = Cast(embedding[colHdrKey], listSpec(SchemaHeaderField));
if (!list) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 109b62e6f..348bdd79e 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -84,7 +84,6 @@ audiotag:hover {
height: 11;
}
-.formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
@@ -92,9 +91,6 @@ audiotag:hover {
width: 100%;
height: unset;
}
-.formattedTextBox-outer-selected {
- cursor: text;
-}
.formattedTextBox-sidebar-handle {
position: absolute;
@@ -148,10 +144,8 @@ audiotag:hover {
}
.formattedTextBox-inner-rounded,
-.formattedTextBox-inner-rounded-selected,
.formattedTextBox-inner,
-.formattedTextBox-inner-minimal,
-.formattedTextBox-inner-selected {
+.formattedTextBox-inner-minimal {
height: 100%;
white-space: pre-wrap;
.ProseMirror:hover {
@@ -169,17 +163,6 @@ audiotag:hover {
border-width: 1px;
}
}
-.formattedTextBox-inner-rounded-selected,
-.formattedTextBox-inner-selected {
- > .ProseMirror {
- padding: 10px;
- }
-}
-.formattedTextBox-outer-selected {
- > .ProseMirror:hover {
- background: unset;
- }
-}
.gpt-typing-wrapper {
padding: 10px;
@@ -640,7 +623,6 @@ footnote::before {
}
}
- .formattedTextBox-outer-selected,
.formattedTextBox-outer {
position: relative;
overflow: auto;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 023814a9d..1dcc445e8 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,9 +12,11 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { DateField } from '../../../../fields/DateField';
-import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, CssSym, Doc, DocListCast, Field, ForceServerWrite, HeightSym, Opt, StrListCast, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, StrListCast, Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
import { PrefetchProxy } from '../../../../fields/Proxy';
import { RichTextField } from '../../../../fields/RichTextField';
import { RichTextUtils } from '../../../../fields/RichTextUtils';
@@ -32,8 +34,8 @@ import { DictationManager } from '../../../util/DictationManager';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { MakeTemplate } from '../../../util/DropConverter';
-import { IsFollowLinkScript } from '../../../util/LinkFollower';
import { LinkManager } from '../../../util/LinkManager';
+import { RTFMarkup } from '../../../util/RTFMarkup';
import { SelectionManager } from '../../../util/SelectionManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -68,8 +70,6 @@ import { schema } from './schema_rts';
import { SummaryView } from './SummaryView';
import applyDevTools = require('prosemirror-dev-tools');
import React = require('react');
-import { RTFMarkup } from '../../../util/RTFMarkup';
-import { List } from '../../../../fields/List';
const translateGoogleApi = require('translate-google-api');
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
@@ -96,6 +96,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef();
private _editorView: Opt<EditorView>;
public _applyingChange: string = '';
+ private _finishingLink = false;
private _searchIndex = 0;
private _lastTimedMark: Mark | undefined = undefined;
private _cachedLinks: Doc[] = [];
@@ -131,7 +132,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return this._showSidebar ? '20%' : StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
}
@computed get sidebarColor() {
- return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
+ return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.fieldKey + '_backgroundColor'], '#e4e4e4'));
}
@computed get layout_autoHeight() {
return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
@@ -243,9 +244,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc;
- const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true });
+ const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc.title), annotationOn: this.rootDoc });
this.addDocument(anchor);
+ this._finishingLink = true;
this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
+ this._finishingLink = false;
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
return anchor;
};
@@ -296,21 +299,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
};
dispatchTransaction = (tx: Transaction) => {
- if (this._editorView) {
+ if (this._editorView && (this._editorView as any).docView) {
const state = this._editorView.state.apply(tx);
this._editorView.updateState(state);
const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
- const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
- const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField) : undefined; // the actual text in the text box
- const curProto = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
- const curLayout = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : undefined; // the default text stored in a layout template
- const json = JSON.stringify(state.toJSON());
+ const newText = state.doc.textBetween(0, state.doc.content.size, ' \n');
+ const newJson = JSON.stringify(state.toJSON());
+ const prevData = Cast(this.layoutDoc[this.fieldKey], RichTextField, null); // the actual text in the text box
+ const templateData = this.rootDoc !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template
+ const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype
const effectiveAcl = GetEffectiveAcl(dataDoc);
- const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"'));
+ const removeSelection = (json: string | undefined) => json?.replace(/"selection":.*/, '');
- if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
+ if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) {
const accumTags = [] as string[];
state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) {
@@ -320,29 +323,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
dataDoc.tags = accumTags.length ? new List<string>(Array.from(new Set<string>(accumTags))) : undefined;
let unchanged = true;
- if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) {
+ if (this._applyingChange !== this.fieldKey && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
this._applyingChange = this.fieldKey;
- const textChange = curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text;
+ const textChange = newText !== prevData?.Text;
textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
- if ((!curTemp && !curProto) || curText || json.includes('dash')) {
+ if ((!prevData && !protoData) || newText || (!newText && !templateData)) {
// if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended)
- if (removeSelection(json) !== removeSelection(curLayout?.Data)) {
+ if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) {
const numstring = NumCast(dataDoc[this.fieldKey], null);
- if (numstring !== undefined) {
- dataDoc[this.fieldKey] = Number(curText);
- } else {
- dataDoc[this.fieldKey] = new RichTextField(json, curText);
- }
- dataDoc[this.fieldKey + '_noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited
- textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
+ dataDoc[this.fieldKey] = numstring !== undefined ? Number(newText) : new RichTextField(newJson, newText);
+ dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited
+ textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
unchanged = false;
}
} else {
// if we've deleted all the text in a note driven by a template, then restore the template data
dataDoc[this.fieldKey] = undefined;
- this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data)));
+ this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((protoData || prevData).Data)));
dataDoc[this.fieldKey + '_noTemplate'] = undefined; // mark the data field as not being split from any template it might have
- ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText });
+ ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText });
unchanged = false;
}
this._applyingChange = '';
@@ -418,6 +417,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
this._editorView?.dispatch(tr);
+ this.prepareForTyping();
}
oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
};
@@ -460,8 +460,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
alink =
alink ??
- (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) ||
- DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!);
+ (LinkManager.Links(this.rootDoc).find(
+ link =>
+ Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && //
+ Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)
+ ) ||
+ DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!);
newAutoLinks.add(alink);
const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }];
allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
@@ -534,36 +538,50 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
@undoBatch
@action
drop = (e: Event, de: DragManager.DropEvent) => {
- if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
+ if (de.complete.annoDragData) {
+ de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true);
+ e.stopPropagation();
+ return true;
+ }
const dragData = de.complete.docDragData;
if (dragData) {
- const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0];
- // replace text contents whend dragging with Alt
- if (draggedDoc && draggedDoc.type === DocumentType.RTF && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) {
- if (draggedDoc.data instanceof RichTextField) {
- Doc.GetProto(this.dataDoc)[this.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text);
- e.stopPropagation();
- }
- // embed document when dragg marked as embed
- } else if (de.embedKey) {
- const target = dragData.droppedDocuments[0];
- const node = schema.nodes.dashDoc.create({
- width: target[WidthSym](),
- height: target[HeightSym](),
- title: 'dashDoc',
- docId: target[Id],
- float: 'unset',
- });
- if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
- dragData.removeDocument?.(dragData.draggedDocuments[0]);
+ const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc;
+ const effectiveAcl = GetEffectiveAcl(dataDoc);
+ let added = [AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl);
+ const draggedDoc = dragData.draggedDocuments.lastElement();
+ if (added) {
+ // replace text contents when dragging with Alt
+ if (de.altKey) {
+ const fieldKey = Doc.LayoutFieldKey(draggedDoc);
+ if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.props.Document)) {
+ Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]);
+ }
+
+ // embed document when drag marked as embed
+ } else if (de.embedKey) {
+ const node = schema.nodes.dashDoc.create({
+ width: draggedDoc[Width](),
+ height: draggedDoc[Height](),
+ title: 'dashDoc',
+ docId: draggedDoc[Id],
+ float: 'unset',
+ });
+ if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
+ added = dragData.removeDocument?.(draggedDoc) ? true : false;
+ }
+ if (added) {
+ draggedDoc._freeform_fitContentsToBox = true;
+ Doc.SetContainer(draggedDoc, this.rootDoc);
+ const view = this._editorView!;
+ view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
+ }
}
- target._freeform_fitContentsToBox = true;
- target.embedContainer = this.rootDoc;
- const view = this._editorView!;
- view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node));
- e.stopPropagation();
} // otherwise, fall through to outer collection to handle drop
+ !added && e.preventDefault();
+ e.stopPropagation();
+ return added;
}
+ return false;
};
getNodeEndpoints(context: Node, node: Node): { from: number; to: number } | null {
@@ -650,7 +668,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const hr = Math.round(Date.now() / 1000 / 60 / 60);
numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, 'UM-hr-' + (hr - i), { opacity: ((10 - i - 1) / 10).toString() }));
}
- this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView)
+ this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css changes happen outside of react/mobx. so we need to set a flag that will notify anyone intereted in layout changes triggered by css changes (eg., CollectionLinkView)
};
@observable _showSidebar = false;
@@ -667,7 +685,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
};
sidebarDown = (e: React.PointerEvent) => {
- const batch = UndoManager.StartBatch('sidebar');
+ const batch = UndoManager.StartBatch('toggle sidebar');
setupMoveUpEvents(
this,
e,
@@ -686,7 +704,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
- const width = this.layoutDoc[WidthSym]() + localDelta[0];
+ const width = this.layoutDoc[Width]() + localDelta[0];
this.layoutDoc._layout_sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
this.layoutDoc.width = width;
this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
@@ -694,13 +712,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return false;
};
- @undoBatch
deleteAnnotation = (anchor: Doc) => {
+ const batch = UndoManager.StartBatch('delete link');
LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]);
// const docAnnotations = DocListCast(this.props.dataDoc[this.fieldKey]);
// this.props.dataDoc[this.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
// AnchorMenu.Instance.fadeOut(true);
this.props.select(false);
+ setTimeout(batch.end); // wait for reaction to remove link from document
};
@undoBatch
@@ -733,17 +752,22 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.split(' ')
.filter(h => h);
const anchorDoc = Array.from(hrefs).lastElement().replace(Doc.localServerPath(), '').split('?')[0];
+ const deleteMarkups = undoBatch(() => {
+ const sel = editor.state.selection;
+ editor.dispatch(editor.state.tr.removeMark(sel.from, sel.to, editor.state.schema.marks.linkAnchor));
+ });
e.persist();
anchorDoc &&
DocServer.GetRefField(anchorDoc).then(
action(anchor => {
+ anchor && SelectionManager.SelectSchemaViewDoc(anchor as Doc);
AnchorMenu.Instance.Status = 'annotation';
- AnchorMenu.Instance.Delete = () => this.deleteAnnotation(anchor as Doc);
+ AnchorMenu.Instance.Delete = !anchor && editor.state.selection.empty ? returnFalse : !anchor ? deleteMarkups : () => this.deleteAnnotation(anchor as Doc);
AnchorMenu.Instance.Pinned = false;
- AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc);
- AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc);
- AnchorMenu.Instance.ShowTargetTrail = () => this.showTargetTrail(anchor as Doc);
- AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc);
+ AnchorMenu.Instance.PinToPres = !anchor ? returnFalse : () => this.pinToPres(anchor as Doc);
+ AnchorMenu.Instance.MakeTargetToggle = !anchor ? returnFalse : () => this.makeTargetToggle(anchor as Doc);
+ AnchorMenu.Instance.ShowTargetTrail = !anchor ? returnFalse : () => this.showTargetTrail(anchor as Doc);
+ AnchorMenu.Instance.IsTargetToggler = !anchor ? returnFalse : () => this.isTargetToggler(anchor as Doc);
AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
})
);
@@ -808,9 +832,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
});
uicontrols.push({
- description: (this.Document._layout_altContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
- event: () => (this.layoutDoc._layout_altContentUI = !this.layoutDoc._layout_altContentUI),
- icon: !this.Document._layout_altContentUI ? 'eye-slash' : 'eye',
+ description: (this.Document._layout_enableAltContentUI ? 'Hide' : 'Show') + ' Alt Content UI',
+ event: () => (this.layoutDoc._layout_enableAltContentUI = !this.layoutDoc._layout_enableAltContentUI),
+ icon: !this.Document._layout_enableAltContentUI ? 'eye-slash' : 'eye',
});
uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' });
!Doc.noviceMode &&
@@ -823,7 +847,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const appearance = cm.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
+ appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' });
// this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
!Doc.noviceMode &&
@@ -862,11 +886,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const optionItems = options && 'subitems' in options ? options.subitems : [];
optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' });
optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' });
- optionItems.push({
- description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
- event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
- icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
- });
+ this.props.renderDepth &&
+ optionItems.push({
+ description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns',
+ event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR),
+ icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars',
+ });
!Doc.noviceMode &&
optionItems.push({
description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`,
@@ -906,7 +931,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
if (image_url) {
const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
- const source = Utils.prepend(result.accessPaths.agnostic.client);
+ const source = result.accessPaths.agnostic.client;
const newDoc = Docs.Create.ImageDocument(source, {
x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10,
y: NumCast(this.rootDoc.y),
@@ -915,10 +940,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
data_nativeWidth: result.nativeWidth,
data_nativeHeight: result.nativeHeight,
});
- if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc)) {
+ if (Doc.IsInMyOverlay(this.rootDoc)) {
newDoc.overlayX = this.rootDoc.x;
newDoc.overlayY = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
- Doc.AddDocToList(Doc.MyOverlayDocs, undefined, newDoc);
+ Doc.AddToMyOverlay(newDoc);
} else {
this.props.addDocument?.(newDoc);
}
@@ -960,7 +985,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (this._editorView && this._recordingStart) {
if (this._break) {
const textanchorFunc = () => {
- const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor', unrendered: true });
+ const tanch = Docs.Create.ConfigDocument({ title: 'dictation anchor' });
return this.addDocument(tanch) ? tanch : undefined;
};
const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
@@ -998,7 +1023,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const splitter = state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
let tr = state.tr.addMark(sel.from, sel.to, splitter);
if (sel.from !== sel.to) {
- const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: '#' + this._editorView?.state.doc.textBetween(sel.from, sel.to), annotationOn: this.dataDoc, unrendered: true });
+ const anchor =
+ anchorDoc ??
+ Docs.Create.ConfigDocument({
+ //
+ title: 'text(' + this._editorView?.state.doc.textBetween(sel.from, sel.to) + ')',
+ annotationOn: this.dataDoc,
+ });
const href = targetHref ?? Doc.localServerPath(anchor);
if (anchor !== anchorDoc && addAsAnnotation) this.addDocument(anchor);
tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
@@ -1014,6 +1045,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter));
this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false;
anchor.text = selectedText;
+ anchor.title = selectedText.substring(0, 30);
return anchor;
}
return anchorDoc ?? this.rootDoc;
@@ -1022,7 +1054,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
getView = async (doc: Doc) => {
- if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
!this.SidebarShown && this.toggleSidebar(false);
setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc));
}
@@ -1129,11 +1161,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
() => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layout_autoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
({ sidebarHeight, textHeight, layout_autoHeight, marginsHeight }) => {
const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
- if (layout_autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
+ if (
+ (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this.props.isSelected()) && //
+ layout_autoHeight &&
+ newHeight &&
+ newHeight !== this.rootDoc.height &&
+ !this.props.dontRegisterView
+ ) {
this.props.setHeight?.(newHeight);
}
},
- { fireImmediately: true }
+ { fireImmediately: !Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') }
);
this._disposers.links = reaction(
() => LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks
@@ -1155,7 +1193,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
() => {
const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc?.proto), this.fieldKey) ? DocCast(this.layoutDoc?.proto) : this?.dataDoc;
const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : dataDoc?.[this.fieldKey + '_noTemplate'] || !this.layoutDoc[this.fieldKey] ? dataDoc : this.layoutDoc;
- return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(whichDoc[this.fieldKey]) };
+ return !whichDoc ? undefined : { data: Cast(whichDoc[this.fieldKey], RichTextField, null), str: Field.toString(DocCast(whichDoc[this.fieldKey])) };
},
incomingValue => {
if (this._editorView && this._applyingChange !== this.fieldKey) {
@@ -1199,15 +1237,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._disposers.selected = reaction(
() => this.props.isSelected(),
action(selected => {
+ selected && this.prepareForTyping();
if (FormattedTextBox._globalHighlights.has('Bold Text')) {
- this.layoutDoc[CssSym] = this.layoutDoc[CssSym] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
+ this.layoutDoc[DocCss] = this.layoutDoc[DocCss] + 1; // css change happens outside of mobx/react, so this will notify anyone interested in the layout that it has changed
}
if (RichTextMenu.Instance?.view === this._editorView && !selected) {
RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined);
}
if (this._editorView && selected) {
RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
- this.autoLink();
+ setTimeout(this.autoLink, 20);
}
// Accessing editor and text doc for gpt assisted text edits
if (this._editorView && selected) {
@@ -1474,11 +1513,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
- if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+ if (this._editorView && selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
const selLoadChar = FormattedTextBox.SelectOnLoadChar;
FormattedTextBox.SelectOnLoad = '';
this.props.select(false);
- if (selLoadChar && this._editorView) {
+ if (selLoadChar) {
const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
@@ -1488,30 +1527,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size)
.setStoredMarks(storedMarks);
this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
- } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
+ } else if (curText && !FormattedTextBox.DontSelectInitialText) {
selectAll(this._editorView.state, this._editorView?.dispatch);
- this.startUndoTypingBatch();
- } else if (this._editorView) {
- this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
}
}
selectOnLoad && this._editorView!.focus();
- // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
+ if (this.props.isContentActive()) this.prepareForTyping();
if (this._editorView) {
const tr = this._editorView.state.tr;
const { from, to } = tr.selection;
- // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selectoin after the document has ben fully instantiated.
+ // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the selection after the document has ben fully instantiated.
if (FormattedTextBox.DontSelectInitialText) setTimeout(() => this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(from), tr.doc.resolve(to)))), 250);
- this._editorView.state.storedMarks = [
- ...(this._editorView.state.storedMarks ?? []),
- ...(!this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark) ? [schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })] : []),
- ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
- ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
- ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
- ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
- ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
- ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
- ];
+
if (FormattedTextBox.PasteOnLoad) {
const pdfAnchorId = FormattedTextBox.PasteOnLoad.clipboardData?.getData('dash/pdfAnchor');
FormattedTextBox.PasteOnLoad = undefined;
@@ -1521,9 +1548,31 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
FormattedTextBox.DontSelectInitialText = false;
}
+ // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet.
+ prepareForTyping = () => {
+ this._editorView?.dispatch(
+ this._editorView?.state.tr.setStoredMarks([
+ ...(this._editorView.state.storedMarks?.filter(mark => ![schema.marks.em, schema.marks.underline, schema.marks.pFontFamily, schema.marks.pFontSize, schema.marks.strong, schema.marks.pFontColor].includes(mark.type)) ?? []),
+ ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })],
+ ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []),
+ ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []),
+ ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []),
+ ...(Doc.UserDoc().fontFamily ? [schema.mark(schema.marks.pFontFamily, { family: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontFamily) })] : []),
+ ...(Doc.UserDoc().fontSize ? [schema.mark(schema.marks.pFontSize, { fontSize: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.FontSize) })] : []),
+ ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []),
+ ])
+ );
+ };
+
+ @action
componentWillUnmount() {
+ if (this._recording) {
+ this._recording = !this._recording;
+ }
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
+ FormattedTextBox.LiveTextUndo?.end();
+ FormattedTextBox.LiveTextUndo = undefined;
this.unhighlightSearchTerms();
this._editorView?.destroy();
RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined);
@@ -1558,8 +1607,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
if (this._recording && !e.ctrlKey && e.button === 0) {
this.breakupDictation();
- e.preventDefault();
- e.stopPropagation();
}
this._downX = e.clientX;
this._downY = e.clientY;
@@ -1624,7 +1671,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onFocused = (e: React.FocusEvent): void => {
//applyDevTools.applyDevTools(this._editorView);
this.ProseRef?.children[0] === e.nativeEvent.target && this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props);
- this.startUndoTypingBatch();
+ e.stopPropagation();
};
onClick = (e: React.MouseEvent): void => {
@@ -1699,13 +1746,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
startUndoTypingBatch() {
- !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('undoTyping'));
+ !this._undoTyping && (this._undoTyping = UndoManager.StartBatch('text edits on ' + this.rootDoc.title));
}
public endUndoTypingBatch() {
- const wasUndoing = this._undoTyping;
this._undoTyping?.end();
this._undoTyping = undefined;
- return wasUndoing;
}
@action
@@ -1733,7 +1778,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
const state = this._editorView!.state;
const curText = state.doc.textBetween(0, state.doc.content.size, ' \n');
- if (this.layoutDoc.sidebarViewType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
+ if (this.layoutDoc.sidebar_collectionType === 'translation' && !this.fieldKey.includes('translation') && curText.endsWith(' ') && curText !== this._lastText) {
try {
translateGoogleApi(curText, { from: 'en', to: 'es' }).then((result1: any) => {
setTimeout(
@@ -1799,8 +1844,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
default:
if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
case ' ':
- [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) &&
- this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
+ if (e.code !== 'Space') {
+ [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) &&
+ this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
+ }
+ break;
}
this.startUndoTypingBatch();
};
@@ -1822,17 +1870,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
tryUpdateScrollHeight = () => {
const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
- if (children) {
+ if (children && !SnappingManager.GetIsDragging()) {
const toNum = (val: string) => Number(val.replace('px', '').replace('auto', '0'));
const toHgt = (node: Element) => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
return toNum(height) + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
};
const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + toHgt(child), margins);
- const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+ const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.layout_maxAutoHeight, proseHeight), proseHeight);
if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
+
if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
setScrollHeight();
} else {
@@ -1941,25 +1990,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
fitContentsToBox={this.fitContentsToBox}
noSidebar={true}
treeViewHideTitle={true}
- fieldKey={this.layoutDoc.sidebarViewType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
+ fieldKey={this.layoutDoc.sidebar_collectionType === 'translation' ? `${this.fieldKey}_translation` : `${this.fieldKey}_sidebar`}
/>
</div>
);
};
return (
<div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
+ {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
</div>
);
}
cycleAlternateText = () => {
- if (this.layoutDoc._layout_altContentUI) {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ if (this.layoutDoc._layout_enableAltContentUI) {
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined;
}
};
@computed get overlayAlternateIcon() {
- const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`];
+ const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`];
return (
<Tooltip
title={
@@ -1995,62 +2044,69 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return this.props.fieldKey + (usePath && (!usePath.includes(':hover') || this._isHovering) ? `_${usePath.replace(':hover', '')}` : '');
}
@observable _isHovering = false;
+ onPassiveWheel = (e: WheelEvent) => {
+ // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
+ if (this.props.isContentActive() && !this.props.allowScroll) {
+ if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
+ e.stopPropagation();
+ }
+ };
+ _oldWheel: any;
+ @computed get fontColor() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color);
+ }
+ @computed get fontSize() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize);
+ }
+ @computed get fontFamily() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily);
+ }
+ @computed get fontWeight() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight);
+ }
render() {
TraceMobx();
- const active = this.props.isContentActive() || this.props.isSelected();
- const selected = active;
const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
- const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
- const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
- if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
- const minimal = this.props.ignoreAutoHeight;
+ const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : '';
+ setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
- const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
- const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : '';
const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' >
return styleFromLayoutString?.height === '0px' ? null : (
<div
className="formattedTextBox"
onPointerEnter={action(() => (this._isHovering = true))}
onPointerLeave={action(() => (this._isHovering = false))}
- ref={r =>
- r?.addEventListener(
- 'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
- (e: WheelEvent) => {
- if (this.props.isContentActive() && !this.props.allowScroll) {
- if (!NumCast(this.layoutDoc._layout_scrollTop) && e.deltaY <= 0) e.preventDefault();
- e.stopPropagation();
- }
- },
- { passive: false }
- )
- }
+ ref={r => {
+ this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+ this._oldWheel = r;
+ r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ }}
style={{
...(this.props.dontScale
? {}
: {
transform: `scale(${scale})`,
- transformOrigin: 'top left',
width: `${100 / scale}%`,
height: `${100 / scale}%`,
}),
- display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
+ // display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined,
transition: 'inherit',
// overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined,
- color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
- fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize),
- fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily),
- fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight),
+ color: this.fontColor,
+ fontSize: this.fontSize,
+ fontFamily: this.fontFamily,
+ fontWeight: this.fontWeight,
...styleFromLayoutString,
}}>
<div
className="formattedTextBox-cont"
ref={this._ref}
style={{
+ cursor: this.props.isContentActive() ? 'text' : undefined,
overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox
height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined),
- pointerEvents: interactive ? undefined : 'none',
+ pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none',
}}
onContextMenu={this.specificContextMenu}
onKeyDown={this.onKeyDown}
@@ -2062,32 +2118,30 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
onPointerDown={this.onPointerDown}
onDoubleClick={this.onDoubleClick}>
<div
- className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
+ className={`formattedTextBox-outer`}
ref={this._scrollRef}
style={{
width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
- pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
onDrop={this.ondrop}>
<div
- className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`}
+ className={`formattedTextBox-inner${rounded}`}
ref={this.createDropTarget}
style={{
padding: StrCast(this.layoutDoc._textBoxPadding),
- paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
- paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`),
- paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
- paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`),
- pointerEvents: !active && !SnappingManager.GetIsDragging() ? (IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined) : undefined,
+ paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`),
+ paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`),
+ paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
+ paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`),
}}
/>
</div>
{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 ? null : this.sidebarHandle}
{this.audioHandle}
- {this.layoutDoc._layout_altContentUI ? this.overlayAlternateIcon : null}
+ {this.layoutDoc._layout_enableAltContentUI ? this.overlayAlternateIcon : null}
</div>
</div>
);
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 4dfe07b24..112a0d87e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -4,7 +4,7 @@ import { Schema } from 'prosemirror-model';
import { splitListItem, wrapInList } from 'prosemirror-schema-list';
import { EditorState, NodeSelection, TextSelection, Transaction } from 'prosemirror-state';
import { liftTarget } from 'prosemirror-transform';
-import { AclAugment, AclSelfEdit, Doc } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit } from '../../../../fields/DocSymbols';
import { GetEffectiveAcl } from '../../../../fields/util';
import { Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
@@ -12,6 +12,7 @@ import { RTFMarkup } from '../../../util/RTFMarkup';
import { SelectionManager } from '../../../util/SelectionManager';
import { OpenWhere } from '../DocumentView';
import { liftListItem, sinkListItem } from './prosemirrorPatches.js';
+import { Doc } from '../../../../fields/Doc';
const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false;
@@ -48,15 +49,11 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
const canEdit = (state: any) => {
switch (GetEffectiveAcl(props.DataDoc)) {
case AclAugment:
- return false;
- case AclSelfEdit:
- for (var i = state.selection.from; i < state.selection.to; i++) {
- const marks = state.doc.resolve(i)?.marks?.();
- if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) {
- return false;
- }
+ const prevNode = state.selection.$cursor.nodeBefore;
+ const prevUser = !prevNode ? Doc.CurrentUserEmail : prevNode.marks[prevNode.marks.length - 1].attrs.userid;
+ if (prevUser != Doc.CurrentUserEmail) {
+ return false;
}
- break;
}
return true;
};
@@ -337,7 +334,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
//Command to create a blank space
bind('Space', (state: EditorState, dispatch: (tx: Transaction) => void) => {
- if (!canEdit(state)) return true;
+ if (GetEffectiveAcl(props.DataDoc) != AclEdit && GetEffectiveAcl(props.DataDoc) != AclAugment && GetEffectiveAcl(props.DataDoc) != AclAdmin) return true;
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
dispatch(splitMetadata(marks, state.tr));
return false;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 5e0041b84..7c3e4baad 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -138,7 +138,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.activeListType = this.getActiveListStyle();
this._activeAlignment = this.getActiveAlignment();
- this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
+ this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document._text_fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various';
this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0];
this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...';
this._activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...';
@@ -221,7 +221,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight));
});
} else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
- SelectionManager.Views().forEach(dv => StrCast(dv.rootDoc._fontSize) && activeSizes.add(StrCast(dv.rootDoc._fontSize)));
+ SelectionManager.Views().forEach(dv => StrCast(dv.rootDoc._text_fontSize) && activeSizes.add(StrCast(dv.rootDoc._text_fontSize)));
}
return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) };
}
@@ -345,8 +345,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
this.view.focus();
}
} else if (SelectionManager.Views().some(dv => dv.ComponentView instanceof EquationBox)) {
- SelectionManager.Views().forEach(dv => (dv.rootDoc._fontSize = fontSize));
- } else Doc.UserDoc()._fontSize = fontSize;
+ SelectionManager.Views().forEach(dv => (dv.rootDoc._text_fontSize = fontSize));
+ } else Doc.UserDoc().fontSize = fontSize;
this.updateMenu(this.view, undefined, this.props);
};
@@ -355,7 +355,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family });
this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true);
this.view.focus();
- } else Doc.UserDoc()._fontFamily = family;
+ } else Doc.UserDoc().fontFamily = family;
this.updateMenu(this.view, undefined, this.props);
};
@@ -758,11 +758,11 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> {
// <div className="collectionMenu-divider" key="divider 3" />
// {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => {
// this.activeFontSize = val;
- // SelectionManager.Views().map(dv => dv.props.Document._fontSize = val);
+ // SelectionManager.Views().map(dv => dv.props.Document._text_fontSize = val);
// })),
// this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => {
// this.activeFontFamily = val;
- // SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val);
+ // SelectionManager.Views().map(dv => dv.props.Document._text_fontFamily = val);
// })),
// <div className="collectionMenu-divider" key="divider 4" />,
// this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})),
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 104aed058..8bafc2cef 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -1,11 +1,11 @@
import { ellipsis, emDash, InputRule, smartQuotes, textblockTypeInputRule } from 'prosemirror-inputrules';
import { NodeSelection, TextSelection } from 'prosemirror-state';
-import { DataSym, Doc, StrListCast } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
+import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { ComputedField } from '../../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../../fields/Types';
-import { normalizeEmail } from '../../../../fields/util';
+import { NumCast } from '../../../../fields/Types';
import { Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
@@ -76,12 +76,21 @@ export class RichTextRules {
//Create annotation to a field on the text document
new InputRule(new RegExp(/>>$/), (state, match, start, end) => {
- const textDoc = this.Document[DataSym];
+ const textDoc = this.Document[DocData];
const numInlines = NumCast(textDoc.inlineTextCount);
textDoc.inlineTextCount = numInlines + 1;
const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to
const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation
- const textDocInline = Docs.Create.TextDocument('', { _layout_fieldKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _layout_fitWidth: true, _layout_autoHeight: true, _fontSize: '9px', title: 'inline comment' });
+ const textDocInline = Docs.Create.TextDocument('', {
+ _layout_fieldKey: inlineLayoutKey,
+ _width: 75,
+ _height: 35,
+ annotationOn: textDoc,
+ _layout_fitWidth: true,
+ _layout_autoHeight: true,
+ _text_fontSize: '9px',
+ title: 'inline comment',
+ });
textDocInline.title = inlineFieldKey; // give the annotation its own title
textDocInline.title_custom = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc
textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point
@@ -234,13 +243,13 @@ export class RichTextRules {
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
// [[<fieldKey> : <Doc>]]
- // [[:Doc]] => hyperlink
+ // [[:docTitle]] => hyperlink
// [[fieldKey]] => show field
// [[fieldKey=value]] => show field and also set its value
- // [[fieldKey:Doc]] => show field of doc
+ // [[fieldKey:docTitle]] => show field of doc
new InputRule(new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => {
const fieldKey = match[1];
- const docId = match[3]?.replace(':', '');
+ const docTitle = match[3]?.replace(':', '');
const value = match[2]?.substring(1);
const linkToDoc = (target: Doc) => {
const rstate = this.TextBox.EditorView?.state;
@@ -257,21 +266,25 @@ export class RichTextRules {
}
};
if (!fieldKey) {
- if (docId) {
- const target = DocServer.QUERY_SERVER_CACHE(docId);
- if (target) setTimeout(() => linkToDoc(target));
- else DocServer.GetRefField(docId).then(docx => linkToDoc((docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docId + '(auto)', _width: 500, _height: 500 }, docId)));
-
- return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ if (docTitle) {
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ setTimeout(() => linkToDoc(target));
+ return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 3);
+ }
}
return state.tr;
}
if (value !== '' && value !== undefined) {
const num = value.match(/^[0-9.]$/);
- this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
+ this.Document[DocData][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value;
+ }
+ const target = DocServer.FindDocByTitle(docTitle);
+ if (target) {
+ const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId: target[Id], hideKey: false });
+ return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
}
- const fieldView = state.schema.nodes.dashField.create({ fieldKey, docId, hideKey: false });
- return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true);
+ return state.tr;
}),
// create a text display of a metadata field on this or another document, or create a hyperlink portal to another document
@@ -303,11 +316,11 @@ export class RichTextRules {
new InputRule(new RegExp(/#([a-zA-Z_\-]+[a-zA-Z_\-0-9]*)\s$/), (state, match, start, end) => {
const tag = match[1];
if (!tag) return state.tr;
- //this.Document[DataSym]['#' + tag] = '#' + tag;
- const tags = StrListCast(this.Document[DataSym].tags);
+ //this.Document[DocData]['#' + tag] = '#' + tag;
+ const tags = StrListCast(this.Document[DocData].tags);
if (!tags.includes(tag)) {
tags.push(tag);
- this.Document[DataSym].tags = new List<string>(tags);
+ this.Document[DocData].tags = new List<string>(tags);
}
const fieldView = state.schema.nodes.dashField.create({ fieldKey: '#' + tag });
return state.tr