diff options
author | bobzel <zzzman@gmail.com> | 2022-11-04 12:43:59 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2022-11-04 12:43:59 -0400 |
commit | fe98c7d46df1852a74cd84dbe9ad010bfb3d5550 (patch) | |
tree | d1c96f67d824e98907fbe36697e0523d1af9b7bb | |
parent | 770f546a38d7ec827bff55fd0dd64b873a112180 (diff) |
more fixes to pdf and text to allow dashfieldview nodes to be link anchors and make sidebar annotations work better.
-rw-r--r-- | src/client/views/SidebarAnnos.tsx | 34 | ||||
-rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/LinkAnchorBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.scss | 9 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/DashFieldView.tsx | 65 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 7 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx | 14 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/RichTextRules.ts | 2 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/marks_rts.ts | 4 |
10 files changed, 71 insertions, 68 deletions
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 78480da03..c285b2e34 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -67,7 +67,6 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { _height: 50, _fitWidth: true, _autoHeight: true, - _isLinkButton: true, _fontSize: StrCast(Doc.UserDoc().fontSize), _fontFamily: StrCast(Doc.UserDoc().fontFamily), }); @@ -84,20 +83,33 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { return { type: 'dashField', attrs: { fieldKey: key, docid: '', hideKey: false, editable: true }, - marks: [ - { type: 'pFontSize', attrs: { fontSize: '12px' } }, - { type: 'pFontFamily', attrs: { family: 'Arial' } }, - { type: 'pFontColor', attrs: { color: 'black' } }, - { type: 'strong' }, - { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } }, - ], + marks: [{ type: 'pFontSize', attrs: { fontSize: '12px' } }, { type: 'strong' }, { type: 'user_mark', attrs: { userid: Doc.CurrentUserEmail, modified: 0 } }], }; }); - const textLines = [ + + const textLines: any = [ { type: 'paragraph', attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, - content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false } }], + content: [ + { + type: 'dashField', + marks: [ + { + type: 'linkAnchor', + attrs: { + allAnchors: [{ href: `/doc/${target[Id]}`, title: 'Anchored Selection', noPreview: true, anchorId: `${target[Id]}` }], + location: 'add:right', + title: 'Anchored Selection', + docref: false, + }, + }, + { type: 'pFontSize', attrs: { fontSize: '8px' } }, + { type: 'em' }, + ], + attrs: { fieldKey: 'text', docid: anchor[Id], hideKey: true, editable: false }, + }, + ], }, { type: 'paragraph', attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null } }, ]; @@ -118,7 +130,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> { '' ); this.addDocument(target); - this._stackRef.current?.focusDocument(target, {}); + setTimeout(() => this._stackRef.current?.focusDocument(target, {})); return target; }; makeDocUnfiltered = (doc: Doc) => { diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 3f9db2612..387e0e3d5 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -155,6 +155,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { linkDoc: this.props.linkDoc, showHeader: false, location: [this._drag.current?.getBoundingClientRect().right ?? 100, this._drag.current?.getBoundingClientRect().top ?? e.clientY], + noPreview: false, }) } onPointerDown={this.onLinkButtonDown}> diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 5102eae51..d6cf79f87 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -144,6 +144,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() { linkDoc: this.rootDoc, showHeader: true, location: [e.clientX, e.clientY + 20], + noPreview: false, }) } onPointerDown={this.onPointerDown} diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 27e79a83b..0e66214d1 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -26,6 +26,7 @@ interface LinkDocPreviewProps { location: number[]; hrefs?: string[]; showHeader?: boolean; + noPreview?: boolean; } @observer export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { @@ -111,6 +112,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } this._toolTipText = ''; + if (LinkDocPreview.LinkInfo?.noPreview) this.followLink(); } }) ); diff --git a/src/client/views/nodes/formattedText/DashFieldView.scss b/src/client/views/nodes/formattedText/DashFieldView.scss index c36e6804b..f17579853 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.scss +++ b/src/client/views/nodes/formattedText/DashFieldView.scss @@ -1,3 +1,5 @@ +@import '../../global/globalCssVariables'; + .dashFieldView { position: relative; display: inline-flex; @@ -22,7 +24,7 @@ position: relative; display: inline-block; font-weight: normal; - background: rgba(0,0,0,0.1); + background: rgba(0, 0, 0, 0.1); } .dashFieldView-fieldSpan { min-width: 8px; @@ -31,11 +33,12 @@ padding-left: 2px; display: inline-block; background-color: rgba(155, 155, 155, 0.24); - font-weight: bold; span { min-width: 100%; display: inline-block; } } } -
\ No newline at end of file +.ProseMirror-selectedNode { + outline: solid 1px $light-blue !important; +} diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index f088b39d1..fbcf25215 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -16,21 +16,20 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { Tooltip } from '@material-ui/core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { NodeSelection } from 'prosemirror-state'; +import { FormattedTextBoxComment } from './FormattedTextBoxComment'; export class DashFieldView { dom: HTMLDivElement; // container for label and value root: any; constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { - const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey); - this.dom = document.createElement('div'); this.dom.style.width = node.attrs.width; this.dom.style.height = node.attrs.height; - this.dom.style.fontWeight = 'bold'; this.dom.style.position = 'relative'; this.dom.style.display = 'inline-block'; - this.dom.textContent = node.attrs.fieldKey.startsWith('#') ? node.attrs.fieldKey : node.attrs.fieldKey + ' ' + strVal; + //this.dom.contentEditable = 'true'; this.dom.onkeypress = function (e: any) { e.stopPropagation(); }; @@ -45,12 +44,20 @@ export class DashFieldView { }; this.root = ReactDOM.createRoot(this.dom); - this.root.render(<DashFieldViewInternal fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} />); + this.root.render( + <DashFieldViewInternal node={node} getPos={getPos} fieldKey={node.attrs.fieldKey} docid={node.attrs.docid} width={node.attrs.width} height={node.attrs.height} hideKey={node.attrs.hideKey} editable={node.attrs.editable} tbox={tbox} /> + ); } destroy() { //this.root.unmount(); } - selectNode() {} + deselectNode() { + this.dom.classList.remove('ProseMirror-selectednode'); + } + selectNode() { + this.dom.classList.add('ProseMirror-selectednode'); + console.log('SELECT'); + } } interface IDashFieldViewInternal { @@ -61,6 +68,8 @@ interface IDashFieldViewInternal { width: number; height: number; editable: boolean; + node: any; + getPos: any; } @observer @@ -127,7 +136,13 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna r?.addEventListener('blur', e => r && this.updateText(r.textContent!, false)); r?.addEventListener( 'pointerdown', - action(e => e.stopPropagation()) + action(e => { + // 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; + this.props.tbox.EditorView!.dispatch(this.props.tbox.EditorView!.state.tr.setSelection(new NodeSelection(this.props.tbox.EditorView!.state.doc.resolve(this.props.getPos())))); + // FormattedTextBoxComment.update(this.props.tbox, this.props.tbox.EditorView!, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc); + // e.stopPropagation(); + }) ); }}> {strVal} @@ -162,42 +177,6 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna updateText = (nodeText: string, forceMatch: boolean) => { if (nodeText) { const newText = nodeText.startsWith(':=') || nodeText.startsWith('=:=') ? ':=-computed-' : nodeText; - - // const json = { - // doc: { - // type: 'doc', - // content: [ - // { - // type: 'paragraph', - // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, - // content: [{ type: 'dashField', attrs: { fieldKey: 'text', docid: '7e02a420-8add-49b0-ad20-54680567575f', hideKey: true, editable: false }, marks: [{ type: 'strong' }] }], - // }, - // { - // type: 'paragraph', - // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, - // content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334077 } }] }], - // }, - // ], - // }, - // }; - - // const json = { - // doc: { - // type: 'doc', - // content: [ - // { - // type: 'paragraph', - // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, - // content: [{ type: 'dashField', attrs: { fieldKey: 'hello', docid: '', hideKey: true, editable: true }, marks: [{ type: 'strong' }, { type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334006 } }] }], - // }, - // { - // type: 'paragraph', - // attrs: { align: null, color: null, id: null, indent: null, inset: null, lineSpacing: null, paddingBottom: null, paddingTop: null }, - // content: [{ type: 'text', marks: [{ type: 'user_mark', attrs: { userid: 'mart@bar.com', modified: 1667334088 } }] }], - // }, - // ], - // }, - // }; // look for a document whose id === the fieldKey being displayed. If there's a match, then that document // holds the different enumerated values for the field in the titles of its collected documents. // if there's a partial match from the start of the input text, complete the text --- TODO: make this an auto suggest box and select from a drop down. diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index e9f59f17d..4378c10f7 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -393,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); @@ -1485,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) { @@ -1494,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) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index bdf59863b..e7ca26d5c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -1,5 +1,5 @@ import { Mark, ResolvedPos } from 'prosemirror-model'; -import { EditorState } from 'prosemirror-state'; +import { EditorState, NodeSelection } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { Doc } from '../../../../fields/Doc'; import { DocServer } from '../../../DocServer'; @@ -92,7 +92,7 @@ export class FormattedTextBoxComment { FormattedTextBoxComment.tooltip.style.display = ''; } - static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '') { + static update(textBox: FormattedTextBox, view: EditorView, lastState?: EditorState, hrefs: string = '', linkDoc: string = '', noPreview: boolean = false) { FormattedTextBoxComment.textBox = textBox; if (hrefs || !lastState?.doc.eq(view.state.doc) || !lastState?.selection.eq(view.state.selection)) { FormattedTextBoxComment.setupPreview( @@ -102,12 +102,13 @@ export class FormattedTextBoxComment { ?.trim() .split(' ') .filter(h => h), - linkDoc + linkDoc, + noPreview ); } } - static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string) { + static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[], linkDoc?: string, noPreview?: boolean) { const state = view.state; // this section checks to see if the insertion point is over text entered by a different user. If so, it sets ths comment text to indicate the user and the modification date if (state.selection.$from) { @@ -130,8 +131,8 @@ export class FormattedTextBoxComment { if (state.selection.$from && hrefs?.length) { const nbef = findStartOfMark(state.selection.$from, view, findLinkMark); const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef; - nbef && - naft && + //nbef && + naft && LinkDocPreview.SetLinkInfo({ docProps: textBox.props, linkSrc: textBox.rootDoc, @@ -139,6 +140,7 @@ export class FormattedTextBoxComment { location: (pos => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - Math.max(0, nbef - 1))), hrefs, showHeader: true, + noPreview, }); } } diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index dc5d8ada8..e3c67ad2e 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -274,7 +274,7 @@ export class RichTextRules { const num = value.match(/^[0-9.]$/); this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value; } - const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: true }); + const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid, hideKey: false }); return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true); }), diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 00c41e187..4206a1006 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -75,6 +75,7 @@ export const marks: { [index: string]: MarkSpec } = { allAnchors: { default: [] as { href: string; title: string; anchorId: string }[] }, location: { default: null }, title: { default: null }, + noPreview: { default: false }, docref: { default: false }, // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text }, inclusive: false, @@ -85,6 +86,7 @@ export const marks: { [index: string]: MarkSpec } = { return { location: dom.getAttribute('location'), title: dom.getAttribute('title'), + noPreview: dom.getAttribute('noPreview'), }; }, }, @@ -111,7 +113,7 @@ export const marks: { [index: string]: MarkSpec } = { ['br'], ] : //node.attrs.allLinks.length === 1 ? - ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, location: node.attrs.location, style: `text-decoration: underline` }, 0]; + ['a', { class: anchorids, 'data-targethrefs': targethrefs, title: node.attrs.title, noPreview: node.attrs.noPreview, location: node.attrs.location, style: `text-decoration: underline` }, 0]; // ["div", { class: "prosemirror-anchor" }, // ["span", { class: "prosemirror-linkBtn" }, // ["a", { ...node.attrs, class: linkids, "data-targetids": targetids, title: `${node.attrs.title}` }, 0], |