diff options
Diffstat (limited to 'src/client/views/LightboxView.tsx')
-rw-r--r-- | src/client/views/LightboxView.tsx | 228 |
1 files changed, 112 insertions, 116 deletions
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 99d50b4a2..976c8763e 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,20 +1,23 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import 'normalize.css'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils'; import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { Transform } from '../util/Transform'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { TabDocView } from './collections/TabDocView'; +import { GestureOverlay } from './GestureOverlay'; import './LightboxView.scss'; -import { DocumentView } from './nodes/DocumentView'; +import { MainView } from './MainView'; +import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; interface LightboxViewProps { @@ -23,6 +26,13 @@ interface LightboxViewProps { maxBorder: number[]; } +type LightboxSavedState = { + panX: Opt<number>; + panY: Opt<number>; + scale: Opt<number>; + scrollTop: Opt<number>; + layoutKey: Opt<string>; +}; @observer export class LightboxView extends React.Component<LightboxViewProps> { @computed public static get LightboxDoc() { @@ -30,40 +40,43 @@ export class LightboxView extends React.Component<LightboxViewProps> { } private static LightboxDocTemplate = () => LightboxView._layoutTemplate; @observable private static _layoutTemplate: Opt<Doc>; + @observable private static _layoutTemplateString: Opt<string>; @observable private static _doc: Opt<Doc>; @observable private static _docTarget: Opt<Doc>; @observable private static _docFilters: string[] = []; // filters - @observable private static _tourMap: Opt<Doc[]> = []; // list of all tours available from the current target - private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>; + private static _savedState: Opt<LightboxSavedState>; private static _history: Opt<{ doc: Doc; target?: Doc }[]> = []; @observable private static _future: Opt<Doc[]> = []; - private static _docView: Opt<DocumentView>; - private static openInTabFunc: any; - static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }> }[] = []; - @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) { + @observable private static _docView: Opt<DocumentView>; + static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<LightboxSavedState> }[] = []; + @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) { if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) { - this.LightboxDoc._panX = this._savedState.panX; - this.LightboxDoc._panY = this._savedState.panY; - this.LightboxDoc._scrollTop = this._savedState.scrollTop; - this.LightboxDoc._viewScale = this._savedState.scale; - this.LightboxDoc._viewTransition = undefined; + if (this._savedState.panX !== undefined) this.LightboxDoc._panX = this._savedState.panX; + if (this._savedState.panY !== undefined) this.LightboxDoc._panY = this._savedState.panY; + if (this._savedState.scrollTop !== undefined) this.LightboxDoc._scrollTop = this._savedState.scrollTop; + if (this._savedState.scale !== undefined) this.LightboxDoc._viewScale = this._savedState.scale; + this.LightboxDoc.layoutKey = this._savedState.layoutKey; } if (!doc) { this._docFilters && (this._docFilters.length = 0); this._future = this._history = []; + Doc.ActiveTool = InkTool.None; + MainView.Instance._exploreMode = false; } else { if (doc) { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); } + CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); //TabDocView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { this._savedState = { - panX: Cast(doc._panX, 'number', null), - panY: Cast(doc._panY, 'number', null), - scale: Cast(doc._viewScale, 'number', null), - scrollTop: Cast(doc._scrollTop, 'number', null), + layoutKey: StrCast(doc.layoutKey), + panX: Cast(doc.panX, 'number', null), + panY: Cast(doc.panY, 'number', null), + scale: Cast(doc.viewScale, 'number', null), + scrollTop: Cast(doc.scrollTop, 'number', null), }; } } @@ -74,24 +87,20 @@ export class LightboxView extends React.Component<LightboxViewProps> { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length), + .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), ]; } this._doc = doc; - this._layoutTemplate = layoutTemplate; + this._layoutTemplate = layoutTemplate instanceof Doc ? layoutTemplate : undefined; + if (doc && (typeof layoutTemplate === 'string' ? layoutTemplate : undefined)) { + doc.layoutKey = layoutTemplate; + } this._docTarget = target || doc; - this._tourMap = DocListCast(doc?.links) - .map(link => { - const opp = LinkManager.getOppositeAnchor(link, doc!); - return opp?.TourMap ? opp : undefined; - }) - .filter(m => m) - .map(m => m!); return true; } public static IsLightboxDocView(path: DocumentView[]) { - return path.includes(this._docView!); + return (path ?? []).includes(this._docView!); } @computed get leftBorder() { return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); @@ -130,13 +139,14 @@ export class LightboxView extends React.Component<LightboxViewProps> { this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); } } - public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc, openInTabFunc?: any) => { - LightboxView.openInTabFunc = openInTabFunc; + public static AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) => { SelectionManager.DeselectAll(); return LightboxView.SetLightboxDoc( doc, undefined, - [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']), ...(LightboxView._future ?? [])].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)), + [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']).filter(anno => anno.annotationOn !== doc), ...(LightboxView._future ?? [])].sort( + (a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow) + ), layoutTemplate ); }; @@ -147,9 +157,9 @@ export class LightboxView extends React.Component<LightboxViewProps> { const target = (LightboxView._docTarget = this._future?.pop()); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement(); + const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); - targetDocView.focus(target, { originalTarget: target, willZoom: true, scale: 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) { @@ -159,7 +169,6 @@ export class LightboxView extends React.Component<LightboxViewProps> { LightboxView.LightboxDoc._panY = saved.panY; LightboxView.LightboxDoc._viewScale = saved.scale; LightboxView.LightboxDoc._scrollTop = saved.scrollTop; - LightboxView.LightboxDoc._viewTransition = undefined; } const pop = LightboxView.path.pop(); if (pop) { @@ -173,13 +182,6 @@ export class LightboxView extends React.Component<LightboxViewProps> { LightboxView.SetLightboxDoc(target); } } - LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links) - .map(link => { - const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!); - return opp?.TourMap ? opp : undefined; - }) - .filter(m => m) - .map(m => m!); } @action public static Previous() { @@ -192,19 +194,11 @@ export class LightboxView extends React.Component<LightboxViewProps> { const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); if (docView) { LightboxView._docTarget = target; - if (!target) docView.ComponentView?.shrinkWrap?.(); - else docView.focus(target, { willZoom: true, scale: 0.9 }); + target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { LightboxView.SetLightboxDoc(doc, target); } if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc); - LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links) - .map(link => { - const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!); - return opp?.TourMap ? opp : undefined; - }) - .filter(m => m) - .map(m => m!); } @action stepInto = () => { @@ -215,27 +209,19 @@ export class LightboxView extends React.Component<LightboxViewProps> { history: LightboxView._history, saved: LightboxView._savedState, }); - const tours = LightboxView._tourMap; - if (tours && tours.length) { - const fieldKey = Doc.LayoutFieldKey(tours[0]); - LightboxView._future?.push(...DocListCast(tours[0][fieldKey]).reverse()); - } else { - const coll = LightboxView._docTarget; - if (coll) { - const fieldKey = Doc.LayoutFieldKey(coll); - const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])]; - const links = DocListCast(coll.links) - .map(link => LinkManager.getOppositeAnchor(link, coll)) - .filter(doc => doc) - .map(doc => doc!); - LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links); - TabDocView.PinDoc(coll, { hidePresBox: true }); - } + const coll = LightboxView._docTarget; + if (coll) { + const fieldKey = Doc.LayoutFieldKey(coll); + const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])]; + const links = LinkManager.Links(coll) + .map(link => LinkManager.getOppositeAnchor(link, coll)) + .filter(doc => doc) + .map(doc => doc!); + LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links); } }; future = () => LightboxView._future; - tourMap = () => LightboxView._tourMap; render() { let downx = 0, downy = 0; @@ -261,45 +247,37 @@ export class LightboxView extends React.Component<LightboxViewProps> { clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`, }}> {/* <CollectionMenu /> TODO:glr This is where it would go*/} - <DocumentView - ref={action((r: DocumentView | null) => { - LightboxView._docView = r !== null ? r : undefined; - r && - setTimeout( - action(() => { - const target = LightboxView._docTarget; - const doc = LightboxView._doc; - const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); - if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); - //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button - }) - ); - })} - Document={LightboxView.LightboxDoc} - DataDoc={undefined} - LayoutTemplate={LightboxView.LightboxDocTemplate} - addDocument={undefined} - isDocumentActive={returnFalse} - isContentActive={returnTrue} - addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} - rootSelected={returnTrue} - docViewPath={returnEmptyDoclist} - docFilters={this.docFilters} - removeDocument={undefined} - styleProvider={DefaultStyleProvider} - ScreenToLocalTransform={this.lightboxScreenToLocal} - PanelWidth={this.lightboxWidth} - PanelHeight={this.lightboxHeight} - focus={DocUtils.DefaultFocus} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - renderDepth={0} - /> + + <GestureOverlay isActive={true}> + <DocumentView + ref={action((r: DocumentView | null) => (LightboxView._docView = r !== null ? r : undefined))} + Document={LightboxView.LightboxDoc} + DataDoc={undefined} + PanelWidth={this.lightboxWidth} + PanelHeight={this.lightboxHeight} + LayoutTemplate={LightboxView.LightboxDocTemplate} + isDocumentActive={returnTrue} // without this being true, sidebar annotations need to be activated before text can be selected. + isContentActive={returnTrue} + styleProvider={DefaultStyleProvider} + ScreenToLocalTransform={this.lightboxScreenToLocal} + renderDepth={0} + rootSelected={returnTrue} + docViewPath={returnEmptyDoclist} + docFilters={this.docFilters} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + addDocument={undefined} + removeDocument={undefined} + whenChildContentsActiveChanged={emptyFunction} + addDocTab={this.addDocTab} + pinToPres={TabDocView.PinDoc} + bringToFront={emptyFunction} + onBrowseClick={MainView.Instance.exploreMode} + focus={DocUtils.DefaultFocus} + /> + </GestureOverlay> </div> {this.navBtn( @@ -325,27 +303,46 @@ export class LightboxView extends React.Component<LightboxViewProps> { }, this.future()?.length.toString() )} - <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} /> + <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} /> + <div + className="lightboxView-navBtn" + title="toggle fit width" + onClick={e => { + e.stopPropagation(); + LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; + }}> + <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" /> + </div> <div className="lightboxView-tabBtn" - title={'open in tab'} + title="open in tab" onClick={e => { e.stopPropagation(); - CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, ''); - //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace"); + CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, OpenWhereMod.none); SelectionManager.DeselectAll(); LightboxView.SetLightboxDoc(undefined); }}> <FontAwesomeIcon icon={'file-download'} size="2x" /> </div> <div - className="lightboxView-navBtn" - title={'toggle fit width'} + className="lightboxView-penBtn" + title="toggle pen annotation" + style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }} onClick={e => { e.stopPropagation(); - LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; + Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; }}> - <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" /> + <FontAwesomeIcon color={Doc.ActiveTool === InkTool.Pen ? 'black' : 'white'} icon={'pen'} size="2x" /> + </div> + <div + className="lightboxView-exploreBtn" + title="toggle explore mode to navigate among documents only" + style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }} + onClick={action(e => { + e.stopPropagation(); + MainView.Instance._exploreMode = !MainView.Instance._exploreMode; + })}> + <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" /> </div> </div> ); @@ -353,7 +350,6 @@ export class LightboxView extends React.Component<LightboxViewProps> { } interface LightboxTourBtnProps { navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element; - tourMap: () => Opt<Doc[]>; future: () => Opt<Doc[]>; stepInto: () => void; } @@ -370,7 +366,7 @@ export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> { e.stopPropagation(); this.props.stepInto(); }, - StrCast(this.props.tourMap()?.lastElement()?.TourMap) + '' ); } } |