From ea4588185f165f66a4d596d51a8a8ad955985762 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 22 Feb 2023 15:58:20 -0500 Subject: fixed pres hide before/after, select w/o nav, and updating contents.. made collections fitWidth by default so that they resize like a normal window. Made freeform pan/zoom animate when changing keyframes. allow nesting of keyframe collections.. made presEffects work even if there's no transition movement. made nested collection timelines follow parent by default, but with ability to change ordering. fixed treeView incremental render. treat doc anchors are preselements instead of delegating. don't highlight ink when dragging ctrl pts. --- src/client/views/PropertiesView.tsx | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index a2bc37095..203b42cbc 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1814,12 +1814,7 @@ export class PropertiesView extends React.Component { {!selectedItem ? null : (
-
{ - this.openPresTransitions = !this.openPresTransitions; - })} - style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}> +
(this.openPresTransitions = !this.openPresTransitions))} style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>     Transitions
@@ -1830,12 +1825,7 @@ export class PropertiesView extends React.Component { )} {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
-
{ - this.openSlideOptions = !this.openSlideOptions; - })} - style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}> +
(this.openSlideOptions = !this.openSlideOptions))} style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>     {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
-- cgit v1.2.3-70-g09d2 From 70c34720d2d3ecbbcbf44a7cb6504ee132f4d12a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Feb 2023 09:44:45 -0500 Subject: fix so that link menu will populate correctly in properties view. --- src/client/views/PropertiesDocBacklinksSelector.tsx | 17 +---------------- src/client/views/PropertiesView.tsx | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index f7173a593..00c3400cb 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -1,7 +1,6 @@ -import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../fields/Doc'; +import { Doc } from '../../fields/Doc'; import { Cast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; @@ -19,20 +18,6 @@ type PropertiesDocBacklinksSelectorProps = { @observer export class PropertiesDocBacklinksSelector extends React.Component { - @computed get _docs() { - const linkSource = this.props.Document; - const links = DocListCast(linkSource.links); - const collectedLinks = [] as Doc[]; - links.map(link => { - const other = LinkManager.getOppositeAnchor(link, linkSource); - 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); - } - }); - return collectedLinks; - } - getOnClick = (link: Doc) => { const linkSource = this.props.Document; const other = LinkManager.getOppositeAnchor(link, linkSource); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 203b42cbc..355038556 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -306,8 +306,8 @@ export class PropertiesView extends React.Component { } @computed get links() { - const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor; - return !selAnchor ? null : ; + const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc; + return ; } @computed get layoutPreview() { -- cgit v1.2.3-70-g09d2 From dae946507efa91a7216e64a22f8a25b411a16c59 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 23 Feb 2023 10:15:39 -0500 Subject: fixed display of link lines --- src/client/views/PropertiesView.tsx | 2 +- src/client/views/collections/TabDocView.scss | 6 +++++- src/client/views/collections/TabDocView.tsx | 4 +--- .../collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 355038556..f7708d801 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -307,7 +307,7 @@ export class PropertiesView extends React.Component { @computed get links() { const selAnchor = this.selectedDocumentView?.anchorViewDoc ?? LinkManager.currentLinkAnchor ?? this.selectedDoc; - return ; + return !selAnchor ? null : ; } @computed get layoutPreview() { diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss index 0d045bada..58605c3f4 100644 --- a/src/client/views/collections/TabDocView.scss +++ b/src/client/views/collections/TabDocView.scss @@ -1,5 +1,9 @@ -@import "../global/globalCssVariables.scss"; +@import '../global/globalCssVariables.scss'; +.tabDocView-content { + height: 100%; + width: 100%; +} input.lm_title:focus, input.lm_title { diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 8d3c8fe2f..f93810deb 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -462,11 +462,9 @@ export class TabDocView extends React.Component { render() { return (
{ if ((this._mainCont = ref)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 9811c239b..7f1e15c2f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -136,6 +136,7 @@ export class CollectionFreeFormLinkView extends React.Component Date: Tue, 28 Feb 2023 20:57:01 -0500 Subject: cleaning up of following inPlace links to overlay current inplaceContainer contents instead of deleting them. --- src/client/util/LinkFollower.ts | 8 ++--- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/LightboxView.tsx | 11 ++---- src/client/views/PropertiesButtons.tsx | 39 ++++++++++------------ src/client/views/PropertiesView.tsx | 6 ++-- src/client/views/collections/CollectionSubView.tsx | 1 + src/client/views/collections/TabDocView.tsx | 12 ++++++- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++- src/client/views/nodes/DocumentView.tsx | 9 ++--- src/client/views/nodes/PDFBox.tsx | 10 +++++- src/client/views/nodes/WebBox.tsx | 9 ++++- .../views/nodes/formattedText/FormattedTextBox.tsx | 8 +++++ src/client/views/pdf/PDFViewer.tsx | 2 +- 13 files changed, 72 insertions(+), 49 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index bbfd516da..50bc89ed2 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -1,11 +1,11 @@ import { action, runInAction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { List } from '../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; -import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { CollectionDockingView } from '../views/collections/CollectionDockingView'; +import { DocumentType } from '../documents/DocumentTypes'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; import { LinkManager } from './LinkManager'; @@ -35,7 +35,7 @@ export class LinkFollower { const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => { const createTabForTarget = (didFocus: boolean) => new Promise(res => { - const where = LightboxView.LightboxDoc ? OpenWhere.inPlace : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); + const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); docViewProps.addDocTab(doc, where); setTimeout(() => { const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise. diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 20c35739e..af868ba9c 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -243,7 +243,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}> + onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false)))}>
{followBtn( true, diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index e531bf71c..c9b1dcfd8 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -145,13 +145,6 @@ export class LightboxView extends React.Component { } } public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => { - if (location !== OpenWhere.lightbox) { - const inPlaceView = DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; - if (inPlaceView) { - inPlaceView.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List([doc]); - return true; - } - } SelectionManager.DeselectAll(); return LightboxView.SetLightboxDoc( doc, @@ -318,7 +311,7 @@ export class LightboxView extends React.Component {
{ e.stopPropagation(); LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; @@ -327,7 +320,7 @@ export class LightboxView extends React.Component {
{ e.stopPropagation(); CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index a152b4948..7e33ee495 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -6,6 +6,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; +import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { DocUtils } from '../documents/Documents'; @@ -129,27 +130,21 @@ export class PropertiesButtons extends React.Component<{}, {}> { onClick => { SelectionManager.Views().forEach(dv => { const containerDoc = dv.rootDoc; - containerDoc.followAllLinks = - containerDoc.noShadow = - containerDoc.noHighlighting = - containerDoc._isLinkButton = - containerDoc._fitContentsToBox = - containerDoc._forceActive = - containerDoc._isInPlaceContainer = - !containerDoc._isInPlaceContainer; - containerDoc.followLinkLocation = containerDoc._isInPlaceContainer ? OpenWhere.inPlace : undefined; + //containerDoc.followAllLinks = + // containerDoc.noShadow = + // containerDoc.noHighlighting = + // containerDoc._forceActive = + containerDoc._fitContentsToBox = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer; containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined; - const menuDoc = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]).lastElement(); - if (menuDoc) { - menuDoc.hideDecorations = menuDoc._forceActive = menuDoc._fitContentsToBox = menuDoc._isLinkButton = menuDoc._noShadow = menuDoc.noHighlighting = containerDoc._isInPlaceContainer; - if (!dv.allLinks.find(link => link.anchor1 === menuDoc || link.anchor2 === menuDoc)) { - DocUtils.MakeLink({ doc: dv.rootDoc }, { doc: menuDoc }, 'back link to container'); - } - DocListCast(menuDoc[Doc.LayoutFieldKey(menuDoc)]).forEach(menuItem => { - menuItem.followLinkAudio = menuItem.followAllLinks = menuItem._isLinkButton = true; - menuItem._followLinkLocation = OpenWhere.inPlace; + const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); + dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); + containerContents.forEach(doc => { + DocListCast(doc.links).forEach(link => { + doc.isLinkButton = true; + //doc.followLinkLocation = OpenWhere.inPlace; + link.linkDisplay = false; }); - } + }); }); } ); @@ -309,10 +304,12 @@ export class PropertiesButtons extends React.Component<{}, {}> { docView.setToggleDetail(); break; case 'linkInPlace': - docView.toggleFollowLink('inPlace', false, false); + docView.toggleFollowLink(false, false); + docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.inPlace : undefined; break; case 'linkOnRight': - docView.toggleFollowLink('add:right', false, false); + docView.toggleFollowLink(false, false); + docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.addRight : undefined; break; } }); diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index f7708d801..e9d1433c3 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1406,7 +1406,7 @@ export class PropertiesView extends React.Component { }); @undoBatch - changeFollowBehavior = action((follow: string) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow)); + changeFollowBehavior = action((follow: Opt) => this.sourceAnchor && (this.sourceAnchor.followLinkLocation = follow)); @undoBatch changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior)); @@ -1615,8 +1615,8 @@ export class PropertiesView extends React.Component {

Follow by

- this.changeFollowBehavior(e.currentTarget.value === 'Default' ? undefined : e.currentTarget.value)} value={Cast(this.sourceAnchor?.followLinkLocation, 'string', null)}> + diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 27ae3041f..c88ae314e 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -67,6 +67,7 @@ export function CollectionSubView(moreProps?: X) { // to its children which may be templates. // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' @computed get dataField() { + if (this.layoutDoc[this.props.fieldKey]) return this.layoutDoc[this.props.fieldKey]; // sets the dataDoc's data field to an empty list if the data field is undefined - prevents issues with addonly // setTimeout changes it outside of the @computed section !this.dataDoc[this.props.fieldKey] && setTimeout(() => !this.dataDoc[this.props.fieldKey] && (this.dataDoc[this.props.fieldKey] = new List())); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index b75f315ca..01872df84 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -358,7 +358,17 @@ export class TabDocView extends React.Component { if (doc.dockingConfig) return DashboardView.openDashboard(doc); // prettier-ignore switch (whereFields[0]) { - case OpenWhere.inPlace: // fall through to lightbox + case undefined: + case OpenWhere.inPlace: { + if (this.layoutDoc?.isInPlaceContainer) { + const inPlaceView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; + const data = inPlaceView?.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)]; + if (inPlaceView && (!data || data instanceof List)) { + inPlaceView.layoutDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List([doc]); + return true; + } + } + } case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0477c6a16..78804b070 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1395,6 +1395,7 @@ export class CollectionFreeFormView extends CollectionSubView { + if (this.props.isAnnotationOverlay) return this.props.addDocTab(doc, where); switch (where) { case OpenWhere.inParent: return this.props.addDocument?.(doc) || false; @@ -1412,9 +1413,10 @@ export class CollectionFreeFormView extends CollectionSubView(doc instanceof Doc ? [doc] : doc); + this.layoutDoc[this.props.fieldKey] = new List(doc instanceof Doc ? [doc] : doc); return true; } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 039a75a1b..beb0f8e3a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -792,7 +792,7 @@ export class DocumentViewInternal extends DocComponent, zoom?: boolean, setTargetToggle?: boolean): void => { + toggleFollowLink = (zoom?: boolean, setTargetToggle?: boolean): void => { this.Document.ignoreClick = false; if (setTargetToggle) { this.Document.followLinkToggle = !this.Document.followLinkToggle; @@ -802,7 +802,6 @@ export class DocumentViewInternal extends DocComponent (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', 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') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' }); + onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(false, false), icon: 'link' }); + onClicks.push({ description: (this.Document.followLinkToggle ? 'Remove' : 'Make') + ' Target Visibility Toggle', event: () => this.toggleFollowLink(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) { diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8d7ab2f0d..6b2f2246a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -22,7 +22,7 @@ import { LightboxView } from '../LightboxView'; import { CreateImage } from '../nodes/WebBoxRenderer'; import { PDFViewer } from '../pdf/PDFViewer'; import { SidebarAnnos } from '../SidebarAnnos'; -import { DocFocusOptions, DocumentView } from './DocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './PDFBox.scss'; @@ -200,6 +200,13 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._pdfViewer?.brushView(view); + sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { + if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(false); + return true; + } + return this.props.addDocTab(doc, where); + }; scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { let didToggle = false; if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { @@ -487,6 +494,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { + if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(false); + return true; + } + return this.props.addDocTab(doc, where); + }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { let ele: Opt = undefined; try { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 123499647..dc7a11e6e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -230,6 +230,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { + this.toggleSidebar(false); + return true; + } + return this.props.addDocTab(doc, where); + }; + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { if (!pinProps) return this.rootDoc; const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 9877690f8..ffc49e4f3 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -16,7 +16,7 @@ import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; -import { DocFocusOptions, DocumentViewProps } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentViewProps, OpenWhere } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import { StyleProp } from '../StyleProvider'; -- cgit v1.2.3-70-g09d2 From 9c63f3833f15cc995c2255b83923384686127f3e Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Mar 2023 11:57:05 -0500 Subject: changed inPlace link following to be lightbox. allowed collections to be labeled as lightboxes. lightbox collections display link targets in an overlay that hides the rest of their content --- src/client/documents/Documents.ts | 1 + src/client/util/DocumentManager.ts | 28 ++++++---- src/client/util/LinkFollower.ts | 10 ++-- src/client/views/DocumentDecorations.tsx | 13 ++--- src/client/views/MainView.tsx | 1 - src/client/views/PropertiesButtons.tsx | 62 +++++++++++----------- src/client/views/PropertiesView.tsx | 1 - src/client/views/collections/CollectionView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 22 ++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 17 +++--- src/client/views/nodes/MapBox/MapBox.tsx | 1 - src/fields/documentSchemas.ts | 4 +- 13 files changed, 88 insertions(+), 78 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d2ab67849..70c57e8fa 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -160,6 +160,7 @@ export class DocumentOptions { _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed _followLinkToggle?: boolean; // whether document, when clicked, toggles display of its link target _showTitle?: string; // field name to display in header (:hover is an optional suffix) + _isLightbox?: boolean; // whether a collection acts as a lightbox by opening lightbox links by hiding all other documents in collection besides link target _showCaption?: string; // which field to display in the caption area. leave empty to have no caption _scrollTop?: number; // scroll location for pdfs _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index d9273c2c9..0d10bed43 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,7 +10,7 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentView, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; @@ -307,16 +307,22 @@ export class DocumentManager { } //} if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc); - const doFocus = (forceDidFocus: boolean) => - focusView.focus(originalTarget, { - ...options, - originalTarget, - afterFocus: (didFocus: boolean) => - new Promise(res => { - focusAndFinish(forceDidFocus || didFocus); - res(ViewAdjustment.doNothing); - }), - }); + const doFocus = (forceDidFocus: boolean) => { + if (options.openInLightbox || (!options.originatingDoc?.followLinkLocation && DocCast(focusView.rootDoc.context)?._isLightbox)) { + focusView.props.addDocTab(targetDoc, OpenWhere.lightbox); + focusAndFinish(true); + } else { + focusView.focus(originalTarget, { + ...options, + originalTarget, + afterFocus: (didFocus: boolean) => + new Promise(res => { + focusAndFinish(forceDidFocus || didFocus); + res(ViewAdjustment.doNothing); + }), + }); + } + }; if (focusView.props.Document.layoutKey === 'layout_icon' && focusView.rootDoc.type !== DocumentType.SCRIPTING) { focusView.iconify(() => doFocus(true)); } else { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 50bc89ed2..e86e68c21 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -3,6 +3,7 @@ import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { List } from '../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; +import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { LightboxView } from '../views/LightboxView'; import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; @@ -36,7 +37,9 @@ export class LinkFollower { const createTabForTarget = (didFocus: boolean) => new Promise(res => { const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); - docViewProps.addDocTab(doc, where); + const lightbox = where === OpenWhere.lightbox && DocumentManager.GetContextPath(doc).find(container => container.isLightbox && DocumentManager.Instance.getDocumentView(container)); + const addDocTab = lightbox ? DocumentManager.Instance.getDocumentView(lightbox)?.ComponentView?.addDocTab : undefined; + (addDocTab ?? docViewProps.addDocTab)(doc, where); setTimeout(() => { const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise. if (targDocView) { @@ -51,7 +54,7 @@ export class LinkFollower { }); } else { finished?.(); - res(where !== OpenWhere.inPlace || BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace' resetting the initial focus&zoom would negate the zoom into the target + res(BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); } }, 100); }); @@ -109,6 +112,7 @@ export class LinkFollower { zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, + openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox, effect: sourceDoc, originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), @@ -123,7 +127,7 @@ export class LinkFollower { } else { const containerDocContext = DocumentManager.GetContextPath(target); const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext; - DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.inPlace), finished), targetContexts, allFinished); + DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.lightbox), finished), targetContexts, allFinished); } }; let movedTarget = false; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4e51f10a8..eeef01d17 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -27,7 +27,7 @@ import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { InkStrokeProperties } from './InkStrokeProperties'; import { LightboxView } from './LightboxView'; -import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); @@ -283,11 +283,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc); Doc.deiconifyView(openDoc); } - LightboxView.SetLightboxDoc( - openDoc, - undefined, - selectedDocs.slice(1).map(view => view.props.Document) - ); + selectedDocs[0].props.addDocTab(openDoc, OpenWhere.lightbox); + // LightboxView.SetLightboxDoc( + // openDoc, + // undefined, + // selectedDocs.slice(1).map(view => view.props.Document) + // ); } } SelectionManager.DeselectAll(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 1e9220e1e..95c0f3755 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -692,7 +692,6 @@ export class MainView extends React.Component { if (doc.dockingConfig) return DashboardView.openDashboard(doc); // prettier-ignore switch (whereFields[0]) { - case OpenWhere.inPlace: // fall through to lightbox case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 7e33ee495..a5c01490f 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -7,7 +7,7 @@ import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { RichTextField } from '../../fields/RichTextField'; import { ScriptField } from '../../fields/ScriptField'; -import { BoolCast, StrCast } from '../../fields/Types'; +import { BoolCast, ScriptCast, StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { DocUtils } from '../documents/Documents'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; @@ -20,6 +20,7 @@ import { VideoBox } from './nodes/VideoBox'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); +import { LinkManager } from '../util/LinkManager'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -118,14 +119,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { on => 'object-group' ); } - // this implments a container pattern by marking the targetDoc (collection) as an inPlace container, - // and then making the contained collection be a "menu" such that when any of its contents are clicked, - // they will open their targets in the outer container. To get back to the "menu", you click on the main container. - @computed get inPlaceContainerButton() { + // this implments a container pattern by marking the targetDoc (collection) as a lightbox + // that always fits its contents to its container and that hides all other documents when + // a link is followed that targets a 'lightbox' destination + @computed get isLightboxButton() { return this.propertyToggleBtn( - 'In Place', - 'isInPlaceContainer', - on => `${on ? 'Make' : 'Remove'} in place container flag`, + 'Lightbox', + 'isLightbox', + on => `${on ? 'Set' : 'Remove'} lightbox flag`, on => 'window-restore', onClick => { SelectionManager.Views().forEach(dv => { @@ -134,16 +135,12 @@ export class PropertiesButtons extends React.Component<{}, {}> { // containerDoc.noShadow = // containerDoc.noHighlighting = // containerDoc._forceActive = - containerDoc._fitContentsToBox = containerDoc._isInPlaceContainer = !containerDoc._isInPlaceContainer; - containerDoc._xPadding = containerDoc._yPadding = containerDoc._isInPlaceContainer ? 10 : undefined; + containerDoc._fitContentsToBox = containerDoc._isLightbox = !containerDoc._isLightbox; + containerDoc._xPadding = containerDoc._yPadding = containerDoc._isLightbox ? 10 : undefined; const containerContents = DocListCast(dv.dataDoc[dv.props.fieldKey ?? Doc.LayoutFieldKey(containerDoc)]); dv.rootDoc.onClick = ScriptField.MakeScript('{self.data = undefined; documentView.select(false)}', { documentView: 'any' }); containerContents.forEach(doc => { - DocListCast(doc.links).forEach(link => { - doc.isLinkButton = true; - //doc.followLinkLocation = OpenWhere.inPlace; - link.linkDisplay = false; - }); + DocListCast(doc.links).forEach(link => (link.linkDisplay = false)); }); }); } @@ -290,7 +287,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch @action handleOptionChange = (onClick: string) => { - this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick); SelectionManager.Views() .filter(dv => dv.docView) .map(dv => dv.docView!) @@ -305,7 +301,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { break; case 'linkInPlace': docView.toggleFollowLink(false, false); - docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.inPlace : undefined; + docView.props.Document.followLinkLocation = docView.props.Document._isLinkButton ? OpenWhere.lightbox : undefined; break; case 'linkOnRight': docView.toggleFollowLink(false, false); @@ -327,24 +323,26 @@ export class PropertiesButtons extends React.Component<{}, {}> { ['nothing', 'Select Document'], ['enterPortal', 'Enter Portal'], ['toggleDetail', 'Toggle Detail'], - ['linkInPlace', 'Open in Place'], + ['linkInPlace', 'Open Link in Lightbox'], ['linkOnRight', 'Open Link on Right'], ]; - const currentSelection = this.selectedDoc.onClickBehavior; - // Get items to place into the list - const list = buttonList.map(value => { - const click = () => { - this.handleOptionChange(value[0]); - }; + const click = () => this.handleOptionChange(value[0]); + const linkButton = BoolCast(this.selectedDoc._isLinkButton); + const followLoc = this.selectedDoc._followLinkLocation; + const linkedToLightboxView = () => DocListCast(this.selectedDoc.links).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + + let active = false; + // prettier-ignore + switch (value[0]) { + case 'linkInPlace': active = linkButton && followLoc === OpenWhere.lightbox && !linkedToLightboxView(); break; + case 'linkOnRight': active = linkButton && followLoc === OpenWhere.addRight; break; + case 'enterPortal': active = linkButton && this.selectedDoc._followLinkLocation === OpenWhere.lightbox && linkedToLightboxView(); break; + case 'toggleDetail':active = ScriptCast(this.selectedDoc.onClick)?.script.originalScript.includes('toggleDetail'); break; + case 'nothing': active = !linkButton && this.selectedDoc.onClick === undefined;break; + } return ( -
+
{value[1]}
); @@ -413,7 +411,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.freezeThumb)} {toggle(this.forceActiveButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })} - {toggle(this.inPlaceContainerButton, { display: !isFreeForm && !isMap ? 'none' : '' })} + {toggle(this.isLightboxButton, { display: !isFreeForm && !isMap ? 'none' : '' })} {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })} {toggle(this.maskButton, { display: !isInk ? 'none' : '' })} {toggle(this.hideImageButton, { display: !isImage ? 'none' : '' })} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index e9d1433c3..0fe165783 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1626,7 +1626,6 @@ export class PropertiesView extends React.Component { - {LinkManager.currentLink?.linksToAnnotation ? : null}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 90406ed3c..316f1e4e9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -191,7 +191,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' }); } - !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' }); + !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._isLightbox), icon: 'project-diagram' }); if (!Doc.noviceMode && false) { optionItems.push({ diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 01872df84..042c2b71b 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -350,7 +350,7 @@ export class TabDocView extends React.Component { // replace[:{left,right,top,bottom,}] - e.g., "replace" will replace the current stack contents, // "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name, // "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right - // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack + // lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox addDocTab = (doc: Doc, location: OpenWhere) => { SelectionManager.DeselectAll(); const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':'); @@ -359,17 +359,15 @@ export class TabDocView extends React.Component { // prettier-ignore switch (whereFields[0]) { case undefined: - case OpenWhere.inPlace: { - if (this.layoutDoc?.isInPlaceContainer) { - const inPlaceView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; - const data = inPlaceView?.dataDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)]; - if (inPlaceView && (!data || data instanceof List)) { - inPlaceView.layoutDoc[Doc.LayoutFieldKey(inPlaceView.rootDoc)] = new List([doc]); - return true; - } - } - } - case OpenWhere.lightbox: return LightboxView.AddDocTab(doc, location); + case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { + const lightboxView = !doc.annotationOn && DocCast(doc.context) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.context)) : undefined; + const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)]; + if (lightboxView && (!data || data instanceof List)) { + lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List([doc]); + return true; + } + } + return LightboxView.AddDocTab(doc, location); case OpenWhere.dashboard: return DashboardView.openDashboard(doc); case OpenWhere.fullScreen: return CollectionDockingView.OpenFullScreen(doc); case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 78804b070..be8a72369 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1414,8 +1414,8 @@ export class CollectionFreeFormView extends CollectionSubView(doc instanceof Doc ? [doc] : doc); return true; } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index beb0f8e3a..8257c9c49 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -72,7 +72,6 @@ export enum ViewAdjustment { } export enum OpenWhere { - inPlace = 'inPlace', lightbox = 'lightbox', add = 'add', addLeft = 'add:left', @@ -109,6 +108,7 @@ export interface DocFocusOptions { effect?: Doc; // animation effect for focus noSelect?: boolean; // whether target should be selected after focusing playAudio?: boolean; // whether to play audio annotation on focus + openInLightbox?: boolean; // whether to open target in lightbox or just focus on it zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections toggleTarget?: boolean; // whether to toggle target on and off originatingDoc?: Doc; // document that triggered the focus @@ -123,6 +123,7 @@ export interface DocComponentView { scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; + addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected. @@ -640,7 +641,8 @@ export class DocumentViewInternal extends DocComponent (func().result?.select === true ? this.props.select(false) : ''), 'on double click'); } else if (!Doc.IsSystem(this.rootDoc) && (![DocumentType.INK].includes(this.rootDoc.type as any) || Doc.UserDoc().openInkInLightbox) && !this.rootDoc.isLinkButton) { - UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap'); + //UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, OpenWhere.lightbox, this.props.LayoutTemplate?.()), 'double tap'); + UndoManager.RunInBatch(() => this.props.addDocTab(this.rootDoc, OpenWhere.lightbox), 'double tap'); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); } @@ -870,13 +872,16 @@ export class DocumentViewInternal extends DocComponent { + makeIntoPortal = () => { const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document); if (!portalLink) { - const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }); - DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from'); + DocUtils.MakeLink( + { doc: this.props.Document }, + { doc: Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _isLightbox: true, _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' }) }, + 'portal to:portal from' + ); } - this.Document.followLinkLocation = OpenWhere.inPlace; + this.Document.followLinkLocation = OpenWhere.lightbox; this.Document.followLinkZoom = true; this.Document._isLinkButton = true; }; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 039f11cd1..c182dfd19 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -331,7 +331,6 @@ export class MapBox extends ViewBoxAnnotatableComponent { if (doc.lat !== undefined && doc.lng !== undefined) { const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng); - doc.onClickBehavior = 'enterPortal'; if (existingMarker) { Doc.AddDocToList(existingMarker, 'data', doc); } else { diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index 10324449f..5b489a96c 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -84,11 +84,11 @@ export const documentSchema = createSchema({ onPointerDown: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onPointerUp: ScriptField, // script to run when document is clicked (can be overriden by an onClick prop) onDragStart: ScriptField, // script to run when document is dragged (without being selected). the script should return the Doc to be dropped. - followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, inPlace, default, ) + followLinkLocation: 'string', // flag for where to place content when following a click interaction (e.g., add:right, lightbox, default, ) hideLinkButton: 'boolean', // whether the blue link counter button should be hidden hideAllLinks: 'boolean', // whether all individual blue anchor dots should be hidden linkDisplay: 'boolean', // whether a link connection should be shown between link anchor endpoints. - isInPlaceContainer: 'boolean', // whether the marked object will display addDocTab() calls that target "inPlace" destinations + isLightbox: 'boolean', // whether the marked object will display addDocTab() calls that target "lightbox" destinations isLinkButton: 'boolean', // whether document functions as a link follow button to follow the first link on the document when clicked layers: listSpec('string'), // which layers the document is part of _lockedPosition: 'boolean', // whether the document can be moved (dragged) -- cgit v1.2.3-70-g09d2 From bcdd26bd71a40ba78cdc38d957c91cfa1d0b6d63 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 1 Mar 2023 19:22:07 -0500 Subject: fixes for animation effect time setting when following links. fixes for starting animation effect right away, not waiting for focus to complete. --- src/client/util/CurrentUserUtils.ts | 11 +++-------- src/client/util/LinkFollower.ts | 4 ++-- src/client/views/PropertiesView.tsx | 4 ++-- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentView.tsx | 4 ++-- src/client/views/nodes/trails/PresBox.tsx | 4 +++- 6 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 67c730089..a7b2c3d04 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -589,20 +589,15 @@ export class CurrentUserUtils { { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }}, { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }}, { scripts: { }, opts: { title: "linker", icon: "linkui", toolTip: "link started"}}, - { scripts: { }, opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing audio"}}, - // { scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, opts: { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, width: 20}}, - // { scripts: { onClick:""}, opts: { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, width: 20}, funcs: { buttonText: 'selectedDocs()?.lastElement()?.currentFrame.toString()'}}, - // { scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, opts:{title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, width: 20,} }, - ]; + { scripts: { }, opts: { title: "currently playing", icon: "currentlyplayingui", toolTip: "currently playing media"}}, + ]; const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts:DocumentOptions = { title: "docked buttons", _height: 40, flexGap: 0, boxShadow: "standard", childDropAction: 'alias', childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true }; reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); - reaction(() => UndoManager.undoStack.slice(), () => { - Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4; - }, { fireImmediately: true }); + reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns); } diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index e86e68c21..b40c7bdf9 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -108,8 +108,8 @@ export class LinkFollower { playAudio: BoolCast(sourceDoc.followLinkAudio), toggleTarget: canToggle && BoolCast(sourceDoc.followLinkToggle), willPan: true, - willPanZoom: BoolCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkZoom, false), - zoomTime: NumCast(LinkManager.getOppositeAnchor(linkDoc, target)?.followLinkTransitionTime, 500), + willPanZoom: BoolCast(sourceDoc.followLinkZoom, false), + zoomTime: NumCast(sourceDoc.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox, diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 0fe165783..5105cc615 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -1412,11 +1412,11 @@ export class PropertiesView extends React.Component { changeAnimationBehavior = action((behavior: string) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimEffect = behavior)); @undoBatch - changeEffectDirection = action((effect: PresEffectDirection) => this.sourceAnchor && (this.sourceAnchor.linkAnimDirection = effect)); + changeEffectDirection = action((effect: PresEffectDirection) => this.sourceAnchor && (this.sourceAnchor.followLinkAnimDirection = effect)); animationDirection = (direction: PresEffectDirection, icon: string, gridColumn: number, gridRow: number, opts: object) => { const lanch = this.sourceAnchor; - const color = lanch?.linkAnimDirection === direction || (direction === PresEffectDirection.Center && !lanch?.linkAnimDirection) ? Colors.MEDIUM_BLUE : ''; + const color = lanch?.followLinkAnimDirection === direction || (direction === PresEffectDirection.Center && !lanch?.followLinkAnimDirection) ? Colors.MEDIUM_BLUE : ''; return ( {direction}
}>
, root: Doc) { - const dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.linkAnimDirection; + const dir = presEffectDoc?.presEffectDirection ?? presEffectDoc?.followLinkAnimDirection; const effectProps = { left: dir === PresEffectDirection.Left, right: dir === PresEffectDirection.Right, @@ -1479,7 +1479,7 @@ export class DocumentViewInternal extends DocComponent() { (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; } + const effect = activeItem.presEffect && activeItem.presEffect !== PresEffect.None ? activeItem.presEffect : undefined; + const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500); const options: DocFocusOptions = { willPan: activeItem.presMovement !== PresMovement.None, willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1), - zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500), + zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime), effect: activeItem, noSelect: true, originatingDoc: activeItem, -- cgit v1.2.3-70-g09d2 From 0c38e4dc096d6abf82ef11286616856b7119c6e1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 5 Mar 2023 21:24:09 -0500 Subject: replace jumpToDocument with showDocument. restructure code to get rid of scrollFocus by adding getView() and fixing focus() and restoreTargetView --- src/Utils.ts | 27 +++ src/client/documents/Documents.ts | 2 +- src/client/util/DocumentManager.ts | 235 +++++++-------------- .../util/Import & Export/DirectoryImportBox.tsx | 2 +- src/client/util/LinkFollower.ts | 56 +---- src/client/util/SharingManager.tsx | 2 +- src/client/views/InkingStroke.tsx | 8 +- src/client/views/LightboxView.tsx | 4 +- src/client/views/MainView.tsx | 2 +- src/client/views/PropertiesButtons.tsx | 4 +- src/client/views/PropertiesView.tsx | 2 +- src/client/views/SidebarAnnos.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 15 +- .../collections/CollectionStackedTimeline.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 13 +- .../views/collections/CollectionTimeView.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 10 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 151 ++++--------- .../collections/collectionFreeForm/MarqueeView.tsx | 3 +- .../collectionLinear/CollectionLinearView.tsx | 2 +- .../CollectionMulticolumnView.tsx | 3 +- .../CollectionMultirowView.tsx | 1 - .../collectionSchema/CollectionSchemaCells.tsx | 2 +- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/AudioBox.tsx | 7 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 7 +- src/client/views/nodes/ComparisonBox.tsx | 19 +- src/client/views/nodes/DocumentView.tsx | 66 ++---- src/client/views/nodes/FilterBox.tsx | 2 +- src/client/views/nodes/FunctionPlotBox.tsx | 20 +- src/client/views/nodes/ImageBox.tsx | 7 +- src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 24 +-- src/client/views/nodes/VideoBox.tsx | 35 +-- src/client/views/nodes/WebBox.tsx | 82 ++++--- .../views/nodes/formattedText/FormattedTextBox.tsx | 28 +-- src/client/views/nodes/trails/PresBox.scss | 2 +- src/client/views/nodes/trails/PresBox.tsx | 160 +++++++------- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 9 +- src/client/views/search/SearchBox.tsx | 2 +- src/fields/ScriptField.ts | 7 +- src/fields/util.ts | 6 +- 43 files changed, 410 insertions(+), 635 deletions(-) (limited to 'src/client/views/PropertiesView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 22c8cb902..ae1478943 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1,6 +1,7 @@ import v4 = require('uuid/v4'); import v5 = require('uuid/v5'); import { ColorState } from 'react-color'; +import * as rp from 'request-promise'; import { Socket } from 'socket.io'; import { Colors } from './client/views/global/globalEnums'; import { Message } from './server/Message'; @@ -34,6 +35,31 @@ export namespace Utils { return v5(seed, v5.URL); } + /** + * Uploads an image buffer to the server and stores with specified filename. by default the image + * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large) + * @param imageUri the bytes of the image + * @param returnedFilename the base filename to store the image on the server + * @param nosuffix optionally suppress creating multiple resolution images + */ + export async function convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) { + try { + const posting = Utils.prepend('/uploadURI'); + const returnedUri = await rp.post(posting, { + body: { + uri: imageUri, + name: returnedFilename, + nosuffix, + replaceRootFilename, + }, + json: true, + }); + return returnedUri; + } catch (e) { + console.log('VideoBox :' + e); + } + } + export function GetScreenTransform(ele?: HTMLElement): { scale: number; translateX: number; translateY: number } { if (!ele) { return { scale: 1, translateX: 1, translateY: 1 }; @@ -218,6 +244,7 @@ export namespace Utils { } export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) { + if (!targetHgt) return targetY; // if there's no height, then assume that if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) { return Math.ceil(targetY + minSpacing + targetHgt - contextHgt); } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ebd06dbe2..98469a2f9 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1319,7 +1319,7 @@ export namespace DocUtils { } export function DefaultFocus(doc: Doc, options: DocFocusOptions) { - options?.afterFocus?.(false); + return undefined; } export let ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 0d10bed43..1a38e9aff 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,17 +1,22 @@ +import { loadAsync } from 'jszip'; import { action, observable, ObservableSet, runInAction } from 'mobx'; import { AnimationSym, Doc, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../fields/Types'; import { AudioField } from '../../fields/URLField'; -import { returnFalse } from '../../Utils'; -import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { emptyFunction } from '../../Utils'; +import { CollectionViewType } from '../documents/DocumentTypes'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { CollectionView } from '../views/collections/CollectionView'; +import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod, ViewAdjustment } from '../views/nodes/DocumentView'; +import { MainView } from '../views/MainView'; +import { DocFocusOptions, DocumentView, OpenWhere, OpenWhereMod } from '../views/nodes/DocumentView'; +import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; +import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; const { Howl } = require('howler'); @@ -165,12 +170,12 @@ export class DocumentManager { public getLightboxDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { const views: DocumentView[] = []; Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view)); - return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); + return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined); }; public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt = undefined): DocumentView | undefined => { if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind, originatingDoc); const views = this.getDocumentViews(toFind); //.filter(view => view.rootDoc !== originatingDoc); - return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined); + return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /*&& view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse*/) || (views.length ? views[0] : undefined); }; public getDocumentViews(toFindIn: Doc): DocumentView[] { const toFind = @@ -198,7 +203,7 @@ export class DocumentManager { static GetContextPath(doc: Opt, includeExistingViews?: boolean) { if (!doc) return []; - const srcContext = Cast(doc.context, Doc, null) ?? Cast(Cast(doc.annotationOn, Doc, null)?.context, Doc, null); + const srcContext = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null); var containerDocContext = srcContext ? [srcContext, doc] : [doc]; while ( containerDocContext.length && @@ -235,171 +240,91 @@ export class DocumentManager { CollectionDockingView.AddSplit(doc, OpenWhereMod.right); finished?.(); }; - public jumpToDocument = ( + + // shows a documentView by: + // traverses down through the viewPath of contexts to the view: + // focusing on each context + public showDocumentView = async (targetDocView: DocumentView, options: DocFocusOptions) => { + const docViewPath = targetDocView.docViewPath.slice(); + let rootContextView = docViewPath.shift(); + return rootContextView && this.focusViewsInPath(rootContextView, options, async () => ({ childDocView: docViewPath.shift(), viewSpec: undefined })); + }; + + // shows a document by first: + // traversing down through the contexts that contain target until an existing view is found + // if no container view is found, create one by: opening an existing tab that has the top-level view, or showing the top-level context in the lightbox. + // once a containing view is found, it then traverses back down through the contexts to the target document by: + // focusing on each context + // and finally restoring the targetDoc to the viewSpec specified by the last document which may either be the targetDoc, or a viewSpec that describes the targetDoc configuration + public showDocument = async ( targetDoc: Doc, // document to display options: DocFocusOptions, // options for how to navigate to target - createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist - docContextPath: Doc[], // context to load that should contain the target finished?: () => void - ): void => { - const originalTarget = options.originalTarget ?? targetDoc; - const docView = this.getFirstDocumentView(targetDoc, options.originatingDoc); - const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); - const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? docView?.rootDoc ?? targetDoc : docView?.rootDoc ?? targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field - var wasHidden = resolvedTarget.hidden; - if (wasHidden) { - runInAction(() => { - resolvedTarget.hidden = false; // if the target is hidden, un-hide it here. - docView?.props.bringToFront(resolvedTarget); - }); - } - const focusAndFinish = action((didFocus: boolean) => { - const finalTargetDoc = resolvedTarget; - if (options.toggleTarget) { - if (!didFocus && !wasHidden) { - // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal - finalTargetDoc.hidden = !finalTargetDoc.hidden; - } - } else { - finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined); - !options.noSelect && docView?.select(false); - } - if (targetDoc.textHtml && options.zoomTextSelections) { - const containerView = DocumentManager.Instance.getFirstDocumentView(finalTargetDoc); - if (containerView) { - containerView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect)); - containerView.textHtmlOverlay = StrCast(targetDoc.textHtml); - DocumentManager._overlayViews.add(containerView); - if (Doc.UnhighlightTimer) { - Doc.AddUnHighlightWatcher(() => { - DocumentManager.removeOverlayViews(); - containerView.htmlOverlayEffect = ''; - }); - } else setTimeout(() => (containerView.htmlOverlayEffect = '')); - } - } - finished?.(); + ) => { + const docContextPath = DocumentManager.GetContextPath(targetDoc, true); + let rootContextView = await new Promise(res => { + const viewIndex = docContextPath.findIndex(doc => this.getDocumentView(doc)); + if (viewIndex !== -1) return res(this.getDocumentView(docContextPath[viewIndex])!); + docContextPath.some(doc => TabDocView.Activate(doc)) || MainView.addDocTabFunc(docContextPath[0], options.openLocation as OpenWhere); + this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); }); - const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && this.getFirstDocumentView(annotatedDoc); - if (annoContainerView) { - if (annoContainerView.props.Document.layoutKey === 'layout_icon') { - return annoContainerView.iconify(() => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, originalTarget, toggleTarget: false }, createViewFunc, docContextPath, finished)), 30); - } - if (!docView && targetDoc.type !== DocumentType.MARKER) { - annoContainerView.focus(targetDoc, options); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below - } - } - const contextDoc = docContextPath.length ? docContextPath[0] : undefined; - const remainingDocContext = docContextPath.length ? docContextPath.slice(1) : []; - const targetDocContext = contextDoc || annotatedDoc; - const targetDocContextView = (targetDocContext && this.getFirstDocumentView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above - const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView; - if (focusView) { - // if (focusView.rootDoc === originalTarget) { - if (!options.noSelect) Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox - else { - Doc.linkFollowHighlight(focusView.rootDoc, undefined, options.effect); //TODO:glr make this a setting in PresBox - focusView.rootDoc[AnimationSym] = options.effect; - if (Doc.UnhighlightTimer) { - Doc.AddUnHighlightWatcher(action(() => (focusView.rootDoc[AnimationSym] = undefined))); - } - } - //} - if (options.playAudio) DocumentManager.playAudioAnno(focusView.rootDoc); - const doFocus = (forceDidFocus: boolean) => { - if (options.openInLightbox || (!options.originatingDoc?.followLinkLocation && DocCast(focusView.rootDoc.context)?._isLightbox)) { - focusView.props.addDocTab(targetDoc, OpenWhere.lightbox); - focusAndFinish(true); - } else { - focusView.focus(originalTarget, { - ...options, - originalTarget, - afterFocus: (didFocus: boolean) => - new Promise(res => { - focusAndFinish(forceDidFocus || didFocus); - res(ViewAdjustment.doNothing); - }), - }); - } - }; - if (focusView.props.Document.layoutKey === 'layout_icon' && focusView.rootDoc.type !== DocumentType.SCRIPTING) { - focusView.iconify(() => doFocus(true)); - } else { - doFocus(false); - } - } else { - if (!targetDocContext) { - // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default - createViewFunc(Doc.BrushDoc(targetDoc), () => focusAndFinish(true)); // bcz: should we use this?: Doc.MakeAlias(targetDoc))); - } else { - // otherwise try to get a view of the context of the target - if (targetDocContextView) { - // we found a context view and aren't forced to create a new one ... focus on the context first.. - wasHidden = wasHidden || targetDocContextView.rootDoc.hidden; - targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden + docContextPath.shift(); + const childViewIterator = async () => { + const innerDoc = docContextPath.shift(); + return { viewSpec: innerDoc, childDocView: innerDoc && !innerDoc.unrendered ? (await rootContextView.ComponentView?.getView?.(innerDoc)) ?? this.getDocumentView(innerDoc) : undefined }; + }; + const target = await this.focusViewsInPath(rootContextView, options, childViewIterator); + this.restoreDocView(target.viewSpec, target.docView, options, target.contextView ?? target.docView, targetDoc); - if (targetDocContext.layoutKey === 'layout_icon') { - return targetDocContextView.iconify( - () => DocumentManager.Instance.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(resolvedTarget ?? targetDoc, { ...options /* originalTarget - needed? */ }, createViewFunc, docContextPath, finished)), - 30 - ); - } + finished?.(); + }; - const contextFocusTime = options.zoomTime ? options.zoomTime / 2 : 500; - const remainingFocustime = options.zoomTime ? options.zoomTime - contextFocusTime : undefined; - targetDocContextView.setViewTransition('transform', contextFocusTime); - // this makes focusing on contexts run in parallel -- jutmp to document below makes them run sequentially - this.AddViewRenderedCb(targetDoc, () => this.jumpToDocument(targetDoc, { ...options, zoomTime: remainingFocustime }, createViewFunc, remainingDocContext, finished)); - targetDocContextView.props.focus(targetDocContextView.rootDoc, { - ...options, - zoomTime: contextFocusTime, - // originalTarget, // needed? - afterFocus: async () => { - // now find the target document within the context - if (targetDoc._timecodeToShow) { - // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode; - targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow; - finished?.(); - } else { - // otherwise, just look for the target document in this context view now that we've focused the context view - if (this.getFirstDocumentView(resolvedTarget)) { - // test again for the target view snce we presumably created the context above by focusing on it - this.jumpToDocument(targetDoc, { ...options, zoomTime: remainingFocustime }, createViewFunc, remainingDocContext, finished); - } else if (targetDoc.layout) { - // there will no layout for a TEXTANCHOR type document - createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target - } - } - return ViewAdjustment.doNothing; - }, - }); - } else { - if (docContextPath.length && docContextPath[0]?.layoutKey === 'layout_icon') { - Doc.deiconifyView(docContextPath[0]); - this.jumpToDocument(targetDoc, options, createViewFunc, docContextPath, finished); - } else { - // there's no context view so we need to create one first and try again when that finishes - createViewFunc( - targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target - () => this.jumpToDocument(targetDoc, { ...options }, (doc: Doc, finished?: () => void) => doc !== targetDocContext && createViewFunc(doc, finished), remainingDocContext, finished) - ); - } - } - } + focusViewsInPath = async (docView: DocumentView, options: DocFocusOptions, iterator: () => Promise<{ viewSpec: Opt; childDocView: Opt }>) => { + let contextView: DocumentView | undefined; // view containing context that contains target + while (true) { + docView.rootDoc.layoutKey === 'layout_icon' ? await new Promise(res => docView.iconify(res)) : undefined; + docView.props.focus(docView.rootDoc, options); // focus the view within its container + const { childDocView, viewSpec } = await iterator(); + if (!childDocView) return { viewSpec: viewSpec ?? docView.rootDoc, docView, contextView }; + contextView = docView; + docView = childDocView; } }; + + @action + restoreDocView(viewSpec: Opt, docView: DocumentView, options: DocFocusOptions, contextView: Opt, targetDoc: Doc) { + if (viewSpec && docView) { + if (docView.ComponentView instanceof FormattedTextBox) docView.ComponentView?.focus(viewSpec, options); + PresBox.restoreTargetDocView(docView, viewSpec, options.zoomTime ?? 500); + Doc.linkFollowHighlight(docView.rootDoc, undefined, options.effect); + if (options.playAudio) DocumentManager.playAudioAnno(docView.rootDoc); + if (options.toggleTarget) docView.rootDoc.hidden = !docView.rootDoc.hidden; + if (options.effect) docView.rootDoc[AnimationSym] = options.effect; + + if (options.zoomTextSelections && Doc.UnhighlightTimer && contextView && viewSpec.textHtml) { + // if the docView is a text anchor, the contextView is the PDF/Web/Text doc + contextView.htmlOverlayEffect = StrCast(options?.effect?.presEffect, StrCast(options?.effect?.followLinkAnimEffect)); + contextView.textHtmlOverlay = StrCast(targetDoc.textHtml); + DocumentManager._overlayViews.add(contextView); + } + Doc.AddUnHighlightWatcher(() => { + docView.rootDoc[AnimationSym] = undefined; + DocumentManager.removeOverlayViews(); + contextView && (contextView.htmlOverlayEffect = ''); + }); + } + } } export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc); const dv = DocumentManager.Instance.getDocumentView(doc, (cv?.ComponentView as CollectionFreeFormView)?.props.CollectionView); - if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) { - dv.props.focus(dv.props.Document, { willPanZoom: true }); - Doc.linkFollowHighlight(dv?.props.Document, false); + if (dv) { + DocumentManager.Instance.showDocumentView(dv, { willZoomCentered: true }); } else { const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null); const showDoc = context || doc; - CollectionDockingView.AddSplit(Doc.BestAlias(showDoc), OpenWhereMod.right) && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc, {})); + DocumentManager.Instance.showDocument(Doc.BestAlias(showDoc), { openLocation: OpenWhere.addRight }, () => DocumentManager.Instance.showDocument(doc, { willZoomCentered: true })); } } ScriptingGlobals.add(DocFocusOrOpen); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 7f0c8a3e8..559958c2b 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -168,7 +168,7 @@ export class DirectoryImportBox extends React.Component { await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer }); Doc.AddDocToList(Doc.GetProto(parent.props.Document), 'data', importContainer); !this.persistent && this.props.removeDocument && this.props.removeDocument(doc); - DocumentManager.Instance.jumpToDocument(importContainer, { willPanZoom: true }, undefined, []); + DocumentManager.Instance.showDocument(importContainer, { willZoomCentered: true }); } runInAction(() => { diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index b40c7bdf9..c9a178db7 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -1,15 +1,11 @@ import { action, runInAction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; -import { List } from '../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; -import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { DocumentDecorations } from '../views/DocumentDecorations'; -import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentViewSharedProps, OpenWhere } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; import { DocumentManager } from './DocumentManager'; -import { LinkManager } from './LinkManager'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; @@ -32,46 +28,10 @@ export class LinkFollower { // depending on the followLinkLocation property of the source (or the link itself as a fallback); public static FollowLink = (linkDoc: Opt, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean) => { const batch = UndoManager.StartBatch('follow link click'); - // open up target if it's not already in view ... - const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => { - const createTabForTarget = (didFocus: boolean) => - new Promise(res => { - const where = LightboxView.LightboxDoc ? OpenWhere.lightbox : (StrCast(sourceDoc.followLinkLocation, followLoc) as OpenWhere); - const lightbox = where === OpenWhere.lightbox && DocumentManager.GetContextPath(doc).find(container => container.isLightbox && DocumentManager.Instance.getDocumentView(container)); - const addDocTab = lightbox ? DocumentManager.Instance.getDocumentView(lightbox)?.ComponentView?.addDocTab : undefined; - (addDocTab ?? docViewProps.addDocTab)(doc, where); - setTimeout(() => { - const targDocView = DocumentManager.Instance.getFirstDocumentView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise. - if (targDocView) { - targDocView.props.focus(doc, { - willPan: true, - willPanZoom: BoolCast(sourceDoc.followLinkZoom, false), - afterFocus: (didFocus: boolean) => { - finished?.(); - res(ViewAdjustment.resetView); - return new Promise(res2 => res2(ViewAdjustment.doNothing)); - }, - }); - } else { - finished?.(); - res(BoolCast(sourceDoc.followLinkZoom) ? ViewAdjustment.resetView : ViewAdjustment.doNothing); - } - }, 100); - }); - - if (!sourceDoc.followLinkZoom) { - createTabForTarget(false); - } else { - // first focus & zoom onto this (the clicked document). Then execute the function to focus on the target - docViewProps.focus(sourceDoc, { willPan: true, willPanZoom: BoolCast(sourceDoc.followLinkZoom, true), zoomTime: 1000, zoomScale: 1, afterFocus: createTabForTarget }); - } - }; runInAction(() => (DocumentDecorations.Instance.overrideBounds = true)); // turn off decoration bounds while following links since animations may occur, and DocDecorations is based on screenToLocal which is not always an observable value LinkFollower.traverseLink( linkDoc, sourceDoc, - createViewFunc, - docViewProps.ContainingCollectionDoc, action(() => { batch.end(); Doc.AddUnHighlightWatcher(action(() => (DocumentDecorations.Instance.overrideBounds = false))); @@ -80,7 +40,7 @@ export class LinkFollower { ); }; - public static traverseLink(link: Opt, sourceDoc: Doc, createViewFunc: CreateViewFunc, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) { + public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { const linkDocs = link ? [link] : DocListCast(sourceDoc.links); const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1 const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2 @@ -104,15 +64,17 @@ export class LinkFollower { ) as Doc; if (target) { const doFollow = (canToggle?: boolean) => { + const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle); const options: DocFocusOptions = { playAudio: BoolCast(sourceDoc.followLinkAudio), - toggleTarget: canToggle && BoolCast(sourceDoc.followLinkToggle), + toggleTarget, + noSelect: true, willPan: true, - willPanZoom: BoolCast(sourceDoc.followLinkZoom, false), + willZoomCentered: BoolCast(sourceDoc.followLinkZoom, false), zoomTime: NumCast(sourceDoc.followLinkTransitionTime, 500), zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null), easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any, - openInLightbox: sourceDoc.followLinkLocation === OpenWhere.lightbox, + openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox), effect: sourceDoc, originatingDoc: sourceDoc, zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText), @@ -125,9 +87,7 @@ export class LinkFollower { } finished?.(); } else { - const containerDocContext = DocumentManager.GetContextPath(target); - const targetContexts = !sourceDoc.followLinkToOuterContext && containerDocContext.length ? [containerDocContext.lastElement()] : containerDocContext; - DocumentManager.Instance.jumpToDocument(target, options, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, OpenWhere.lightbox), finished), targetContexts, allFinished); + DocumentManager.Instance.showDocument(target, options, allFinished); } }; let movedTarget = false; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 36095700c..3c05af4bb 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -375,7 +375,7 @@ export class SharingManager extends React.Component<{}> { onClick={() => { let context: Opt; if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) { - DocumentManager.Instance.jumpToDocument(this.targetDoc, { willPanZoom: true }, undefined, [context.props.Document]); + DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 73e46a553..4f08a8e22 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -91,18 +91,12 @@ export class InkingStroke extends ViewBoxBaseComponent() { if (anchor) { anchor.backgroundColor = 'transparent'; // /* addAsAnnotation &&*/ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: true, inkable: true } }, this.rootDoc); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.rootDoc); return anchor; } return this.rootDoc; }; - scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { - if (this._subContentView) return this._subContentView?.scrollFocus?.(docView, anchor, options); - - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; - }; /** * @returns the center of the ink stroke in the ink document's coordinate space (not screen space, and not the ink data coordinate space); * DocumentDecorations calls getBounds() on DocumentViews which call getCenter() if defined - in the case of ink it needs to be defined since diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 8d60da0dd..2567d44bb 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -160,7 +160,7 @@ export class LightboxView extends React.Component { if (targetDocView && target) { const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); - targetDocView.focus(target, { originalTarget: target, willPanZoom: true, zoomScale: 0.9 }); + DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target }); } else { if (!target && LightboxView.path.length) { @@ -195,7 +195,7 @@ export class LightboxView extends React.Component { const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); if (docView) { LightboxView._docTarget = target; - target && docView.focus(target, { willPanZoom: true, zoomScale: 0.9 }); + target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { LightboxView.SetLightboxDoc(doc, target); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 95c0f3755..e1ba5943d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -205,7 +205,7 @@ export class MainView extends React.Component { document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.jumpToDocument(doc, { willPan: false }, undefined, []) : null)); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); this.initEventListeners(); diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index a5c01490f..ac1b66013 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -16,11 +16,11 @@ import { undoBatch } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; -import { VideoBox } from './nodes/VideoBox'; import { pasteImageBitmap } from './nodes/WebBoxRenderer'; import './PropertiesButtons.scss'; import React = require('react'); import { LinkManager } from '../util/LinkManager'; +import { Utils } from '../../Utils'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -222,7 +222,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { setTimeout(() => pasteImageBitmap((data_url: any, error: any) => { error && console.log(error); - data_url && VideoBox.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename))); + data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename))); }) ); } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5105cc615..e750dc43a 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -335,7 +335,7 @@ export class PropertiesView extends React.Component { NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined} PanelWidth={panelWidth} PanelHeight={panelHeight} - focus={returnFalse} + focus={emptyFunction} ScreenToLocalTransform={this.getTransform} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index b9af28413..74ea624a6 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -139,7 +139,7 @@ export class SidebarAnnos extends React.Component { return target; }; makeDocUnfiltered = (doc: Doc) => { - if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) { + if (DocListCast(this.props.rootDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) { if (this.props.layoutDoc[this.filtersKey]) { this.props.layoutDoc[this.filtersKey] = new List(); } diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 0e1601f35..6035871cd 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -3,7 +3,7 @@ import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; -import { Copy, Id } from '../../../fields/FieldSymbols'; +import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; @@ -11,7 +11,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; -import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -19,7 +18,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { LightboxView } from '../LightboxView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; @@ -187,20 +186,16 @@ export class CollectionNoteTakingView extends CollectionSubView() { // let's dive in and get the actual document we want to drag/move around focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); - let focusSpeed = 0; const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + let focusSpeed = options.zoomTime ?? 500; + smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + return focusSpeed; } } - const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing); - this.props.focus(this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), - }); }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 6fd4c8c0a..572fbfec2 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -765,7 +765,7 @@ class StackedTimelineAnchor extends React.Component // starting the drag event for anchor resizing onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { - this.props._timeline?.setPointerCapture(e.pointerId); + //this.props._timeline?.setPointerCapture(e.pointerId); const newTime = (e: PointerEvent) => { const rect = (e.target as any).getBoundingClientRect(); return this.props.toTimeline(e.clientX - rect.x, rect.width); @@ -793,7 +793,7 @@ class StackedTimelineAnchor extends React.Component }, e => { this.props.setTime(newTime(e)); - this.props._timeline?.releasePointerCapture(e.pointerId); + // this.props._timeline?.releasePointerCapture(e.pointerId); undo?.end(); }, emptyFunction diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 6314b4529..4805a748b 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -22,7 +22,7 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; @@ -250,20 +250,17 @@ export class CollectionStackingView extends CollectionSubView { Doc.BrushDoc(doc); - let focusSpeed = 0; const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + let focusSpeed = options.zoomTime ?? 500; + smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + return focusSpeed; } } - const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing; - this.props.focus(this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), - }); + return undefined; }; styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 437b22040..89b2fbfe3 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -66,11 +66,11 @@ export class CollectionTimeView extends CollectionSubView() { }; @action - scrollPreview = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { + scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => { // if in preview, then override document's fields with view spec - this._focusFilters = StrListCast(anchor.presPinDocFilters); + this._focusFilters = StrListCast(anchor.presDocFilters); this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters); - this._focusPivotField = StrCast(anchor.presPinPivotField); + this._focusPivotField = StrCast(anchor.presPivotField); return undefined; }; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 042c2b71b..99283996e 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -305,7 +305,7 @@ export class TabDocView extends React.Component { const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); - setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), { willPan: true }, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things + setTimeout(() => DocumentManager.Instance.showDocument(docList.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things } setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } @@ -388,13 +388,17 @@ export class TabDocView extends React.Component { getCurrentFrame = () => { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); }; + static Activate = (tabDoc: Doc) => { + const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc); + tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) + return tab !== undefined; + }; @action focusFunc = (doc: Doc, options: DocFocusOptions) => { - options?.afterFocus?.(false); - if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) } + return undefined; }; active = () => this._isActive; @observable _forceInvalidateScreenToLocal = 0; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 79832eb39..63b566f28 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { DateField } from '../../../../fields/DateField'; -import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../../fields/Doc'; +import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -15,13 +15,12 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro import { ImageField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; -import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnTransparent, setupMoveUpEvents, Utils } from '../../../../Utils'; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils'; import { CognitiveServices } from '../../../cognitive_services/CognitiveServices'; import { Docs, DocUtils } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager, dropActionType } from '../../../util/DragManager'; -import { HistoryUtil } from '../../../util/History'; import { InteractionUtils } from '../../../util/InteractionUtils'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; @@ -33,15 +32,15 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; +import { DocumentDecorations } from '../../DocumentDecorations'; import { GestureOverlay } from '../../GestureOverlay'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; -import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, ViewAdjustment } from '../../nodes/DocumentView'; +import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { PinProps, PresBox } from '../../nodes/trails/PresBox'; -import { VideoBox } from '../../nodes/VideoBox'; import { CreateImage } from '../../nodes/WebBoxRenderer'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; @@ -52,8 +51,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); -import { DocumentDecorations } from '../../DocumentDecorations'; -import { PresEffect } from '../../nodes/trails'; export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -300,6 +297,30 @@ export class CollectionFreeFormView extends CollectionSubView= -1e-4 && curTime <= endTime); } + focus = (anchor: Doc, options: DocFocusOptions) => { + const xfToCollection = options?.docTransform ?? Transform.Identity(); + const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined }; + const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); + const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale || 0.75 : undefined); + + // focus on the document in the collection + const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale); + // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... + if (didMove) { + const focusTime = options?.instant ? 0 : options.zoomTime ?? 500; + options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale); + this.setPan(panX, panY, focusTime, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow + return focusTime; + } + }; + + getView = async (doc: Doc): Promise> => { + return new Promise>(res => { + const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + findDoc(dv => res(dv)); + }); + }; + @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false; @@ -1199,79 +1220,6 @@ export class CollectionFreeFormView extends CollectionSubView(res => setTimeout(() => res(runInAction(() => (this._panZoomTransition = 0))), this._panZoomTransition)); // set transition to be smooth, then reset } - _focusCount = 0; - focusDocument = (doc: Doc, options: DocFocusOptions) => { - const state = HistoryUtil.getState(); - - // TODO This technically isn't correct if type !== "doc", as - // currently nothing is done, but we should probably push a new state - if (state.type === 'doc' && this.Document._panX !== undefined && this.Document._panY !== undefined) { - const init = state.initializers![this.Document[Id]]; - if (!init) { - state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) }; - HistoryUtil.pushState(state); - } else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) { - init.panX = NumCast(this.Document._panX); - init.panY = NumCast(this.Document._panY); - HistoryUtil.pushState(state); - } - } - // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) { - // SelectionManager.DeselectAll(); - // } - if (this.props.getScrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) { - this.props.focus(doc, options); - } else { - const xfToCollection = options?.docTransform ?? Transform.Identity(); - const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willPanZoom ? this.Document[this.scaleFieldKey] : undefined }; - const newState = HistoryUtil.getState(); - const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); - const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willPanZoom) ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willPanZoom ? options?.zoomScale || 0.75 : undefined); - if (!cantTransform) { - // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection - newState.initializers![this.Document[Id]] = { panX, panY }; - HistoryUtil.pushState(newState); - } - // focus on the document in the collection - const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale); - const focusSpeed = options?.instant ? 0 : didMove ? options.zoomTime ?? 500 : 0; - // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... - if (didMove) { - options.zoomScale && scale && (this.Document[this.scaleFieldKey] = scale); - this.setPan(panX, panY, focusSpeed, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow - } - - const focusCount = ++this._focusCount; - const startTime = Date.now(); - // focus on this collection within its parent view. the parent view after focusing determines whether to reset the view change within the collection - const endFocus = async (moved: boolean) => { - doc.hidden && Doc.UnHighlightDoc(doc); - const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing; - if (resetView) { - const restoreState = savedState; - if (typeof restoreState !== 'boolean') { - this.Document._panX = restoreState.panX; - this.Document._panY = restoreState.panY; - this.Document[this.scaleFieldKey] = restoreState.scale; - } - } - this._focusCount === focusCount && didMove && runInAction(() => (this._panZoomTransition = 0)); - return resetView; - }; - const xf = !cantTransform - ? Transform.Identity() - : this.props.isAnnotationOverlay - ? new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc)) - : new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1); - - this.props.focus(!cantTransform ? this.rootDoc : doc, { - ...options, - docTransform: xf, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))), - }); - } - }; - calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { const layoutdoc = Doc.Layout(doc); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); @@ -1371,7 +1319,7 @@ export class CollectionFreeFormView extends CollectionSubView { - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - if (options.preview) { - this._focusFilters = StrListCast(anchor.presPinDocFilters); - this._focusRangeFilters = StrListCast(anchor.presPinDocRangeFilters); - return undefined; - } - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; - }; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document - getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { // create an anchor that saves information about the current state of the freeform view (pan, zoom, view type) const anchor = Docs.Create.CollectionAnchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), unrendered: true, presTransition: 500, annotationOn: this.rootDoc }); @@ -1744,7 +1681,7 @@ export class CollectionFreeFormView extends CollectionSubView { - const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); + const returnedFilename = await Utils.convertDataUri(data_url, filename, noSuffix, replaceRootFilename); cb(returnedFilename as string, nativeWidth, nativeHeight); }) .catch(function (error: any) { @@ -2303,21 +2240,21 @@ class CollectionFreeFormBackgroundGrid extends React.Component { - if (!didMove) { - const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; - const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; - const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview - await ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5); - } - return ViewAdjustment.doNothing; - }, - }); - Doc.linkFollowHighlight(dv?.props.Document, false); + if ( + dv.props.focus(dv.props.Document, { + willZoomCentered: true, + zoomScale: 0.8, + zoomTime: browseTransitionTime, + }) === undefined + ) { + const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; + const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; + const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview + ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime); + Doc.linkFollowHighlight(dv?.props.Document, false); + } } ScriptingGlobals.add(CollectionBrowseClick); ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index bd33c4d80..a73627165 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -19,7 +19,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { OpenWhere } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; -import { VideoBox } from '../../nodes/VideoBox'; import { pasteImageBitmap } from '../../nodes/WebBoxRenderer'; import { PreviewCursor } from '../../PreviewCursor'; import { SubCollectionViewProps } from '../CollectionSubView'; @@ -167,7 +166,7 @@ export class MarqueeView extends React.Component { error && console.log(error); data && - VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => { + Utils.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => { this.props.Document['thumb-frozen'] = new ImageField(returnedfilename); }); }) diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index b9cda7130..d54e8ce98 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -152,7 +152,7 @@ export class CollectionLinearView extends CollectionSubView() { Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => ( <> - DocumentManager.Instance.jumpToDocument(clip.rootDoc, { willPanZoom: true }, undefined, [])}> + DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}> {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} clip.ComponentView?.TogglePause?.()} />{' '} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index b88da5191..88d045fa7 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -234,7 +234,6 @@ export class CollectionMulticolumnView extends CollectionSubView() { onChildClickHandler = () => ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); - focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { @@ -260,7 +259,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { hideResizeHandles={this.props.childHideResizeHandles?.()} hideDecorationTitle={this.props.childHideDecorationTitle?.()} fitContentsToBox={this.props.fitContentsToBox} - focus={this.focusDocument} + focus={this.props.focus} docFilters={this.childDocFilters} docRangeFilters={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 407deaabd..f18917bef 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -234,7 +234,6 @@ export class CollectionMultirowView extends CollectionSubView() { onChildClickHandler = () => ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick); - focusDocument = (doc: Doc, options: DocFocusOptions) => this.props.focus(this.rootDoc, options); isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive(); isChildContentActive = () => (((this.props.childDocumentsActive?.() || this.Document._childDocumentsActive) && this.props.isDocumentActive?.() && SnappingManager.GetIsDragging()) || this.isContentActive() ? true : false); getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => { diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx index 97a6c5c18..18ddd881b 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx @@ -214,7 +214,7 @@ export class CollectionSchemaCell extends React.Component { const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); // Jump to the this document - DocumentManager.Instance.jumpToDocument(this._rowDoc, { willPan: true }, emptyFunction, targetContext ? [targetContext] : [], () => this.props.setPreviewDoc(this._rowDoc)); + DocumentManager.Instance.showDocument(this._rowDoc, { willPan: true }, () => this.props.setPreviewDoc(this._rowDoc)); } }; diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index a2a2255e6..cdac91a62 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -139,7 +139,7 @@ export class LinkMenuItem extends React.Component { ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined; - if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, { instant: true }); + if (focusDoc) this.props.docView.props.focus(focusDoc, { instant: true }); LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false); } } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 3966aecf6..4c26468b9 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -71,12 +71,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val))); CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); - CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode))); - const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so - doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) + CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => (doc[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', doc, currTimecode))); + const targetDoc = doc; // data fields, like rtf 'text' exist on the data doc, so + //doc !== targetDoc && (targetDoc.context = doc.context); // the computed fields don't see the layout doc -- need to copy the context to the data doc (HACK!!!) and set the activeFrame on the data doc (HACK!!!) targetDoc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); targetDoc.dataTransition = 'inherit'; }); @@ -190,7 +190,6 @@ export class CollectionFreeFormDocumentView extends DocComponent this.sizeProvider?.width || this.props.PanelWidth?.(); panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.(); screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y); - focusDoc = (doc: Doc) => this.props.focus(doc, {}); returnThis = () => this; render() { TraceMobx(); diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index dd03b9b99..ecffe6c4f 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { emptyFunction, OmitKeys, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; @@ -12,6 +13,7 @@ import { StyleProp } from '../StyleProvider'; import './ComparisonBox.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { PinProps, PresBox } from './trails'; import React = require('react'); @observer @@ -24,6 +26,10 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { this._disposers[disposerId]?.(); if (ele) { @@ -72,6 +78,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { + const anchor = Docs.Create.ImageanchorDocument({ title: 'ImgAnchor:' + this.rootDoc.title, presTransition: 1000, unrendered: true, annotationOn: this.rootDoc }); + if (anchor) { + if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; + /* addAsAnnotation &&*/ this.addDocument(anchor); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.rootDoc); + return anchor; + } + return this.rootDoc; + }; + @undoBatch clearDoc = (e: React.MouseEvent, fieldKey: string) => { e.stopPropagation; // prevent click event action (slider movement) in registerSliding @@ -103,7 +120,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent { - whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true }); + //whichDoc !== targetDoc && r?.focus(whichDoc, { instant: true }); }} {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit} isContentActive={returnFalse} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f2aaa5cbc..dcccd1143 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -66,11 +66,6 @@ declare class MediaRecorder { constructor(e: any); } -export enum ViewAdjustment { - resetView = 1, - doNothing = 0, -} - export enum OpenWhere { lightbox = 'lightbox', add = 'add', @@ -96,33 +91,30 @@ export enum OpenWhereMod { } export interface DocFocusOptions { - originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab willPan?: boolean; // determines whether to pan to target document - willPanZoom?: boolean; // determines whether to zoom in on target document + willZoomCentered?: boolean; // determines whether to zoom in on target document zoomScale?: number; // percent of containing frame to zoom into document zoomTime?: number; - afterFocus?: DocAfterFocusFunc; // function to call after focusing on a document docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) preview?: boolean; // whether changes should be previewed by the componentView or written to the document effect?: Doc; // animation effect for focus noSelect?: boolean; // whether target should be selected after focusing playAudio?: boolean; // whether to play audio annotation on focus - openInLightbox?: boolean; // whether to open target in lightbox or just focus on it + openLocation?: string; // where to open a missing document zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections toggleTarget?: boolean; // whether to toggle target on and off originatingDoc?: Doc; // document that triggered the focus easeFunc?: 'linear' | 'ease'; // transition method for scrolling } -export type DocAfterFocusFunc = (notFocused: boolean) => Promise; -export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void; +export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => Opt; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { updateIcon?: () => void; // updates the icon representation of the document getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => 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) - scrollPreview?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus - scrollFocus?: (docView: DocumentView, doc: Doc, options: DocFocusOptions) => Opt; // returns the duration of the focus + scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: DocFocusOptions) => Opt; // returns the duration of the focus brushView?: (view: { width: number; height: number; panX: number; panY: number }) => void; + getView?: (doc: Doc) => Promise>; // returns a nested DocumentView for the specified doc or undefined addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. shrinkWrap?: () => void; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views @@ -131,10 +123,10 @@ export interface DocComponentView { getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; - Pause?: () => void; - IsPlaying?: () => boolean; - TogglePause?: (keep?: boolean) => void; - setFocus?: () => void; + Pause?: () => void; // pause a media document (eg, audio/video) + IsPlaying?: () => boolean; // is a media document playing + TogglePause?: (keep?: boolean) => void; // toggle media document playing state + setFocus?: () => void; // sets input focus to the componentView componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null; incrementalRendering?: () => void; fieldKey?: string; @@ -583,32 +575,15 @@ export class DocumentViewInternal extends DocComponent { - LightboxView.SetCookie(StrCast(anchor['cookies-set'])); - - // Restore viewing specification of target by reading them out of the anchor and applying to the target doc. - const presItem = DocCast(options.originatingDoc); // if originating doc was a presItem, then anchor will be its proto. use presItem instead - const targetMatch = Doc.AreProtosEqual(anchor, this.rootDoc) || (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) ? true : false; - const scrollFocus = - (LinkDocPreview.LinkInfo ? this._componentView?.scrollPreview : undefined) ?? - this._componentView?.scrollFocus ?? - ((docView: DocumentView, anchor: Doc, options: DocFocusOptions) => (focusSpeed => (PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined))(options.instant ? 0 : options.zoomTime ?? 500)); - const focusSpeed = targetMatch && scrollFocus?.(this.props.DocumentView(), presItem?.proto === anchor ? presItem : anchor, options); - - // FOCUS: navigate through the display hierarchy making sure the target is in view - const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing; - const startTime = Date.now(); - this.props.focus(options?.docTransform ? anchor : this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => - new Promise(async res => - setTimeout( - async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), // - didFocus ? Math.max(0, (options.zoomTime ?? 500) - (Date.now() - startTime)) : 0 - ) - ), - }); + defaultRestoreTargetView = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => { + const targetMatch = + Doc.AreProtosEqual(anchor, this.rootDoc) || // anchor is this document, so anchor's properties apply to this document + (DocCast(anchor)?.unrendered && Doc.AreProtosEqual(DocCast(anchor.annotationOn), this.rootDoc)) // the anchor is an unrendered annotation on this document, so anchor properties applie to this document + ? true + : false; + return targetMatch && PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; }; + onClick = action((e: React.MouseEvent | React.PointerEvent) => { if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { let stopPropagate = true; @@ -1201,7 +1176,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints} @@ -1242,8 +1217,8 @@ export class DocumentViewInternal extends DocComponent Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) || Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) || - ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) || - ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc)) + ((link.anchor1 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor1 as Doc)?.annotationOn as Doc, this.rootDoc)) || + ((link.anchor2 as Doc)?.unrendered && Doc.AreProtosEqual((link.anchor2 as Doc)?.annotationOn as Doc, this.rootDoc)) ); } @computed get allLinks() { @@ -1721,7 +1696,6 @@ export class DocumentView extends React.Component { } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); - focus = (doc: Doc, options: DocFocusOptions) => this.docView?.focus(doc, options); getBounds = () => { if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 78ba499ec..d41a4bb82 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -465,7 +465,7 @@ export class FilterBox extends ViewBoxBaseComponent() { isContentActive={returnTrue} whenChildContentsActiveChanged={returnFalse} treeViewHideTitle={true} - focus={returnFalse} + focus={emptyFunction} onCheckedClick={this.scriptField} treeViewHideHeaderFields={false} dontRegisterView={true} diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 8fa01c97a..75b4907cd 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -15,6 +15,7 @@ import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { DocFocusOptions, DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { PinProps, PresBox } from './trails'; const EquationSchema = createSchema({}); @@ -37,23 +38,18 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent componentDidMount() { this.props.setContentView?.(this); reaction( - () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.dataDoc.xRange, this.dataDoc.yRange], + () => [DocListCast(this.dataDoc[this.fieldKey]).map(doc => doc?.text), this.layoutDoc.width, this.layoutDoc.height, this.rootDoc.xRange, this.rootDoc.yRange], () => this.createGraph() ); } - getAnchor = (addAsAnnotation: boolean) => { + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); - anchor.xRange = new List(Array.from(this._plot.options.xAxis.domain)); - anchor.yRange = new List(Array.from(this._plot.options.yAxis.domain)); + PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.rootDoc); + anchor.presXRange = new List(Array.from(this._plot.options.xAxis.domain)); + anchor.presYRange = new List(Array.from(this._plot.options.yAxis.domain)); if (addAsAnnotation) this.addDocument(anchor); return anchor; }; - @action - scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { - this.dataDoc.xRange = new List(Array.from(Cast(doc.xRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10])))); - this.dataDoc.yRange = new List(Array.from(Cast(doc.yRange, listSpec('number'), Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9])))); - return 0; - }; createGraph = (ele?: HTMLDivElement) => { this._plotEle = ele || this._plotEle; const width = this.props.PanelWidth(); @@ -65,8 +61,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent target: '#' + this._plotEle.id, width, height, - xAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-10, 10]) }, - yAxis: { domain: Cast(this.dataDoc.xRange, listSpec('number'), [-1, 9]) }, + xAxis: { domain: Cast(this.rootDoc.xRange, listSpec('number'), [-10, 10]) }, + yAxis: { domain: Cast(this.rootDoc.yRange, listSpec('number'), [-1, 9]) }, grid: true, data: fns.map(fn => ({ fn, diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 363cd1d94..c6d4cb694 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -8,7 +8,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { DashColor, emptyFunction, OmitKeys, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; @@ -17,6 +17,7 @@ import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_ser import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; +import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../../views/ContextMenu'; @@ -26,15 +27,13 @@ import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComp import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; -import { DocFocusOptions, DocumentView, OpenWhere } from './DocumentView'; +import { OpenWhere } from './DocumentView'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import './ImageBox.scss'; import { PinProps, PresBox } from './trails'; import React = require('react'); import Color = require('color'); -import { LinkDocPreview } from './LinkDocPreview'; -import { DocumentManager } from '../../util/DocumentManager'; export const pageSchema = createSchema({ googlePhotosUrl: 'string', diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 1eab06381..bbe9b4323 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -253,7 +253,7 @@ export class LinkDocPreview extends React.Component { { const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor, {}); + targetanchor && this._targetDoc !== targetanchor && r?.props.focus?.(targetanchor, {}); }} Document={this._targetDoc!} moveDocument={returnFalse} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 6b2f2246a..5f207cc0d 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -11,6 +11,7 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; @@ -27,7 +28,6 @@ import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './PDFBox.scss'; import { PinProps, PresBox } from './trails'; -import { VideoBox } from './VideoBox'; import React = require('react'); @observer @@ -141,7 +141,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - VideoBox.convertDataUri(data_url, region[Id]).then(returnedfilename => + Utils.convertDataUri(data_url, region[Id]).then(returnedfilename => setTimeout( action(() => { croppingProto.data = new ImageField(returnedfilename); @@ -207,17 +207,16 @@ export class PDFBox extends ViewBoxAnnotatableComponent { - let didToggle = false; - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - didToggle = true; - } - if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + focus = (anchor: Doc, options: DocFocusOptions) => { this._initialScrollTarget = anchor; - PresBox.restoreTargetDocView(docView, anchor, options.zoomTime ?? 500); - return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.presPinViewScroll, NumCast(anchor.y)), options) ?? (didToggle ? 1 : undefined); + return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.presViewScroll)), options); + }; + + getView = async (doc: Doc) => { + if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); }; + getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { let ele: Opt = undefined; if (this._pdfViewer?.selectionContent()) { @@ -296,7 +295,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent this._playing; - _keepCurrentlyPlaying = false; TogglePause = () => { if (!this._playing) this.Play(); else { @@ -369,7 +344,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); + Utils.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY)); } }; @@ -439,6 +414,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + // return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + // }; + // extracts video thumbnails and saves them as field of doc getVideoThumbnails = () => { if (this.dataDoc.thumbnails !== undefined) return; @@ -455,7 +434,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent a.textInlineAnnotations); } @computed get webField() { - return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; + return Cast(this.rootDoc[this.props.fieldKey], WebField)?.url; } @computed get webThumb() { return ( @@ -163,7 +163,7 @@ export class WebBox extends ViewBoxAnnotatableComponent + Utils.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename => setTimeout( action(() => { this.rootDoc.thumbLockout = false; @@ -188,10 +188,10 @@ export class WebBox extends ViewBoxAnnotatableComponent this._urlHash + '-annotations'; const reqdFuncs: { [key: string]: string } = {}; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) - reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`; + reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"])`; reqdFuncs[this.fieldKey + '-annotations-setter'] = `this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"] = value`; - reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`; - DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs); + reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"])`; + DocUtils.AssignScripts(this.rootDoc, {}, reqdFuncs); }); reaction( () => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document), @@ -294,32 +294,25 @@ export class WebBox extends ViewBoxAnnotatableComponent void) => (this._setBrushViewer = func); brushView = (view: { width: number; height: number; panX: number; panY: number }) => this._setBrushViewer?.(view); - focus = (doc: Doc, options: DocFocusOptions) => { - !doc.unrendered && this.props.DocumentView?.() && this.scrollFocus(this.props.DocumentView?.(), doc, {}); - this.props.focus(doc, options); - }; - scrollFocus = (docView: DocumentView, anchor: Doc, options: DocFocusOptions) => { - if (this._url && StrCast(anchor.webUrl) !== this._url) this.submitURL(StrCast(anchor.webUrl), options.preview); - if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(anchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - } - if (this._sidebarRef?.current?.makeDocUnfiltered(anchor)) return 1; + focus = (anchor: Doc, options: DocFocusOptions) => { if (anchor !== this.rootDoc && this._outerRef.current) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = !anchor[HeightSym]() - ? NumCast(anchor.y) - : Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); - const focusSpeed = options.instant ? 0 : options.zoomTime ?? 500; - if (scrollTo !== undefined && this._initialScroll === undefined) { - this.goTo(scrollTo, focusSpeed, options.easeFunc); - PresBox.restoreTargetDocView(docView, anchor, focusSpeed); - return focusSpeed; - } else if (!this._webPageHasBeenRendered || !this._scrollHeight || this._initialScroll !== undefined) { - this._initialScroll = scrollTo; - return PresBox.restoreTargetDocView(docView, anchor, focusSpeed) ? focusSpeed : undefined; + const scrollTo = Utils.scrollIntoView(NumCast(anchor.y), anchor[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(anchor.y) + anchor[HeightSym](), this._scrollHeight)); + if (scrollTo !== undefined) { + if (this._initialScroll === undefined) { + this.goTo(scrollTo, options.zoomTime ?? 500, options.easeFunc); + } else { + this._initialScroll = scrollTo; + } } } - return undefined; + }; + + getView = async (doc: Doc) => { + if (this.rootDoc.layoutKey === 'layout_icon') this.props.DocumentView?.().iconify(); + if (this._url && StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl)); + if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); }; sidebarAddDocTab = (doc: Doc, where: OpenWhere) => { @@ -545,14 +538,14 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'), []); - const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []); + const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string'), []); + const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []); if (checkAvailable) return future.length; runInAction(() => { if (future.length) { const curUrl = this._url; - this.dataDoc[this.fieldKey + '-history'] = new List([...history, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); + this.rootDoc[this.fieldKey + '-history'] = new List([...history, this._url]); + this.rootDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -566,15 +559,15 @@ export class WebBox extends ViewBoxAnnotatableComponent { - const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string')); - const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []); + const future = Cast(this.rootDoc[this.fieldKey + '-future'], listSpec('string')); + const history = Cast(this.rootDoc[this.fieldKey + '-history'], listSpec('string'), []); if (checkAvailable) return history.length; runInAction(() => { if (history.length) { const curUrl = this._url; - if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List([this._url]); - else this.dataDoc[this.fieldKey + '-future'] = new List([...future, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); + if (future === undefined) this.rootDoc[this.fieldKey + '-future'] = new List([this._url]); + else this.rootDoc[this.fieldKey + '-future'] = new List([...future, this._url]); + this.layoutDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); if (this._webUrl === this._url) { this._webUrl = curUrl; setTimeout(action(() => (this._webUrl = this._url))); @@ -601,11 +594,11 @@ export class WebBox extends ViewBoxAnnotatableComponent([...(history || []), url]); + this.rootDoc[this.fieldKey + '-history'] = new List([...(history || []), url]); this.layoutDoc._scrollTop = 0; if (this._webPageHasBeenRendered) { this.layoutDoc.thumb = undefined; @@ -616,7 +609,7 @@ export class WebBox extends ViewBoxAnnotatableComponent e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />; @@ -849,7 +842,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); if (!nativeWidth) { - const defaultNativeWidth = this.dataDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym](); + const defaultNativeWidth = this.rootDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym](); Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth); Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth); nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']); @@ -1006,6 +999,7 @@ export class WebBox extends ViewBoxAnnotatableComponent (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance?.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() || Doc.ActiveTool !== InkTool.None ? 'all' : 'none'); render() { + setTimeout(() => DocListCast(this.rootDoc[this.annotationKey]).forEach(doc => (doc.webUrl = this._url))); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const scale = previewScale * (this.props.NativeDimScaling?.() || 1); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index dc7a11e6e..d857b150c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -230,16 +230,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (DocListCast(this.props.Document[this.props.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) { - this.toggleSidebar(false); - return true; - } - return this.props.addDocTab(doc, where); - }; - getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { - if (!pinProps) return this.rootDoc; + if (!pinProps && this._editorView?.state.selection.empty) return this.rootDoc; const anchor = Docs.Create.TextanchorDocument({ annotationOn: this.rootDoc, unrendered: true }); this.addDocument(anchor); this.makeLinkAnchor(anchor, OpenWhere.addRight, undefined, 'Anchored Selection', false, addAsAnnotation); @@ -948,12 +940,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - let didToggle = false; - if (DocListCast(this.Document[this.fieldKey + '-sidebar']).includes(textAnchor) && !this.SidebarShown) { - this.toggleSidebar(options.preview); - didToggle = true; + getView = async (doc: Doc) => { + if (DocListCast(this.rootDoc[this.SidebarKey]).find(anno => Doc.AreProtosEqual(doc.unrendered ? DocCast(doc.annotationOn) : doc, anno))) { + !this.SidebarShown && this.toggleSidebar(false); + setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc)); } + return new Promise>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv))); + }; + focus = (textAnchor: Doc, options: DocFocusOptions) => { + const focusSpeed = options.zoomTime ?? 500; const textAnchorId = textAnchor[Id]; const findAnchorFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; @@ -992,7 +987,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 2 || (content?.length && content[0].type === this._editorView.state.schema.nodes.audiotag)) && ret.start >= 0) { - !options.instant && (this._focusSpeed = 500); + !options.instant && (this._focusSpeed = focusSpeed); let selection = TextSelection.near(editor.state.doc.resolve(ret.start)); // default to near the start if (ret.frag.firstChild) { selection = TextSelection.between(editor.state.doc.resolve(ret.start), editor.state.doc.resolve(ret.start + ret.frag.firstChild.nodeSize)); // bcz: looks better to not have the target selected @@ -1004,9 +999,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent clearStyleSheetRules(FormattedTextBox._highlightStyleSheet), Math.max(this._focusSpeed || 0, 3000)); } } - const finalFocusSpeed = this._didScroll ? this._focusSpeed : didToggle ? 1 : undefined; - PresBox.restoreTargetDocView(docView, textAnchor, finalFocusSpeed ?? 0); - return finalFocusSpeed; // if we actually scrolled, then return some focusSpeed }; // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc. diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index fd202590e..eb91c82f3 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -12,7 +12,7 @@ height: 100%; min-height: 35px; letter-spacing: 2px; - overflow: hidden; + //overflow: hidden; transition: 0.7s opacity ease; .presBox-listCont { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ab4822117..94962650a 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -46,6 +46,7 @@ export interface pinDataTypes { pivot?: boolean; temporal?: boolean; clippable?: boolean; + datarange?: boolean; dataview?: boolean; textview?: boolean; poslayoutview?: boolean; @@ -329,6 +330,7 @@ export class PresBox extends ViewBoxBaseComponent() { const pannable = [DocumentType.IMG, DocumentType.PDF].includes(targetType) || (targetType === DocumentType.COL && target?._viewType === CollectionViewType.Freeform); const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(targetType); const clippable = [DocumentType.COMPARISON].includes(targetType); + const datarange = [DocumentType.FUNCPLOT].includes(targetType); const dataview = [DocumentType.INK, DocumentType.COL, DocumentType.IMG].includes(targetType) && target?.activeFrame === undefined; const poslayoutview = [DocumentType.COL].includes(targetType) && target?.activeFrame === undefined; const textview = [DocumentType.RTF].includes(targetType) && target?.activeFrame === undefined; @@ -350,23 +352,33 @@ export class PresBox extends ViewBoxBaseComponent() { if ( bestTarget.x !== NumCast(activeItem.presX, NumCast(bestTarget.x)) || bestTarget.y !== NumCast(activeItem.presY, NumCast(bestTarget.y)) || - bestTarget.rotation !== NumCast(activeItem.presRot, NumCast(bestTarget.rotation)) || + bestTarget.rotation !== NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)) || bestTarget.width !== NumCast(activeItem.presWidth, NumCast(bestTarget.width)) || bestTarget.height !== NumCast(activeItem.presHeight, NumCast(bestTarget.height)) ) { bestTarget._dataTransition = `all ${transTime}ms`; bestTarget.x = NumCast(activeItem.presX, NumCast(bestTarget.x)); bestTarget.y = NumCast(activeItem.presY, NumCast(bestTarget.y)); - bestTarget.rotation = NumCast(activeItem.presRot, NumCast(bestTarget.rotation)); + bestTarget.rotation = NumCast(activeItem.presRotation, NumCast(bestTarget.rotation)); bestTarget.width = NumCast(activeItem.presWidth, NumCast(bestTarget.width)); bestTarget.height = NumCast(activeItem.presHeight, NumCast(bestTarget.height)); setTimeout(() => (bestTarget._dataTransition = undefined), transTime + 10); changed = true; } } - if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presPinClipWidth !== undefined)) { - if (bestTarget._clipWidth !== activeItem.presPinClipWidth) { - bestTarget._clipWidth = activeItem.presPinClipWidth; + if (pinDataTypes?.datarange || (!pinDataTypes && activeItem.presXRange !== undefined)) { + if (bestTarget.xRange !== activeItem.presXRange) { + bestTarget.xRange = (activeItem.presXRange as ObjectField)?.[Copy](); + changed = true; + } + if (bestTarget.yRange !== activeItem.presYRange) { + bestTarget.yRange = (activeItem.presYRange as ObjectField)?.[Copy](); + changed = true; + } + } + if (pinDataTypes?.clippable || (!pinDataTypes && activeItem.presClipWidth !== undefined)) { + if (bestTarget._clipWidth !== activeItem.presClipWidth) { + bestTarget._clipWidth = activeItem.presClipWidth; changed = true; } } @@ -381,8 +393,8 @@ export class PresBox extends ViewBoxBaseComponent() { Doc.GetProto(bestTarget).fillColor = activeItem.presFillColor; changed = true; } - if (bestTarget.color !== activeItem.color) { - Doc.GetProto(bestTarget).color = activeItem.color; + if (bestTarget.color !== activeItem.presColor) { + Doc.GetProto(bestTarget).color = activeItem.presColor; changed = true; } if (bestTarget.width !== activeItem.width) { @@ -394,31 +406,31 @@ export class PresBox extends ViewBoxBaseComponent() { changed = true; } } - if ((pinDataTypes?.viewType && activeItem.presPinViewType !== undefined) || (!pinDataTypes && activeItem.presPinViewType !== undefined)) { - if (bestTarget._viewType !== activeItem.presPinViewType) { - bestTarget._viewType = activeItem.presPinViewType; + if ((pinDataTypes?.viewType && activeItem.presViewType !== undefined) || (!pinDataTypes && activeItem.presViewType !== undefined)) { + if (bestTarget._viewType !== activeItem.presViewType) { + bestTarget._viewType = activeItem.presViewType; changed = true; } } - if ((pinDataTypes?.filters && activeItem.presPinDocFilters !== undefined) || (!pinDataTypes && activeItem.presPinDocFilters !== undefined)) { - if (bestTarget.docFilters !== activeItem.presPinDocFilters) { - bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presPinDocFilters as ObjectField) || new List([]); + if ((pinDataTypes?.filters && activeItem.presDocFilters !== undefined) || (!pinDataTypes && activeItem.presDocFilters !== undefined)) { + if (bestTarget.docFilters !== activeItem.presDocFilters) { + bestTarget.docFilters = ObjectField.MakeCopy(activeItem.presDocFilters as ObjectField) || new List([]); changed = true; } } - if ((pinDataTypes?.pivot && activeItem.presPinPvitoField !== undefined) || (!pinDataTypes && activeItem.presPinPivotField !== undefined)) { - if (bestTarget.pivotField !== activeItem.presPinPivotField) { - bestTarget.pivotField = activeItem.presPinPivotField; + if ((pinDataTypes?.pivot && activeItem.presPivotField !== undefined) || (!pinDataTypes && activeItem.presPivotField !== undefined)) { + if (bestTarget.pivotField !== activeItem.presPivotField) { + bestTarget.pivotField = activeItem.presPivotField; bestTarget._prevFilterIndex = 1; // need to revisit this...see CollectionTimeView changed = true; } } - if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presPinViewScroll !== undefined)) { - if (bestTarget._scrollTop !== activeItem.presPinViewScroll) { - bestTarget._scrollTop = activeItem.presPinViewScroll; + if (pinDataTypes?.scrollable || (!pinDataTypes && activeItem.presViewScroll !== undefined)) { + if (bestTarget._scrollTop !== activeItem.presViewScroll) { + bestTarget._scrollTop = activeItem.presViewScroll; changed = true; const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { @@ -479,7 +491,7 @@ export class PresBox extends ViewBoxBaseComponent() { }); setTimeout(() => Array.from(transitioned).forEach(action(doc => (doc._dataTransition = undefined))), transTime + 10); } - if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPinViewX !== undefined || activeItem.presPinViewScale !== undefined))) { + if (pinDataTypes?.pannable || (!pinDataTypes && (activeItem.presPinViewBounds !== undefined || activeItem.presPanX !== undefined || activeItem.presViewScale !== undefined))) { const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; @@ -492,10 +504,10 @@ export class PresBox extends ViewBoxBaseComponent() { dv.ComponentView?.brushView?.(viewport); } } else { - if (bestTarget._panX !== activeItem.presPinViewX || bestTarget._panY !== activeItem.presPinViewY || bestTarget._viewScale !== activeItem.presPinViewScale) { - bestTarget._panX = activeItem.presPinViewX; - bestTarget._panY = activeItem.presPinViewY; - bestTarget._viewScale = activeItem.presPinViewScale; + if (bestTarget._panX !== activeItem.presPanX || bestTarget._panY !== activeItem.presPanY || bestTarget._viewScale !== activeItem.presViewScale) { + bestTarget._panX = activeItem.presPanX; + bestTarget._panY = activeItem.presPanY; + bestTarget._viewScale = activeItem.presViewScale; changed = true; } } @@ -514,7 +526,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presPinLayout = true; pinDoc.presX = NumCast(targetDoc.x); pinDoc.presY = NumCast(targetDoc.y); - pinDoc.presRot = NumCast(targetDoc.rotation); + pinDoc.presRotation = NumCast(targetDoc.rotation); pinDoc.presWidth = NumCast(targetDoc.width); pinDoc.presHeight = NumCast(targetDoc.height); } @@ -526,6 +538,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinProps.pinData.pannable || pinProps.pinData.viewType || pinProps.pinData.clippable || + pinProps.pinData.datarange || pinProps.pinData.dataview || pinProps.pinData.textview || pinProps.pinData.poslayoutview || @@ -546,8 +559,12 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presWidth = targetDoc._width; pinDoc.presHeight = targetDoc._height; } - if (pinProps.pinData.scrollable) pinDoc.presPinViewScroll = targetDoc._scrollTop; - if (pinProps.pinData.clippable) pinDoc.presPinClipWidth = targetDoc._clipWidth; + if (pinProps.pinData.scrollable) pinDoc.presViewScroll = targetDoc._scrollTop; + if (pinProps.pinData.clippable) pinDoc.presClipWidth = targetDoc._clipWidth; + if (pinProps.pinData.datarange) { + pinDoc.presXRange = undefined; //targetDoc?.xrange; + pinDoc.presYRange = undefined; //targetDoc?.yrange; + } if (pinProps.pinData.poslayoutview) pinDoc.presPinLayoutData = new List( DocListCast(targetDoc[fkey] as ObjectField).map(d => @@ -564,13 +581,13 @@ export class PresBox extends ViewBoxBaseComponent() { }) ) ); - if (pinProps.pinData.viewType) pinDoc.presPinViewType = targetDoc._viewType; - if (pinProps.pinData.filters) pinDoc.presPinDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField); - if (pinProps.pinData.pivot) pinDoc.presPinPivotField = targetDoc._pivotField; + if (pinProps.pinData.viewType) pinDoc.presViewType = targetDoc._viewType; + if (pinProps.pinData.filters) pinDoc.presDocFilters = ObjectField.MakeCopy(targetDoc.docFilters as ObjectField); + if (pinProps.pinData.pivot) pinDoc.presPivotField = targetDoc._pivotField; if (pinProps.pinData.pannable) { - pinDoc.presPinViewX = NumCast(targetDoc._panX); - pinDoc.presPinViewY = NumCast(targetDoc._panY); - pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); + pinDoc.presPanX = NumCast(targetDoc._panX); + pinDoc.presPanY = NumCast(targetDoc._panY); + pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1); } if (pinProps.pinData.temporal) { pinDoc.presStartTime = targetDoc._currentTimecode; @@ -582,9 +599,9 @@ export class PresBox extends ViewBoxBaseComponent() { // If pinWithView option set then update scale and x / y props of slide const bounds = pinProps.pinViewport; pinDoc.presPinView = true; - pinDoc.presPinViewScale = NumCast(targetDoc._viewScale, 1); - pinDoc.presPinViewX = bounds.left + bounds.width / 2; - pinDoc.presPinViewY = bounds.top + bounds.height / 2; + pinDoc.presViewScale = NumCast(targetDoc._viewScale, 1); + pinDoc.presPanX = bounds.left + bounds.width / 2; + pinDoc.presPanY = bounds.top + bounds.height / 2; pinDoc.presPinViewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } } @@ -618,14 +635,10 @@ export class PresBox extends ViewBoxBaseComponent() { } finished(); }); - const createDocView = (doc: Doc, finished?: () => void) => { - DocumentManager.Instance.AddViewRenderedCb(doc, () => finished?.()); - LightboxView.AddDocTab(doc, OpenWhere.lightbox); - }; - PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, resetSelection); + PresBox.NavigateToTarget(targetDoc, activeItem, resetSelection); }; - static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, finished?: () => void) { + static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) { if (activeItem.presMovement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; @@ -634,7 +647,7 @@ export class PresBox extends ViewBoxBaseComponent() { const presTime = NumCast(activeItem.presTransition, effect ? 750 : 500); const options: DocFocusOptions = { willPan: activeItem.presMovement !== PresMovement.None, - willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, + willZoomCentered: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, zoomScale: activeItem.presMovement === PresMovement.Center ? 0 : NumCast(activeItem.presZoom, 1), zoomTime: activeItem.presMovement === PresMovement.Jump ? 0 : Math.min(Math.max(effect ? 750 : 500, (effect ? 0.2 : 1) * presTime), presTime), effect: activeItem, @@ -644,38 +657,23 @@ export class PresBox extends ViewBoxBaseComponent() { zoomTextSelections: BoolCast(activeItem.presZoomText), playAudio: BoolCast(activeItem.presPlayAudio), }; - const restoreLayout = () => { - // After navigating to the document, if it is added as a presPinView then it will - // adjust the pan and scale to that of the pinView when it was added. - const pinDocLayout = (BoolCast(activeItem.presPinLayout) || BoolCast(activeItem.presPinView)) && DocCast(targetDoc.context)?._currentFrame === undefined; - if (activeItem.presPinData || activeItem.presPinView || pinDocLayout) { - // targetDoc may or may not be displayed. so get the first available document (or alias) view that matches targetDoc and use it - // PresBox.restoreTargetDocView(DocumentManager.Instance.getFirstDocumentView(targetDoc), { pinDocLayout }, activeItem, NumCast(activeItem.presTransition, 500), undefined, targetDoc); - } - }; - const finishAndRestoreLayout = () => { - finished?.(); - restoreLayout(); - }; - const containerDocContext = DocumentManager.GetContextPath(targetDoc); - - let context = containerDocContext.length ? containerDocContext[0] : targetDoc; if (activeItem.presOpenInLightbox) { - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(DocCast(targetDoc.annotationOn) ?? targetDoc))) { - context = DocCast(targetDoc.annotationOn) ?? targetDoc; - LightboxView.SetLightboxDoc(context); // openInTab(targetDoc); + const context = DocCast(targetDoc.annotationOn) ?? targetDoc; + if (!DocumentManager.Instance.getLightboxDocumentView(context)) { + LightboxView.SetLightboxDoc(context); } } if (targetDoc) { if (activeItem.presentationTargetDoc instanceof Doc) activeItem.presentationTargetDoc[AnimationSym] = undefined; DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => { - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(context.annotationOn) ?? context)) { + // if target or the doc it annotates is not in the lightbox, then close the lightbox + if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { LightboxView.SetLightboxDoc(undefined); } - DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finishAndRestoreLayout); + DocumentManager.Instance.showDocument(targetDoc, options, finished); }); - } else finishAndRestoreLayout(); + } else finished?.(); } /** @@ -992,11 +990,14 @@ export class PresBox extends ViewBoxBaseComponent() { presDocView && SelectionManager.SelectView(presDocView, false); }; - focusElement = (doc: Doc, options: DocFocusOptions) => this.selectElement(doc); + focusElement = (doc: Doc, options: DocFocusOptions) => { + this.selectElement(doc); + return undefined; + }; //Regular click @action - selectElement = async (doc: Doc, noNav = false) => { + selectElement = (doc: Doc, noNav = false) => { CollectionStackedTimeline.CurrentlyPlaying?.map((clip, i) => clip?.ComponentView?.Pause?.()); if (noNav) { const index = this.childDocs.indexOf(doc); @@ -1192,15 +1193,15 @@ export class PresBox extends ViewBoxBaseComponent() { } } else if (doc.presPinView && presCollection === tagDoc && dv) { // Case B: Document is presPinView and is presCollection - const scale: number = 1 / NumCast(doc.presPinViewScale); + const scale: number = 1 / NumCast(doc.presViewScale); const height: number = dv.props.PanelHeight() * scale; const width: number = dv.props.PanelWidth() * scale; const indWidth = width / 10; const indHeight = Math.max(height / 10, 15); const indEdge = Math.max(indWidth, indHeight); const indFontSize = indEdge * 0.8; - const xLoc: number = NumCast(doc.presPinViewX) - width / 2; - const yLoc: number = NumCast(doc.presPinViewY) - height / 2; + const xLoc: number = NumCast(doc.presPanX) - width / 2; + const yLoc: number = NumCast(doc.presPanY) - height / 2; docs.push(tagDoc); order.push( <> @@ -1233,8 +1234,8 @@ export class PresBox extends ViewBoxBaseComponent() { if ((index = 0)) pathPoints = n1x + ',' + n1y; else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; } else if (doc.presPinView) { - const n1x = NumCast(doc.presPinViewX); - const n1y = NumCast(doc.presPinViewY); + const n1x = NumCast(doc.presPanX); + const n1y = NumCast(doc.presPanY); if ((index = 0)) pathPoints = n1x + ',' + n1y; else pathPoints = pathPoints + ' ' + n1x + ',' + n1y; } @@ -1960,12 +1961,6 @@ export class PresBox extends ViewBoxBaseComponent() { ); } - scrollFocus = (docView: DocumentView, doc: Doc, options: DocFocusOptions) => { - // this.gotoDocument(0); - // this.startOrPause(false); - return undefined; - }; - _keyTimer: NodeJS.Timeout | undefined; /** @@ -2041,9 +2036,10 @@ export class PresBox extends ViewBoxBaseComponent() { const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); const activeColor = Colors.LIGHT_BLUE; const inactiveColor = Colors.WHITE; - return mode === CollectionViewType.Carousel3D ? null : ( + return mode === CollectionViewType.Carousel3D || inOverlay ? null : (
{/*
{"Add new slide"}
}>
this.newDocumentTools = !this.newDocumentTools)}> @@ -2404,11 +2400,7 @@ export class PresBox extends ViewBoxBaseComponent() { ); } static NavigateToDoc(bestTarget: Doc, activeItem: Doc) { - const openInTab = (doc: Doc, finished?: () => void) => { - CollectionDockingView.AddSplit(doc, OpenWhereMod.right); - finished?.(); - }; - PresBox.NavigateToTarget(bestTarget, activeItem, openInTab); + PresBox.NavigateToTarget(bestTarget, activeItem); } } diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index d19b78dbc..698a2817e 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -297,7 +297,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const targetDoc = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; activeItem.presX = NumCast(targetDoc.x); activeItem.presY = NumCast(targetDoc.y); - activeItem.presRot = NumCast(targetDoc.rotation); + activeItem.presRotation = NumCast(targetDoc.rotation); activeItem.presWidth = NumCast(targetDoc.width); activeItem.presHeight = NumCast(targetDoc.height); activeItem.presPinLayout = true; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ffc49e4f3..86d14c22e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -179,10 +179,10 @@ export class PDFViewer extends React.Component { let focusSpeed: Opt; if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); - const scrollTo = doc.unrendered ? scrollTop : Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); + const scrollTo = Utils.scrollIntoView(scrollTop, doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, this._scrollHeight); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { if (!this._pdfViewer) this._initialScroll = { loc: scrollTo, easeFunc: options.easeFunc }; - else if (!options.instant && !options.preview) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); + else if (!options.instant) this._scrollStopper = smoothScroll((focusSpeed = options.zoomTime ?? 500), mainCont, scrollTo, options.easeFunc, this._scrollStopper); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { @@ -518,10 +518,6 @@ export class PDFViewer extends React.Component { return this.props.styleProvider?.(doc, props, property); }; - focus = (doc: Doc, options: DocFocusOptions) => { - !doc.unrendered && this.props.DocumentView?.() && this.scrollToAnnotation(doc); - this.props.focus(doc, options); - }; renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => (
{ PanelWidth={this.panelWidth} ScreenToLocalTransform={this.overlayTransform} dropAction={'alias'} - focus={this.focus} docFilters={docFilters} select={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 4c4275ce7..3ba60edd7 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -414,7 +414,7 @@ export class SearchBox extends ViewBoxBaseComponent() { * or opening it in a new tab. */ selectElement = async (doc: Doc, finishFunc: () => void) => { - await DocumentManager.Instance.jumpToDocument(doc, { willPanZoom: true }, undefined, [], finishFunc); + await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, finishFunc); }; /** diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 5f02bd73d..16da0f9e2 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -218,7 +218,12 @@ export class ComputedField extends ScriptField { doc[`${fieldKey}-indexed`] = flist; } const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {}); - const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); + const setField = ScriptField.CompileScript( + `{setIndexVal (self['${fieldKey}-indexed'], self.${interpolatorKey}, value); console.log(self["data-indexed"][self.${interpolatorKey}],self.data,self["data-indexed"]))}`, + { value: 'any' }, + false, + {} + ); return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } } diff --git a/src/fields/util.ts b/src/fields/util.ts index 6024705ec..e517e7604 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -335,8 +335,10 @@ export function setter(target: any, in_prop: string | symbol | number, value: an return true; } } - if (target.__fields[prop] instanceof ComputedField && target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { - return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + if (target.__fields[prop] instanceof ComputedField) { + if (target.__fields[prop].setterscript && value !== undefined && !(value instanceof ComputedField)) { + return ScriptCast(target.__fields[prop])?.setterscript?.run({ self: target[SelfProxy], this: target[SelfProxy], value }).success ? true : false; + } } return _setter(target, prop, value, receiver); } -- cgit v1.2.3-70-g09d2