diff options
author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-03-06 16:33:39 -0500 |
---|---|---|
committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-03-06 16:33:39 -0500 |
commit | 3a6f179aedf8e148fe9828426b43aa31ca87bcb1 (patch) | |
tree | 9f49ba8d54bda361826c17cfe7f3cb0a37854ace /src/client/views/nodes/formattedText/FormattedTextBox.tsx | |
parent | c6425a0469727305f76d00e3f8c545e04aad61cc (diff) | |
parent | 4c2584baf8bae0cde714c832b0768d3c08864422 (diff) |
Merge branch 'master' into pres-trail-sophie
Diffstat (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 66 |
1 files changed, 37 insertions, 29 deletions
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index c6801bb79..60be9775c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -45,7 +45,7 @@ import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; -import { DocFocusOptions, DocumentViewInternal, OpenWhere } from '../DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { LinkDocPreview } from '../LinkDocPreview'; import { DashDocCommentView } from './DashDocCommentView'; @@ -66,6 +66,7 @@ import React = require('react'); import { Configuration, OpenAIApi } from 'openai'; import { Networking } from '../../../Network'; import { gptAPICall, GPTCallType } from '../../../apis/gpt/Summarization'; +import { PinProps, PresBox } from '../trails'; const translateGoogleApi = require('translate-google-api'); export const GoogleRef = 'googleDocId'; @@ -84,7 +85,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps static _highlightStyleSheet: any = addStyleSheet(); static _bulletStyleSheet: any = addStyleSheet(); static _userStyleSheet: any = addStyleSheet(); - static _canAnnotate = true; static _hadSelection: boolean = false; private _sidebarRef = React.createRef<SidebarAnnos>(); private _ref: React.RefObject<HTMLDivElement> = React.createRef(); @@ -105,7 +105,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps private _rules: RichTextRules | undefined; private _forceUncollapse = true; // if the cursor doesn't move between clicks, then the selection will disappear for some reason. This flags the 2nd click as happening on a selection which allows bullet points to toggle private _forceDownNode: Node | undefined; - private _downEvent: any; private _downX = 0; private _downY = 0; private _break = true; @@ -121,7 +120,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } @computed get noSidebar() { - return this.props.docViewPath?.()[this.props.docViewPath().length - 2]?.rootDoc.type === DocumentType.RTF || this.props.noSidebar || this.Document._noSidebar; + return this.props.docViewPath().lastElement()?.props.hideDecorationTitle || this.props.noSidebar || this.Document._noSidebar; } @computed get sidebarWidthPercent() { return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%'); @@ -238,7 +237,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } } - getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation); + 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 }); + 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); + return anchor; + }; @action setupAnchorMenu = () => { @@ -261,7 +267,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }); }; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { - this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch); + this._editorView?.state && RichTextMenu.Instance.setHighlight(color); return undefined; }); AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true); @@ -316,7 +322,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps let unchanged = true; if (this._applyingChange !== this.fieldKey && removeSelection(json) !== removeSelection(curProto?.Data)) { this._applyingChange = this.fieldKey; - curText !== Cast(dataDoc[this.fieldKey], RichTextField)?.Text && (dataDoc[this.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()))); + 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')) { // 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)) { @@ -327,7 +334,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps 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 }); + textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: curText }); unchanged = false; } } else { @@ -790,13 +797,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps runInAction(() => (this.layoutDoc._highlights = FormattedTextBox._globalHighlights.join(''))); this.updateHighlights(); }, - icon: 'expand-arrows-alt', + icon: FormattedTextBox._globalHighlights.indexOf(option) === -1 ? 'highlighter' : 'remove-format', }) ); const uicontrols: ContextMenuProps[] = []; - !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ''} Show Menu on Selections`, event: () => (FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate), icon: 'expand-arrows-alt' }); - uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: 'expand-arrows-alt' }); + 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: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' }); !Doc.noviceMode && uicontrols.push({ @@ -847,8 +853,8 @@ 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: 'expand-arrows-alt' }); - optionItems.push({ description: `${this.Document._autoHeight ? 'Lock' : 'Auto'} Height`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' }); + 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' }); !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); this._downX = this._downY = Number.NaN; }; @@ -1013,12 +1019,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps return anchorDoc ?? this.rootDoc; } - scrollFocus = (textAnchor: Doc, options: DocFocusOptions) => { - let didToggle = false; - if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) { - this.toggleSidebar(options.instant); - didToggle = true; + getView = async (doc: Doc) => { + if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + !this.SidebarShown && this.toggleSidebar(false); + setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); } + return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + }; + focus = (textAnchor: Doc, options: DocFocusOptions) => { + const focusSpeed = options.zoomTime ?? 500; const textAnchorId = textAnchor[Id]; const findAnchorFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; @@ -1057,7 +1066,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const content = (ret.frag as any)?.content; if ((ret.frag.size > 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) { - !options.instant && (this._focusSpeed = 500); + !options.instant && (this._focusSpeed = focusSpeed); let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected @@ -1069,11 +1078,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps setTimeout(() => clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); } } - - return this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; // if we actually scrolled, then return some focusSpeed }; - getScrollHeight = () => this.scrollHeight; // if the scroll height has changed and we're in 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) => { @@ -1535,7 +1541,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } this._downX = e.clientX; this._downY = e.clientY; - this._downEvent = true; FormattedTextBoxComment.textBox = this; if (e.button === 0 && (this.props.rootSelected(true) || this.props.isSelected(true)) && !e.altKey && !e.ctrlKey && !e.metaKey) { if (e.clientX < this.ProseRef!.getBoundingClientRect().right) { @@ -1557,16 +1562,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps document.removeEventListener('pointermove', this.onSelectMove); }; onPointerUp = (e: React.PointerEvent): void => { - 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._editorView?.state.selection.empty && this.props.isContentActive(true) && !(e.nativeEvent as any).dash) { - const editor = this._editorView!; + const editor = this._editorView!; + const state = editor?.state; + if (!state || !editor) return; + 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(editor.state.tr.setSelection(new TextSelection(editor.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'); + if (pcords && pcords.inside > 0 && state.doc.nodeAt(pcords.inside)?.type === state.schema.nodes.dashDoc) { + return; + } } if (e.button === 0 && this.props.isSelected(true) && !e.altKey) { |