aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/formattedText/FormattedTextBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx446
1 files changed, 264 insertions, 182 deletions
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 3e60441aa..115777c18 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,9 +1,9 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
import { isEqual } from 'lodash';
import { action, computed, IReactionDisposer, observable, ObservableSet, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { Configuration, OpenAIApi } from 'openai';
import { baseKeymap, selectAll } from 'prosemirror-commands';
import { history } from 'prosemirror-history';
import { inputRules } from 'prosemirror-inputrules';
@@ -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, UpdatingFromServer, WidthSym } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } 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';
@@ -34,6 +36,7 @@ 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';
@@ -72,8 +75,11 @@ const translateGoogleApi = require('translate-google-api');
export const GoogleRef = 'googleDocId';
type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
+export interface FormattedTextBoxProps {
+ allowScroll?: boolean;
+}
@observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps & FormattedTextBoxProps>() {
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
@@ -113,38 +119,38 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
return this._editorView;
}
public get SidebarKey() {
- return this.fieldKey + '-sidebar';
+ return this.fieldKey + '_sidebar';
}
@computed get allSidebarDocs() {
return DocListCast(this.dataDoc[this.SidebarKey]);
}
@computed get noSidebar() {
- return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._noSidebar;
+ return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._layout_noSidebar;
}
- @computed get sidebarWidthPercent() {
- return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+ @computed get layout_sidebarWidthPercent() {
+ 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 autoHeight() {
- return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight;
+ @computed get layout_autoHeight() {
+ return (this.props.forceAutoHeight || this.layoutDoc._layout_autoHeight) && !this.props.ignoreAutoHeight;
}
@computed get textHeight() {
- return NumCast(this.rootDoc[this.fieldKey + '-height']);
+ return NumCast(this.rootDoc[this.fieldKey + '_height']);
}
@computed get scrollHeight() {
- return NumCast(this.rootDoc[this.fieldKey + '-scrollHeight']);
+ return NumCast(this.rootDoc[this.fieldKey + '_scrollHeight']);
}
@computed get sidebarHeight() {
- return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '-height']);
+ return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + '_height']);
}
@computed get titleHeight() {
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
}
- @computed get autoHeightMargins() {
- return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins);
+ @computed get layout_autoHeightMargins() {
+ return this.titleHeight + NumCast(this.layoutDoc._layout_autoHeightMargins);
}
@computed get _recording() {
return this.dataDoc?.mediaState === 'recording';
@@ -154,7 +160,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
@computed get config() {
this._keymap = buildKeymap(schema, this.props);
- this._rules = new RichTextRules(this.props.Document, this);
+ this._rules = new RichTextRules(this.rootDoc, this);
return {
schema,
plugins: [
@@ -208,8 +214,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
public RemoveLinkFromDoc(linkDoc?: Doc) {
this.unhighlightSearchTerms();
const state = this._editorView?.state;
- const a1 = linkDoc?.anchor1 as Doc;
- const a2 = linkDoc?.anchor2 as Doc;
+ const a1 = linkDoc?.link_anchor_1 as Doc;
+ const a2 = linkDoc?.link_anchor_2 as Doc;
if (state && a1 && a2 && this._editorView) {
this.removeDocument(a1);
this.removeDocument(a2);
@@ -238,7 +244,7 @@ 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.TextConfigDocument({ annotationOn: this.rootDoc });
this.addDocument(anchor);
this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation);
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc);
@@ -250,18 +256,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
AnchorMenu.Instance.Status = 'marquee';
AnchorMenu.Instance.OnClick = (e: PointerEvent) => {
- !this.layoutDoc.showSidebar && this.toggleSidebar();
+ !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created
};
AnchorMenu.Instance.OnAudio = (e: PointerEvent) => {
- !this.layoutDoc.showSidebar && this.toggleSidebar();
+ !this.layoutDoc.layout_showSidebar && this.toggleSidebar();
const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true);
setTimeout(() => {
const target = this._sidebarRef.current?.anchorMenuClick(anchor);
if (target) {
anchor.followLinkAudio = true;
DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target));
- target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`);
+ target.title = ComputedField.MakeFunction(`self["text_audioAnnotations_text"].lastElement()`);
}
});
};
@@ -296,11 +302,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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 prevLayoutData = this.rootDoc !== this.layoutDoc ? Cast(this.layoutDoc[this.fieldKey], RichTextField, null) : 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"'));
@@ -312,36 +318,28 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
accumTags.push(node.attrs.fieldKey);
}
});
- const curTags = Object.keys(dataDoc).filter(key => key.startsWith('#'));
- const added = accumTags.filter(tag => !curTags.includes(tag));
- const removed = curTags.filter(tag => !accumTags.includes(tag));
- removed.forEach(r => (dataDoc[r] = undefined));
- added.forEach(a => (dataDoc[a] = a));
+ 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;
- textChange && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now())));
- if ((!curTemp && !curProto) || curText || json.includes('dash')) {
+ const textChange = newText !== prevData?.Text;
+ textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now())));
+ if ((!prevData && !protoData) || newText || (!newText && !protoData)) {
// 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 (removeSelection(newJson) !== removeSelection(prevLayoutData?.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; //(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: 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)));
- 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 });
+ 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: newText });
unchanged = false;
}
this._applyingChange = '';
@@ -370,7 +368,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
let linkAnchor;
let link;
LinkManager.Links(this.dataDoc).forEach((l, i) => {
- const anchor = (l.anchor1 as Doc).annotationOn ? (l.anchor1 as Doc) : (l.anchor2 as Doc).annotationOn ? (l.anchor2 as Doc) : undefined;
+ const anchor = (l.link_anchor_1 as Doc).annotationOn ? (l.link_anchor_1 as Doc) : (l.link_anchor_2 as Doc).annotationOn ? (l.link_anchor_2 as Doc) : undefined;
if (anchor && (anchor.annotationOn as Doc).mediaState === 'recording') {
linkTime = NumCast(anchor._timecodeToShow /* audioStart */);
linkAnchor = anchor;
@@ -406,7 +404,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
autoLink = () => {
const newAutoLinks = new Set<Doc>();
- const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
+ const oldAutoLinks = LinkManager.Links(this.props.Document).filter(link => link.link_relationship === LinkManager.AutoKeywords);
if (this._editorView?.state.doc.textContent) {
const isNodeSel = this._editorView.state.selection instanceof NodeSelection;
const f = this._editorView.state.selection.from;
@@ -418,7 +416,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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);
}
- oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
+ oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
};
updateTitle = () => {
@@ -427,7 +425,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!this.props.dontRegisterView && // (this.props.Document.isTemplateForField === "text" || !this.props.Document.isTemplateForField) && // only update the title if the data document's data field is changing
(title.startsWith('-') || title.startsWith('@')) &&
this._editorView &&
- !this.dataDoc['title-custom'] &&
+ !this.dataDoc.title_custom &&
(Doc.LayoutFieldKey(this.rootDoc) === this.fieldKey || this.fieldKey === 'text')
) {
let node = this._editorView.state.doc;
@@ -459,8 +457,8 @@ 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.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) ||
- DocUtils.MakeLink(this.props.Document, target, { linkRelationship: LinkManager.AutoKeywords })!);
+ (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 })!);
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 ?? []));
@@ -527,7 +525,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this.setupEditor(this.config, this.fieldKey);
this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc);
}
- // if (this.autoHeight) this.tryUpdateScrollHeight();
+ // if (this.layout_autoHeight) this.tryUpdateScrollHeight();
};
@undoBatch
@@ -547,17 +545,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
} else if (de.embedKey) {
const target = dragData.droppedDocuments[0];
const node = schema.nodes.dashDoc.create({
- width: target[WidthSym](),
- height: target[HeightSym](),
+ width: target[Width](),
+ height: target[Height](),
title: 'dashDoc',
docId: target[Id],
float: 'unset',
});
- if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) {
+ if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) {
dragData.removeDocument?.(dragData.draggedDocuments[0]);
}
- target._fitContentsToBox = true;
- target.context = this.rootDoc;
+ 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();
@@ -614,7 +612,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (Array.from(highlights).join('') === FormattedTextBox._globalHighlightsCache) return;
setTimeout(() => (FormattedTextBox._globalHighlightsCache = Array.from(highlights).join('')));
clearStyleSheetRules(FormattedTextBox._userStyleSheet);
- if (highlights.includes('Audio Tags')) {
+ if (!highlights.includes('Audio Tags')) {
addStyleSheetRule(FormattedTextBox._userStyleSheet, 'audiotag', { display: 'none' }, '');
}
if (highlights.includes('Text from Others')) {
@@ -649,24 +647,24 @@ 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;
@computed get SidebarShown() {
- return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+ return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
}
@action
toggleSidebar = (preview: boolean = false) => {
const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
if (preview) this._showSidebar = true;
- else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
+ else this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
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,
@@ -684,22 +682,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
.ScreenToLocalTransform()
.scale(this.props.NativeDimScaling?.() || 1)
.transformDirection(delta[0], delta[1]);
- const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.sidebarWidthPercent.replace('%', ''))) / 100;
- const width = this.layoutDoc[WidthSym]() + localDelta[0];
- this.layoutDoc._sidebarWidthPercent = Math.max(0, (sidebarWidth + localDelta[0]) / width) * 100 + '%';
+ const sidebarWidth = (NumCast(this.layoutDoc._width) * Number(this.layout_sidebarWidthPercent.replace('%', ''))) / 100;
+ 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._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
+ this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
e.preventDefault();
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
@@ -732,17 +731,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);
})
);
@@ -756,7 +760,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
description: 'plain',
event: undoBatch(() => {
Doc.setNativeView(this.rootDoc);
- this.layoutDoc.autoHeightMargins = undefined;
+ this.layoutDoc.layout_autoHeightMargins = undefined;
}),
icon: 'eye',
});
@@ -764,18 +768,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
description: 'metadata',
event: undoBatch(() => {
this.dataDoc.layout_meta = Cast(Doc.UserDoc().emptyHeader, Doc, null)?.layout;
- this.rootDoc.layoutKey = 'layout_meta';
- setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._autoHeightMargins = 50), 50);
+ this.rootDoc.layout_fieldKey = 'layout_meta';
+ setTimeout(() => (this.rootDoc._headerHeight = this.rootDoc._layout_autoHeightMargins = 50), 50);
}),
icon: 'eye',
});
- const noteTypesDoc = Cast(Doc.UserDoc()['template-notes'], Doc, null);
+ const noteTypesDoc = Cast(Doc.UserDoc().template_notes, Doc, null);
DocListCast(noteTypesDoc?.data).forEach(note => {
const icon: IconProp = StrCast(note.icon) as IconProp;
changeItems.push({
description: StrCast(note.title),
event: undoBatch(() => {
- this.layoutDoc.autoHeightMargins = undefined;
+ this.layoutDoc.layout_autoHeightMargins = undefined;
Doc.setNativeView(this.rootDoc);
DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.TreeDocument, StrCast(note.title), note);
}),
@@ -801,7 +805,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
);
const uicontrols: ContextMenuProps[] = [];
- uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: !this.Document._noSidebar ? 'eye-slash' : 'eye' });
+ uicontrols.push({
+ description: !this.Document._layout_noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle',
+ event: () => (this.layoutDoc._layout_noSidebar = !this.layoutDoc._layout_noSidebar),
+ icon: !this.Document._layout_noSidebar ? 'eye-slash' : 'eye',
+ });
+ uicontrols.push({
+ 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 &&
uicontrols.push({
@@ -831,10 +844,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
this.rootDoc.isTemplateDoc = false;
this.rootDoc.isTemplateForField = '';
- this.rootDoc.layoutKey = 'layout';
+ this.rootDoc.layout_fieldKey = 'layout';
MakeTemplate(this.rootDoc, true, title);
setTimeout(() => {
- this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height
+ this.rootDoc._layout_autoHeight = this.layoutDoc._layout_autoHeight; // layout_autoHeight, width and height
this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template
this.rootDoc._height = this.layoutDoc._height || 200; // we need to copy them over to the root. This should probably apply to all '_' fields
this.rootDoc._backgroundColor = Cast(this.layoutDoc._backgroundColor, 'string', null);
@@ -842,7 +855,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}, 10);
}
Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc);
- Doc.AddDocToList(Cast(Doc.UserDoc()['template-notes'], Doc, null), 'data', this.rootDoc);
+ Doc.AddDocToList(Cast(Doc.UserDoc().template_notes, Doc, null), 'data', this.rootDoc);
},
icon: 'eye',
});
@@ -852,8 +865,18 @@ 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._singleLine ? 'Make Single Line' : 'Make Multi Line', event: () => (this.layoutDoc._singleLine = !this.layoutDoc._singleLine), icon: !this.Document._singleLine ? 'grip-lines' : 'bars' });
- optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: this.Document._autoHeight ? 'lock' : 'unlock' });
+ 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`,
+ event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight),
+ icon: this.Document._layout_autoHeight ? 'lock' : 'unlock',
+ });
+ optionItems.push({ description: `show markdown options`, event: RTFMarkup.Instance.open, icon: 'text' });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
this._downX = this._downY = Number.NaN;
};
@@ -885,23 +908,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
try {
let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text);
if (image_url) {
- const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
- const source = Utils.prepend(accessPaths.agnostic.client);
+ const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] });
+ const source = Utils.prepend(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),
_height: 200,
_width: 200,
+ 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);
}
// Create link between prompt and image
- DocUtils.MakeLink(this.rootDoc, newDoc, { linkRelationship: 'Image Prompt' });
+ DocUtils.MakeLink(this.rootDoc, newDoc, { link_relationship: 'Image Prompt' });
}
} catch (err) {
console.log(err);
@@ -938,14 +963,14 @@ 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.TextConfigDocument({ title: 'dictation anchor' });
return this.addDocument(tanch) ? tanch : undefined;
};
const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement();
if (link) {
Doc.GetProto(link).isDictation = true;
- const audioanchor = Cast(link.anchor2, Doc, null);
- const textanchor = Cast(link.anchor1, Doc, null);
+ const audioanchor = Cast(link.link_anchor_2, Doc, null);
+ const textanchor = Cast(link.link_anchor_1, Doc, null);
if (audioanchor) {
audioanchor.backgroundColor = 'tan';
const audiotag = this._editorView.state.schema.nodes.audiotag.create({
@@ -976,7 +1001,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.TextConfigDocument({
+ //
+ 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) => {
@@ -1000,7 +1031,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));
}
@@ -1069,11 +1100,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
};
- // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc.
+ // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
// Since we also monitor all component height changes, this will update the document's height.
resetNativeHeight = (scrollHeight: number) => {
const nh = this.layoutDoc.isTemplateForField ? 0 : NumCast(this.layoutDoc._nativeHeight);
- this.rootDoc[this.fieldKey + '-height'] = scrollHeight;
+ this.rootDoc[this.fieldKey + '_height'] = scrollHeight;
if (nh) this.layoutDoc._nativeHeight = scrollHeight;
};
@@ -1084,9 +1115,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
!this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
this._cachedLinks = LinkManager.Links(this.Document);
this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation);
- this._disposers.autoHeight = reaction(
- () => this.autoHeight,
- autoHeight => autoHeight && this.tryUpdateScrollHeight()
+ this._disposers.layout_autoHeight = reaction(
+ () => this.layout_autoHeight,
+ layout_autoHeight => layout_autoHeight && this.tryUpdateScrollHeight()
);
this._disposers.highlights = reaction(
() => Array.from(FormattedTextBox._globalHighlights).slice(),
@@ -1098,16 +1129,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
width => this.tryUpdateScrollHeight()
);
this._disposers.scrollHeight = reaction(
- () => ({ scrollHeight: this.scrollHeight, autoHeight: this.autoHeight, width: NumCast(this.layoutDoc._width) }),
- ({ width, scrollHeight, autoHeight }) => width && autoHeight && this.resetNativeHeight(scrollHeight),
+ () => ({ scrollHeight: this.scrollHeight, layout_autoHeight: this.layout_autoHeight, width: NumCast(this.layoutDoc._width) }),
+ ({ width, scrollHeight, layout_autoHeight }) => width && layout_autoHeight && this.resetNativeHeight(scrollHeight),
{ fireImmediately: true }
);
this._disposers.componentHeights = reaction(
- // set the document height when one of the component heights changes and autoHeight is on
- () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, autoHeight: this.autoHeight, marginsHeight: this.autoHeightMargins }),
- ({ sidebarHeight, textHeight, autoHeight, marginsHeight }) => {
+ // set the document height when one of the component heights changes and layout_autoHeight is on
+ () => ({ 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 (autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
+ if (layout_autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) {
this.props.setHeight?.(newHeight);
}
},
@@ -1132,8 +1163,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
this._disposers.editorState = reaction(
() => {
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]) };
+ 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(DocCast(whichDoc[this.fieldKey])) };
},
incomingValue => {
if (this._editorView && this._applyingChange !== this.fieldKey) {
@@ -1178,7 +1209,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
() => this.props.isSelected(),
action(selected => {
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);
@@ -1210,7 +1241,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
var quickScroll: string | undefined = '';
this._disposers.scroll = reaction(
- () => NumCast(this.layoutDoc._scrollTop),
+ () => NumCast(this.layoutDoc._layout_scrollTop),
pos => {
if (!this._ignoreScroll && this._scrollRef.current && !this.props.dontSelectOnLoad) {
const viewTrans = quickScroll ?? StrCast(this.Document._viewTransition);
@@ -1287,7 +1318,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}, 0);
dataDoc.title = exportState.title;
- this.dataDoc['title-custom'] = true;
+ this.dataDoc.title_custom = true;
dataDoc.googleDocUnchanged = true;
} else {
delete dataDoc[GoogleRef];
@@ -1344,7 +1375,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
view.state.schema.marks.linkAnchor.create({
allAnchors: [{ href: `/doc/${this.rootDoc[Id]}`, title: this.rootDoc.title, anchorId: `${this.rootDoc[Id]}` }],
location: 'add:right',
- title: `from: ${DocCast(pdfAnchor.context).title}`,
+ title: `from: ${DocCast(pdfAnchor.embedContainer).title}`,
noPreview: true,
docref: false,
}),
@@ -1353,7 +1384,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
]),
]);
- const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { linkRelationship: 'PDF pasted' });
+ const link = DocUtils.MakeLink(pdfAnchor, this.rootDoc, { link_relationship: 'PDF pasted' });
if (link) {
view.dispatch(view.state.tr.replaceSelectionWith(dashField, false).scrollIntoView().setMeta('paste', true).setMeta('uiEvent', 'paste'));
}
@@ -1441,7 +1472,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (startupText) {
dispatch(state.tr.insertText(startupText));
}
- const textAlign = StrCast(this.dataDoc['text-align'], StrCast(Doc.UserDoc().textAlign, 'left'));
+ const textAlign = StrCast(this.dataDoc.text_align, StrCast(Doc.UserDoc().textAlign, 'left'));
if (textAlign !== 'left') {
selectAll(this._editorView.state, tr => {
this._editorView!.dispatch(tr.replaceSelectionWith(state.schema.nodes.paragraph.create({ align: textAlign })));
@@ -1452,11 +1483,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) ?? [];
@@ -1466,10 +1497,9 @@ 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) {
+ } else {
this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
}
}
@@ -1478,18 +1508,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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)] : []),
- ];
+ this._editorView.dispatch(
+ this._editorView.state.tr.setStoredMarks([
+ ...(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;
@@ -1514,7 +1546,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
} else (e.nativeEvent as any).handledByInnerReactInstance = true;
if (this.Document.forceActive) e.stopPropagation();
- this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different context (eg freeform & lightbox), then it will have conflicting heights. so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
+ this.tryUpdateScrollHeight(); // if a doc a fitWidth doc is being viewed in different embedContainer (eg freeform & lightbox), then it will have conflicting heights. so when the doc is clicked on, we want to make sure it has the appropriate height for the selected view.
if ((e.target as any).tagName === 'AUDIOTAG') {
e.preventDefault();
e.stopPropagation();
@@ -1565,7 +1597,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
else if (this.props.isContentActive(true)) {
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- !this.props.isSelected(true) && editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
+ // !this.props.isSelected(true) &&
+ editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
@@ -1601,7 +1634,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 => {
@@ -1676,13 +1709,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
@@ -1710,13 +1741,13 @@ 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(
() =>
translateGoogleApi(result1[0], { from: 'es', to: 'en' }).then((result: any) => {
- this.dataDoc[this.fieldKey + '-translation'] = result1 + '\r\n\r\n' + result[0];
+ this.dataDoc[this.fieldKey + '_translation'] = result1 + '\r\n\r\n' + result[0];
}),
1000
);
@@ -1726,10 +1757,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
this._lastText = curText;
}
- if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc['title-custom']) {
+ if (StrCast(this.rootDoc.title).startsWith('@') && !this.dataDoc.title_custom) {
UndoManager.RunInBatch(() => {
- this.dataDoc['title-custom'] = true;
- this.dataDoc.showTitle = 'title';
+ this.dataDoc.title_custom = true;
+ this.dataDoc.layout_showTitle = 'title';
const tr = this._editorView!.state.tr;
this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection());
}, 'titler');
@@ -1789,7 +1820,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!LinkDocPreview.LinkInfo && this._scrollRef.current) {
if (!this.props.dontSelectOnLoad) {
this._ignoreScroll = true;
- this.layoutDoc._scrollTop = this._scrollRef.current.scrollTop;
+ this.layoutDoc._layout_scrollTop = this._scrollRef.current.scrollTop;
this._ignoreScroll = false;
e.stopPropagation();
e.preventDefault();
@@ -1800,13 +1831,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
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) {
- const proseHeight = !this.ProseRef
- ? 0
- : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace('px', '')) + Number(getComputedStyle(child).marginTop.replace('px', '')) + Number(getComputedStyle(child).marginBottom.replace('px', '')), margins);
- const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
+ 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.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);
+ const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight);
if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) {
setScrollHeight();
} else {
@@ -1815,21 +1849,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}
}
};
- fitContentsToBox = () => BoolCast(this.props.Document._fitContentsToBox);
- sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
+ fitContentsToBox = () => BoolCast(this.props.Document._freeform_fitContentsToBox);
+ sidebarContentScaling = () => (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
sidebarAddDocument = (doc: Doc | Doc[], sidebarKey: string = this.SidebarKey) => {
- if (!this.layoutDoc._showSidebar) this.toggleSidebar();
+ if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
return this.addDocument(doc, sidebarKey);
};
sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey);
sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey);
- setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '-height'] = height);
- sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+ setSidebarHeight = (height: number) => (this.rootDoc[this.SidebarKey + '_height'] = height);
+ sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
sidebarScreenToLocal = () =>
this.props
.ScreenToLocalTransform()
.translate(-(this.props.PanelWidth() - this.sidebarWidth()) / (this.props.NativeDimScaling?.() || 1), 0)
- .scale(1 / NumCast(this.layoutDoc._viewScale, 1) / (this.props.NativeDimScaling?.() || 1));
+ .scale(1 / NumCast(this.layoutDoc._freeform_scale, 1) / (this.props.NativeDimScaling?.() || 1));
@computed get audioHandle() {
return !this._recording ? null : (
@@ -1863,7 +1897,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
color,
opacity: annotated ? 1 : undefined,
}}>
- <FontAwesomeIcon icon={'comment-alt'} />
+ <FontAwesomeIcon icon="comment-alt" />
</div>
);
}
@@ -1915,46 +1949,92 @@ 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.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
- {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
+ <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+ {renderComponent(StrCast(this.layoutDoc.sidebar_collectionType))}
</div>
);
}
+ cycleAlternateText = () => {
+ 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`];
+ return (
+ <Tooltip
+ title={
+ <div className="dash-tooltip">
+ toggle (%/) between
+ <span style={{ color: usePath === undefined ? 'black' : undefined }}>
+ <em> primary, </em>
+ </span>
+ <span style={{ color: usePath === 'alternate' ? 'black' : undefined }}>
+ <em>alternate, </em>
+ </span>
+ and show
+ <span style={{ color: usePath === 'alternate:hover' ? 'black' : undefined }}>
+ <em> alternate on hover</em>
+ </span>
+ </div>
+ }>
+ <div
+ className="formattedTextBox-alternateButton"
+ onPointerDown={e => setupMoveUpEvents(e.target, e, returnFalse, emptyFunction, e => this.cycleAlternateText())}
+ style={{
+ display: this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'flex' : 'none',
+ background: usePath === undefined ? 'white' : usePath === 'alternate' ? 'black' : 'gray',
+ color: usePath === undefined ? 'black' : 'white',
+ }}>
+ <FontAwesomeIcon icon="turn-up" size="sm" />
+ </div>
+ </Tooltip>
+ );
+ }
+ @computed get fieldKey() {
+ const usePath = StrCast(this.rootDoc[`${this.props.fieldKey}_usePath`]);
+ 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;
render() {
TraceMobx();
const active = this.props.isContentActive() || this.props.isSelected();
const selected = active;
- const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
- const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
+ const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1);
+ const rounded = StrCast(this.layoutDoc.layout_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 paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0);
- const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0;
- const selPaddingClass = selected && !this.layoutDoc._singleLine && paddingY >= 10 ? '-selected' : '';
+ 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-cont"
- 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()) {
- if (!NumCast(this.layoutDoc._scrollTop) && e.deltaY <= 0) e.preventDefault();
- e.stopPropagation();
- }
- },
- { passive: false }
- )
- }
+ className="formattedTextBox"
+ onPointerEnter={action(() => (this._isHovering = true))}
+ onPointerLeave={action(() => (this._isHovering = false))}
+ ref={r => {
+ this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+ this._oldWheel = r;
+ r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ }}
style={{
...(this.props.dontScale
? {}
@@ -1964,8 +2044,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
width: `${100 / scale}%`,
height: `${100 / scale}%`,
}),
+ display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined,
transition: 'inherit',
- // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined,
+ // 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),
@@ -1973,11 +2054,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
...styleFromLayoutString,
}}>
<div
- className={`formattedTextBox-cont`}
+ className="formattedTextBox-cont"
ref={this._ref}
style={{
- overflow: this.autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an autoHeight doc in its own tab, or in the lightbox
- height: this.props.height || (this.autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : 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',
}}
onContextMenu={this.specificContextMenu}
@@ -1993,9 +2074,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
className={`formattedTextBox-outer${selected ? '-selected' : ''}`}
ref={this._scrollRef}
style={{
- width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.sidebarWidthPercent})`,
+ width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined,
- overflow: this.layoutDoc._singleLine ? 'hidden' : this.layoutDoc._autoHeight ? 'visible' : undefined,
+ overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
onDrop={this.ondrop}>
@@ -2012,9 +2093,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
}}
/>
</div>
- {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
- {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+ {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_enableAltContentUI ? this.overlayAlternateIcon : null}
</div>
</div>
);