From 5e559aa308102d7dd11adb7ed8c07d6c4b4c092b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 23 Jun 2020 18:57:47 -0400 Subject: fixed coloring links --- src/client/views/linking/LinkMenu.tsx | 1 - src/client/views/linking/LinkMenuItem.tsx | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/linking') diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 786d6be47..56f40ad69 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -9,7 +9,6 @@ import { LinkManager } from "../../util/LinkManager"; import { LinkMenuGroup } from "./LinkMenuGroup"; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; library.add(faTrash); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 17cd33241..3e28e8572 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -77,7 +77,9 @@ export class LinkMenuItem extends React.Component { } editMoved = (e: PointerEvent) => { - DragManager.StartDocumentDrag([this._editRef.current!], new DragManager.DocumentDragData([this.props.linkDoc]), e.x, e.y); + const dragData = new DragManager.DocumentDragData([this.props.linkDoc]); + dragData.dropAction = "alias"; // need to alias the link since it will have its _backgroundColor removed on drop + DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); return true; } -- cgit v1.2.3-70-g09d2 From a80aa9609f0cf1d2431e17f31af4cd3369d2a2c1 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 23 Jun 2020 19:28:01 -0400 Subject: cleaner approach to fixing linkAnchor colors --- src/client/documents/Documents.ts | 2 +- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/linking/LinkMenuItem.tsx | 1 - src/client/views/nodes/ColorBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 5 +++-- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views/linking') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ceacd3630..6c2c6faea 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -625,7 +625,7 @@ export namespace Docs { export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) { const doc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, { isLinkButton: true, treeViewHideTitle: true, treeViewOpen: false, backgroundColor: "lightBlue", // lightBlue is default color for linking dot and link documents text comment area - removeDropProperties: new List(["isBackground", "isLinkButton", "_backgroundColor"]), ...options + removeDropProperties: new List(["isBackground", "isLinkButton"]), ...options }, id); const linkDocProto = Doc.GetProto(doc); linkDocProto.anchor1 = source.doc; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 29dabd67c..76c1fc9f7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -88,7 +88,7 @@ export class CurrentUserUtils { } if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to. - const linkTemplate = Docs.Create.TextDocument(" ", { title: "header", _backgroundColor: "transparent", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created + const linkTemplate = Docs.Create.TextDocument(" ", { title: "header", _height: 100 }, "header"); // text needs to be a space to allow templateText to be created Doc.GetProto(linkTemplate).layout = "
" + " " + diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 3e28e8572..edc18b6a9 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -78,7 +78,6 @@ export class LinkMenuItem extends React.Component { editMoved = (e: PointerEvent) => { const dragData = new DragManager.DocumentDragData([this.props.linkDoc]); - dragData.dropAction = "alias"; // need to alias the link since it will have its _backgroundColor removed on drop DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y); return true; } diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 61aeb1efa..7b7c4d266 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -36,7 +36,7 @@ export class ColorBox extends ViewBoxBaseComponent void; backgroundHalo?: () => boolean; backgroundColor?: (doc: Doc) => string | undefined; + forcedBackgroundColor?: (doc: Doc) => string | undefined; opacity?: () => number | undefined; ChromeHeight?: () => number; dontRegisterView?: boolean; @@ -1064,7 +1065,7 @@ export class DocumentView extends DocComponent(Docu PanelWidth={this.anchorPanelWidth} PanelHeight={this.anchorPanelHeight} ContentScaling={returnOne} - backgroundColor={returnTransparent} + forcedBackgroundColor={returnTransparent} removeDocument={this.hideLinkAnchor} pointerEvents={false} LayoutTemplate={undefined} @@ -1150,7 +1151,7 @@ export class DocumentView extends DocComponent(Docu render() { if (this.props.Document[AclSym] && this.props.Document[AclSym] === AclPrivate) return (null); if (!(this.props.Document instanceof Doc)) return (null); - const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); + const backgroundColor = Doc.UserDoc().renderStyle === "comic" ? undefined : this.props.forcedBackgroundColor?.(this.Document) || StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.Document.backgroundColor) || this.props.backgroundColor?.(this.Document); const opacity = Cast(this.layoutDoc._opacity, "number", Cast(this.layoutDoc.opacity, "number", Cast(this.Document.opacity, "number", null))); const finalOpacity = this.props.opacity ? this.props.opacity() : opacity; const finalColor = this.layoutDoc.type === DocumentType.FONTICON || this.layoutDoc._viewType === CollectionViewType.Linear ? undefined : backgroundColor; diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 9f252c651..2b64cdab6 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -116,7 +116,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent 1 ? NumCast(this.rootDoc[this.fieldKey + "_x"], 100) : 0; const y = this.props.PanelWidth() > 1 ? NumCast(this.rootDoc[this.fieldKey + "_y"], 100) : 0; - const c = StrCast(this.layoutDoc.backgroundColor, StrCast(this.dataDoc.backgroundColor, "lightBlue")); // note this is not where the typical lightBlue default color comes from. See Documents.Create.LinkDocument() + const c = StrCast(this.layoutDoc._backgroundColor, StrCast(this.layoutDoc.backgroundColor, StrCast(this.dataDoc.backgroundColor, "lightBlue"))); // note this is not where the typical lightBlue default color comes from. See Documents.Create.LinkDocument() const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1"; const anchorScale = (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25; -- cgit v1.2.3-70-g09d2 From 8e9f77d93ec4393c52a215e75bcd9a362173e06a Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 24 Jun 2020 16:24:37 -0400 Subject: made persistent link Icon in bottom left of document --- src/client/views/DocumentButtonBar.tsx | 18 +++--- src/client/views/DocumentDecorations.tsx | 2 +- src/client/views/MainView.tsx | 3 + src/client/views/globalCssVariables.scss | 1 + src/client/views/linking/LinkMenu.scss | 4 ++ src/client/views/linking/LinkMenu.tsx | 37 ++++++----- src/client/views/nodes/DocumentLinksButton.scss | 39 +++++++++++ src/client/views/nodes/DocumentLinksButton.tsx | 75 ++++++++++++++++++++++ src/client/views/nodes/DocumentView.tsx | 10 +-- .../views/nodes/formattedText/RichTextMenu.tsx | 7 +- 10 files changed, 160 insertions(+), 36 deletions(-) create mode 100644 src/client/views/nodes/DocumentLinksButton.scss create mode 100644 src/client/views/nodes/DocumentLinksButton.tsx (limited to 'src/client/views/linking') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 62a95116f..208b4d57a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -239,14 +239,14 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV get linkButton() { const view0 = this.view0; const linkCount = view0 && DocListCast(view0.props.Document.links).length; - return !view0 ? (null) :
- }> -
- {linkCount ? linkCount : } + return !view0 || linkCount ? (null) : +
+
+
+ {linkCount ? linkCount : } +
- -
; +
; } @computed @@ -317,9 +317,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV const considerPull = isText && this.considerGoogleDocsPull; const considerPush = isText && this.considerGoogleDocsPush; return
-
- {this.linkButton} -
+ {this.linkButton}
{this.templateButton}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index beb6155ca..d7b0ab7a9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -552,7 +552,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> onPointerDown={this.onRadiusDown} onContextMenu={(e) => e.preventDefault()}>
-
+
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 415f8b364..374fd7421 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -55,6 +55,8 @@ import { TimelineMenu } from './animationtimeline/TimelineMenu'; import { SnappingManager } from '../util/SnappingManager'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { DocumentManager } from '../util/DocumentManager'; +import { DocumentLinksButton } from './nodes/DocumentLinksButton'; +import { LinkMenu } from './linking/LinkMenu'; @observer export class MainView extends React.Component { @@ -578,6 +580,7 @@ export class MainView extends React.Component { {this.mainContent} + {DocumentLinksButton.EditLink ? : (null)} diff --git a/src/client/views/globalCssVariables.scss b/src/client/views/globalCssVariables.scss index 4e85fe0ca..3e54d001b 100644 --- a/src/client/views/globalCssVariables.scss +++ b/src/client/views/globalCssVariables.scss @@ -12,6 +12,7 @@ $lighter-alt-accent: rgb(207, 220, 240); $darker-alt-accent: rgb(178, 206, 248); $intermediate-color: #9c9396; $dark-color: #121721; +$link-color: lightBlue; $antimodemenu-height: 35px; // fonts $sans-serif: "Noto Sans", diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 7dee22f66..c372e7098 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -8,6 +8,10 @@ .linkMenu-list { max-height: 200px; overflow-y: scroll; + position: absolute; + z-index: 10; + background: $link-color; + min-width: 150px } .linkMenu-group { diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx index 56f40ad69..0fcc0f0b9 100644 --- a/src/client/views/linking/LinkMenu.tsx +++ b/src/client/views/linking/LinkMenu.tsx @@ -4,11 +4,12 @@ import { DocumentView } from "../nodes/DocumentView"; import { LinkEditor } from "./LinkEditor"; import './LinkMenu.scss'; import React = require("react"); -import { Doc } from "../../../fields/Doc"; +import { Doc, Opt } from "../../../fields/Doc"; import { LinkManager } from "../../util/LinkManager"; import { LinkMenuGroup } from "./LinkMenuGroup"; import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { library } from "@fortawesome/fontawesome-svg-core"; +import { DocumentLinksButton } from "../nodes/DocumentLinksButton"; library.add(faTrash); @@ -16,16 +17,29 @@ interface Props { docView: DocumentView; changeFlyout: () => void; addDocTab: (document: Doc, where: string) => boolean; + location: number[]; } @observer export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; + @observable private _linkMenuRef: Opt; + @action + onClick = (e: PointerEvent) => { + if (!Array.from(this._linkMenuRef?.getElementsByTagName((e.target as HTMLElement).tagName) || []).includes(e.target as any)) { + DocumentLinksButton.EditLink = undefined; + } + } @action componentDidMount() { this._editingLink = undefined; + document.addEventListener("pointerdown", this.onClick); + } + + componentWillUnmount() { + document.removeEventListener("pointerdown", this.onClick); } clearAllLinks = () => { @@ -56,20 +70,11 @@ export class LinkMenu extends React.Component { render() { const sourceDoc = this.props.docView.props.Document; const groups: Map = LinkManager.Instance.getRelatedGroupedLinks(sourceDoc); - if (this._editingLink === undefined) { - return ( -
- {/* */} - {/* */} -
- {this.renderAllGroups(groups)} -
-
- ); - } else { - return ( - this._editingLink = undefined)}> - ); - } + return
this._linkMenuRef = r} style={{ left: this.props.location[0], top: this.props.location[1] }}> + {!this._editingLink ? + this.renderAllGroups(groups) : + this._editingLink = undefined)} /> + } +
; } } \ No newline at end of file diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss new file mode 100644 index 000000000..b9d5d437b --- /dev/null +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -0,0 +1,39 @@ +@import "../globalCssVariables.scss"; + + +.documentLinksButton-button-empty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.documentLinksButton-button-nonempty:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; +} + +.documentLinksButton-button-empty, +.documentLinksButton-button-nonempty { + height: 20px; + width: 20px; + border-radius: 50%; + opacity: 0.9; + pointer-events: auto; + background-color: $link-color; + color: black; + text-transform: uppercase; + letter-spacing: 2px; + font-size: 75%; + transition: transform 0.2s; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + + &:hover { + background: $main-accent; + transform: scale(1.05); + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx new file mode 100644 index 000000000..4f94b06cb --- /dev/null +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -0,0 +1,75 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { computed, action, runInAction, observable } from "mobx"; +import { observer } from "mobx-react"; +import './DocumentLinksButton.scss'; +import React = require("react"); +import { emptyFunction, setupMoveUpEvents } from "../../../Utils"; +import { DocListCast, Doc } from "../../../fields/Doc"; +import { DocumentView } from "./DocumentView"; +import { LinkMenu } from "../linking/LinkMenu"; +import { UndoManager } from "../../util/UndoManager"; +import { DragManager } from "../../util/DragManager"; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; + +interface DocumentLinksButtonProps { + View: DocumentView; +} +@observer +export class DocumentLinksButton extends React.Component { + private _linkButton = React.createRef(); + + @action + onLinkButtonMoved = (e: PointerEvent) => { + if (this._linkButton.current !== null) { + const linkDrag = UndoManager.StartBatch("Drag Link"); + this.props.View && DragManager.StartLinkDrag(this._linkButton.current, this.props.View.props.Document, e.pageX, e.pageY, { + dragComplete: dropEv => { + const linkDoc = dropEv.linkDragData?.linkDocument as Doc; // equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop + if (this.props.View && linkDoc) { + !linkDoc.linkRelationship && (Doc.GetProto(linkDoc).linkRelationship = "hyperlink"); + + // we want to allow specific views to handle the link creation in their own way (e.g., rich text makes text hyperlinks) + // the dragged view can regiser a linkDropCallback to be notified that the link was made and to update their data structures + // however, the dropped document isn't so accessible. What we do is set the newly created link document on the documentView + // The documentView passes a function prop returning this link doc to its descendants who can react to changes to it. + dropEv.linkDragData?.linkDropCallback?.(dropEv.linkDragData); + runInAction(() => this.props.View._link = linkDoc); + setTimeout(action(() => this.props.View._link = undefined), 0); + } + linkDrag?.end(); + }, + hideSource: false + }); + return true; + } + return false; + } + + onLinkButtonDown = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e) => { + DocumentLinksButton.EditLink = this.props.View; + DocumentLinksButton.EditLinkLoc = [e.clientX, e.clientY]; + })); + } + + @observable + public static EditLink: DocumentView | undefined; + public static EditLinkLoc: number[] = [0, 0]; + + @computed + get linkButton() { + const view0 = this.props.View; + const linkCount = view0 && DocListCast(view0.props.Document.links).length; + return !view0 || !linkCount ? (null) : +
+
+ {linkCount ? linkCount : } +
+
; + } + render() { + return this.linkButton; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7e20b40a3..d9a211e7a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -41,6 +41,7 @@ import "./DocumentView.scss"; import { LinkAnchorBox } from './LinkAnchorBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); +import { DocumentLinksButton } from './DocumentLinksButton'; library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -735,10 +736,8 @@ export class DocumentView extends DocComponent(Docu const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null); templateDoc && optionItems.push({ description: "Open Template ", event: () => this.props.addDocTab(templateDoc, "onRight"), icon: "eye" }); - if (!options) { - options = { description: "Options...", subitems: optionItems, icon: "compass" }; - cm.addItem(options); - } + optionItems.push({ description: "Toggle Show Each Link Dot", event: () => this.layoutDoc.showLinks = !this.layoutDoc.showLinks, icon: "eye" }); + !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; @@ -1037,7 +1036,8 @@ export class DocumentView extends DocComponent(Docu select={this.select} onClick={this.onClickHandler} layoutKey={this.finalLayoutKey} /> - {this.anchors} + {this.layoutDoc.showLinks ? this.anchors : (null)} + {this.props.forcedBackgroundColor?.(this.Document) === "transparent" ? (null) : }
); } diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 3a4363587..839943aac 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -11,7 +11,7 @@ import { EditorState, NodeSelection, TextSelection } from "prosemirror-state"; import { EditorView } from "prosemirror-view"; import { Doc } from "../../../../fields/Doc"; import { DarkPastelSchemaPalette, PastelSchemaPalette } from '../../../../fields/SchemaHeaderField'; -import { Cast, StrCast } from "../../../../fields/Types"; +import { Cast, StrCast, BoolCast } from "../../../../fields/Types"; import { unimplementedFunction, Utils } from "../../../../Utils"; import { DocServer } from "../../../DocServer"; import { LinkManager } from "../../../util/LinkManager"; @@ -72,7 +72,7 @@ export default class RichTextMenu extends AntimodeMenu { super(props); RichTextMenu.Instance = this; this._canFade = false; - this.Pinned = true; + this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]); this.fontSizeOptions = [ { mark: schema.marks.pFontSize.create({ fontSize: 7 }), title: "Set font size", label: "7pt", command: this.changeFontSize }, @@ -147,7 +147,6 @@ export default class RichTextMenu extends AntimodeMenu { this.updateFromDash(view, lastState, this.editorProps); } - public MakeLinkToSelection = (linkDocId: string, title: string, location: string, targetDocId: string): string => { if (this.view) { const link = this.view.state.schema.marks.link.create({ href: Utils.prepend("/doc/" + linkDocId), title: title, location: location, linkId: linkDocId, targetId: targetDocId }); @@ -750,7 +749,7 @@ export default class RichTextMenu extends AntimodeMenu { @action toggleMenuPin = (e: React.MouseEvent) => { - this.Pinned = !this.Pinned; + Doc.UserDoc()["menuRichText-pinned"] = this.Pinned = !this.Pinned; if (!this.Pinned) { this.fadeOut(true); } -- cgit v1.2.3-70-g09d2