aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/documents/DocUtils.ts5
-rw-r--r--src/client/views/GlobalKeyHandler.ts19
-rw-r--r--src/client/views/MarqueeAnnotator.tsx2
-rw-r--r--src/client/views/SidebarAnnos.tsx9
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx7
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx2
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx21
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx26
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx2
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx2
-rw-r--r--src/client/views/nodes/DocumentView.tsx6
-rw-r--r--src/client/views/nodes/EquationBox.tsx10
-rw-r--r--src/client/views/nodes/LabelBox.tsx5
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx2
-rw-r--r--src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx2
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx2
-rw-r--r--src/client/views/nodes/formattedText/DailyJournal.tsx25
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx41
-rw-r--r--src/fields/Doc.ts4
-rw-r--r--src/fields/RichTextField.ts87
24 files changed, 149 insertions, 141 deletions
diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts
index 7e0416447..9797a407b 100644
--- a/src/client/documents/DocUtils.ts
+++ b/src/client/documents/DocUtils.ts
@@ -385,7 +385,7 @@ export namespace DocUtils {
newDoc.x = x;
newDoc.y = y;
newDoc[DocData].backgroundColor = Doc.UserDoc().textBackgroundColor;
- Doc.SetSelectOnLoad(newDoc);
+ DocumentView.SetSelectOnLoad(newDoc);
if (pivotField) {
newDoc[pivotField] = pivotValue;
}
@@ -408,6 +408,7 @@ export namespace DocUtils {
description: ':Daily Journal',
event: undoable(() => {
const newDoc = Docs.Create.DailyJournalDocument('', { x, y });
+ DocumentView.SetSelectOnLoad(newDoc);
docAdder?.(newDoc);
}, 'Create Daily Journal'),
icon: 'book',
@@ -456,7 +457,7 @@ export namespace DocUtils {
newDoc.author = ClientUtils.CurrentUserEmail();
newDoc.x = x;
newDoc.y = y;
- Doc.SetSelectOnLoad(newDoc);
+ DocumentView.SetSelectOnLoad(newDoc);
if (pivotField) {
newDoc[pivotField] = pivotValue;
}
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index b200aff65..2d342d1b1 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -23,7 +23,6 @@ import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocume
import { DocumentLinksButton } from './nodes/DocumentLinksButton';
import { DocumentView } from './nodes/DocumentView';
import { OpenWhereMod } from './nodes/OpenWhere';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
import { AnchorMenu } from './pdf/AnchorMenu';
const modifiers = ['control', 'meta', 'shift', 'alt'];
@@ -75,7 +74,6 @@ export class KeyManager {
public handle = action((e: KeyboardEvent) => {
// accumulate buffer of characters to insert in a new text note. once the note is created, it will stop keyboard events from reaching this function.
- if (FormattedTextBox.SelectOnLoadChar) FormattedTextBox.SelectOnLoadChar += e.key === 'Enter' ? '\n' : e.key;
const keyname = e.key && e.key.toLowerCase();
this.handleGreedy(/* keyname */);
@@ -106,6 +104,10 @@ export class KeyManager {
};
private unmodified = action((keyname: string, e: KeyboardEvent) => {
+ const nothing = {
+ stopPropagation: false,
+ preventDefault: false,
+ };
switch (keyname) {
case 'u':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
@@ -169,17 +171,14 @@ export class KeyManager {
return { stopPropagation: true, preventDefault: true };
}
break;
- case 'arrowleft': return (e.target as any).type !== 'text' && this.nudge(-1, 0, 'nudge left') // if target is an input box, then we don't want to nudge any Docs since we're justing moving within the text itself.
- case 'arrowright': return (e.target as any).type !== 'text' && this.nudge( 1, 0, 'nudge right');
- case 'arrowup': return (e.target as any).type !== 'text' && this.nudge(0, -1, 'nudge up');
- case 'arrowdown': return (e.target as any).type !== 'text' && this.nudge(0, 1, 'nudge down');
+ case 'arrowleft': return (e.target as HTMLInputElement)?.type !== 'text' ? this.nudge(-1, 0, 'nudge left') : nothing; // if target is an input box, then we don't want to nudge any Docs since we're justing moving within the text itself.
+ case 'arrowright': return (e.target as HTMLInputElement)?.type !== 'text' ? this.nudge( 1, 0, 'nudge right') : nothing;
+ case 'arrowup': return (e.target as HTMLInputElement)?.type !== 'text' ? this.nudge(0, -1, 'nudge up') : nothing;
+ case 'arrowdown': return (e.target as HTMLInputElement | null)?.type !== 'text'? this.nudge(0, 1, 'nudge down'): nothing;
default:
} // prettier-ignore
- return {
- stopPropagation: false,
- preventDefault: false,
- };
+ return nothing;
});
private shift = action((keyname: string) => {
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 02516264c..3f4200dce 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -199,7 +199,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
const targetCreator = (annotationOn: Doc | undefined) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
- Doc.SetSelectOnLoad(target);
+ DocumentView.SetSelectOnLoad(target);
return target;
};
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 87076bf65..816fc8ed3 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -3,23 +3,23 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, returnAll, returnFalse, returnOne, returnZero } from '../../ClientUtils';
import { emptyFunction } from '../../Utils';
-import { Doc, DocListCast, Field, FieldType, FieldResult, StrListCast } from '../../fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, FieldType, StrListCast } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { List } from '../../fields/List';
import { RichTextField } from '../../fields/RichTextField';
import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { DocUtils } from '../documents/DocUtils';
import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { Docs } from '../documents/Documents';
-import { DocUtils } from '../documents/DocUtils';
import { SearchUtil } from '../util/SearchUtil';
import { Transform } from '../util/Transform';
import { ObservableReactComponent } from './ObservableReactComponent';
import './SidebarAnnos.scss';
import { StyleProp } from './StyleProp';
import { CollectionStackingView } from './collections/CollectionStackingView';
+import { DocumentView } from './nodes/DocumentView';
import { FieldViewProps } from './nodes/FieldView';
-import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
interface ExtraProps {
fieldKey: string;
@@ -81,8 +81,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
text_fontSize: StrCast(Doc.UserDoc().fontSize),
text_fontFamily: StrCast(Doc.UserDoc().fontFamily),
});
- Doc.SetSelectOnLoad(target);
- FormattedTextBox.DontSelectInitialText = true;
+ DocumentView.SetSelectOnLoad(target);
DocUtils.MakeLink(anchor, target, { link_relationship: 'inline comment:comment on' });
const taggedContent = this.childFilters()
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index a0d5f9f70..996626575 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -16,7 +16,7 @@ import { Transform } from '../../util/Transform';
import { undoBatch, undoable } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { DocumentView } from '../nodes/DocumentView';
import { CollectionStackingView } from './CollectionStackingView';
import './CollectionStackingView.scss';
@@ -162,9 +162,8 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
if (!value && !forceEmptyNote) return false;
this._createEmbeddingSelected = false;
const { pivotField } = this._props;
- const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
- Doc.SetSelectOnLoad(newDoc);
- FormattedTextBox.SelectOnLoadChar = value;
+ const newDoc = Docs.Create.TextDocument(value, { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
+ DocumentView.SetSelectOnLoad(newDoc);
pivotField && (newDoc[DocData][pivotField] = this.getValue(this._props.heading));
const docs = this._props.parent.childDocList;
return docs ? !!docs.splice(0, 0, newDoc) : this._props.parent._props.addDocument?.(newDoc) || false; // should really extend addDocument to specify insertion point (at beginning of list)
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 4c23d4c48..c499bd288 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -442,7 +442,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
e.stopPropagation?.();
const newDoc = Doc.MakeCopy(fieldProps.Document, true);
newDoc[DocData].text = undefined;
- Doc.SetSelectOnLoad(newDoc);
+ DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
return undefined;
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 226d06f37..40b3f9ef2 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { lightOrDark, returnEmptyString } from '../../../ClientUtils';
+import { lightOrDark, returnEmptyString, returnTrue } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -17,8 +17,8 @@ import { undoBatch, undoable } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { EditableProps, EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionNoteTakingView.scss';
+import { DocumentView } from '../nodes/DocumentView';
interface CSVFieldColumnProps {
Document: Doc;
@@ -140,20 +140,15 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
this._hover = false;
};
- addTextNote = undoable(() => this.addNewTextDoc('-typed text-', false, true), 'add text note');
-
// addNewTextDoc is called when a user starts typing in a column to create a new node
- @action
- addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
- if (!value && !forceEmptyNote) return false;
+ addTextNote = undoable(() => {
const key = this._props.pivotField;
- const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
+ const newDoc = Docs.Create.TextDocument('', { _height: 18, _width: 200, _layout_fitWidth: true, _layout_autoHeight: true });
const colValue = this.getValue(this._props.heading);
newDoc[key] = colValue;
- Doc.SetSelectOnLoad(newDoc);
- FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
+ DocumentView.SetSelectOnLoad(newDoc);
return this._props.addDocument?.(newDoc) || false;
- };
+ }, 'add text note');
// deleteColumn is called when a user deletes a column using the 'trash' icon in the button area.
// If the user deletes the first column, the documents get moved to the second column. Otherwise,
@@ -177,7 +172,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
doc => {
const key = this._props.pivotField;
doc[key] = this.getValue(this._props.heading);
- Doc.SetSelectOnLoad(doc);
+ DocumentView.SetSelectOnLoad(doc);
return this._props.addDocument?.(doc);
},
this._props.addDocument,
@@ -249,7 +244,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
{!this._props.chromeHidden ? (
<div className="collectionNoteTakingView-DocumentButtons" style={{ display: this._props.isContentActive() ? 'flex' : 'none', marginBottom: 10 }}>
<div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
- <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents="+ Node" menuCallback={this.menuCallback} />
+ <EditableView GetValue={returnEmptyString} SetValue={returnTrue} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents="+ Node" menuCallback={this.menuCallback} />
</div>
<div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
<EditableView {...this._props.editableViewProps()} />
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 96125f2c2..72e6bb8f2 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -321,7 +321,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey];
}
newDoc[DocData].text = undefined;
- Doc.SetSelectOnLoad(newDoc);
+ DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
return false;
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index ed0cabd0a..6f32dd2e0 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -2,7 +2,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { DivHeight, DivWidth, returnEmptyString, setupMoveUpEvents } from '../../../ClientUtils';
+import { DivHeight, DivWidth, returnEmptyString, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { RichTextField } from '../../../fields/RichTextField';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -23,7 +23,7 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { DocumentView } from '../nodes/DocumentView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import './CollectionStackingView.scss';
@@ -149,19 +149,14 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
@action pointerEntered = () => { SnappingManager.IsDragging && (this._background = '#b4b4b4'); } // prettier-ignore
@action pointerLeave = () => { this._background = 'inherit'}; // prettier-ignore
- @undoBatch typedNote = () => this.addNewTextDoc('-typed text-', false, true);
-
- @action
- addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
- if (!value && !forceEmptyNote) return false;
+ @undoBatch typedNote = () => {
const key = this._props.pivotField;
- const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
+ const newDoc = Docs.Create.TextDocument('', { _height: 18, _width: 200, _layout_fitWidth: true, _layout_autoHeight: true });
key && (newDoc[key] = this.getValue(this._props.heading));
const maxHeading = this._props.docList.reduce((prevHeading, doc) => (NumCast(doc.heading) > prevHeading ? NumCast(doc.heading) : prevHeading), 0);
const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
newDoc.heading = heading;
- Doc.SetSelectOnLoad(newDoc);
- FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
+ DocumentView.SetSelectOnLoad(newDoc);
return this._props.addDocument?.(newDoc) || false;
};
@@ -240,7 +235,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
const height = this._ele ? DivHeight(this._ele) : 0;
DocUtils.addDocumentCreatorMenuItems(
doc => {
- Doc.SetSelectOnLoad(doc);
+ DocumentView.SetSelectOnLoad(doc);
return this._props.addDocument?.(doc);
},
this._props.addDocument,
@@ -394,14 +389,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
onKeyDown={e => e.stopPropagation()}
className="collectionStackingView-addDocumentButton"
style={{ width: 'calc(100% - 25px)', maxWidth: this._props.columnWidth / this._props.numGroupColumns - 25, marginBottom: 10 }}>
- <EditableView
- GetValue={returnEmptyString}
- SetValue={this.addNewTextDoc}
- textCallback={this.typedNote}
- placeholder={"Type ':' for commands"}
- contents={<FontAwesomeIcon icon="plus" />}
- menuCallback={this.menuCallback}
- />
+ <EditableView GetValue={returnEmptyString} SetValue={returnTrue} textCallback={this.typedNote} placeholder={"Type ':' for commands"} contents={<FontAwesomeIcon icon="plus" />} menuCallback={this.menuCallback} />
</div>
) : null}
</div>
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a60cd98ac..e93724dd4 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -174,7 +174,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
const prev = ind && DocListCast(targetDataDoc[this._props.fieldKey])[ind - 1];
this._props.removeDocument?.(docIn);
if (ind > 0 && prev) {
- Doc.SetSelectOnLoad(prev);
+ DocumentView.SetSelectOnLoad(prev);
DocumentView.getDocumentView(prev, this.DocumentView?.())?.select(false);
}
return true;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ab4d8b060..6208905fe 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1300,7 +1300,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const fieldKey = Doc.LayoutFieldKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
- Doc.SetSelectOnLoad(child);
+ DocumentView.SetSelectOnLoad(child);
TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
newParent.treeView_Open = true;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 43addfc29..89aa53c35 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -293,7 +293,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
isAnyChildContentActive = () => this._props.isAnyChildContentActive();
addLiveTextBox = (newDoc: Doc) => {
- Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ DocumentView.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newDoc);
};
selectDocuments = (docs: Doc[]) => {
@@ -1496,8 +1496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
- Doc.SetSelectOnLoad(newDoc);
- FormattedTextBox.DontSelectInitialText = true;
+ DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}, 'copied text note');
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 9cfb0416c..00d7ea451 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -173,7 +173,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
slide.x = x;
slide.y = y;
- Doc.SetSelectOnLoad(slide);
+ DocumentView.SetSelectOnLoad(slide);
TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
this._props.addDocument?.(slide);
e.stopPropagation();
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 80bf4bd12..e7f1de7f1 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -368,7 +368,7 @@ export class CollectionGridView extends CollectionSubView() {
undoable(
action(() => {
const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
- Doc.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ DocumentView.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
Doc.AddDocToList(this.Document, this._props.fieldKey, text);
this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY))));
}),
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 595abc7f8..a514ee04e 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1110,6 +1110,12 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public static DeselectAll: (except?: Doc) => void | undefined;
public static DeselectView: (dv: DocumentView | undefined) => void | undefined;
public static SelectView: (dv: DocumentView | undefined, extendSelection: boolean) => void | undefined;
+
+ public static SelectOnLoad: Doc | undefined;
+ public static SetSelectOnLoad(doc?: Doc) {
+ DocumentView.SelectOnLoad = doc;
+ doc && DocumentView.addViewRenderedCb(doc, dv => dv.select(false));
+ }
/**
* returns a list of all currently selected DocumentViews
*/
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 576b5bbe0..dcc6e27ed 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -30,12 +30,12 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- if (Doc.SelectOnLoad === this.Document && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) {
+ if (DocumentView.SelectOnLoad === this.Document && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) {
this._props.select(false);
- this._ref.current!.mathField.focus();
- this.dataDoc.text === 'x' && this._ref.current!.mathField.select();
- Doc.SetSelectOnLoad(undefined);
+ this._ref.current?.mathField.focus();
+ this.dataDoc.text === 'x' && this._ref.current?.mathField.select();
+ DocumentView.SetSelectOnLoad(undefined);
}
reaction(
() => this._props.isSelected(),
@@ -66,7 +66,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
color: StrCast(this.Document.color),
fontSize: this.fontSize,
});
- Doc.SetSelectOnLoad(nextEq);
+ DocumentView.SetSelectOnLoad(nextEq);
this._props.addDocument?.(nextEq);
e.stopPropagation();
}
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index dcf9e1fed..7fb83571f 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -17,6 +17,7 @@ import { FieldView, FieldViewProps } from './FieldView';
import './LabelBox.scss';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import { RichTextMenu } from './formattedText/RichTextMenu';
+import { DocumentView } from './DocumentView';
@observer
export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -230,8 +231,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (this._divRef) {
this._divRef.addEventListener('beforeinput', this.beforeInput);
- if (Doc.SelectOnLoad === this.Document) {
- Doc.SelectOnLoad = undefined;
+ if (DocumentView.SelectOnLoad === this.Document) {
+ DocumentView.SetSelectOnLoad(undefined);
this._divRef.focus();
}
this.fitTextToBox(this._divRef);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 541b41bf7..792cb6b46 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -353,7 +353,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const targetCreator = (annotationOn: Doc | undefined) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
- Doc.SetSelectOnLoad(target);
+ DocumentView.SetSelectOnLoad(target);
return target;
};
const docView = this.DocumentView?.();
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index eb0431b85..a27a8bda1 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -32,7 +32,7 @@
// addNoteClick = (e: React.PointerEvent) => {
// setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
// const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true });
-// Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+// DocumentView.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
// Doc.AddDocToList(this.props.place, 'data', newDoc);
// this._stack?.scrollToBottom();
// e.stopPropagation();
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index 95f89a573..0627d382e 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -237,7 +237,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
const targetCreator = (annotationOn: Doc | undefined) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
- Doc.SetSelectOnLoad(target);
+ DocumentView.SetSelectOnLoad(target);
return target;
};
const docView = this.DocumentView?.();
diff --git a/src/client/views/nodes/formattedText/DailyJournal.tsx b/src/client/views/nodes/formattedText/DailyJournal.tsx
index 9decbfaf0..7999357b0 100644
--- a/src/client/views/nodes/formattedText/DailyJournal.tsx
+++ b/src/client/views/nodes/formattedText/DailyJournal.tsx
@@ -51,22 +51,13 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
@action
setDailyText() {
console.log('setDailyText() called...');
- const initialText = `Journal Entry - ${this.journalDate}\n\nStart writing here...`;
+ const placeholderText = 'Start writing here...';
+ const initialText = `Journal Entry - ${this.journalDate}\n${placeholderText}`;
console.log('Checking if dataDoc has text field...');
console.log('Setting new text field with:', initialText);
- this.dataDoc[this.fieldKey] = new RichTextField(
- JSON.stringify({
- doc: {
- type: 'doc',
- content: [{ type: 'paragraph', content: [{ type: 'text', text: initialText }] }],
- },
- selection: { type: 'text', anchor: 1, head: 1 },
- storedMarks: [],
- }),
- initialText
- );
+ this.dataDoc[this.fieldKey] = RichTextField.textToRtf(initialText, undefined, placeholderText.length);
console.log('Current text field:', this.dataDoc[this.fieldKey]);
}
@@ -85,10 +76,11 @@ export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>()
}
render() {
- return <div style={{ background: 'beige', width: '100%', height: '100%' }}>
- <FormattedTextBox {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
-
- </div>;
+ return (
+ <div style={{ background: 'beige', width: '100%', height: '100%' }}>
+ <FormattedTextBox {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
+ </div>
+ );
}
}
@@ -99,6 +91,7 @@ Docs.Prototypes.TemplateMap.set(DocumentType.JOURNAL, {
_height: 35,
_xMargin: 10,
_yMargin: 10,
+ _layout_autoHeight: true,
_layout_nativeDimEditable: true,
_layout_reflowVertical: true,
_layout_reflowHorizontal: true,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c2a2caecf..83ee619d0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -99,7 +99,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
public static Init(nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor }) { FormattedTextBox._nodeViews = nodeViews; } // prettier-ignore
public static PasteOnLoad: ClipboardEvent | undefined;
- public static DontSelectInitialText = false; // whether initial text should be selected or not
public static SelectOnLoadChar = '';
public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch when typing a new text note into a collection
@@ -286,7 +285,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
e.stopPropagation();
const targetCreator = (annotationOn?: Doc) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn);
- Doc.SetSelectOnLoad(target);
+ DocumentView.SetSelectOnLoad(target);
return target;
};
@@ -1542,44 +1541,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._editorView.TextView = this;
}
- const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, Doc.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
+ const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, DocumentView.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
const selLoadChar = FormattedTextBox.SelectOnLoadChar;
if (selectOnLoad) {
- Doc.SetSelectOnLoad(undefined);
+ DocumentView.SetSelectOnLoad(undefined);
FormattedTextBox.SelectOnLoadChar = '';
}
if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
- this._props.select(false);
+ 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: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
+ const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
+ const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
+ let { tr } = this.EditorView.state;
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: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
- const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
const tr1 = this.EditorView.state.tr.setStoredMarks(storedMarks);
- const tr2 = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
- const tr = tr2.setStoredMarks(storedMarks);
-
- this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
- this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
- } else if (!FormattedTextBox.DontSelectInitialText) {
- const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- selectAll(this.EditorView.state, (tx: Transaction) => {
- this.EditorView?.dispatch(tx.addStoredMark(mark));
- });
- this.EditorView?.dispatch(this.EditorView.state.tr.setSelection(new TextSelection(this.EditorView.state.doc.resolve(1))));
- this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
- } else {
- 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: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
- const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
- const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
- const { tr } = this.EditorView.state;
- this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
- this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
+ tr = selLoadChar === 'Enter' ? tr1.insert(this.EditorView.state.doc.content.size - 1, schema.nodes.paragraph.create()) : tr1.insertText(selLoadChar, this.EditorView.state.doc.content.size - 1);
}
+ this.EditorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))).setStoredMarks(storedMarks));
+ this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
}
if (selectOnLoad) {
- FormattedTextBox.DontSelectInitialText = false;
this.EditorView!.focus();
}
if (this._props.isContentActive()) this.prepareForTyping();
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index bdd41b0bb..805bb4526 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -468,10 +468,6 @@ export class Doc extends RefField {
}
}
export namespace Doc {
- export let SelectOnLoad: Doc | undefined;
- export function SetSelectOnLoad(doc: Doc | undefined) {
- SelectOnLoad = doc;
- }
export let DocDragDataName: string = '';
export function SetDocDragDataName(name: string) {
DocDragDataName = name;
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index dc636031a..4b1403b88 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -48,26 +48,77 @@ export class RichTextField extends ObjectField {
''
);
}
+ static Initialize = (initial?: string) => {
+ const content: object[] = [];
+ const state = {
+ doc: {
+ type: 'doc',
+ content,
+ },
+ selection: {
+ type: 'text',
+ anchor: 0,
+ head: 0,
+ },
+ };
+ if (initial && initial.length) {
+ content.push({
+ type: 'paragraph',
+ content: {
+ type: 'text',
+ text: initial,
+ },
+ });
+ state.selection.anchor = state.selection.head = initial.length + 1;
+ }
+ return JSON.stringify(state);
+ };
+
+ private static ToProsemirrorState = (plainText: string, selectBack?: number, delimeter = '\n') => {
+ // Remap the text, creating blocks split on newlines
+ const elements = plainText.split(delimeter);
+
+ // Google Docs adds in an extra carriage return automatically, so this counteracts it
+ !elements[elements.length - 1].length && elements.pop();
+
+ // Preserve the current state, but re-write the content to be the blocks
+ const parsed: Record<string, unknown> = JSON.parse(RichTextField.Initialize());
+ (parsed.doc as Record<string, unknown>).content = elements.map(text => {
+ const paragraph: object = {
+ type: 'paragraph',
+ content: text.length ? [{ type: 'text', marks: [], text }] : undefined, // An empty paragraph gets treated as a line break
+ };
+ return paragraph;
+ });
- public static textToRtf(text: string, imgDocId?: string) {
+ // If the new content is shorter than the previous content and selection is unchanged, may throw an out of bounds exception, so we reset it
+ parsed.selection = { type: 'text', anchor: 2 + plainText.length - (selectBack ?? 0), head: 2 + plainText.length };
+
+ // Export the ProseMirror-compatible state object we've just built
+ return JSON.stringify(parsed);
+ };
+
+ public static textToRtf(text: string, imgDocId?: string, selectBack?: number) {
return new RichTextField(
- JSON.stringify({
- // this is a RichText json that has the question text placed above a related image
- doc: {
- type: 'doc',
- content: [
- {
- type: 'paragraph',
- attrs: { align: 'center', color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
- content: [
- ...(text ? [{ type: 'text', text }] : []), //
- ...(imgDocId ? [{ type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: imgDocId } }] : []),
- ],
- },
- ],
- },
- selection: { type: 'text', anchor: 2, head: 2 },
- }),
+ !imgDocId
+ ? this.ToProsemirrorState(text, selectBack)
+ : JSON.stringify({
+ // this is a RichText json that has the question text placed above a related image
+ doc: {
+ type: 'doc',
+ content: [
+ {
+ type: 'paragraph',
+ attrs: { align: 'center', color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null },
+ content: [
+ ...(text ? [{ type: 'text', text }] : []), //
+ ...(imgDocId ? [{ type: 'dashDoc', attrs: { width: '200px', height: '200px', title: 'dashDoc', float: 'unset', hidden: false, docId: imgDocId } }] : []),
+ ],
+ },
+ ],
+ },
+ selection: { type: 'text', anchor: 2 + text.length, head: 2 + text.length },
+ }),
text
);
}