From af8df1fc1af11318cadf1b71373bef776461a5cc Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Apr 2022 10:42:36 -0400 Subject: uses linkMenu in propertiesWindow now. updated linkMenu api, changed layout, made groups collapsible --- src/client/views/MainView.tsx | 2 +- .../views/PropertiesDocBacklinksSelector.scss | 5 +++++ .../views/PropertiesDocBacklinksSelector.tsx | 25 ++++++++++++++-------- src/client/views/SidebarAnnos.tsx | 2 +- src/client/views/linking/LinkMenu.scss | 9 +++++--- src/client/views/linking/LinkMenu.tsx | 10 +++++---- src/client/views/linking/LinkMenuGroup.tsx | 11 +++++++--- src/client/views/linking/LinkMenuItem.scss | 14 ++++++++---- src/client/views/linking/LinkMenuItem.tsx | 15 ++++++++----- src/client/views/nodes/LinkDocPreview.tsx | 2 +- 10 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 src/client/views/PropertiesDocBacklinksSelector.scss (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5e68832fc..a67cb3014 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -631,7 +631,7 @@ export class MainView extends React.Component { {this.topbar} {LinkDescriptionPopup.descriptionPopup ? : null} - {DocumentLinksButton.LinkEditorDocView ? : (null)} + {DocumentLinksButton.LinkEditorDocView ? : (null)} {LinkDocPreview.LinkInfo ? : (null)}
diff --git a/src/client/views/PropertiesDocBacklinksSelector.scss b/src/client/views/PropertiesDocBacklinksSelector.scss new file mode 100644 index 000000000..4d2a61c3b --- /dev/null +++ b/src/client/views/PropertiesDocBacklinksSelector.scss @@ -0,0 +1,5 @@ +.propertiesView-contexts-content { + .linkMenu { + position: relative; + } +} \ No newline at end of file diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index ea0d90e04..082492671 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -2,10 +2,12 @@ import { computed } from "mobx"; import { observer } from "mobx-react"; import * as React from "react"; import { Doc, DocListCast } from "../../fields/Doc"; -import { Id } from "../../fields/FieldSymbols"; -import { Cast, NumCast, StrCast } from "../../fields/Types"; +import { Cast } from "../../fields/Types"; +import { DocumentType } from "../documents/DocumentTypes"; import { LinkManager } from "../util/LinkManager"; -import './PropertiesDocContextSelector.scss'; +import { SelectionManager } from "../util/SelectionManager"; +import { LinkMenu } from "./linking/LinkMenu"; +import './PropertiesDocBacklinksSelector.scss'; type PropertiesDocBacklinksSelectorProps = { Document: Doc, @@ -22,7 +24,7 @@ export class PropertiesDocBacklinksSelector extends React.Component { const other = LinkManager.getOppositeAnchor(link, linkSource); - const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; + const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other; if (otherdoc && !collectedLinks.some(d => Doc.AreProtosEqual(d, otherdoc))) { collectedLinks.push(otherdoc); } @@ -30,15 +32,20 @@ export class PropertiesDocBacklinksSelector extends React.Component { - col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col; - this.props.addDocTab(col, "toggle:right"); + getOnClick = (link: Doc) => { + const linkSource = this.props.Document; + const other = LinkManager.getOppositeAnchor(link, linkSource); + const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other; + + if (otherdoc) { + this.props.addDocTab(Doc.IsPrototype(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, "toggle:right"); + } } render() { - return
+ return !SelectionManager.Views().length ? (null) :
{this.props.hideTitle ? (null) :

Contexts:

} - {this._docs.map(doc =>

this.getOnClick(doc)}>{StrCast(doc.title)}

)} +
; } } \ No newline at end of file diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 62f6e388f..fae385660 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -61,7 +61,7 @@ export class SidebarAnnos extends React.Component { FormattedTextBox.SelectOnLoad = target[Id]; FormattedTextBox.DontSelectInitialText = true; this.allMetadata.map(tag => target[tag] = tag); - DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup", "annotation"); + DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup"); this.addDocument(target); this._stackRef.current?.focusDocument(target); } diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 19c6463d3..77c16a28f 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -45,8 +45,11 @@ } .linkMenu-group-name { - padding: 10px; - + padding: 1px; + padding-left: 5px; + font-size: 10px; + font-style: italic; + font-weight: bold; &:hover { // p { @@ -64,7 +67,7 @@ p { width: 100%; - line-height: 12px; + line-height: 1; border-radius: 5px; text-transform: capitalize; } diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 53fe3f682..bc922be36 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -12,7 +12,8 @@ import React = require("react"); interface Props { docView: DocumentView; - changeFlyout: () => void; + position?: { x?: number, y?: number }; + itemHandler?: (doc: Doc) => void; } /** @@ -25,7 +26,7 @@ export class LinkMenu extends React.Component { @observable _linkMenuRef = React.createRef(); @computed get position() { - return ((dv) => ({ x: dv?.left || 0, y: dv?.top || 0, r: dv?.right || 0, b: dv?.bottom || 0 }))(this.props.docView.getBounds()); + return this.props.position ?? (dv => ({ x: dv?.left || 0, y: (dv?.bottom || 0) + 15 }))(this.props.docView.getBounds()); } componentDidMount() { document.addEventListener("pointerdown", this.onPointerDown); } @@ -48,19 +49,20 @@ export class LinkMenu extends React.Component { const linkItems = Array.from(groups.entries()).map(group => this._editingLink = linkDoc)} />); - return linkItems.length ? linkItems : [

No links have been created yet. Drag the linking button onto another document to create a link.

]; + return linkItems.length ? linkItems : this.props.position ? [<>] : [

No links have been created yet. Drag the linking button onto another document to create a link.

]; } render() { const sourceDoc = this.props.docView.props.Document; return
{this._editingLink ?
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index 03377ad4e..5b1e29e67 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -1,4 +1,5 @@ import { observer } from "mobx-react"; +import { observable, action } from "mobx"; import { Doc, StrListCast } from "../../../fields/Doc"; import { Id } from "../../../fields/FieldSymbols"; import { Cast } from "../../../fields/Types"; @@ -14,6 +15,7 @@ interface LinkMenuGroupProps { groupType: string; showEditor: (linkDoc: Doc) => void; docView: DocumentView; + itemHandler?: (doc: Doc) => void; } @observer @@ -36,6 +38,8 @@ export class LinkMenuGroup extends React.Component { return color; } + @observable _collapsed = false; + render() { const set = new Set(this.props.group); const groupItems = Array.from(set.keys()).map(linkDoc => { @@ -43,6 +47,7 @@ export class LinkMenuGroup extends React.Component { LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === this.props.sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null)); if (destination && this.props.sourceDoc) { return { return (
-
+
this._collapsed = !this._collapsed)} style={{ background: this.getBackgroundColor() }}>

{this.props.groupType}:

-
+ {this._collapsed ? (null) :
{groupItems} -
+
}
); } diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index 90722daf9..8333aa374 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -9,8 +9,8 @@ padding-left: 6.5px; padding-right: 2px; - padding-top: 6.5px; - padding-bottom: 6.5px; + padding-top: 1px; + padding-bottom: 1px; background-color: white; @@ -18,10 +18,12 @@ .linkMenu-name { position: relative; width: auto; + align-items: center; + display: flex; .linkMenu-text { - padding: 4px 2px; + // padding: 4px 2px; //display: inline; .linkMenu-source-title { @@ -37,6 +39,8 @@ .linkMenu-title-wrapper { display: flex; + align-items: center; + min-height: 20px; .destination-icon-wrapper { //border: 0.5px solid rgb(161, 161, 161); @@ -56,7 +60,8 @@ .linkMenu-destination-title { text-decoration: none; color: #4476F7; - font-size: 16px; + font-size: 13px; + line-height: 0.9; padding-bottom: 2px; padding-right: 4px; margin-right: 4px; @@ -77,6 +82,7 @@ font-style: italic; color: rgb(95, 97, 102); font-size: 9px; + line-height: 0.9; margin-left: 20px; max-width: 125px; height: auto; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 96cc6d600..d6400a6b3 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -1,12 +1,12 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, observable, runInAction } from 'mobx'; +import { action, observable } from 'mobx'; import { observer } from "mobx-react"; import { Doc, DocListCast } from '../../../fields/Doc'; import { Cast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; -import { emptyFunction, setupMoveUpEvents, returnFalse } from '../../../Utils'; +import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -14,11 +14,10 @@ import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import './LinkMenuItem.scss'; import React = require("react"); -import { setup } from 'mocha'; interface LinkMenuItemProps { @@ -29,6 +28,7 @@ interface LinkMenuItemProps { destinationDoc: Doc; showEditor: (linkDoc: Doc) => void; menuRef: React.Ref; + itemHandler?: (doc: Doc) => void; } // drag links and drop link targets (aliasing them if needed) @@ -98,7 +98,12 @@ export class LinkMenuItem extends React.Component { emptyFunction, () => { DocumentLinksButton.ClearLinkEditor(); - LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); + if (this.props.itemHandler) { + + this.props.itemHandler?.(this.props.linkDoc); + } else { + LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); + } }); } diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 7637d39be..f5f773595 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -46,7 +46,7 @@ export class LinkDocPreview extends React.Component { if (anchor1 && anchor2) { linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1; } - if (linkTarget?.annotationOn) { + if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { // want to show annotation context document if annotation is not text linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno)); } else { this._targetDoc = linkTarget; -- cgit v1.2.3-70-g09d2 From 27d37f7c50978da6f3eca87e1aa5bfea786b19c6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Apr 2022 11:04:53 -0400 Subject: fixed displaying titles on text to not shrink box to 0 height. fixed autoLink to not self link. --- src/client/views/nodes/CollectionFreeFormDocumentView.tsx | 3 ++- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 6f0f0074a..5149d370f 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -46,6 +46,7 @@ export class CollectionFreeFormDocumentView extends DocComponent); } @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); } @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); } @@ -171,7 +172,7 @@ export class CollectionFreeFormDocumentView extends DocComponent) => { - if (this._editorView && (this._editorView as any).docView) { + if (this._editorView && (this._editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { const flattened1 = this.findInNode(this._editorView, this._editorView.state.doc, StrCast(target.title).replace(/^@/, "")); var alink: Doc | undefined; flattened1.forEach((flat, i) => { -- cgit v1.2.3-70-g09d2 From aeb636371275ed056d7812447984f71ff90b39e0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 6 Apr 2022 11:44:02 -0400 Subject: set publsihed documents to automatically display a title region --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index dcb312980..18be30a88 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1443,6 +1443,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } catch (e: any) { console.log(e.message); } this._lastText = curText; } + if (StrCast(this.rootDoc.title).startsWith("@") && !this.dataDoc["title-custom"]) { + UndoManager.RunInBatch(() => { + this.dataDoc["title-custom"] = true; + this.dataDoc.showTitle = "title"; + const tr = this._editorView!.state.tr; + this._editorView?.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(0), tr.doc.resolve(StrCast(this.rootDoc.title).length + 2))).deleteSelection()); + }, "titler"); + } } onKeyDown = (e: React.KeyboardEvent) => { // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container) -- cgit v1.2.3-70-g09d2 From f05bc3ee02198289dbfbc1b9c8db0b9e0fffd27c Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 7 Apr 2022 12:02:04 -0400 Subject: added multiple target capability for autoLinks. added wiki: --- src/client/views/nodes/LinkDocPreview.tsx | 22 ++++++++++++++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 20 +++++++++++++++----- .../views/nodes/formattedText/RichTextRules.ts | 19 +++++++++++++++++++ src/client/views/nodes/formattedText/marks_rts.ts | 13 +++++++++---- 4 files changed, 59 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index f5f773595..d5f3f3b4e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -84,9 +84,16 @@ export class LinkDocPreview extends React.Component { anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0]; - this._linkSrc = anchor; - const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - this._targetDoc = linkTarget?.type === DocumentType.MARKER && linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; + const automaticLink = this._linkDoc.linkRelationship === "automatic"; + if (automaticLink) { // automatic links specify the target in the link info, not the source + const linkTarget = anchor; + this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); + this._targetDoc = linkTarget; + } else { + this._linkSrc = anchor; + const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + this._targetDoc = linkTarget?.type === DocumentType.MARKER && linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; + } this._toolTipText = ""; } })); @@ -99,7 +106,10 @@ export class LinkDocPreview extends React.Component { 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))); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => { + this._linkDoc = undefined; + this._hrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1); + })); } followLink = (e: React.PointerEvent) => { @@ -152,10 +162,10 @@ export class LinkDocPreview extends React.Component { return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? (null) :
{!this.props.showHeader ? (null) : this.previewHeader} -
+
{this._toolTipText ? this._toolTipText : { - const targetanchor = LinkManager.getOppositeAnchor(this._linkDoc!, this._linkSrc!); + const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor); }} Document={this._targetDoc!} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 18be30a88..ec88b8d1d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -381,8 +381,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp // creates links between terms in a document and documents which have a matching Id hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set) => { - if (this._editorView && (this._editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { - const flattened1 = this.findInNode(this._editorView, this._editorView.state.doc, StrCast(target.title).replace(/^@/, "")); + const editorView = this._editorView; + if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) { + const flattened1 = this.findInNode(editorView, editorView.state.doc, StrCast(target.title).replace(/^@/, "")); var alink: Doc | undefined; flattened1.forEach((flat, i) => { const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, StrCast(target.title).replace(/^@/, "")); @@ -391,9 +392,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, "automatic")!); newAutoLinks.add(alink); - const allAnchors = [{ href: Doc.localServerPath(this.props.Document), title: "a link", anchorId: this.props.Document[Id] }]; - const link = this._editorView!.state.schema.marks.autoLinkAnchor.create({ allAnchors, linkDoc: alink[Id], title: "auto link", location }); - tr = tr.addMark(flattened[i].from, flattened[i].to, link); + const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); + const sel = flattened[i]; + tr = tr.addMark(sel.from, sel.to, splitter); + tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { + if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { + 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 ?? [])); + const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" }); + tr = tr.addMark(pos, pos + node.nodeSize, link); + } + }); + tr = tr.removeMark(sel.from, sel.to, splitter); }); } return tr; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index bafae84dc..00c03875b 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -294,6 +294,25 @@ export class RichTextRules { return state.tr.deleteRange(start, end).insert(start, fieldView); }), + + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document + // wiki:title + new InputRule( + new RegExp(/wiki:([a-zA-Z_@:\.\?\-0-9]+ )$/), + (state, match, start, end) => { + const title = match[1]; + this.TextBox.EditorView?.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end)))); + + this.TextBox.makeLinkAnchor(undefined, "add:right", `https://en.wikipedia.org/wiki/${title.trim()}`, "wikipedia reference"); + + const fstate = this.TextBox.EditorView?.state; + if (fstate) { + const tr = fstate?.tr.deleteRange(start, start + 5); + return tr.setSelection(new TextSelection(tr.doc.resolve(end - 5))).insertText(" "); + } + return state.tr; + }), + // create an inline view of a document {{ : }} // {{:Doc}} => show default view of document // {{}} => show layout for this doc diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts index 52ef06b7f..1f6ce014f 100644 --- a/src/client/views/nodes/formattedText/marks_rts.ts +++ b/src/client/views/nodes/formattedText/marks_rts.ts @@ -19,13 +19,17 @@ export const marks: { [index: string]: MarkSpec } = { }, - // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title` - // defaults to the empty string. Rendered and parsed as an `` + // :: MarkSpec an autoLinkAnchor. These are automatically generated anchors to "published" documents based on the anchor text matching the + // published document's title. + // NOTE: unlike linkAnchors, the autoLinkAnchor's href's indicate the target anchor of the hyperlink and NOT the source. This is because + // automatic links do not create a text selection Marker document for the source anchor, but use the text document itself. Since + // multiple automatic links can be created each having the same source anchor (the whole document), the target href of the link is needed to + // disambiguate links from one another. + // Rendered and parsed as an `` // element. autoLinkAnchor: { attrs: { allAnchors: { default: [] as { href: string, title: string, anchorId: string }[] }, - linkDoc: { default: "" }, location: { default: null }, title: { default: null }, }, @@ -44,7 +48,8 @@ export const marks: { [index: string]: MarkSpec } = { return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0]; } }, - // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each link has an href URL and a title for use in menus and hover (Dash links have linkIDs & targetIDs). `title` + // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text, + // and a title for use in menus and hover. `title` // defaults to the empty string. Rendered and parsed as an `` // element. linkAnchor: { -- cgit v1.2.3-70-g09d2 From e8938d5d7b889551c1d32bcf5385e369ed67cea5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 7 Apr 2022 14:42:11 -0400 Subject: fixed iconizing images to not get the wrong height because of templates. fixed cross-fade of images to work without needing doc to be selected. --- src/client/views/nodes/DocumentView.tsx | 8 +++++++- src/client/views/nodes/ImageBox.scss | 16 +++++++--------- src/client/views/nodes/ImageBox.tsx | 18 +++++++++--------- 3 files changed, 23 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a9abf066e..103c3624d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -111,6 +111,7 @@ export interface DocumentViewSharedProps { ContainingCollectionView: Opt; ContainingCollectionDoc: Opt; thumbShown?: () => boolean; + isHovering?: () => boolean; setContentView?: (view: DocComponentView) => any; CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView; PanelWidth: () => number; @@ -873,6 +874,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} - {this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) : + {!this.props.isSelected() || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) : @@ -1065,6 +1067,8 @@ export class DocumentViewInternal extends DocComponent; } + isHovering = () => this._isHovering; + @observable _isHovering = false; @observable _: string = ""; @computed get renderDoc() { TraceMobx(); @@ -1074,6 +1078,8 @@ export class DocumentViewInternal extends DocComponent this._isHovering = true)} + onPointerLeave={action(() => this._isHovering = false)} style={{ background: isButton || thumb ? undefined : this.backgroundColor, opacity: this.opacity, diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 4238f6d29..cd2b23f02 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -103,7 +103,7 @@ display: flex; height: 100%; - .imageBox-fadeBlocker { + .imageBox-fadeBlocker, .imageBox-fadeBlocker-hover{ width: 100%; height: 100%; position: absolute; @@ -133,12 +133,10 @@ transition: opacity 1s ease-in-out; } -.imageBox:hover { - .imageBox-fadeBlocker { - -webkit-transition: opacity 1s ease-in-out; - -moz-transition: opacity 1s ease-in-out; - -o-transition: opacity 1s ease-in-out; - transition: opacity 1s ease-in-out; - opacity: 0; - } +.imageBox-fadeBlocker-hover { + -webkit-transition: opacity 1s ease-in-out; + -moz-transition: opacity 1s ease-in-out; + -o-transition: opacity 1s ease-in-out; + transition: opacity 1s ease-in-out; + opacity: 0; } \ No newline at end of file diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 0a4168698..17f95c1cc 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -45,7 +45,6 @@ const uploadIcons = { export class ImageBox extends ViewBoxAnnotatableComponent() { protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined; public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); } - private _imgRef: React.RefObject = React.createRef(); private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [name: string]: IReactionDisposer } = {}; @observable _curSuffix = ""; @@ -78,7 +77,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), ({ nativeSize, width }) => { - if (!this.layoutDoc._height) { + if (true || !this.layoutDoc._height) { this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth; } }, @@ -282,17 +281,18 @@ export class ImageBox extends ViewBoxAnnotatableComponent
- - {fadepath === srcpath ? (null) :
- -
} + {fadepath === srcpath ? (null) : +
+ +
}
{this.considerDownloadIcon} {this.considerGooglePhotosLink()} -- cgit v1.2.3-70-g09d2 From 4b6e16e29b50b492b0a8cbdf32414c7a626f68bd Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 12 Apr 2022 11:00:01 -0400 Subject: changed lightOrDark for 'transparent' to return gray. --- src/Utils.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/Utils.ts b/src/Utils.ts index b280badd7..77095005b 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -603,6 +603,7 @@ export function DashColor(color: string) { } export function lightOrDark(color: any) { + if (color === "transparent") return "gray"; const nonAlphaColor = color.startsWith("#") ? (color as string).substring(0, 7) : color.startsWith("rgba") ? color.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : color; const col = DashColor(nonAlphaColor).rgb(); -- cgit v1.2.3-70-g09d2 From 9ec7503370229c17cf8a0d3cf77571010db04a9b Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 14 Apr 2022 17:40:10 -0400 Subject: presesntation groups --- src/client/views/nodes/trails/PresBox.tsx | 1 + src/client/views/nodes/trails/PresElementBox.scss | 78 +++++++++++++++++++- src/client/views/nodes/trails/PresElementBox.tsx | 86 +++++++++++++++++++++-- 3 files changed, 157 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 2e312ee51..961794d65 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -734,6 +734,7 @@ export class PresBox extends ViewBoxBaseComponent() { //Regular click @action selectElement = async (doc: Doc) => { + console.log("got here", this.childDocs.indexOf(doc)) const context = Cast(doc.context, Doc, null); this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0); diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss index 1ad4b820e..dd0520bad 100644 --- a/src/client/views/nodes/trails/PresElementBox.scss +++ b/src/client/views/nodes/trails/PresElementBox.scss @@ -195,6 +195,47 @@ $slide-active: #5B9FDD; box-shadow: 0 0 0px 1.5px $dark-blue; } +.presItem-slide.group { + border-radius: 5px; +} + +.presItem-slide.activeGroup { + border-radius: 5px; + box-shadow: 0 0 0px 1.5px $dark-blue; +} + +.presItem-groupSlideContainer { + position: absolute; + /* grid-row: 3; */ + /* grid-column: 1/8; */ + top: 28; + display: block; + background: #92adb9; + width: 100%; + height: 10px; + border-radius: 0px 0px 5px 5px; + height: calc(100% - 28px); + display: grid; + grid-template-rows: auto auto auto; + grid-template-columns: 100%; + justify-items: left; + align-items: center; +} + +.presItem-groupSlide { + position: relative; + background-color: #d5dce2; + border-radius: 5px; + height: calc(100% - 7px); + width: calc(100% - 20px); + left: 15px; + /* height: 20px; */ + /* width: calc(100% - 19px); */ + display: flex; + grid-template-rows: 16px 10px auto; + grid-template-columns: max-content max-content max-content max-content auto; +} + .presItem-multiDrag { font-family: Roboto; font-weight: 600; @@ -230,6 +271,37 @@ $slide-active: #5B9FDD; box-shadow: 0 0 0px 1.5px $dark-blue; } -// .presItem-slide:hover { -// background: $slide-hover; -// } \ No newline at end of file +.expandButton { + cursor: pointer; + position: absolute; + border-radius: 100px; + bottom: 0; + left: -18; + z-index: 300; + width: 15px; + height: 15px; + display: flex; + font-size: 12px; + justify-self: center; + align-self: center; + background-color: #92adb9; + color: white; + justify-content: center; + align-items: center; + transition: 0.2s; + margin-right: 3px; +} + +.expandButton:hover { + background-color: rgba(0, 0, 0, 1); + transform: scale(1.2); +} + +.presItem-groupNum { + color: #d5dce2; + position: absolute; + left: -15px; + top: 1; + font-weight: 600; + font-size: 12; +} \ No newline at end of file diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index a4ec559f5..1da7a596a 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -2,9 +2,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Tooltip } from "@material-ui/core"; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc"; import { Id } from "../../../../fields/FieldSymbols"; -import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils"; import { DocUtils } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; @@ -106,6 +106,38 @@ export class PresElementBox extends ViewBoxBaseComponent() {
; } + @computed get renderGroupSlides() { + const childDocs = DocListCast(this.targetDoc.data); + const groupSlides = childDocs.map((doc: Doc, ind: number) => +
{ + console.log("Clicked on slide with index: ", ind); + e.stopPropagation(); + e.preventDefault(); + PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + }}> +
+ {`${ind + 1}.`} +
+ {/* style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }} */} +
+ StrCast(doc.title)} + SetValue={(value: string) => { + doc.title = !value.trim().length ? "-untitled-" : value; + return true; + }} + /> +
+
+ ); + return ( + groupSlides + ); + } @computed get duration() { let durationInS: number; if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; } @@ -284,6 +316,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false; const targetDoc: Doc = this.targetDoc; const activeItem: Doc = this.rootDoc; + const isGroup: boolean = BoolCast(targetDoc._isGroup); return (
() { PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); }} onDoubleClick={action(e => { - this.toggleProperties(); - PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); + if (isGroup) { + this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup; + } else { + this.toggleProperties(); + PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); + } })} onPointerOver={this.onPointerOver} onPointerLeave={this.onPointerLeave} @@ -316,7 +353,46 @@ export class PresElementBox extends ViewBoxBaseComponent() {
{`${this.indexInPres + 1}.`}
} - {miniView ? (null) :
+
+ StrCast(activeItem.title)} + SetValue={this.onSetValue} + /> +
+
+
{"Remove from presentation"}
}>
+ e.stopPropagation()} /> +
+
+
+
+ {this.rootDoc.presExpandGroup ? this.renderGroupSlides : (null)} +
+
{activeItem.presPinView ? (<>View of {targetDoc.title}) : targetDoc.title}
+
{ + if (isGroup) { + this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup; + } + }} + > + +
+
+ : (null)} + {miniView || isGroup ? (null) :