diff options
author | mehekj <mehek.jethani@gmail.com> | 2022-11-07 12:57:52 -0500 |
---|---|---|
committer | mehekj <mehek.jethani@gmail.com> | 2022-11-07 12:57:52 -0500 |
commit | a73521cdbccf9bed1326d24522e133fad4a0de26 (patch) | |
tree | e01371f6f845318831cf45f0dbc1d04d0d18f34c /src/client/views/nodes/formattedText/FormattedTextBox.tsx | |
parent | 213a92ba3aa39d144754029fde32b9d69b0f51cf (diff) | |
parent | 31a51e9dda07e48c88166bffbc8f1ad7166cd624 (diff) |
Merge branch 'master' into schema-mehek
Diffstat (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 82 |
1 files changed, 51 insertions, 31 deletions
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 6db199149..63435eea8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -18,7 +18,7 @@ import { PrefetchProxy } from '../../../../fields/Proxy'; import { RichTextField } from '../../../../fields/RichTextField'; import { RichTextUtils } from '../../../../fields/RichTextUtils'; import { ComputedField } from '../../../../fields/ScriptField'; -import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils'; import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; @@ -124,7 +124,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); } @computed get sidebarColor() { - return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4')); + return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.fieldKey + '-backgroundColor'], '#e4e4e4')); } @computed get autoHeight() { return (this.props.forceAutoHeight || this.layoutDoc._autoHeight) && !this.props.ignoreAutoHeight; @@ -281,12 +281,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const state = this._editorView.state.apply(tx); this._editorView.updateState(state); + const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc; const curText = state.doc.textBetween(0, state.doc.content.size, ' \n'); - const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined; // the actual text in the text box - const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype + 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 effectiveAcl = GetEffectiveAcl(this.dataDoc); + const effectiveAcl = GetEffectiveAcl(dataDoc); const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"')); @@ -297,29 +298,34 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps accumTags.push(node.attrs.fieldKey); } }); - const curTags = Object.keys(this.dataDoc).filter(key => key.startsWith('#')); + 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 => (this.dataDoc[r] = undefined)); - added.forEach(a => (this.dataDoc[a] = a)); + removed.forEach(r => (dataDoc[r] = undefined)); + added.forEach(a => (dataDoc[a] = a)); let unchanged = true; if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) { this._applyingChange = this.fieldKey; - curText !== Cast(this.dataDoc[this.fieldKey], RichTextField)?.Text && (this.dataDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()))); + curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()))); if ((!curTemp && !curProto) || curText || json.includes('dash')) { // 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)) { - this.dataDoc[this.props.fieldKey] = new RichTextField(json, curText); - this.dataDoc[this.props.fieldKey + '-noTemplate'] = true; //(curTemp?.Text || "") !== curText; // mark the data field as being split from the template if it has been edited + 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 ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); unchanged = false; } } else { // if we've deleted all the text in a note driven by a template, then restore the template data - this.dataDoc[this.props.fieldKey] = undefined; + dataDoc[this.fieldKey] = undefined; this._editorView.updateState(EditorState.fromJSON(this.config, JSON.parse((curProto || curTemp).Data))); - this.dataDoc[this.props.fieldKey + '-noTemplate'] = undefined; // mark the data field as not being split from any template it might have + 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 }); unchanged = false; } @@ -330,7 +336,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } } } else { - const jsonstring = Cast(this.dataDoc[this.fieldKey], RichTextField)?.Data!; + const jsonstring = Cast(dataDoc[this.fieldKey], RichTextField)?.Data!; if (jsonstring) { const json = JSON.parse(jsonstring); json.selection = state.toJSON().selection; @@ -387,13 +393,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const newAutoLinks = new Set<Doc>(); const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords); if (this._editorView?.state.doc.textContent) { + const isNodeSel = this._editorView.state.selection instanceof NodeSelection; const f = this._editorView.state.selection.from; const t = this._editorView.state.selection.to; var tr = this._editorView.state.tr as any; const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor; tr = tr.removeMark(0, tr.doc.content.size, autoAnch); DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks))); - tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); + 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); @@ -508,7 +515,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._dropDisposer?.(); this.ProseRef = ele; if (ele) { - this.setupEditor(this.config, this.props.fieldKey); + this.setupEditor(this.config, this.fieldKey); this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.layoutDoc); } // if (this.autoHeight) this.tryUpdateScrollHeight(); @@ -524,7 +531,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps // replace text contents whend dragging with Alt if (draggedDoc && draggedDoc.type === DocumentType.RTF && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) { if (draggedDoc.data instanceof RichTextField) { - Doc.GetProto(this.dataDoc)[this.props.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text); + Doc.GetProto(this.dataDoc)[this.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text); e.stopPropagation(); } // embed document when dragg marked as embed @@ -538,6 +545,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps docid: target[Id], float: 'unset', }); + if (!['alias', 'copy'].includes((dragData.dropAction ?? '') as any)) { + dragData.removeDocument?.(dragData.draggedDocuments[0]); + } const view = this._editorView!; view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node)); e.stopPropagation(); @@ -675,8 +685,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @undoBatch deleteAnnotation = (anchor: Doc) => { LinkManager.Instance.deleteLink(DocListCast(anchor.links)[0]); - // const docAnnotations = DocListCast(this.props.dataDoc[this.props.fieldKey]); - // this.props.dataDoc[this.props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion)); + // 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); }; @@ -787,6 +797,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : []; appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' }); // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); + !Doc.noviceMode && appearanceItems.push({ description: 'Make Default Layout', @@ -994,11 +1005,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps () => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight() ); + this._disposers.width = reaction( + () => this.props.PanelWidth(), + 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); - }, + ({ width, scrollHeight, autoHeight }) => width && autoHeight && this.resetNativeHeight(scrollHeight), { fireImmediately: true } ); this._disposers.componentHeights = reaction( @@ -1030,8 +1043,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ); this._disposers.editorState = reaction( () => { - const whichDoc = !this.dataDoc || !this.layoutDoc ? undefined : this.dataDoc?.[this.props.fieldKey + '-noTemplate'] || !this.layoutDoc[this.props.fieldKey] ? this.dataDoc : this.layoutDoc; - return !whichDoc ? undefined : { data: Cast(whichDoc[this.props.fieldKey], RichTextField, null), str: StrCast(whichDoc[this.props.fieldKey]) }; + 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]) }; }, incomingValue => { if (this._editorView && this._applyingChange !== this.fieldKey) { @@ -1119,6 +1133,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ); quickScroll = undefined; this.tryUpdateScrollHeight(); + setTimeout(this.tryUpdateScrollHeight, 250); } pushToGoogleDoc = async () => { @@ -1168,7 +1183,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps let pullSuccess = false; if (exportState !== undefined) { pullSuccess = true; - dataDoc[this.props.fieldKey] = new RichTextField(JSON.stringify(exportState.state.toJSON())); + dataDoc[this.fieldKey] = new RichTextField(JSON.stringify(exportState.state.toJSON())); setTimeout(() => { if (this._editorView) { const state = this._editorView.state; @@ -1296,8 +1311,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } _didScroll = false; setupEditor(config: any, fieldKey: string) { - const curText = Cast(this.dataDoc[this.props.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.props.fieldKey]); - const rtfField = Cast((!curText && this.layoutDoc[this.props.fieldKey]) || this.dataDoc[fieldKey], RichTextField); + const curText = Cast(this.dataDoc[this.fieldKey], RichTextField, null) || StrCast(this.dataDoc[this.fieldKey]); + const rtfField = Cast((!curText && this.layoutDoc[this.fieldKey]) || this.dataDoc[fieldKey], RichTextField); if (this.ProseRef) { const self = this; this._editorView?.destroy(); @@ -1348,7 +1363,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }); const { state, dispatch } = this._editorView; if (!rtfField) { - const startupText = Field.toString(this.dataDoc[fieldKey] as Field); + const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc; + const startupText = Field.toString(dataDoc[fieldKey] as Field); if (startupText) { dispatch(state.tr.insertText(startupText)); } @@ -1383,11 +1399,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } else if (this._editorView) { this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); } - FormattedTextBox.DontSelectInitialText = false; } selectOnLoad && this._editorView!.focus(); // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. if (this._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. + 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) })] : []), @@ -1399,6 +1418,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ...(Doc.UserDoc().fontWeight === 'bold' ? [schema.mark(schema.marks.strong)] : []), ]; } + FormattedTextBox.DontSelectInitialText = false; } componentWillUnmount() { @@ -1466,7 +1486,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps document.removeEventListener('pointermove', this.onSelectMove); }; onPointerUp = (e: React.PointerEvent): void => { - if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu(); + if (!this._editorView?.state.selection.empty && !(this._editorView?.state.selection instanceof NodeSelection) && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu(); if (!this._downEvent) return; this._downEvent = false; if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) { @@ -1475,7 +1495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.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); + FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview); } if (e.button === 0 && this.props.isSelected(true) && !e.altKey) { |