From 84f728cffb94319b86be8d6cc478ce424ec45c2f Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 20 Jan 2023 11:17:38 -0500 Subject: removed tour map from lightbox. added option to create anchors without adding thm as annotations. made zoom of text an option for pres and links --- src/client/views/nodes/AudioBox.tsx | 7 ++-- src/client/views/nodes/DocumentLinksButton.tsx | 6 ++-- src/client/views/nodes/DocumentView.tsx | 14 ++++---- src/client/views/nodes/FunctionPlotBox.tsx | 7 ++-- src/client/views/nodes/ImageBox.tsx | 8 ++--- src/client/views/nodes/LabelBox.tsx | 4 +-- src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/nodes/MapBox/MapBox.tsx | 5 +-- src/client/views/nodes/PDFBox.tsx | 8 +++-- src/client/views/nodes/ScreenshotBox.tsx | 4 +-- src/client/views/nodes/VideoBox.tsx | 11 +++--- src/client/views/nodes/WebBox.tsx | 6 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 39 +++++++++++----------- .../views/nodes/formattedText/RichTextRules.ts | 2 +- src/client/views/nodes/trails/PresBox.tsx | 13 +++++--- 15 files changed, 73 insertions(+), 63 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index d95668c89..1d59d3356 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -136,7 +136,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean) => { return ( CollectionStackedTimeline.createAnchor( this.rootDoc, @@ -144,7 +144,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { updateIcon?: () => void; // updates the icon representation of the document - getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) + getAnchor?: (addAsAnnotation: boolean) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) scrollFocus?: (doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document @@ -783,9 +783,9 @@ export class DocumentViewInternal extends DocComponent, zoom?: boolean, setPushpin?: boolean): void => { + toggleFollowLink = (location: Opt, zoom?: boolean, setTargetToggle?: boolean): void => { this.Document.ignoreClick = false; - if (setPushpin) { + if (setTargetToggle) { this.Document.followLinkToggle = !this.Document.followLinkToggle; this.Document._isLinkButton = this.Document.followLinkToggle || this.Document._isLinkButton; } else { @@ -854,7 +854,7 @@ export class DocumentViewInternal extends DocComponent this.toggleFollowLink('inPlace', false, 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.followLinkToggle ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' }); + onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', 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) { @@ -1091,7 +1091,7 @@ export class DocumentViewInternal extends DocComponent this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); onClickFunc = () => this.onClickHandler; setHeight = (height: number) => (this.layoutDoc._height = height); - setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); + setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); isContentActive = (outsideReaction?: boolean) => { return this.props.isContentActive() === false ? false @@ -1264,7 +1264,7 @@ export class DocumentViewInternal extends DocComponent { const self = this; const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null); - const anno = audioAnnos.lastElement(); + const anno = audioAnnos?.lastElement(); if (anno instanceof AudioField && this.audioAnnoState === 'stopped') { new Howl({ src: [anno.url.href], diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 24562ccbd..5c0005dae 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -12,7 +12,7 @@ import { TraceMobx } from '../../../fields/util'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; -import { ViewBoxBaseComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { DocFocusOptions } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; @@ -22,7 +22,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch const EquationDocument = makeInterface(EquationSchema, documentSchema); @observer -export class FunctionPlotBox extends ViewBoxBaseComponent() { +export class FunctionPlotBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); } @@ -41,10 +41,11 @@ export class FunctionPlotBox extends ViewBoxBaseComponent() { () => this.createGraph() ); } - getAnchor = () => { + getAnchor = (addAsAnnotation: boolean) => { const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc }); anchor.xRange = new List(Array.from(this._plot.options.xAxis.domain)); anchor.yRange = new List(Array.from(this._plot.options.yAxis.domain)); + if (addAsAnnotation) this.addDocument(anchor); return anchor; }; @action diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index ac953d13b..e2ecca0b6 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -52,7 +52,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent) => Opt = () => undefined; + private _getAnchor: (savedAnnotations: Opt>, addAsAnnotation: boolean) => Opt = () => undefined; @observable _curSuffix = ''; @observable _uploadIcon = uploadIcons.idle; @@ -86,13 +86,13 @@ export class ImageBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean) => { const anchor = - this._getAnchor?.(this._savedAnnotations) ?? // use marquee anchor, otherwise, save zoom/pan as anchor + this._getAnchor?.(this._savedAnnotations, false) ?? // use marquee anchor, otherwise, save zoom/pan as anchor Docs.Create.ImageanchorDocument({ presTransition: 1000, unrendered: true, annotationOn: this.rootDoc }); if (anchor) { PresBox.pinDocView(anchor, { pinData: { pannable: true, dataview: true, dataannos: true } }, this.rootDoc); - this.addDocument(anchor); + addAsAnnotation && this.addDocument(anchor); return anchor; } return this.rootDoc; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 10897b48f..a2143f629 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -36,9 +36,7 @@ export class LabelBox extends ViewBoxBaseComponent { - return this.rootDoc; - }; + getAnchor = (addAsAnnotation: boolean) => this.rootDoc; getTitle() { return this.rootDoc['title-custom'] ? StrCast(this.rootDoc.title) : this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === 'string' ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 232c3459e..7eb42994b 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -113,7 +113,7 @@ export class LinkDocPreview extends React.Component { this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } this._toolTipText = ''; - if (LinkDocPreview.LinkInfo?.noPreview || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); + if (LinkDocPreview.LinkInfo?.noPreview || this._linkSrc?.followLinkToggle || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); } }) ); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index c8d5b0154..95cb49037 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -522,10 +522,7 @@ export class MapBox extends ViewBoxAnnotatableComponent { - const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? this.rootDoc; - return anchor; - }; + getAnchor = (addAsAnnotation: boolean) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc; /** * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index e22ee5021..71ef9bc84 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -212,7 +212,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean) => { let ele: Opt = undefined; if (this._pdfViewer?.selectionContent()) { ele = document.createElement('div'); @@ -226,10 +226,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean) => { const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3) || this.rootDoc; + return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3, undefined, addAsAnnotation) || this.rootDoc; }; videoLoad = () => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index a8f78edd5..1dfa55c64 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -385,15 +385,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true)); }; - getAnchor = () => { + getAnchor = (addAsAnnotation: boolean) => { const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null); - const marquee = AnchorMenu.Instance.GetAnchor?.(); - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc; + const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation); + return ( + CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee, addAsAnnotation) || + this.rootDoc + ); }; // sets video info on load diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8be4884ce..acf4fe4b0 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -307,7 +307,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { + getAnchor = (addAsAnnotation: boolean) => { let ele: Opt = undefined; try { const contents = this._iframe?.contentWindow?.getSelection()?.getRangeAt(0).cloneContents(); @@ -317,7 +317,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', false); + getAnchor = (addAsAnnotation: boolean) => this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation); @action setupAnchorMenu = () => { @@ -239,23 +239,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { !this.layoutDoc.showSidebar && this.toggleSidebar(); - setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, 'add:right', undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created + setTimeout(() => this._sidebarRef.current?.anchorMenuClick(this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true))); // give time for sidebarRef to be created }; AnchorMenu.Instance.OnAudio = (e: PointerEvent) => { !this.layoutDoc.showSidebar && this.toggleSidebar(); - const anchor = this.getAnchor(); - const target = this._sidebarRef.current?.anchorMenuClick(anchor); - if (target) { - anchor.followLinkAudio = true; - DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target)); - target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`); - } + const anchor = this.makeLinkAnchor(undefined, OpenWhere.addRight, undefined, 'Anchored Selection', true, true); + setTimeout(() => { + const target = this._sidebarRef.current?.anchorMenuClick(anchor); + if (target) { + anchor.followLinkAudio = true; + DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target)); + target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`); + } + }); }; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch); return undefined; }); - AnchorMenu.Instance.onMakeAnchor = this.getAnchor; + AnchorMenu.Instance.onMakeAnchor = () => this.getAnchor(true); AnchorMenu.Instance.StartCropDrag = unimplementedFunction; /** * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation. @@ -270,7 +272,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.getAnchor(true), targetCreator), e.pageX, e.pageY); }); const coordsB = this._editorView!.coordsAtPos(this._editorView!.state.selection.to); this.props.isSelected(true) && AnchorMenu.Instance.jumpTo(coordsB.left, coordsB.bottom); @@ -518,7 +520,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = this.getAnchor; + if (de.complete.annoDragData) de.complete.annoDragData.dropDocCreator = () => this.getAnchor(true); const dragData = de.complete.docDragData; if (dragData) { const draggedDoc = dragData.draggedDocuments.length && dragData.draggedDocuments[0]; @@ -689,9 +691,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.props.pinToPres(anchor, {}); @undoBatch - makePushpin = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle); + makeTargetToggle = (anchor: Doc) => (anchor.followLinkToggle = !anchor.followLinkToggle); - isPushpin = (anchor: Doc) => BoolCast(anchor.followLinkToggle); + isTargetToggler = (anchor: Doc) => BoolCast(anchor.followLinkToggle); specificContextMenu = (e: React.MouseEvent): void => { const cm = ContextMenu.Instance; @@ -714,8 +716,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this.deleteAnnotation(anchor as Doc); AnchorMenu.Instance.Pinned = false; AnchorMenu.Instance.PinToPres = () => this.pinToPres(anchor as Doc); - AnchorMenu.Instance.MakePushpin = () => this.makePushpin(anchor as Doc); - AnchorMenu.Instance.IsPushpin = () => this.isPushpin(anchor as Doc); + AnchorMenu.Instance.MakeTargetToggle = () => this.makeTargetToggle(anchor as Doc); + AnchorMenu.Instance.IsTargetToggler = () => this.isTargetToggler(anchor as Doc); AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true); }) ); @@ -891,7 +893,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { const allAnchors = [{ href, title, anchorId: anchor[Id] }]; @@ -1476,7 +1478,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true'); - if (target) return; } if (e.button === 0 && this.props.isSelected(true) && !e.altKey) { diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index e3c67ad2e..5675776fb 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -259,7 +259,7 @@ export class RichTextRules { this.TextBox.EditorView?.dispatch(rstate.tr.setSelection(new TextSelection(rstate.doc.resolve(start), rstate.doc.resolve(end - 3)))); } const target = (docx instanceof Doc && docx) || Docs.Create.FreeformDocument([], { title: docid, _width: 500, _height: 500 }, docid); - DocUtils.MakeLink({ doc: this.TextBox.getAnchor() }, { doc: target }, 'portal to:portal from', undefined); + DocUtils.MakeLink({ doc: this.TextBox.getAnchor(true) }, { doc: target }, 'portal to:portal from', undefined); const fstate = this.TextBox.EditorView?.state; if (fstate && selection) { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 855a7f171..226fd8d7d 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -104,7 +104,7 @@ export class PresBox extends ViewBoxBaseComponent() { return NumCast(this.rootDoc._itemIndex); } @computed get activeItem() { - return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null); + return DocCast(this.childDocs[NumCast(this.rootDoc._itemIndex)]); } @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); @@ -452,7 +452,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presWidth = NumCast(targetDoc.width); pinDoc.presHeight = NumCast(targetDoc.height); } - if (pinProps.pinAudioPlay) pinDoc.followLinkAudio = true; + if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true; if (pinProps.pinData) { pinDoc.presPinData = pinProps.pinData.scrollable || @@ -577,7 +577,8 @@ export class PresBox extends ViewBoxBaseComponent() { noSelect: true, originatingDoc: activeItem, easeFunc: StrCast(activeItem.presEaseFunc, 'ease') as any, - zoomTextSelections: true, + zoomTextSelections: BoolCast(activeItem.presZoomText), + playAudio: BoolCast(activeItem.presPlayAudio), }; if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined; var containerDocContext = srcContext ? [srcContext] : []; @@ -1416,7 +1417,11 @@ export class PresBox extends ViewBoxBaseComponent() { Effects
Play Audio Annotation
- (activeItem.followLinkAudio = !BoolCast(activeItem.followLinkAudio))} checked={BoolCast(activeItem.followLinkAudio)} /> + (activeItem.presPlayAudio = !BoolCast(activeItem.presPlayAudio))} checked={BoolCast(activeItem.presPlayAudio)} /> +
+
+
Zoom Text Selections
+ (activeItem.presZoomText = !BoolCast(activeItem.presZoomText))} checked={BoolCast(activeItem.presZoomText)} />