diff options
-rw-r--r-- | src/client/views/linking/LinkMenuItem.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkAnchorBox.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.scss | 3 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 100 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 5 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx | 103 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/marks_rts.ts | 5 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/prosemirrorPatches.js | 18 |
8 files changed, 103 insertions, 135 deletions
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 2bc2bc6a4..53a7ae9ab 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -140,7 +140,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> { <div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize." onPointerLeave={LinkDocPreview.Clear} onPointerEnter={e => this.props.linkDoc && LinkDocPreview.SetLinkInfo({ - docprops: this.props.docView.props, + docProps: this.props.docView.props, linkSrc: this.props.sourceDoc, linkDoc: this.props.linkDoc, showHeader: false, diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 7a4209563..db5414069 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -137,7 +137,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnch return <div className={`linkAnchorBox-cont${small ? "-small" : ""}`} onPointerLeave={LinkDocPreview.Clear} onPointerEnter={e => LinkDocPreview.SetLinkInfo({ - docprops: this.props, + docProps: this.props, linkSrc: linkSource, linkDoc: this.rootDoc, showHeader: true, diff --git a/src/client/views/nodes/LinkDocPreview.scss b/src/client/views/nodes/LinkDocPreview.scss index abbb8cdf0..b7aeaa072 100644 --- a/src/client/views/nodes/LinkDocPreview.scss +++ b/src/client/views/nodes/LinkDocPreview.scss @@ -19,6 +19,9 @@ height: 37px; white-space: pre; + .linkDocPreview-buttonBar { + float: right; + } .linkDocPreview-title { padding-right: 4px; float: left; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 463719824..576d1d15b 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -13,11 +13,12 @@ import { Transform } from "../../util/Transform"; import { DocumentView, DocumentViewSharedProps } from "./DocumentView"; import './LinkDocPreview.scss'; import React = require("react"); +import { undoBatch } from '../../util/UndoManager'; interface LinkDocPreviewProps { linkDoc?: Doc; linkSrc?: Doc; - docprops: DocumentViewSharedProps; + docProps: DocumentViewSharedProps; location: number[]; hrefs?: string[]; showHeader?: boolean; @@ -34,25 +35,24 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { @observable _linkSrc: Opt<Doc>; @observable _toolTipText = ""; @observable _hrefInd = 0; - @observable _linkTarget: Opt<Doc>; - @action componentDidUpdate(props: any) { - if (props.linkSrc !== this.props.linkSrc || - props.linkDoc !== this.props.linkDoc || - props.hrefs !== this.props.hrefs) { - this._linkTarget = this.props.linkDoc; - this._linkSrc = this.props.linkSrc; - this._linkDoc = this.props.linkDoc; - this._toolTipText = ""; - this.updatePreview(); - } - } - @action componentDidMount() { - this._linkTarget = this.props.linkDoc; + @action init() { + var linkTarget = this.props.linkDoc; this._linkSrc = this.props.linkSrc; this._linkDoc = this.props.linkDoc; + const anchor1 = this._linkDoc?.anchor1 as Doc; + const anchor2 = this._linkDoc?.anchor2 as Doc; + if (anchor1 && anchor2) { + linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1; + } + this._targetDoc = linkTarget?.annotationOn as Doc ?? linkTarget; this._toolTipText = ""; - this.updatePreview(); + } + componentDidUpdate(props: any) { + if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init(); + } + componentDidMount() { + this.init(); document.addEventListener("pointerdown", this.onPointerDown); } @@ -62,66 +62,46 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { } onPointerDown = (e: PointerEvent) => { - !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); + !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview } - updatePreview() { - const linkDoc = this.props.linkDoc; - const linkSrc = this.props.linkSrc; + @computed get href() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; - if (href.indexOf(Utils.prepend("/doc/")) !== 0) { + if (href.indexOf(Utils.prepend("/doc/")) !== 0) { // link to a web page URL -- try to show a preview if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500)))); } else { - runInAction(() => this._toolTipText = "external => " + href); + setTimeout(action(() => this._toolTipText = "url => " + href)); } - } else { + } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; - anchorDoc && DocServer.GetRefField(anchorDoc).then(action(async anchor => { + anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = DocListCast(anchor.links)[0]; this._linkSrc = anchor; - const targetanchor = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - runInAction(async () => { - this._linkTarget = targetanchor; - const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget; - runInAction(() => { - this._toolTipText = ""; - this._targetDoc = target; - }); - }); + const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + this._targetDoc = linkTarget?.annotationOn as Doc ?? linkTarget; + this._toolTipText = ""; } })); } - } else if (linkDoc) { - const anchor1 = linkDoc.anchor1 as Doc; - const anchor2 = linkDoc.anchor2 as Doc; - runInAction(async () => { - this._linkTarget = Doc.AreProtosEqual(anchor1, linkSrc) || Doc.AreProtosEqual(anchor1.annotationOn as Doc, linkSrc) ? anchor2 : anchor1; - const target = this._linkTarget?.annotationOn ? await DocCastAsync(this._linkTarget.annotationOn) : this._linkTarget; - runInAction(() => { - this._toolTipText = ""; - this._targetDoc = target; - }); - }); + return href; } + return undefined; } deleteLink = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => this._linkDoc ? LinkManager.Instance.deleteLink(this._linkDoc) : null)); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc))); } nextHref = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => { - this._hrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1); - this.updatePreview(); - })); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => this._hrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1))); } followLink = (e: React.PointerEvent) => { if (this._linkDoc && this._linkSrc) { - LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docprops, false); + LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false); } else if (this.props.hrefs?.length) { - this.props.docprops?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right"); + this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right"); } } width = () => Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225)); @@ -133,13 +113,12 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title} <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p> </div> - <div className="wrapper" style={{ float: "right" }}> - {(this.props.hrefs?.length || 0) <= 1 ? (null) : - <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top"> - <div className="linkDocPreview-button" onPointerDown={this.nextHref}> - <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" /> - </div> - </Tooltip>} + <div className="linkDocPreview-buttonBar" > + <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top"> + <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? "gray" : "green" }} onPointerDown={this.nextHref}> + <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" /> + </div> + </Tooltip> <Tooltip title={<div className="dash-tooltip">Delete Link</div>} placement="top"> <div className="linkDocPreview-button" onPointerDown={this.deleteLink}> @@ -151,6 +130,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { } @computed get docPreview() { + const href = this.href; // needs to be here to trigger lookup of web pages and docs on server return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? (null) : <div className="linkDocPreview-inner"> {!this.props.showHeader ? (null) : this.previewHeader} @@ -163,8 +143,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { Document={this._targetDoc!} moveDocument={returnFalse} rootSelected={returnFalse} - styleProvider={this.props.docprops?.styleProvider} - layerProvider={this.props.docprops?.layerProvider} + styleProvider={this.props.docProps?.styleProvider} + layerProvider={this.props.docProps?.layerProvider} docViewPath={emptyPath} ScreenToLocalTransform={Transform.Identity} parentActive={returnFalse} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index fece45e18..2434bee40 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -177,6 +177,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp }); if (allFoundLinkAnchors.length) { this._editorView.dispatch(removeMarkWithAttrs(state.tr, 0, state.doc.nodeSize - 2, state.schema.marks.linkAnchor, { allAnchors: allFoundLinkAnchors })); + + this.setupEditor(this.config, this.fieldKey); } } } @@ -186,6 +188,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const state = this._editorView?.state; if (state && this._editorView) { this._editorView.dispatch(removeMarkWithAttrs(state.tr, state.selection.from, state.selection.to, state.schema.marks.link, { allAnchors })); + this.setupEditor(this.config, this.fieldKey); } } @@ -1233,7 +1236,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp clipboardTextSerializer: this.clipboardTextSerializer, handlePaste: this.handlePaste, }); - // !Doc.UserDoc().noviceMode && applyDevTools.applyDevTools(this._editorView); const startupText = !rtfField && this._editorView && Field.toString(this.dataDoc[fieldKey] as Field); if (startupText) { const { state: { tr }, dispatch } = this._editorView; @@ -1376,6 +1378,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onFocused = (e: React.FocusEvent): void => { FormattedTextBox.FocusedBox = this; this.tryUpdateHeight(); + //applyDevTools.applyDevTools(this._editorView); // see if we need to preserve the insertion point const prosediv = this.ProseRef?.children?.[0] as any; diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index e4a1e0f1b..89df5e246 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -1,9 +1,7 @@ import { Mark, ResolvedPos } from "prosemirror-model"; import { EditorState } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; -import { Doc, DocListCast } from "../../../../fields/Doc"; -import { Utils } from "../../../../Utils"; -import { DocServer } from "../../../DocServer"; +import { Doc } from "../../../../fields/Doc"; import { LinkDocPreview } from "../LinkDocPreview"; import { FormattedTextBox } from "./FormattedTextBox"; import './FormattedTextBoxComment.scss'; @@ -37,54 +35,51 @@ export function findEndOfMark(rpos: ResolvedPos, view: EditorView, finder: (mark export class FormattedTextBoxComment { static tooltip: HTMLElement; static tooltipText: HTMLElement; - static start: number; - static end: number; - static mark: Mark; + static startUserMarkRegion: number; + static endUserMarkRegion: number; + static userMark: Mark; static textBox: FormattedTextBox | undefined; constructor(view: any) { if (!FormattedTextBoxComment.tooltip) { - FormattedTextBoxComment.tooltip = document.createElement("div"); - FormattedTextBoxComment.tooltip.className = "FormattedTextBox-tooltip"; - FormattedTextBoxComment.tooltip.style.display = "none"; - FormattedTextBoxComment.tooltipText = document.createElement("div"); - FormattedTextBoxComment.tooltipText.className = "FormattedTextBox-tooltipText"; - FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); - FormattedTextBoxComment.tooltip.onpointerdown = (e: PointerEvent) => { - const textBox = FormattedTextBoxComment.textBox; - false && FormattedTextBoxComment.start !== undefined && textBox?.adoptAnnotation( - FormattedTextBoxComment.start, FormattedTextBoxComment.end, FormattedTextBoxComment.mark); + const tooltip = FormattedTextBoxComment.tooltip = document.createElement("div"); + const tooltipText = FormattedTextBoxComment.tooltipText = document.createElement("div"); + tooltip.className = "FormattedTextBox-tooltip"; + tooltipText.className = "FormattedTextBox-tooltipText"; + tooltip.style.display = "none"; + tooltip.appendChild(tooltipText); + tooltip.onpointerdown = (e: PointerEvent) => { + const { textBox, startUserMarkRegion, endUserMarkRegion, userMark } = FormattedTextBoxComment; + false && startUserMarkRegion !== undefined && textBox?.adoptAnnotation(startUserMarkRegion, endUserMarkRegion, userMark); e.stopPropagation(); e.preventDefault(); }; - document.getElementById("root")?.appendChild(FormattedTextBoxComment.tooltip); + document.getElementById("root")?.appendChild(tooltip); } } public static Hide() { FormattedTextBoxComment.textBox = undefined; FormattedTextBoxComment.tooltip.style.display = "none"; } - public static SetState(textBox: any, start: number, end: number, mark: Mark) { + public static saveMarkRegion(textBox: any, start: number, end: number, mark: Mark) { FormattedTextBoxComment.textBox = textBox; - FormattedTextBoxComment.start = start; - FormattedTextBoxComment.end = end; - FormattedTextBoxComment.mark = mark; + FormattedTextBoxComment.startUserMarkRegion = start; + FormattedTextBoxComment.endUserMarkRegion = end; + FormattedTextBoxComment.userMark = mark; FormattedTextBoxComment.tooltip.style.display = ""; } - static showCommentbox(set: string, view: EditorView, nbef: number) { + static showCommentbox(view: EditorView, nbef: number) { const state = view.state; - if (set !== "none") { - // These are in screen coordinates - const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); - // The box in which the tooltip is positioned, to use as base - const box = (document.getElementsByClassName("mainView-container") as any)[0].getBoundingClientRect(); - // Find a center-ish x position from the selection endpoints (when crossing lines, end may be more to the left) - const left = Math.max((start.left + end.left) / 2, start.left + 3); - FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; - FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; - } - FormattedTextBoxComment.tooltip.style.display = set; + // These are in screen coordinates + const start = view.coordsAtPos(state.selection.from - nbef), end = view.coordsAtPos(state.selection.from - nbef); + // The box in which the tooltip is positioned, to use as base + const box = (document.getElementsByClassName("mainView-container") as any)[0].getBoundingClientRect(); + // Find a center-ish x position from the selection endpoints (when crossing lines, end may be more to the left) + const left = Math.max((start.left + end.left) / 2, start.left + 3); + FormattedTextBoxComment.tooltip.style.left = (left - box.left) + "px"; + FormattedTextBoxComment.tooltip.style.bottom = (box.bottom - start.top) + "px"; + FormattedTextBoxComment.tooltip.style.display = ""; } static update(view: EditorView, lastState?: EditorState, hrefs: string = "") { @@ -96,7 +91,6 @@ export class FormattedTextBoxComment { static setupPreview(view: EditorView, textBox: FormattedTextBox, hrefs?: string[]) { 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 - var hide = true; if (state.selection.$from) { const nbef = findStartOfMark(state.selection.$from, view, findOtherUserMark); const naft = findEndOfMark(state.selection.$from, view, findOtherUserMark); @@ -105,43 +99,26 @@ export class FormattedTextBoxComment { state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any, pos: number, parent: any) => !child && node.marks.length && (child = node)); const mark = child && findOtherUserMark(child.marks); if (mark && child && (nbef || naft) && (!mark.attrs.opened || noselection)) { - FormattedTextBoxComment.SetState(textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark); + FormattedTextBoxComment.saveMarkRegion(textBox, state.selection.$from.pos - nbef, state.selection.$from.pos + naft, mark); } if (mark && child && ((nbef && naft) || !noselection)) { FormattedTextBoxComment.tooltipText.textContent = mark.attrs.userid + " on " + (new Date(mark.attrs.modified * 1000)).toLocaleString(); - FormattedTextBoxComment.showCommentbox("", view, nbef); - hide = false; - } + FormattedTextBoxComment.showCommentbox(view, nbef); + } else FormattedTextBoxComment.Hide(); } + // this checks if the selection is a hyperlink. If so, it displays the target doc's text for internal links, and the url of the target for external links. - if (hide && state.selection.$from) { + if (state.selection.$from && hrefs) { const nbef = findStartOfMark(state.selection.$from, view, findLinkMark); const naft = findEndOfMark(state.selection.$from, view, findLinkMark) || nbef; - let child: any = null; - state.doc.nodesBetween(state.selection.from, state.selection.to, (node: any) => !child && node.marks.length && (child = node)); - child = child || (nbef && state.selection.$from.nodeBefore); - const mark = child ? findLinkMark(child.marks) : undefined; - const href = (!mark?.attrs.docref || naft === nbef) && mark?.attrs.allAnchors.find((item: { href: string }) => item.href)?.href; - if ((href && child && nbef && naft && mark?.attrs.showPreview)) { - const showPreview = (link?: Doc) => { - return LinkDocPreview.SetLinkInfo({ - docprops: textBox.props.docViewPath.lastElement().props, - linkSrc: textBox.props.Document, - linkDoc: link, - location: ((pos) => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - nbef)), - hrefs, - showHeader: true - }); - } - const anchorDoc = href.indexOf(Utils.prepend("/doc/")) === 0 ? href.replace(Utils.prepend("/doc/"), "").split("?")[0] : undefined; - if (anchorDoc) { - DocServer.GetRefField(anchorDoc).then(anchor => anchor instanceof Doc && showPreview(DocListCast(anchor.links)[0])); - } else if (hrefs?.length) { - showPreview() - } - } + nbef && naft && LinkDocPreview.SetLinkInfo({ + docProps: textBox.props, + linkSrc: textBox.rootDoc, + location: ((pos) => [pos.left, pos.top + 25])(view.coordsAtPos(state.selection.from - nbef)), + hrefs, + showHeader: true + }); } - if (hide) FormattedTextBoxComment.Hide(); } destroy() { } diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index aceba0387..655ee7e44 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -23,7 +23,6 @@ export const marks: { [index: string]: MarkSpec } = { linkAnchor: { attrs: { allAnchors: { default: [] as { href: string, title: string, anchorId: string }[] }, - showPreview: { default: true }, location: { default: null }, title: { default: null }, docref: { default: false } // flags whether the linked text comes from a document within Dash. If so, an attribution label is appended after the text @@ -32,8 +31,8 @@ export const marks: { [index: string]: MarkSpec } = { parseDOM: [{ tag: "a[href]", getAttrs(dom: any) { return { - allAnchors: [{ href: dom.getAttribute("shref"), title: dom.getAttribute("title"), anchorId: dom.getAttribute("class") }], location: dom.getAttribute("location"), + title: dom.getAttribute("title") }; } }], @@ -47,7 +46,7 @@ export const marks: { [index: string]: MarkSpec } = { href: node.attrs.allAnchors[0].href, }, node.attrs.title], ["br"]] : //node.attrs.allLinks.length === 1 ? - ["a", { ...node.attrs, class: anchorids, "data-targethrefs": targethrefs, title: node.attrs.title, href: node.attrs.allAnchors[0]?.href, style: `text-decoration: "underline"` }, 0]; + ["a", { class: anchorids, "data-targethrefs": targethrefs, title: node.attrs.title, 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], diff --git a/src/client/views/nodes/formattedText/prosemirrorPatches.js b/src/client/views/nodes/formattedText/prosemirrorPatches.js index 746c93868..5bc323a4d 100644 --- a/src/client/views/nodes/formattedText/prosemirrorPatches.js +++ b/src/client/views/nodes/formattedText/prosemirrorPatches.js @@ -143,17 +143,21 @@ function wrappingInputRule(regexp, nodeType, getAttrs, joinPredicate, customWith // :: ([Mark]) → ?Mark // Tests whether there is a mark of this type in the given set. function isInSetWithAttrs(mark, set, attrs) { + var markAllAnchors = attrs.allAnchors !== undefined ? Array.from(attrs.allAnchors.map(al => al.anchorId)) : []; for (var i = 0; i < set.length; i++) { if (set[i].type == mark) { - if (Array.from(Object.keys(attrs)).reduce((p, akey) => { + return Array.from(Object.keys(attrs)).reduce((p, akey) => { if (p && JSON.stringify(set[i].attrs[akey]) === JSON.stringify(attrs[akey])) return true; - set[i].attrs.allLinks = Array.from(set[i].attrs.allLinks).filter(a => !Array.from(attrs.allLinks.map(al => al.targetId)).includes(a.targetId) || !Array.from(attrs.allLinks.map(al => al.linkId).includes(a.linkId))) + // bcz: hack to allow special case of anchors in Dash to be removed from a mark which has multiple + if (set[i].attrs.allAnchors !== undefined) { + set[i].attrs.allAnchors = set[i].attrs.allAnchors.filter(a => !markAllAnchors.includes(a.anchorId)); + if (set[i].attrs.allAnchors.length) return undefined; + } return false; - }, true)) { - return set[i]; - } + }, true); } } + return undefined; }; // :: (number, number, ?union<Mark, MarkType>) → this @@ -170,7 +174,9 @@ function removeMarkWithAttrs(tr, from, to, mark, attrs) { step++; var toRemove = null; if (mark) { - if (isInSetWithAttrs(mark, node.marks, attrs)) { toRemove = [mark]; } + const inset = isInSetWithAttrs(mark, node.marks, attrs); + if (inset === true) { toRemove = [mark]; } + // if (inset === undefined) { console.log("lightened") } // anchorids were removed from the mark, but the mark wasn't removed since there are still anchorsids left } else { toRemove = node.marks; } |