diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 39 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 63 | ||||
-rw-r--r-- | src/client/views/pdf/Annotation.tsx | 11 | ||||
-rw-r--r-- | src/client/views/pdf/PDFMenu.tsx | 89 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 2 |
6 files changed, 114 insertions, 92 deletions
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 97b8da8da..dd754e0fe 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -282,10 +282,11 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" }); subItems.push({ description: "lightbox", event: action(() => this._isLightboxOpen = true), icon: "eye" }); - if (!Doc.IsSystem(this.props.Document)) { + if (!Doc.IsSystem(this.props.Document) && !this.props.Document.annotationOn) { const existingVm = ContextMenu.Instance.findByDescription(category); const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : []; catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" }); + catItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) }); !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" }); } } @@ -313,25 +314,25 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); - const existingOnClick = cm.findByDescription("OnClick..."); - const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; - const funcs = [ - { key: "onChildClick", name: "On Child Clicked" }, - { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; - funcs.map(func => onClicks.push({ - description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { - const alias = Doc.MakeAlias(this.props.Document); - DocUtils.makeCustomViewClicked(alias, undefined, func.key); - this.props.addDocTab(alias, "add:right"); - } - })); - DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick => - onClicks.push({ - description: `Set child ${childClick.title}`, - icon: "edit", - event: () => Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), + if (!Doc.UserDoc().noviceMode && !this.props.Document.annotationOn) { + const existingOnClick = cm.findByDescription("OnClick..."); + const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; + const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }]; + funcs.map(func => onClicks.push({ + description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => { + const alias = Doc.MakeAlias(this.props.Document); + DocUtils.makeCustomViewClicked(alias, undefined, func.key); + this.props.addDocTab(alias, "add:right"); + } })); - !Doc.IsSystem(this.props.Document) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); + DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick => + onClicks.push({ + description: `Set child ${childClick.title}`, + icon: "edit", + event: () => Doc.GetProto(this.props.Document)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)), + })); + !Doc.IsSystem(this.props.Document) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); + } if (!Doc.UserDoc().noviceMode) { const more = cm.findByDescription("More..."); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ef58afeb5..37d2ca303 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1228,7 +1228,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P private thumbIdentifier?: number; onContextMenu = (e: React.MouseEvent) => { - if (this.props.annotationsKey || !ContextMenu.Instance) return; + if (this.props.annotationsKey || this.props.Document.annotationOn || !ContextMenu.Instance) return; const appearance = ContextMenu.Instance.findByDescription("Appearance..."); const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : []; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 15e571839..5aabebf55 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -619,6 +619,27 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu this.Document.onClick = this.layoutDoc.onClick = undefined; } } + @undoBatch @action + toggleTargetOnClick = (): void => { + this.Document.ignoreClick = false; + this.Document.isLinkButton = true; + this.Document.isPushpin = true; + } + @undoBatch @action + followLinkOnClick = (location: Opt<string>, zoom: boolean,): void => { + this.Document.ignoreClick = false; + this.Document.isLinkButton = true; + this.Document.isPushpin = false; + this.Document.followLinkZoom = zoom; + this.Document.followLinkLocation = location; + } + @undoBatch @action + selectOnClick = (): void => { + this.Document.ignoreClick = false; + this.Document.isLinkButton = false; + this.Document.isPushpin = false; + this.Document.onClick = this.layoutDoc.onClick = undefined; + } @undoBatch @@ -767,25 +788,32 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" }); if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) { - const options = cm.findByDescription("Options..."); - const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; - optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" }); - optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" }); - optionItems.push({ description: this.rootDoc["_isBackground-canClick"] ? "Unlock from Back" : "Lock in Back", event: this.toggleLockInBack, icon: "expand-arrows-alt" }); - !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); - !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); - const existingOnClick = cm.findByDescription("OnClick..."); const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : []; - onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); - onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); - onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); - onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" }); - !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" }); - onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" }); - onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" }); - onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" }); - !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, addDivider: true, subitems: onClicks, icon: "mouse-pointer" }); + if (!this.Document.annotationOn) { + const options = cm.findByDescription("Options..."); + const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; + optionItems.push({ description: "Bring to Front", event: () => this.props.bringToFront(this.rootDoc, false), icon: "expand-arrows-alt" }); + optionItems.push({ description: "Send to Back", event: () => this.props.bringToFront(this.rootDoc, true), icon: "expand-arrows-alt" }); + optionItems.push({ description: this.rootDoc["_isBackground-canClick"] ? "Unlock from Back" : "Lock in Back", event: this.toggleLockInBack, icon: "expand-arrows-alt" }); + !this.props.treeViewDoc && this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && optionItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); + !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); + + onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" }); + onClicks.push({ description: "Toggle Detail", event: () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(self, "${this.Document.layoutKey}")`), icon: "concierge-bell" }); + onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" }); + onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" }); + !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" }); + onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" }); + onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" }); + onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" }); + !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" }); + } else if (DocListCast(this.Document.links).length) { + onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" }); + onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" }); + onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" }); + !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" }); + } } const funcs: ContextMenuProps[] = []; @@ -799,7 +827,6 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu const more = cm.findByDescription("More..."); const moreItems = more && "subitems" in more ? more.subitems : []; if (!Doc.IsSystem(this.rootDoc)) { - moreItems.push({ description: "Download document", icon: "download", event: async () => Doc.Zip(this.props.Document) }); moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this), icon: "users" }); //moreItems.push({ description: this.Document.lockedPosition ? "Unlock Position" : "Lock Position", event: this.toggleLockPosition, icon: BoolCast(this.Document.lockedPosition) ? "unlock" : "lock" }); if (!Doc.UserDoc().noviceMode) { diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 5ec564e7b..78438e7e3 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -8,6 +8,7 @@ import { Cast, FieldValue, NumCast, StrCast, PromiseValue } from "../../../field import { DocumentManager } from "../../util/DocumentManager"; import { PDFMenu } from "./PDFMenu"; import "./Annotation.scss"; +import { undoBatch } from "../../util/UndoManager"; interface IAnnotationProps { anno: Doc; @@ -86,6 +87,14 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> { group && this.props.pinToPres(group, isPinned); } + @undoBatch + makePushpin = action(() => { + const group = Cast(this.props.document.group, Doc, null); + group.isPushpin = !group.isPushpin; + }) + + isPushpin = () => Cast(this.props.document.group, Doc, null).isPushpin; + @action onPointerDown = (e: React.PointerEvent) => { if (e.button === 2 || e.ctrlKey) { @@ -94,6 +103,8 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> { PDFMenu.Instance.Pinned = false; PDFMenu.Instance.AddTag = this.addTag.bind(this); PDFMenu.Instance.PinToPres = this.pinToPres; + PDFMenu.Instance.MakePushpin = this.makePushpin; + PDFMenu.Instance.IsPushpin = this.isPushpin; PDFMenu.Instance.jumpTo(e.clientX, e.clientY, true); e.stopPropagation(); } diff --git a/src/client/views/pdf/PDFMenu.tsx b/src/client/views/pdf/PDFMenu.tsx index c5ed75980..1a5b14e07 100644 --- a/src/client/views/pdf/PDFMenu.tsx +++ b/src/client/views/pdf/PDFMenu.tsx @@ -1,15 +1,15 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { ColorState } from "react-color"; import { Doc, Opt } from "../../../fields/Doc"; -import { returnFalse, unimplementedFunction, Utils } from "../../../Utils"; +import { returnFalse, setupMoveUpEvents, unimplementedFunction, Utils } from "../../../Utils"; import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { ButtonDropdown } from "../nodes/formattedText/RichTextMenu"; import "./PDFMenu.scss"; - @observer export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { static Instance: PDFMenu; @@ -46,6 +46,8 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { public Delete: () => void = unimplementedFunction; public AddTag: (key: string, value: string) => boolean = returnFalse; public PinToPres: () => void = unimplementedFunction; + public MakePushpin: () => void = unimplementedFunction; + public IsPushpin: () => boolean = returnFalse; public Marquee: { left: number; top: number; width: number; height: number; } | undefined; public get Active() { return this._left > 0; } @@ -57,31 +59,10 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { } pointerDown = (e: React.PointerEvent) => { - document.removeEventListener("pointermove", this.pointerMove); - document.addEventListener("pointermove", this.pointerMove); - document.removeEventListener("pointerup", this.pointerUp); - document.addEventListener("pointerup", this.pointerUp); - - e.stopPropagation(); - e.preventDefault(); - } - - pointerMove = (e: PointerEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (!this._dragging) { + setupMoveUpEvents(this, e, (e: PointerEvent) => { this.StartDrag(e, this._commentCont.current!); - this._dragging = true; - } - } - - pointerUp = (e: PointerEvent) => { - this._dragging = false; - document.removeEventListener("pointermove", this.pointerMove); - document.removeEventListener("pointerup", this.pointerUp); - e.stopPropagation(); - e.preventDefault(); + return true; + }, returnFalse, returnFalse); } @action @@ -93,7 +74,7 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { @computed get highlighter() { const button = - <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onPointerDown={this.highlightClicked}> + <button className="antimodeMenu-button color-preview-button" title="" key="highlighter-button" onClick={this.highlightClicked}> <FontAwesomeIcon icon="highlighter" size="lg" style={{ transition: "transform 0.1s", transform: this.Highlighting ? "" : "rotate(-45deg)" }} /> <div className="color-preview" style={{ backgroundColor: this.highlightColor }}></div> </button>; @@ -112,12 +93,13 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { </div> </div>; return ( - <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} /> + <Tooltip key="highlighter" title={<div className="dash-tooltip">{"Click to Highlight"}</div>}> + <ButtonDropdown key={"highlighter"} button={button} dropdownContent={dropdownContent} pdf={true} /> + </Tooltip> ); } - @action - changeHighlightColor = (color: string, e: React.PointerEvent) => { + @action changeHighlightColor = (color: string, e: React.PointerEvent) => { const col: ColorState = { hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", @@ -127,25 +109,11 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { this.highlightColor = Utils.colorString(col); } - deleteClicked = (e: React.PointerEvent) => { - this.Delete(); - } - - @action - keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => { - this._keyValue = e.currentTarget.value; - } - - @action - valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => { - this._valueValue = e.currentTarget.value; - } - - @action - addTag = (e: React.PointerEvent) => { + @action keyChanged = (e: React.ChangeEvent<HTMLInputElement>) => { this._keyValue = e.currentTarget.value; } + @action valueChanged = (e: React.ChangeEvent<HTMLInputElement>) => { this._valueValue = e.currentTarget.value; } + @action addTag = (e: React.PointerEvent) => { if (this._keyValue.length > 0 && this._valueValue.length > 0) { this._added = this.AddTag(this._keyValue, this._valueValue); - setTimeout(action(() => this._added = false), 1000); } } @@ -154,13 +122,28 @@ export class PDFMenu extends AntimodeMenu<AntimodeMenuProps> { const buttons = this.Status === "pdf" ? [ this.highlighter, - <button key="2" className="antimodeMenu-button annotate" title="Drag to Annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: "grab" }}> - <FontAwesomeIcon icon="comment-alt" size="lg" /></button>, + + <Tooltip key="annotate" title={<div className="dash-tooltip">{"Drag to Place Annotation"}</div>}> + <button className="antimodeMenu-button annotate" ref={this._commentCont} onPointerDown={this.pointerDown} style={{ cursor: "grab" }}> + <FontAwesomeIcon icon="comment-alt" size="lg" /> + </button> + </Tooltip>, ] : [ - <button key="5" className="antimodeMenu-button" title="Delete Anchor" onPointerDown={this.deleteClicked}> - <FontAwesomeIcon icon="trash-alt" size="lg" /></button>, - <button key="6" className="antimodeMenu-button" title="Pin to Presentation" onPointerDown={this.PinToPres}> - <FontAwesomeIcon icon="map-pin" size="lg" /></button>, + <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}> + <button className="antimodeMenu-button" onPointerDown={this.Delete}> + <FontAwesomeIcon icon="trash-alt" size="lg" /> + </button> + </Tooltip>, + <Tooltip key="Pin" title={<div className="dash-tooltip">{"Pin to Presentation"}</div>}> + <button className="antimodeMenu-button" onPointerDown={this.PinToPres}> + <FontAwesomeIcon icon="map-pin" size="lg" /> + </button> + </Tooltip>, + <Tooltip key="pushpin" title={<div className="dash-tooltip">{"toggle pushpin behavior"}</div>}> + <button className="antimodeMenu-button" style={{ color: this.IsPushpin() ? "black" : "white", backgroundColor: this.IsPushpin() ? "white" : "black" }} onPointerDown={this.MakePushpin}> + <FontAwesomeIcon icon="thumbtack" size="lg" /> + </button> + </Tooltip>, // <div key="7" className="pdfMenu-addTag" > // <input onChange={this.keyChanged} placeholder="Key" style={{ gridColumn: 1 }} /> // <input onChange={this.valueChanged} placeholder="Value" style={{ gridColumn: 3 }} /> diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 79bd190c5..45a862954 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -697,7 +697,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent<IViewerProps, PdfDocu panelHeight = () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : (this.Document._nativeWidth || 0); @computed get overlayLayer() { return <div className={`pdfViewerDash-overlay${Doc.GetSelectedTool() !== InkTool.None || SnappingManager.GetIsDragging() ? "-inking" : ""}`} id="overlay" - style={{ transform: `scale(${this._zoomed})` }}> + style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined, transform: `scale(${this._zoomed})` }}> <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} LibraryPath={this.props.ContainingCollectionView?.props.LibraryPath ?? emptyPath} annotationsKey={this.annotationKey} |