diff options
author | bobzel <zzzman@gmail.com> | 2023-01-17 22:34:32 -0500 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2023-01-17 22:34:32 -0500 |
commit | d5f796b433d7e72130d4109a3775347ccb10c454 (patch) | |
tree | a6981c5d624270b73ec657721a63bfef73487ccf | |
parent | de02143333177a39851f60c540f5a75a303a1c48 (diff) |
fixed linkint to trail to follow trail immediately in lightbox and show trail ui in minimized mode. fixed overlay of pres box to not disappear when lightbox appears. closing /ending trail hackily restores collecftion to prior pan/zoom.
-rw-r--r-- | src/client/util/LinkFollower.ts | 19 | ||||
-rw-r--r-- | src/client/views/MainView.tsx | 14 | ||||
-rw-r--r-- | src/client/views/OverlayView.tsx | 136 | ||||
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/nodes/LinkDocPreview.tsx | 3 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 1 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.scss | 26 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresBox.tsx | 267 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 12 | ||||
-rw-r--r-- | src/fields/FieldLoader.scss | 9 | ||||
-rw-r--r-- | src/fields/FieldLoader.tsx | 14 |
12 files changed, 298 insertions, 211 deletions
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index 807b54cd5..0f216e349 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -2,11 +2,14 @@ import { action, runInAction } from 'mobx'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { DocumentDecorations } from '../views/DocumentDecorations'; import { LightboxView } from '../views/LightboxView'; -import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, ViewAdjustment } from '../views/nodes/DocumentView'; +import { DocFocusOptions, DocumentViewSharedProps, OpenWhere, OpenWhereMod, ViewAdjustment } 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'; type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void; @@ -116,6 +119,20 @@ export class LinkFollower { LightboxView.SetLightboxDoc(currentContext, undefined, tour); setTimeout(LightboxView.Next); allFinished(); + } else if (target.type === DocumentType.PRES) { + const containerAnnoDoc = Cast(sourceDoc, Doc, null); + const containerDoc = containerAnnoDoc || sourceDoc; + var containerDocContext = containerDoc?.context ? [Cast(await containerDoc?.context, Doc, null)] : ([] as Doc[]); + while (containerDocContext.length && containerDocContext[0]?.context && DocCast(containerDocContext[0].context)?.viewType !== CollectionViewType.Docking) { + containerDocContext = [Cast(await containerDocContext[0].context, Doc, null), ...containerDocContext]; + } + if (!DocumentManager.Instance.getDocumentView(containerDocContext[0])) { + CollectionDockingView.AddSplit(containerDocContext[0], OpenWhereMod.right); + } + SelectionManager.DeselectAll(); + DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext[0])); + PresBox.OpenPresMinimized(target, [0, 0]); + finished?.(); } else { const containerAnnoDoc = Cast(target.annotationOn, Doc, null); const containerDoc = containerAnnoDoc || target; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 39cc9ba8e..4494166f2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -574,7 +574,7 @@ export class MainView extends React.Component { Document={this.headerBarDoc} DataDoc={undefined} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={DefaultStyleProvider} @@ -611,7 +611,7 @@ export class MainView extends React.Component { Document={this.mainContainer!} DataDoc={undefined} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={this._hideUI ? DefaultStyleProvider : undefined} @@ -682,7 +682,7 @@ export class MainView extends React.Component { sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); - addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => { + static addDocTabFunc = (doc: Doc, location: OpenWhere): boolean => { const whereFields = doc._viewType === CollectionViewType.Docking ? [OpenWhere.dashboard] : location.split(':'); const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none; if (doc.dockingConfig) return DashboardView.openDashboard(doc); @@ -710,7 +710,7 @@ export class MainView extends React.Component { Document={this._sidebarContent.proto || this._sidebarContent} DataDoc={undefined} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} docViewPath={returnEmptyDoclist} styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider} @@ -744,7 +744,7 @@ export class MainView extends React.Component { Document={Doc.MyLeftSidebarMenu} DataDoc={undefined} addDocument={undefined} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} rootSelected={returnTrue} removeDocument={returnFalse} @@ -808,7 +808,7 @@ export class MainView extends React.Component { </div> )} <div className="properties-container" style={{ width: this.propertiesWidth() }}> - {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />} + {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={MainView.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />} </div> </div> </div> @@ -888,7 +888,7 @@ export class MainView extends React.Component { moveDocument={this.moveButtonDoc} CollectionView={undefined} addDocument={this.addButtonDoc} - addDocTab={this.addDocTabFunc} + addDocTab={MainView.addDocTabFunc} pinToPres={emptyFunction} removeDocument={this.remButtonDoc} ScreenToLocalTransform={this.buttonBarXf} diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 6d7f2c037..08285ff0c 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import * as React from 'react'; @@ -8,15 +8,17 @@ import { Id } from '../../fields/FieldSymbols'; import { NumCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils'; import { DocUtils } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { Transform } from '../util/Transform'; import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView'; import { LightboxView } from './LightboxView'; +import { MainView } from './MainView'; import { DocumentView } from './nodes/DocumentView'; import './OverlayView.scss'; import { ScriptingRepl } from './ScriptingRepl'; -import { DefaultStyleProvider } from './StyleProvider'; +import { DefaultStyleProvider, testDocProps } from './StyleProvider'; export type OverlayDisposer = () => void; @@ -170,74 +172,70 @@ export class OverlayView extends React.Component { ); @computed get overlayDocs() { - return LightboxView.LightboxDoc - ? null - : DocListCast(Doc.MyOverlayDocs?.data).map(d => { - let offsetx = 0, - offsety = 0; - const dref = React.createRef<HTMLDivElement>(); - const onPointerMove = action((e: PointerEvent, down: number[]) => { - if (e.buttons === 1) { - d.overlayX = e.clientX + offsetx; - d.overlayY = e.clientY + offsety; - } - if (e.metaKey) { - const dragData = new DragManager.DocumentDragData([d]); - dragData.offset = [-offsetx, -offsety]; - dragData.dropAction = 'move'; - dragData.removeDocument = (doc: Doc | Doc[]) => { - const docs = doc instanceof Doc ? [doc] : doc; - docs.forEach(d => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, d)); - return true; - }; - dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { - return dragData.removeDocument!(doc) ? addDocument(doc) : false; - }; - DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]); - return true; - } - return false; - }); + return DocListCast(Doc.MyOverlayDocs?.data) + .filter(d => !LightboxView.LightboxDoc || d.type === DocumentType.PRES) + .map(d => { + let offsetx = 0, + offsety = 0; + const dref = React.createRef<HTMLDivElement>(); + const onPointerMove = action((e: PointerEvent, down: number[]) => { + if (e.buttons === 1) { + d.overlayX = e.clientX + offsetx; + d.overlayY = e.clientY + offsety; + } + if (e.metaKey) { + const dragData = new DragManager.DocumentDragData([d]); + dragData.offset = [-offsetx, -offsety]; + dragData.dropAction = 'move'; + dragData.removeDocument = this.removeOverlayDoc; + dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => { + return dragData.removeDocument!(doc) ? addDocument(doc) : false; + }; + DragManager.StartDocumentDrag([dref.current!], dragData, down[0], down[1]); + return true; + } + return false; + }); - const onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction); - offsetx = NumCast(d.overlayX) - e.clientX; - offsety = NumCast(d.overlayY) - e.clientY; - }; - return ( - <div - className="overlayView-doc" - ref={dref} - key={d[Id]} - onPointerDown={onPointerDown} - style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.overlayX}px, ${d.overlayY}px)` }}> - <DocumentView - Document={d} - rootSelected={returnTrue} - bringToFront={emptyFunction} - addDocument={undefined} - removeDocument={this.removeOverlayDoc} - PanelWidth={d[WidthSym]} - PanelHeight={d[HeightSym]} - ScreenToLocalTransform={this.docScreenToLocalXf(d)} - renderDepth={1} - isDocumentActive={returnTrue} - isContentActive={returnTrue} - whenChildContentsActiveChanged={emptyFunction} - focus={DocUtils.DefaultFocus} - styleProvider={DefaultStyleProvider} - docViewPath={returnEmptyDoclist} - addDocTab={returnFalse} - pinToPres={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - /> - </div> - ); - }); + const onPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, onPointerMove, emptyFunction, emptyFunction); + offsetx = NumCast(d.overlayX) - e.clientX; + offsety = NumCast(d.overlayY) - e.clientY; + }; + return ( + <div + className="overlayView-doc" + ref={dref} + key={d[Id]} + onPointerDown={onPointerDown} + style={{ top: d.type === DocumentType.PRES ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.overlayX}px, ${d.overlayY}px)` }}> + <DocumentView + Document={d} + rootSelected={returnTrue} + bringToFront={emptyFunction} + addDocument={undefined} + removeDocument={this.removeOverlayDoc} + PanelWidth={d[WidthSym]} + PanelHeight={d[HeightSym]} + ScreenToLocalTransform={this.docScreenToLocalXf(d)} + renderDepth={1} + isDocumentActive={returnTrue} + isContentActive={returnTrue} + whenChildContentsActiveChanged={emptyFunction} + focus={DocUtils.DefaultFocus} + styleProvider={DefaultStyleProvider} + docViewPath={returnEmptyDoclist} + addDocTab={d.type === DocumentType.PRES ? MainView.addDocTabFunc : returnFalse} + pinToPres={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> + </div> + ); + }); } public static ShowSpinner() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 4c8c65707..8919b1c01 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -204,10 +204,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo const bclipped = bleft !== b.left || btop !== b.top; if (aclipped && bclipped) return undefined; const clipped = aclipped || bclipped; + const pt1inside = NumCast(LinkDocs[0].anchor1_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor1_y) % 100 !== 0; + const pt2inside = NumCast(LinkDocs[0].anchor2_x) % 100 !== 0 && NumCast(LinkDocs[0].anchor2_y) % 100 !== 0; const pt1 = [aleft + a.width / 2, atop + a.height / 2]; const pt2 = [bleft + b.width / 2, btop + b.width / 2]; - const pt2vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]]; - const pt1vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]]; + const pt2vec = pt2inside ? [-0.7071, 0.7071] : [(bDocBounds.left + bDocBounds.right) / 2 - pt2[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt2[1]]; + const pt1vec = pt1inside ? [-0.7071, 0.7071] : [(aDocBounds.left + aDocBounds.right) / 2 - pt1[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt1[1]]; const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]); const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]); const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 7fe74b876..1df46488b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1707,7 +1707,7 @@ export class DocumentView extends React.Component<DocumentViewProps> { 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.presBox || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { + if (!this.docView || !this.docView.ContentDiv || this.props.Document.type === DocumentType.PRES || this.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; } const xf = this.docView?.props diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index b2fdd93fc..232c3459e 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -17,7 +17,6 @@ import { Transform } from '../../util/Transform'; import { DocumentView, DocumentViewSharedProps, OpenWhere } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); -import { SelectionManager } from '../../util/SelectionManager'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -114,7 +113,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } this._toolTipText = ''; - if (LinkDocPreview.LinkInfo?.noPreview) this.followLink(); + if (LinkDocPreview.LinkInfo?.noPreview || this._markerTargetDoc?.type === DocumentType.PRES) this.followLink(); } }) ); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index b895043de..62e215521 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1476,6 +1476,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span> while (target && !target.dataset?.targethrefs) target = target.parentElement; FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true'); + if (target) return; } if (e.button === 0 && this.props.isSelected(true) && !e.altKey) { diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 4d60a02f1..fd202590e 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -938,14 +938,19 @@ min-width: 15px; max-width: 100px; left: 8px; + margin: auto; + margin-left: unset; + height: 100%; } .presBox-presentPanel { display: flex; justify-self: end; width: 100%; - max-width: 300px; - min-width: 150px; + margin: auto; + margin-right: unset; + height: 100%; + position: relative; } select { @@ -955,7 +960,7 @@ .presBox-button { cursor: pointer; - height: 25px; + //height: 100%; border-radius: 5px; display: none; justify-content: center; @@ -986,6 +991,9 @@ width: max-content; position: absolute; right: 10px; + margin: auto; + margin-right: unset; + height: 100%; .present-icon { margin-right: 7px; @@ -999,9 +1007,16 @@ position: relative; display: inline-block; left: 8px; + margin: auto; + margin-left: unset; } } +.presBox-buttons.inOverlay { + padding-top: unset; + padding-bottom: unset; +} + .presBox-backward, .presBox-forward { width: 25px; @@ -1052,6 +1067,8 @@ font-size: 30px; position: absolute; min-width: 50px; + margin: auto; + margin-left: unset; } } @@ -1087,13 +1104,12 @@ color: $white; border-radius: 5px; grid-template-rows: 100%; - height: 25; + height: 100%; width: max-content; min-width: max-content; justify-content: space-evenly; align-items: center; display: flex; - position: absolute; transition: all 0.2s; .presPanel-button-text { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index a609b46e4..855a7f171 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -12,7 +12,7 @@ import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { AudioField } from '../../../../fields/URLField'; -import { emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; +import { emptyFunction, emptyPath, returnFalse, returnOne, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; @@ -136,6 +136,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action componentWillUnmount() { this._unmounting = true; + if (this._presTimer) clearTimeout(this._presTimer); document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); this.resetPresentation(); // Turn of progressivize editors @@ -143,7 +144,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { Object.values(this._disposers).forEach(disposer => disposer?.()); } - @action componentDidMount() { this._disposers.keyboard = reaction( () => this.selectedDoc, @@ -167,10 +167,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { ); this.props.setContentView?.(this); this._unmounting = false; - this.rootDoc._forceRenderEngine = computeTimelineLayout.name; - this.layoutDoc.presStatus = PresStatus.Edit; - this.layoutDoc._gridGap = 0; - this.layoutDoc._yMargin = 0; + if (this.props.renderDepth > 0) { + runInAction(() => { + this.rootDoc._forceRenderEngine = computeTimelineLayout.name; + this.layoutDoc._gridGap = 0; + this.layoutDoc._yMargin = 0; + }); + } this.turnOffEdit(true); this._disposers.selection = reaction( () => SelectionManager.Views(), @@ -247,9 +250,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; this.nextSlide(curLast + 1 === this.childDocs.length ? (this.layoutDoc.presLoop ? 0 : curLast) : curLast + 1); - } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { - // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode - this.nextSlide(0); + } else { + if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { + // Case 2: Last slide and presLoop is toggled ON or it is in Edit mode + this.nextSlide(0); + } + return 0; } return this.itemIndex; }; @@ -510,28 +516,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { const srcContext = Cast(targetDoc.context, Doc, null) ?? Cast(Cast(targetDoc.annotationOn, Doc, null)?.context, Doc, null); const presCollection = Cast(this.layoutDoc.presCollection, Doc, null); const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined; - const includesDoc: boolean = DocumentManager.Instance.getDocumentView(targetDoc) ? true : false; // DocListCast(presCollection?.data).includes(targetDoc); + const includesDoc = () => (DocumentManager.Instance.getDocumentView(targetDoc) ? true : false); // DocListCast(presCollection?.data).includes(targetDoc); const tabMap = CollectionDockingView.Instance?.tabMap; const tab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc === srcContext || tab.DashDoc === targetDoc); // Handles the setting of presCollection - if (includesDoc) { + if (includesDoc()) { //Case 1: Pres collection should not change as it is already the same } else if (tab !== undefined) { // Case 2: Pres collection should update this.layoutDoc.presCollection = srcContext; } - const presStatus = this.rootDoc.presStatus; const selViewCache = Array.from(this.selectedArray); const dragViewCache = Array.from(this._dragArray); const eleViewCache = Array.from(this._eleArray); const resetSelection = action(() => { - const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc); - if (presDocView) SelectionManager.SelectView(presDocView, false); - this.rootDoc.presStatus = presStatus; - this.clearSelectedArray(); - selViewCache.forEach(doc => this.addToSelectedArray(doc)); - this._dragArray.splice(0, this._dragArray.length, ...dragViewCache); - this._eleArray.splice(0, this._eleArray.length, ...eleViewCache); + if (!includesDoc()) { + const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc); + if (presDocView) SelectionManager.SelectView(presDocView, false); + this.clearSelectedArray(); + selViewCache.forEach(doc => this.addToSelectedArray(doc)); + this._dragArray.splice(0, this._dragArray.length, ...dragViewCache); + this._eleArray.splice(0, this._eleArray.length, ...eleViewCache); + } finished(); }); const createDocView = (doc: Doc, finished?: () => void) => { @@ -539,7 +545,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { (collectionDocView ?? this).props.addDocTab(doc, OpenWhere.lightbox); this.layoutDoc.presCollection = targetDoc; }; - PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc || tab ? finished : resetSelection); + PresBox.NavigateToTarget(targetDoc, activeItem, createDocView, srcContext, includesDoc() || tab ? finished : resetSelection); }; static NavigateToTarget(targetDoc: Doc, activeItem: Doc, createDocView: any, srcContext: Doc, finished?: () => void) { @@ -562,7 +568,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { setTimeout(restoreLayout); } else { if (targetDoc) { - LightboxView.SetLightboxDoc(undefined); const options: DocFocusOptions = { willPan: activeItem.presMovement !== PresMovement.None, willPanZoom: activeItem.presMovement === PresMovement.Zoom || activeItem.presMovement === PresMovement.Jump || activeItem.presMovement === PresMovement.Center, @@ -579,10 +584,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) { containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext]; } - + const testTarget = containerDocContext.length ? containerDocContext[0] : targetDoc; + if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(testTarget)) { + DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, dv => { + if (LightboxView.LightboxDoc && !DocumentManager.Instance.getLightboxDocumentView(LightboxView.LightboxDoc)) { + DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); + restoreLayout(); + } else { + LightboxView.SetLightboxDoc(undefined); + DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); + restoreLayout(); + } + }); + return; + } DocumentManager.Instance.jumpToDocument(targetDoc, options, createDocView, containerDocContext, finished); - } - restoreLayout(); + restoreLayout(); + } else restoreLayout(); } } @@ -633,20 +651,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }); }; - //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc - @action - startAutoPres = async (startSlide: number) => { - if (!this.childDocs.length) return; - this.layoutDoc.presStatus = PresStatus.Autoplay; - this.startPresentation(startSlide); - clearTimeout(this._presTimer); - const func = (itemIndex: number) => { - this._presTimer = setTimeout(() => { - if (itemIndex === this.next()) this.layoutDoc.presStatus = PresStatus.Manual; - this.layoutDoc.presStatus !== PresStatus.Manual && func(this.itemIndex); - }, NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition)); + _exitTrail: Opt<() => void>; + PlayTrail = (doc: Doc) => { + const savedState = { c: doc, x: NumCast(doc.panX), y: NumCast(doc.panY), s: NumCast(doc.viewScale) }; + this.startPresentation(0); + this._exitTrail = () => { + const { x, y, s, c } = savedState; + c._panX = x; + c._panY = y; + c._viewScale = s; + LightboxView.SetLightboxDoc(undefined); + Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); + return PresStatus.Edit; }; - func(this.itemIndex); }; // The function pauses the auto presentation @@ -695,40 +712,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { * @param startIndex: index that the presentation will start at */ startPresentation = (startIndex: number) => { - this.childDocs.forEach(doc => { - const tagDoc = doc.presentationTargetDoc as Doc; - if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) { - tagDoc.opacity = 0; - } - if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) { - tagDoc.opacity = 0; - } - }); - this.gotoDocument(startIndex, this.activeItem); + clearTimeout(this._presTimer); + if (this.childDocs.length) { + this.layoutDoc.presStatus = PresStatus.Autoplay; + this.childDocs.forEach(doc => { + const tagDoc = doc.presentationTargetDoc as Doc; + if (doc.presHideBefore && this.childDocs.indexOf(doc) > startIndex) tagDoc.opacity = 0; + if (doc.presHideAfter && this.childDocs.indexOf(doc) < startIndex) tagDoc.opacity = 0; + // if (doc.presHide && this.childDocs.indexOf(doc) === startIndex) tagDoc.opacity = 0; + }); + const func = () => { + const delay = NumCast(this.activeItem.presDuration, this.activeItem.type === DocumentType.SCRIPTING ? 0 : 2500) + NumCast(this.activeItem.presTransition); + this._presTimer = setTimeout(() => { + if (!this.next()) this.layoutDoc.presStatus = this._exitTrail?.() ?? PresStatus.Manual; + this.layoutDoc.presStatus === PresStatus.Autoplay && func(); + }, delay); + }; + this.gotoDocument(startIndex, this.activeItem, undefined, func); + } }; /** * The method called to open the presentation as a minimized view */ @action - updateMinimize = async () => { + enterMinimize = () => { + clearTimeout(this._presTimer); + const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); + this.props.removeDocument?.(this.layoutDoc); + return PresBox.OpenPresMinimized(this.rootDoc, [pt[0] + (this.props.PanelWidth() - 250), pt[1] + 10]); + }; + exitMinimize = () => { if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { - this.layoutDoc.presStatus = PresStatus.Edit; Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc); CollectionDockingView.AddSplit(this.rootDoc, OpenWhereMod.right); - } else { - this.layoutDoc.presStatus = PresStatus.Edit; - clearTimeout(this._presTimer); - const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); - this.rootDoc.overlayX = pt[0] + (this.props.PanelWidth() - 250); - this.rootDoc.overlayY = pt[1] + 10; - this.rootDoc._height = 30; - this.rootDoc._width = 248; - Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc); - this.props.removeDocument?.(this.layoutDoc); } + return PresStatus.Edit; }; + public static minimizedWidth = 198; + public static OpenPresMinimized(doc: Doc, pt: number[]) { + doc.overlayX = pt[0]; + doc.overlayY = pt[1]; + doc._height = 30; + doc._width = PresBox.minimizedWidth; + Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc); + return (doc.presStatus = PresStatus.Manual); + } + /** * Called when the user changes the view type * Either 'List' (stacking) or 'Slides' (carousel) @@ -940,11 +971,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { break; case 'Escape': if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { - this.updateMinimize(); - } else if (this.layoutDoc.presStatus === 'edit') { + this.exitClicked(); + } else if (this.layoutDoc.presStatus === PresStatus.Edit) { this.clearSelectedArray(); this._eleArray.length = this._dragArray.length = 0; - } else this.layoutDoc.presStatus = 'edit'; + } else { + this.layoutDoc.presStatus = PresStatus.Edit; + } if (this._presTimer) clearTimeout(this._presTimer); handled = true; break; @@ -1797,7 +1830,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { className="dropdown-play-button" onClick={undoBatch( action(() => { - this.updateMinimize(); + this.enterMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }) @@ -1820,8 +1853,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { } scrollFocus = () => { - this.gotoDocument(0); - this.startOrPause(false); + // this.gotoDocument(0); + // this.startOrPause(false); return undefined; }; @@ -1951,8 +1984,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get topPanel() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); return ( - <div className="presBox-buttons" style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}> + <div className={`presBox-buttons${inOverlay ? ' inOverlay' : ''}`} style={{ background: Doc.ActivePresentation === this.rootDoc ? Colors.LIGHT_BLUE : undefined, display: !this.rootDoc._chromeHidden ? 'none' : undefined }}> {isMini ? null : ( <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}> <option onPointerDown={StopEvent} value={CollectionViewType.Stacking}> @@ -1969,7 +2003,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </select> )} <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}> - <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}> + <span className={`presBox-button ${this.layoutDoc.presStatus === PresStatus.Edit ? 'present' : ''}`}> <div className="presBox-button-left" onClick={undoBatch(() => { @@ -2001,11 +2035,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get playButtons() { const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); // Case 1: There are still other frames and should go through all frames before going to next slide return ( <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}> <Tooltip title={<div className="dash-tooltip">{'Loop'}</div>}> - <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}> + <div + className="presPanel-button" + style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} + onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}> <FontAwesomeIcon icon={'redo-alt'} /> </div> </Tooltip> @@ -2013,42 +2051,77 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} - onClick={e => { - this.back(); - if (this._presTimer) { - clearTimeout(this._presTimer); - this.layoutDoc.presStatus = PresStatus.Manual; - } - e.stopPropagation(); - }}> + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.back(); + if (this._presTimer) { + clearTimeout(this._presTimer); + this.layoutDoc.presStatus = PresStatus.Manual; + } + e.stopPropagation(); + }, + false, + false + ) + }> <FontAwesomeIcon icon={'arrow-left'} /> </div> <Tooltip title={<div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>}> - <div className="presPanel-button" onClick={e => this.startOrPause(true)}> + <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => this.startOrPause(true), false, false)}> <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} /> </div> </Tooltip> <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} - onClick={e => { - this.next(); - if (this._presTimer) { - clearTimeout(this._presTimer); - this.layoutDoc.presStatus = PresStatus.Manual; - } - e.stopPropagation(); - }}> + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.next(); + if (this._presTimer) { + clearTimeout(this._presTimer); + this.layoutDoc.presStatus = PresStatus.Manual; + } + e.stopPropagation(); + }, + false, + false + ) + }> <FontAwesomeIcon icon={'arrow-right'} /> </div> <div className="presPanel-divider"></div> <Tooltip title={<div className="dash-tooltip">{'Click to return to 1st slide'}</div>}> - <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.nextSlide(0)}> + <div + className="presPanel-button" + style={{ border: 'solid 1px white' }} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + () => { + this.nextSlide(0); + }, + false, + false + ) + }> <b>1</b> </div> </Tooltip> - <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}> - Slide {this.itemIndex + 1} / {this.childDocs.length} + <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: inOverlay || this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}> + {`${inOverlay ? '' : 'Slide'} ${this.itemIndex + 1} / ${this.childDocs.length}`} </div> <div className="presPanel-divider"></div> {this.props.PanelWidth() > 250 ? ( @@ -2056,14 +2129,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { className="presPanel-button-text" onClick={undoBatch( action(() => { - this.layoutDoc.presStatus = 'edit'; + this.layoutDoc.presStatus = PresStatus.Edit; clearTimeout(this._presTimer); }) )}> EXIT </div> ) : ( - <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}> + <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.exitClicked, false, false)}> <FontAwesomeIcon icon={'times'} /> </div> )} @@ -2074,7 +2147,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { @action startOrPause = (makeActive = true) => { makeActive && this.updateCurrentPresentation(); - if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex); + if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startPresentation(this.itemIndex); else this.pauseAutoPres(); }; @@ -2097,9 +2170,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { }; @undoBatch @action - exitClicked = (e: PointerEvent) => { - this.updateMinimize(); - this.layoutDoc.presStatus = PresStatus.Edit; + exitClicked = () => { + this.layoutDoc.presStatus = this._exitTrail?.() ?? this.exitMinimize(); clearTimeout(this._presTimer); }; @@ -2134,8 +2206,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { // needed to ensure that the childDocs are loaded for looking up fields this.childDocs.slice(); const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; - const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; - const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0; + const presEnd = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1; + const presStart = !this.layoutDoc.presLoop && this.itemIndex === 0; + const inOverlay = DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc); return this.props.addDocTab === returnFalse ? ( // bcz: hack!! - addDocTab === returnFalse only when this is being rendered by the OverlayView which means the doc is a mini player <div className="miniPres" onClick={e => e.stopPropagation()} onPointerEnter={action(e => (this._forceKeyEvents = true))}> <div @@ -2177,7 +2250,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() { </div> </div> ) : ( - <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}> + <div className="presBox-cont" style={{ minWidth: inOverlay ? PresBox.minimizedWidth : undefined }}> {this.topPanel} {this.toolbar} {this.newDocumentToolbarDropdown} diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 5e1474b89..f1c97d26a 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -50,9 +50,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get collapsedHeight() { return 35; } // the collapsed height changes depending on the state of the presBox. We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up - @computed get presStatus() { - return this.presBox.presStatus; - } @computed get selectedArray() { return this.presBoxView?.selectedArray; } @@ -364,14 +361,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { }; @computed get recordingIsInOverlay() { - let isInOverlay = false; - DocListCast(Doc.MyOverlayDocs.data).forEach(doc => { - if (doc.slides === this.rootDoc) { - isInOverlay = true; - // return - } - }); - return isInOverlay; + return DocListCast(Doc.MyOverlayDocs.data).some(doc => doc.slides === this.rootDoc); } // a previously recorded video will have timecode defined diff --git a/src/fields/FieldLoader.scss b/src/fields/FieldLoader.scss index 123488c7d..9a23c3e4f 100644 --- a/src/fields/FieldLoader.scss +++ b/src/fields/FieldLoader.scss @@ -1,12 +1,15 @@ .fieldLoader { z-index: 10000; width: 200px; - height: 50; - background: white; + height: 30; + background: lightblue; position: absolute; left: calc(50% - 99px); - top: calc(50% + 99px); + top: calc(50% + 110px); display: flex; align-items: center; padding: 20px; + margin: auto; + display: 'block'; + box-shadow: darkslategrey 0.2vw 0.2vw 0.8vw; } diff --git a/src/fields/FieldLoader.tsx b/src/fields/FieldLoader.tsx index 36dca89f2..2a7b936f7 100644 --- a/src/fields/FieldLoader.tsx +++ b/src/fields/FieldLoader.tsx @@ -10,18 +10,6 @@ export class FieldLoader extends React.Component { public static active = false; render() { - return ( - <div - className="fieldLoader" - style={{ - zIndex: 10000, - margin: 'auto', - width: 200, - height: 75, - background: 'lightblue', - display: 'block', - position: 'absolute', - }}>{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div> - ); + return <div className="fieldLoader">{`Requested: ${FieldLoader.ServerLoadStatus.requested} ... ${FieldLoader.ServerLoadStatus.retrieved} `}</div>; } } |