diff options
Diffstat (limited to 'src/client/views/nodes/formattedText/FormattedTextBox.tsx')
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 240 |
1 files changed, 141 insertions, 99 deletions
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 115777c18..2afbbb457 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -12,8 +12,8 @@ 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 { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, Height, UpdatingFromServer, Width } from '../../../../fields/DocSymbols'; +import { Doc, DocListCast, StrListCast, Field, Opt } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, Height, Width, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -34,7 +34,6 @@ import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { MakeTemplate } from '../../../util/DropConverter'; -import { IsFollowLinkScript } from '../../../util/LinkFollower'; import { LinkManager } from '../../../util/LinkManager'; import { RTFMarkup } from '../../../util/RTFMarkup'; import { SelectionManager } from '../../../util/SelectionManager'; @@ -97,6 +96,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps private _scrollRef: React.RefObject<HTMLDivElement> = React.createRef(); private _editorView: Opt<EditorView>; public _applyingChange: string = ''; + private _finishingLink = false; private _searchIndex = 0; private _lastTimedMark: Mark | undefined = undefined; private _cachedLinks: Doc[] = []; @@ -156,7 +156,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps return this.dataDoc?.mediaState === 'recording'; } set _recording(value) { - !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? 'recording' : undefined); + !this.dataDoc[`${this.fieldKey}_recordingSource`] && (this.dataDoc.mediaState = value ? 'recording' : undefined); } @computed get config() { this._keymap = buildKeymap(schema, this.props); @@ -244,9 +244,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc; - const anchor = Docs.Create.TextConfigDocument({ annotationOn: this.rootDoc }); + const anchor = Docs.Create.ConfigDocument({ title: StrCast(this.rootDoc.title), annotationOn: this.rootDoc }); this.addDocument(anchor); + this._finishingLink = true; this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation); + this._finishingLink = false; PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true } }, this.rootDoc); return anchor; }; @@ -297,7 +299,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps }; dispatchTransaction = (tx: Transaction) => { - if (this._editorView) { + if (this._editorView && (this._editorView as any).docView) { const state = this._editorView.state.apply(tx); this._editorView.updateState(state); @@ -305,13 +307,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps 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 templateData = this.rootDoc !== this.layoutDoc ? prevData : undefined; // the default text stored in a layout template const protoData = Cast(Cast(dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null); // the default text inherited from a prototype const effectiveAcl = GetEffectiveAcl(dataDoc); - const removeSelection = (json: string | undefined) => (json?.indexOf('"storedMarks"') === -1 ? json?.replace(/"selection":.*/, '') : json?.replace(/"selection":"\"storedMarks\""/, '"storedMarks"')); + const removeSelection = (json: string | undefined) => json?.replace(/"selection":.*/, ''); - if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) { + if ([AclEdit, AclAdmin, AclSelfEdit, AclAugment].includes(effectiveAcl)) { const accumTags = [] as string[]; state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => { if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith('#')) { @@ -325,12 +327,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._applyingChange = this.fieldKey; const textChange = newText !== prevData?.Text; textChange && (dataDoc[this.fieldKey + '_modificationDate'] = new DateField(new Date(Date.now()))); - if ((!prevData && !protoData) || newText || (!newText && !protoData)) { + if ((!prevData && !protoData) || newText || (!newText && !templateData)) { // if no template, or there's text that didn't come from the layout template, write it to the document. (if this is driven by a template, then this overwrites the template text which is intended) - if (removeSelection(newJson) !== removeSelection(prevLayoutData?.Data)) { + if ((this._finishingLink || this.props.isContentActive()) && removeSelection(newJson) !== removeSelection(prevData?.Data)) { const numstring = NumCast(dataDoc[this.fieldKey], null); 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 + dataDoc[this.fieldKey + '_noTemplate'] = true; // mark the data field as being split from the template if it has been edited textChange && ScriptCast(this.layoutDoc.onTextChanged, null)?.script.run({ this: this.layoutDoc, self: this.rootDoc, text: newText }); unchanged = false; } @@ -415,6 +417,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks))); tr = tr.setSelection(isNodeSel && false ? new NodeSelection(tr.doc.resolve(f)) : new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t))); this._editorView?.dispatch(tr); + // this.prepareForTyping(); } oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink); }; @@ -457,8 +460,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (LinkManager.Links(this.Document).find(link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target)) || - DocUtils.MakeLink(this.props.Document, target, { link_relationship: LinkManager.AutoKeywords })!); + (LinkManager.Links(this.rootDoc).find( + link => + Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.rootDoc) && // + Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target) + ) || + DocUtils.MakeLink(this.rootDoc, target, { link_relationship: LinkManager.AutoKeywords })!); newAutoLinks.add(alink); const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); @@ -531,36 +538,50 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true); + if (de.complete.annoDragData) { + de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true); + e.stopPropagation(); + return true; + } const dragData = de.complete.docDragData; if (dragData) { - const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0]; - // replace text contents whend dragging with Alt - if (draggedDoc && draggedDoc.type === DocumentType.RTF && !Doc.AreProtosEqual(draggedDoc, this.props.Document) && de.altKey) { - if (draggedDoc.data instanceof RichTextField) { - Doc.GetProto(this.dataDoc)[this.fieldKey] = new RichTextField(draggedDoc.data.Data, draggedDoc.data.Text); - e.stopPropagation(); - } - // embed document when dragg marked as embed - } else if (de.embedKey) { - const target = dragData.droppedDocuments[0]; - const node = schema.nodes.dashDoc.create({ - width: target[Width](), - height: target[Height](), - title: 'dashDoc', - docId: target[Id], - float: 'unset', - }); - if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) { - dragData.removeDocument?.(dragData.draggedDocuments[0]); + const dataDoc = Doc.IsDelegateField(DocCast(this.layoutDoc.proto), this.fieldKey) ? DocCast(this.layoutDoc.proto) : this.dataDoc; + const effectiveAcl = GetEffectiveAcl(dataDoc); + let added = [AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl); + const draggedDoc = dragData.draggedDocuments.lastElement(); + if (added) { + // replace text contents when dragging with Alt + if (de.altKey) { + const fieldKey = Doc.LayoutFieldKey(draggedDoc); + if (draggedDoc[fieldKey] instanceof RichTextField && !Doc.AreProtosEqual(draggedDoc, this.props.Document)) { + Doc.GetProto(this.dataDoc)[this.fieldKey] = Field.Copy(draggedDoc[fieldKey]); + } + + // embed document when drag marked as embed + } else if (de.embedKey) { + const node = schema.nodes.dashDoc.create({ + width: draggedDoc[Width](), + height: draggedDoc[Height](), + title: 'dashDoc', + docId: draggedDoc[Id], + float: 'unset', + }); + if (!['embed', 'copy'].includes((dragData.dropAction ?? '') as any)) { + added = dragData.removeDocument?.(draggedDoc) ? true : false; + } + if (added) { + draggedDoc._freeform_fitContentsToBox = true; + Doc.SetContainer(draggedDoc, this.rootDoc); + const view = this._editorView!; + view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node)); + } } - target._freeform_fitContentsToBox = true; - target.embedContainer = this.rootDoc; - const view = this._editorView!; - view.dispatch(view.state.tr.insert(view.posAtCoords({ left: de.x, top: de.y })!.pos, node)); - e.stopPropagation(); } // otherwise, fall through to outer collection to handle drop + !added && e.preventDefault(); + e.stopPropagation(); + return added; } + return false; }; getNodeEndpoints(context: Node, node: Node): { from: number; to: number } | null { @@ -709,7 +730,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps @undoBatch showTargetTrail = (anchor: Doc) => { - const trail = DocCast(anchor.presTrail); + const trail = DocCast(anchor.presentationTrail); if (trail) { Doc.ActivePresentation = trail; this.props.addDocTab(trail, OpenWhere.replaceRight); @@ -826,7 +847,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const appearance = cm.findByDescription('Appearance...'); const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : []; - appearanceItems.push({ description: 'Change Perspective...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' }); + appearanceItems.push({ description: 'Change Style...', noexpand: true, subitems: changeItems, icon: 'external-link-alt' }); // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" }); !Doc.noviceMode && @@ -865,11 +886,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps const optionItems = options && 'subitems' in options ? options.subitems : []; optionItems.push({ description: `Generate Dall-E Image`, event: () => this.generateImage(), icon: 'star' }); optionItems.push({ description: `Ask GPT-3`, event: () => this.askGPT(), icon: 'lightbulb' }); - optionItems.push({ - description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns', - event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR), - icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars', - }); + this.props.renderDepth && + optionItems.push({ + description: !this.Document._createDocOnCR ? 'Create New Doc on Carriage Return' : 'Allow Carriage Returns', + event: () => (this.layoutDoc._createDocOnCR = !this.layoutDoc._createDocOnCR), + icon: !this.Document._createDocOnCR ? 'grip-lines' : 'bars', + }); !Doc.noviceMode && optionItems.push({ description: `${this.Document._layout_autoHeight ? 'Lock' : 'Auto'} Height`, @@ -909,7 +931,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps let image_url = await gptImageCall((this.dataDoc.text as RichTextField)?.Text); if (image_url) { const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_url] }); - const source = Utils.prepend(result.accessPaths.agnostic.client); + const source = result.accessPaths.agnostic.client; const newDoc = Docs.Create.ImageDocument(source, { x: NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) + 10, y: NumCast(this.rootDoc.y), @@ -963,7 +985,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (this._editorView && this._recordingStart) { if (this._break) { const textanchorFunc = () => { - const tanch = Docs.Create.TextConfigDocument({ title: 'dictation anchor' }); + const tanch = Docs.Create.ConfigDocument({ title: 'dictation anchor' }); return this.addDocument(tanch) ? tanch : undefined; }; const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement(); @@ -1003,7 +1025,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (sel.from !== sel.to) { const anchor = anchorDoc ?? - Docs.Create.TextConfigDocument({ + Docs.Create.ConfigDocument({ // title: 'text(' + this._editorView?.state.doc.textBetween(sel.from, sel.to) + ')', annotationOn: this.dataDoc, @@ -1023,6 +1045,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._editorView!.dispatch(tr.removeMark(sel.from, sel.to, splitter)); this.dataDoc[UpdatingFromServer] = this.dataDoc[ForceServerWrite] = false; anchor.text = selectedText; + anchor.title = selectedText.substring(0, 30); return anchor; } return anchorDoc ?? this.rootDoc; @@ -1138,11 +1161,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps () => ({ sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layout_autoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }), ({ sidebarHeight, textHeight, layout_autoHeight, marginsHeight }) => { const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight)); - if (layout_autoHeight && newHeight && newHeight !== this.rootDoc.height && !this.props.dontRegisterView) { + if ( + (!Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') || this.props.isSelected()) && // + layout_autoHeight && + newHeight && + newHeight !== this.rootDoc.height && + !this.props.dontRegisterView + ) { this.props.setHeight?.(newHeight); } }, - { fireImmediately: true } + { fireImmediately: !Array.from(FormattedTextBox._globalHighlights).includes('Bold Text') } ); this._disposers.links = reaction( () => LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks @@ -1208,6 +1237,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._disposers.selected = reaction( () => this.props.isSelected(), action(selected => { + //selected && setTimeout(() => this.prepareForTyping()); if (FormattedTextBox._globalHighlights.has('Bold Text')) { 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 } @@ -1216,7 +1246,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } if (this._editorView && selected) { RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); - this.autoLink(); + setTimeout(this.autoLink, 20); } // Accessing editor and text doc for gpt assisted text edits if (this._editorView && selected) { @@ -1232,9 +1262,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps () => this._recording, () => { this.stopDictation(true); - if (this._recording) { - this.recordDictation(); - } + this._recording && this.recordDictation(); } ); if (this._recording) setTimeout(this.recordDictation); @@ -1499,29 +1527,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size)))); } else if (curText && !FormattedTextBox.DontSelectInitialText) { selectAll(this._editorView.state, this._editorView?.dispatch); - } else { - this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); } } selectOnLoad && this._editorView!.focus(); - // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. + if (this.props.isContentActive()) this.prepareForTyping(); if (this._editorView) { const tr = this._editorView.state.tr; const { from, to } = tr.selection; // for some reason, the selection is sometimes lost in the sidebar view when prosemirror syncs the seledtion with the dom, so reset the 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.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; @@ -1531,9 +1546,30 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps FormattedTextBox.DontSelectInitialText = false; } + // add user mark for any first character that was typed since the user mark that gets set in KeyPress won't have been called yet. + prepareForTyping = () => { + if (!this._editorView) return; + const docDefaultMarks = [ + ...(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)] : []), + ...[schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })], + ]; + this._editorView?.dispatch(this._editorView?.state.tr.setStoredMarks(docDefaultMarks)); + }; + + @action componentWillUnmount() { + if (this._recording) { + this._recording = !this._recording; + } Object.values(this._disposers).forEach(disposer => disposer?.()); this.endUndoTypingBatch(); + FormattedTextBox.LiveTextUndo?.end(); + FormattedTextBox.LiveTextUndo = undefined; this.unhighlightSearchTerms(); this._editorView?.destroy(); RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined); @@ -1568,8 +1604,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } if (this._recording && !e.ctrlKey && e.button === 0) { this.breakupDictation(); - e.preventDefault(); - e.stopPropagation(); } this._downX = e.clientX; this._downY = e.clientY; @@ -1807,8 +1841,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break; case ' ': - [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) && - this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({})).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); + if (e.code !== 'Space') { + [AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.rootDoc)) && + this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }))); + } + break; } this.startUndoTypingBatch(); }; @@ -1830,7 +1867,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps tryUpdateScrollHeight = () => { const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; - if (children) { + if (children && !SnappingManager.GetIsDragging()) { const toNum = (val: string) => Number(val.replace('px', '').replace('auto', '0')); const toHgt = (node: Element) => { const { height, marginTop, marginBottom } = getComputedStyle(node); @@ -1841,6 +1878,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps if (this.props.setHeight && scrollHeight && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => (this.rootDoc[this.fieldKey + '_scrollHeight'] = scrollHeight); + if (this.rootDoc === this.layoutDoc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); } else { @@ -1962,12 +2000,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } cycleAlternateText = () => { if (this.layoutDoc._layout_enableAltContentUI) { - const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`]; + const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`]; this.rootDoc[`_${this.props.fieldKey}_usePath`] = usePath === undefined ? 'alternate' : usePath === 'alternate' ? 'alternate:hover' : undefined; } }; @computed get overlayAlternateIcon() { - const usePath = this.rootDoc[`${this.props.fieldKey}_usePath`]; + const usePath = this.rootDoc[`_${this.props.fieldKey}_usePath`]; return ( <Tooltip title={ @@ -2011,19 +2049,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } }; _oldWheel: any; + @computed get fontColor() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); + } + @computed get fontSize() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize); + } + @computed get fontFamily() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily); + } + @computed get fontWeight() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight); + } render() { TraceMobx(); - const active = this.props.isContentActive() || this.props.isSelected(); - const selected = active; const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); const rounded = StrCast(this.layoutDoc.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; + setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0); const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const selPad = (selected && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0; - const selPaddingClass = selected && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : ''; const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' > return styleFromLayoutString?.height === '0px' ? null : ( <div @@ -2040,26 +2084,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ? {} : { transform: `scale(${scale})`, - transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%`, }), - display: !SnappingManager.GetIsDragging() && this.props.thumbShown?.() ? 'none' : undefined, + // display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined, transition: 'inherit', // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined, - color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), - fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), - fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily), - fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight), + color: this.fontColor, + fontSize: this.fontSize, + fontFamily: this.fontFamily, + fontWeight: this.fontWeight, ...styleFromLayoutString, }}> <div className="formattedTextBox-cont" ref={this._ref} style={{ + cursor: this.props.isContentActive() ? 'text' : undefined, overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined), - pointerEvents: interactive ? undefined : 'none', + pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none', }} onContextMenu={this.specificContextMenu} onKeyDown={this.onKeyDown} @@ -2071,25 +2115,23 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps onPointerDown={this.onPointerDown} onDoubleClick={this.onDoubleClick}> <div - className={`formattedTextBox-outer${selected ? '-selected' : ''}`} + className={`formattedTextBox-outer`} ref={this._scrollRef} style={{ width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`, - pointerEvents: !active && !SnappingManager.GetIsDragging() ? 'none' : undefined, overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined, }} onScroll={this.onScroll} onDrop={this.ondrop}> <div - className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`} + className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ padding: StrCast(this.layoutDoc._textBoxPadding), - paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`), - paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`), - paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`), - paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`), - pointerEvents: !active && !SnappingManager.GetIsDragging() ? (IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined) : undefined, + paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`), + paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`), + paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`), + paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`), }} /> </div> |