From ee2f9feea2ec70dbf5a60d2b26a61b05eca3dbf5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 23 Feb 2021 10:23:26 -0500 Subject: cleaned up EditableView a bit --- src/client/views/nodes/KeyValuePair.tsx | 7 ++----- src/client/views/nodes/LinkDescriptionPopup.tsx | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index ce9d8bed5..ebb953dad 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -125,12 +125,9 @@ export class KeyValuePair extends React.Component { contents={contents} maxHeight={36} height={"auto"} - GetValue={() => { - return Field.toKeyValueString(props.Document, props.fieldKey); - }} + GetValue={() => Field.toKeyValueString(props.Document, props.fieldKey)} SetValue={(value: string) => - KeyValueBox.SetField(props.Document, props.fieldKey, value)}> - + KeyValueBox.SetField(props.Document, props.fieldKey, value)} /> diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 720af6c9d..30b272a9a 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -2,7 +2,6 @@ import React = require("react"); import { observer } from "mobx-react"; import "./LinkDescriptionPopup.scss"; import { observable, action } from "mobx"; -import { EditableView } from "../EditableView"; import { LinkManager } from "../../util/LinkManager"; import { TaskCompletionBox } from "./TaskCompletedBox"; -- cgit v1.2.3-70-g09d2 From 684faaab165681c34fe824e1b6ac887d25b73f36 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 23 Feb 2021 17:18:14 -0500 Subject: reorg and cleanup a bit of colelctionfreeformview. minor other cleanup. --- .../collectionFreeForm/CollectionFreeFormView.tsx | 340 ++++++++++----------- src/client/views/nodes/DocumentView.tsx | 6 +- src/fields/Doc.ts | 9 +- 3 files changed, 165 insertions(+), 190 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 18cf825a3..9041d9b21 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -13,7 +13,7 @@ import { ScriptField } from "../../../../fields/ScriptField"; import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types"; import { TraceMobx } from "../../../../fields/util"; import { GestureUtils } from "../../../../pen-gestures/GestureUtils"; -import { aggregateBounds, intersectRect, returnFalse, setupMoveUpEvents, Utils, emptyFunction } from "../../../../Utils"; +import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; @@ -58,24 +58,20 @@ export const panZoomSchema = createSchema({ _timecodeToShow: "number", _currentFrame: "number", _useClusters: "boolean", - fitToBox: "boolean", + _viewTransition: "string", _xPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set _yPadding: "number", // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set - _viewTransition: "string", - scrollHeight: "number", - fitX: "number", - fitY: "number", - fitW: "number", - fitH: "number" + _fitToBox: "boolean", + scrollHeight: "number" // this will be set when the collection is an annotation overlay for a PDF/Webpage }); type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>; const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema); export type collectionFreeformViewProps = { + parentActive: (outsideReaction: boolean) => boolean; forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: boolean; - parentActive: (outsideReaction: boolean) => boolean; scaleField?: string; noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale) engineProps?: any; @@ -94,29 +90,32 @@ export class CollectionFreeFormView extends CollectionSubView = new Map(); private _clusterDistance: number = 75; private _hitCluster: number = -1; - private _layoutComputeReaction: IReactionDisposer | undefined; - private _boundsReaction: IReactionDisposer | undefined; + private _disposers: { [name: string]: IReactionDisposer } = {}; private _layoutPoolData = new ObservableMap(); private _layoutSizeData = new ObservableMap(); private _cachedPool: Map = new Map(); private _lastTap = 0; private _nudgeTime = 0; - private _thumbIdentifier?: number; + private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } + private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; } + private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } + + @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _hLines: number[] | undefined; @observable _vLines: number[] | undefined; @observable _pullCoords: number[] = [0, 0]; @observable _pullDirection: string = ""; @observable _showAnimTimeline = false; - @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. - - @observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables @observable _clusterSets: (Doc[])[] = []; @observable _timelineRef = React.createRef(); @observable _marqueeRef = React.createRef(); @observable _focusFilters: Opt; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it @observable _focusRangeFilters: Opt; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it + @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. + @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } + @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); } @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } @computed get fitToContentVals() { return { @@ -131,13 +130,6 @@ export class CollectionFreeFormView extends CollectionSubView e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10)); } @computed get nativeWidth() { return this.fitToContent ? 0 : Doc.NativeWidth(this.Document); } @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); } - private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; } - private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; } - private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; } - private panX = () => this.freeformData()?.panX ?? NumCast(this.Document._panX); - private panY = () => this.freeformData()?.panY ?? NumCast(this.Document._panY); - private zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1)); - private contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)` @computed get cachedCenteringShiftX(): number { const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections @@ -156,16 +148,31 @@ export class CollectionFreeFormView extends CollectionSubView this.cachedGetTransform.copy(); - private getLocalTransform = () => this.cachedGetLocalTransform.copy(); - private getContainerTransform = () => this.cachedGetContainerTransform.copy(); - private getTransformOverlay = () => this.getContainerTransform().translate(1, 1); - private addLiveTextBox = (newBox: Doc) => { + onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); + onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); + parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false; + elementFunc = () => this._layoutElements; + freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined; + freeformDocFilters = () => this._focusFilters || this.docFilters(); + freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); + panX = () => this.freeformData()?.panX ?? NumCast(this.Document._panX); + panY = () => this.freeformData()?.panY ?? NumCast(this.Document._panY); + zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1)); + contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)` + getTransform = () => this.cachedGetTransform.copy(); + getLocalTransform = () => this.cachedGetLocalTransform.copy(); + getContainerTransform = () => this.cachedGetContainerTransform.copy(); + getTransformOverlay = () => this.getContainerTransform().translate(1, 1); + getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); + addLiveTextBox = (newBox: Doc) => { FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newBox); } - - addDocument = action((newBox: Doc | Doc[]) => { + selectDocuments = (docs: Doc[]) => { + SelectionManager.DeselectAll(); + docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true)); + } + addDocument = (newBox: Doc | Doc[]) => { let retVal = false; if (newBox instanceof Doc) { retVal = this.props.addDocument?.(newBox) || false; @@ -191,25 +198,6 @@ export class CollectionFreeFormView extends CollectionSubView { - SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(dv => dv && SelectionManager.SelectView(dv, true)); - } - public isCurrent(doc: Doc) { - const dispTime = NumCast(doc._timecodeToShow, -1); - const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5); - const curTime = NumCast(this.Document._currentTimecode, -1); - return dispTime === -1 || ((curTime - dispTime) >= -1e-4 && curTime <= endTime); - } - - public getActiveDocuments = () => { - return this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); - } - - onExternalDrop = (e: React.DragEvent) => { - return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY)); } updateGroupBounds = () => { @@ -231,6 +219,13 @@ export class CollectionFreeFormView extends CollectionSubView= -1e-4 && curTime <= endTime); + } + @action internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) { if (!this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false; @@ -271,7 +266,6 @@ export class CollectionFreeFormView extends CollectionSubView { const [xp, yp] = this.getTransform().transformPoint(de.x, de.y); if (this.isAnnotationOverlay !== true && de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp); @@ -308,6 +300,10 @@ export class CollectionFreeFormView extends CollectionSubView { + return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY)); + } + pickCluster(probe: number[]) { return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => { const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1); @@ -435,6 +431,16 @@ export class CollectionFreeFormView extends CollectionSubView { + if (this._hitCluster !== -1) { + !addToSel && SelectionManager.DeselectAll(); + const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster); + this.selectDocuments(eles); + return true; + } + return false; + } + @action onPointerDown = (e: React.PointerEvent): void => { if (e.nativeEvent.cancelBubble || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) || InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || (Doc.GetSelectedTool() === InkTool.Highlighter || Doc.GetSelectedTool() === InkTool.Pen)) { @@ -774,10 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView { switch (this._pullDirection) { - case "left": - case "right": - case "top": - case "bottom": + case "left": case "right": case "top": case "bottom": CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), this._pullDirection); } @@ -868,7 +871,21 @@ export class CollectionFreeFormView extends CollectionSubView { + @action + nudge = (x: number, y: number) => { + if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || + this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out... + this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(), + NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "transform 500ms", true); + this._nudgeTime = Date.now(); + setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document._viewTransition = undefined), 500); + return true; + } + return false; + } + + @action + bringToFront = (doc: Doc, sendToBack?: boolean) => { if (sendToBack || StrListCast(doc.layers).includes(StyleLayers.Background)) { doc.zIndex = 0; } else if (doc.isInkMask) { @@ -883,7 +900,7 @@ export class CollectionFreeFormView extends CollectionSubView new Promise(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))) }); } } - setPanIntoView = (doc: Doc, xf: Transform, scale?: number) => { + calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => { const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1); const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1); const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); @@ -1000,43 +1018,51 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childClickScript || ScriptCast(this.Document.onChildClick); - onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); - parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false; - getChildDocumentViewProps(childLayout: Doc, childData?: Doc): DocumentViewProps { - return { - addDocument: this.props.addDocument, - removeDocument: this.props.removeDocument, - moveDocument: this.props.moveDocument, - pinToPres: this.props.pinToPres, - whenActiveChanged: this.props.whenActiveChanged, - parentActive: this.parentActive, - docViewPath: this.props.docViewPath, - DataDoc: childData, - Document: childLayout, - ContainingCollectionView: this.props.CollectionView, - ContainingCollectionDoc: this.props.Document, - LayoutTemplate: childLayout.z ? undefined : this.props.childLayoutTemplate, - LayoutTemplateString: childLayout.z ? undefined : this.props.childLayoutString, - rootSelected: childData ? this.rootSelected : returnFalse, - onClick: this.onChildClickHandler, - onDoubleClick: this.onChildDoubleClickHandler, - ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, - PanelWidth: childLayout[WidthSym], - PanelHeight: childLayout[HeightSym], - docFilters: this.freeformDocFilters, - docRangeFilters: this.freeformRangeDocFilters, - searchFilterDocs: this.searchFilterDocs, - focus: this.focusDocument, - styleProvider: this.getClusterColor, - layerProvider: this.props.layerProvider, - freezeDimensions: this.props.childFreezeDimensions, - dropAction: StrCast(this.props.Document.childDropAction) as dropActionType, - bringToFront: this.bringToFront, - addDocTab: this.addDocTab, - renderDepth: this.props.renderDepth + 1, - dontRegisterView: this.props.dontRegisterView, - }; + getChildDocView(entry: PoolData) { + const childLayout = entry.pair.layout; + const childData = entry.pair.data; + const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); + return ; } addDocTab = action((doc: Doc, where: string) => { @@ -1075,15 +1101,16 @@ export class CollectionFreeFormView extends CollectionSubView { - return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!); - } - onViewDefDivClick = (e: React.MouseEvent, payload: any) => { (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload }); e.stopPropagation(); } - private viewDefToJSX(viewDef: ViewDefBounds): Opt { + + viewDefsToJSX = (views: ViewDefBounds[]) => { + return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!); + } + + viewDefToJSX(viewDef: ViewDefBounds): Opt { const { x, y, z } = viewDef; const color = StrCast(viewDef.color); const width = Cast(viewDef.width, "number"); @@ -1165,24 +1192,9 @@ export class CollectionFreeFormView extends CollectionSubView this._cachedPool.set(k[0], k[1])); const elements = computedElementData.slice(); - const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry => elements.push({ - ele: , + ele: this.getChildDocView(entry[1]), bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica) })); @@ -1195,8 +1207,6 @@ export class CollectionFreeFormView extends CollectionSubView this._focusFilters || this.docFilters(); - freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); @action setViewSpec = (anchor: Doc, preview: boolean) => { if (preview) { @@ -1226,16 +1236,16 @@ export class CollectionFreeFormView extends CollectionSubView this.fitToContent || force ? this.fitToContentVals : undefined; + @action componentDidMount() { super.componentDidMount?.(); this.props.setContentView?.(this); - this._layoutComputeReaction = reaction(() => this.doLayoutComputation, + this._disposers.layoutComputation = reaction(() => this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); if (!this.props.isAnnotationOverlay) { - this._boundsReaction = reaction(() => this.contentBounds, + this._disposers.contentBounds = reaction(() => this.contentBounds, bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => { const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]); if (rbounds[0] !== bounds.x || rbounds[1] !== bounds.y || rbounds[2] !== bounds.r || rbounds[3] !== bounds.b) { @@ -1248,14 +1258,10 @@ export class CollectionFreeFormView extends CollectionSubView disposer?.()); this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); } - @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } - elementFunc = () => this._layoutElements; - @action onCursorMove = (e: React.PointerEvent) => { // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); @@ -1281,7 +1287,8 @@ export class CollectionFreeFormView extends CollectionSubView { + @undoBatch + promoteCollection = () => { const childDocs = this.childDocs.slice(); childDocs.forEach(doc => { const scr = this.getTransform().inverse().transformPoint(NumCast(doc.x), NumCast(doc.y)); @@ -1290,10 +1297,10 @@ export class CollectionFreeFormView extends CollectionSubView { + layoutDocsInGrid = () => { const docs = this.childLayoutPairs; const startX = this.Document._panX || 0; let x = startX; @@ -1311,19 +1318,14 @@ export class CollectionFreeFormView extends CollectionSubView { - Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight); - } + toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight); @undoBatch @action - toggleLockTransform = (): void => { - this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true; - } + toggleLockTransform = () => this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true; onContextMenu = (e: React.MouseEvent) => { if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return; @@ -1430,24 +1432,6 @@ export class CollectionFreeFormView extends CollectionSubView]; } - @computed get placeholder() { - return
- {this.props.Document.title?.toString()} -
; - } - - nudge = action((x: number, y: number) => { - if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || - this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out... - this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(), - NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), "transform 500ms", true); - this._nudgeTime = Date.now(); - setTimeout(() => (Date.now() - this._nudgeTime >= 500) && (this.Document._viewTransition = undefined), 500); - return true; - } - return false; - }); - chooseGridSpace = (gridSpace: number): number => { const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3; return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10); @@ -1486,14 +1470,10 @@ export class CollectionFreeFormView extends CollectionSubView; } - trySelectCluster = (addToSel: boolean) => { - if (this._hitCluster !== -1) { - !addToSel && SelectionManager.DeselectAll(); - const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === this._hitCluster); - this.selectDocuments(eles); - return true; - } - return false; + @computed get placeholder() { + return
+ {this.props.Document.title?.toString()} +
; } @computed get marqueeView() { @@ -1536,7 +1516,6 @@ export class CollectionFreeFormView extends CollectionSubView {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} @@ -1667,17 +1646,17 @@ class CollectionFreeFormViewPannableContents extends React.Component +
-
-
-
-
+
+
+
+
; } @@ -1697,16 +1676,13 @@ class CollectionFreeFormViewPannableContents extends React.Component{PresBox.Instance.order}
- + - + - + diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b79c1d474..e31101459 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -378,8 +378,6 @@ export class DocumentViewInternal extends DocComponent (ffview().props.CollectionFreeFormView.ChildDrag = this.props.DocumentView())); const dragData = new DragManager.DocumentDragData([this.props.Document]); const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0); dragData.offset = this.props.ScreenToLocalTransform().scale(this.ContentScale).transformDirection(x - left, y - top); @@ -387,8 +385,10 @@ export class DocumentViewInternal extends DocComponent (ffview.ChildDrag = this.props.DocumentView())); DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: !dropAction && !this.layoutDoc.onDragStart }, - () => setTimeout(action(() => ffview && (ffview().props.CollectionFreeFormView.ChildDrag = undefined)))); // this needs to happen after the drop event is processed. + () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed. } } diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 4ae436083..ce5b08440 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -25,6 +25,7 @@ import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types"; import { AudioField, ImageField, PdfField, VideoField, WebField } from "./URLField"; import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util"; import JSZip = require("jszip"); +import { prefix } from "@fortawesome/free-regular-svg-icons"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -230,10 +231,11 @@ export class Doc extends RefField { const sameAuthor = this.author === Doc.CurrentUserEmail; if (set) { for (const key in set) { - if (!key.startsWith("fields.")) { + const fprefix = "fields."; + if (!key.startsWith(fprefix)) { continue; } - const fKey = key.substring(7); + const fKey = key.substring(fprefix.length); const fn = async () => { const value = await SerializationHelper.Deserialize(set[key]); const prev = GetEffectiveAcl(this); @@ -246,9 +248,6 @@ export class Doc extends RefField { if (prev === AclPrivate && GetEffectiveAcl(this) !== AclPrivate) { DocServer.GetRefField(this[Id], true); } - // if (prev !== AclPrivate && GetEffectiveAcl(this) === AclPrivate) { - // this[FieldsSym](true); - // } }; if (sameAuthor || fKey.startsWith("acl") || DocServer.getFieldWriteMode(fKey) !== DocServer.WriteMode.Playground) { delete this[CachedUpdates][fKey]; -- cgit v1.2.3-70-g09d2 From 2cddc1cd6012a1c71c919124f640ce1d04593ec8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 23 Feb 2021 22:36:52 -0500 Subject: removed _renderContentBounds and updated miniMap view. --- src/client/views/collections/TabDocView.tsx | 186 ++++++++++++--------- .../collectionFreeForm/CollectionFreeFormView.tsx | 10 +- src/client/views/nodes/DocumentView.tsx | 2 +- 3 files changed, 105 insertions(+), 93 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index c1a1a6f04..c7eb9ef48 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -9,7 +9,6 @@ import * as ReactDOM from 'react-dom'; import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { FieldId } from "../../../fields/RefField"; -import { listSpec } from '../../../fields/Schema'; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; @@ -55,13 +54,7 @@ export class TabDocView extends React.Component { @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); } - @computed get renderBounds() { - const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; - const xbounds = bounds[2] - bounds[0]; - const ybounds = bounds[3] - bounds[1]; - const dim = Math.max(xbounds, ybounds); - return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; - } + get stack() { return (this.props as any).glContainer.parent.parent; } get tab() { return (this.props as any).glContainer.tab; } @@ -290,76 +283,9 @@ export class TabDocView extends React.Component { } } - childLayoutTemplate = () => Cast(this._document?.childLayoutTemplate, Doc, null); - returnMiniSize = () => NumCast(this._document?._miniMapSize, 150); - miniDown = (e: React.PointerEvent) => { - const doc = this._document; - const miniSize = this.returnMiniSize(); - doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * this.renderBounds.dim, this.renderBounds.l, this.renderBounds.l + this.renderBounds.dim); - doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * this.renderBounds.dim, this.renderBounds.t, this.renderBounds.t + this.renderBounds.dim); - return false; - }), emptyFunction, emptyFunction); - } getCurrentFrame = () => { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); } - renderMiniMap() { - const miniWidth = this.PanelWidth() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100; - const miniHeight = this.PanelHeight() / NumCast(this._document?._viewScale, 1) / this.renderBounds.dim * 100; - const miniLeft = 50 + (NumCast(this._document?._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; - const miniTop = 50 + (NumCast(this._document?._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; - const miniSize = this.returnMiniSize(); - return <> -
- -
-
-
-
- - {"toggle minimap"}
}> -
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} - style={{ background: DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor) }} > - -
- - ; - } @action focusFunc = (doc: Doc, options?: DocFocusOptions) => { const vals = (!options?.originalTarget || options?.originalTarget === this._document) && this.view?.ComponentView?.freeformData?.(true); @@ -402,6 +328,10 @@ export class TabDocView extends React.Component { } } } + miniMapColor = () => this.tabColor; + miniPanelWidth = () => this.PanelWidth(); + miniPanelHeight = () => this.PanelHeight(); + tabView = () => this._view; @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); } @computed get docView() { TraceMobx(); @@ -431,14 +361,18 @@ export class TabDocView extends React.Component { docViewPath={returnEmptyDoclist} bringToFront={emptyFunction} pinToPres={TabDocView.PinDoc} /> - {this._document._viewType !== CollectionViewType.Freeform ? (null) : - <>{this._document.hideMinimap ? (null) : this.renderMiniMap()} - {"toggle minimap"}
}> -
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} > - -
- - } + + {"toggle minimap"}
}> +
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} > + +
+ ; } @@ -454,4 +388,90 @@ export class TabDocView extends React.Component {
); } +} + +interface TabMinimapViewProps { + document: Doc; + tabView: () => DocumentView | undefined; + addDocTab: (doc: Doc, where: string) => boolean; + PanelWidth: () => number; + PanelHeight: () => number; + background: () => string; +} +@observer +export class TabMinimapView extends React.Component { + @computed get renderBounds() { + const bounds = this.props.tabView()?.ComponentView?.freeformData?.()?.bounds ?? { x: 0, y: 0, r: this.returnMiniSize(), b: this.returnMiniSize() }; + const xbounds = bounds.r - bounds.x; + const ybounds = bounds.b - bounds.y; + const dim = Math.max(xbounds, ybounds); + return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, dim }; + } + childLayoutTemplate = () => Cast(this.props.document.childLayoutTemplate, Doc, null); + returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150); + miniDown = (e: React.PointerEvent) => { + const doc = this.props.document; + const miniSize = this.returnMiniSize(); + doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { + doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * this.renderBounds.dim, this.renderBounds.l, this.renderBounds.l + this.renderBounds.dim); + doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * this.renderBounds.dim, this.renderBounds.t, this.renderBounds.t + this.renderBounds.dim); + return false; + }), emptyFunction, emptyFunction); + } + render() { + const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; + const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; + const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; + const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; + const miniSize = this.returnMiniSize(); + return this.props.document._viewType !== CollectionViewType.Freeform || this.props.document.hideMinimap ? (null) : <> +
+ +
+
+
+
+ + {"toggle minimap"}
}> +
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.props.document.hideMinimap = !this.props.document.hideMinimap; })} + style={{ background: DefaultStyleProvider(this.props.document, undefined, StyleProp.BackgroundColor) }} > + +
+ + ; + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 9fa5b8670..ba89579cd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -120,6 +120,7 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); - if (!this.props.isAnnotationOverlay) { - this._disposers.contentBounds = reaction(() => this.contentBounds, - bounds => (!this.fitToContent && this._layoutElements?.length) && setTimeout(() => { - const rbounds = Cast(this.Document._renderContentBounds, listSpec("number"), [0, 0, 0, 0]); - if (rbounds[0] !== bounds.x || rbounds[1] !== bounds.y || rbounds[2] !== bounds.r || rbounds[3] !== bounds.b) { - this.Document._renderContentBounds = new List([bounds.x, bounds.y, bounds.r, bounds.b]); - } - })); - } this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index e31101459..5dfb3e99b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -83,7 +83,7 @@ export interface DocComponentView { forward?: () => boolean; url?: () => string; submitURL?: (url: string) => boolean; - freeformData?: (force?: boolean) => Opt<{ panX: number, panY: number, scale: number }>; + freeformData?: (force?: boolean) => Opt<{ panX: number, panY: number, scale: number, bounds: { x: number, y: number, r: number, b: number } }>; } export interface DocumentViewSharedProps { renderDepth: number; -- cgit v1.2.3-70-g09d2 From 477fa6b85e24046edbebc44b49aba8c286558cd0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 24 Feb 2021 10:45:37 -0500 Subject: fixed carouself view which wasn't displaying vertically oriented documents. cleaned up collectionTime script to not capture variables (causing doc's to be create) --- src/client/views/collections/CollectionCarousel3DView.scss | 1 - src/client/views/collections/CollectionCarousel3DView.tsx | 10 +++++----- src/client/views/collections/CollectionTimeView.tsx | 5 ++--- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 +--- 5 files changed, 9 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss index 652293ed6..5c8b491eb 100644 --- a/src/client/views/collections/CollectionCarousel3DView.scss +++ b/src/client/views/collections/CollectionCarousel3DView.scss @@ -9,7 +9,6 @@ position: absolute; top: 15%; height: 60%; - width: 100%; align-items: center; transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955); diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index 9b1e3b80d..c5a05da00 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -40,7 +40,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume @computed get content() { const currentIndex = NumCast(this.layoutDoc._itemIndex); const displayDoc = (childPair: { layout: Doc, data: Doc }) => { - const script = ScriptField.MakeScript("child._showCaption = 'caption'", { child: Doc.name }, { child: childPair.layout }); + const script = ScriptField.MakeScript("this._showCaption = 'caption'", { this: Doc.name }); const onChildClick = script && (() => script); return + { opacity: '1', transform: 'scale(1.3)', width: this.panelWidth() } : + { opacity: '0.5', transform: 'scale(0.6)', userSelect: 'none', width: this.panelWidth() }}> {displayDoc(childPair)} ); })); @@ -166,10 +166,10 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume render() { const index = NumCast(this.layoutDoc._itemIndex); - const translateX = 33 * (1 - index); + const translateX = this.panelWidth() * (1 - index); return
-
+
{this.content}
{this.props.Document._chromeStatus !== "replaced" ? this.buttons : (null)} diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index 857d5e1d7..1f6322997 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -62,11 +62,10 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { async componentDidMount() { this.props.setContentView?.(this); - const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), ""); + //const detailView = (await DocCastAsync(this.props.Document.childClickedOpenTemplateView)) || DocUtils.findTemplate("detailView", StrCast(this.rootDoc.type), ""); ///const childText = "const alias = getAlias(self); switchView(alias, detailView); alias.dropAction='alias'; alias.removeDropProperties=new List(['dropAction']); useRightSplit(alias, shiftKey); "; - const childText = "openInLightbox(self, shiftKey); "; runInAction(() => { - this._childClickedScript = ScriptField.MakeScript(childText, { this: Doc.name, shiftKey: "boolean" }, { detailView: detailView! }); + this._childClickedScript = ScriptField.MakeScript("openInLightbox(self, shiftKey)", { this: Doc.name, shiftKey: "boolean" });//, { detailView: detailView! }); this._viewDefDivClick = ScriptField.MakeScript("pivotColumnClick(this,payload)", { payload: "any" }); }); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba89579cd..96ef7af01 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1199,7 +1199,7 @@ export class CollectionFreeFormView extends CollectionSubView([]); + proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List([]); if (Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), null) !== undefined) { Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), []).push(anchor); } else { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index da7ed9ac7..ce54bf57f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -109,9 +109,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _ignoreScroll = false; @computed get _recording() { return this.dataDoc?.audioState === "recording"; } - set _recording(value) { - this.dataDoc.audioState = value ? "recording" : undefined; - } + set _recording(value) { this.dataDoc.audioState = value ? "recording" : undefined; } public static FocusedBox: FormattedTextBox | undefined; public static SelectOnLoad = ""; -- cgit v1.2.3-70-g09d2 From 58adadeaaf97c57a15d42fea86e75180fc3b2c14 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 24 Feb 2021 12:24:25 -0500 Subject: fixed layerProviders for treeViews to return true. fixed sidebar annotations for text to add/remove from correct field. --- src/client/views/DocComponent.tsx | 14 +++++++------- src/client/views/collections/CollectionTreeView.tsx | 2 +- src/client/views/collections/TreeView.tsx | 11 +++++++---- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 12 +++++++----- 4 files changed, 22 insertions(+), 17 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index b800ba777..ef1228976 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -156,15 +156,15 @@ export function ViewBoxAnnotatableComponent

boolean): boolean { - return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc) ? addDocument(doc) : false; + moveDocument(doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string): boolean { + return Doc.AreProtosEqual(this.props.Document, targetCollection) ? true : this.removeDocument(doc, annotationKey) ? addDocument(doc) : false; } @action.bound - addDocument(doc: Doc | Doc[]): boolean { + addDocument(doc: Doc | Doc[], annotationKey?: string): boolean { const docs = doc instanceof Doc ? [doc] : doc; docs.map(doc => doc.context = Doc.GetProto(doc).annotationOn = this.props.Document); const targetDataDoc = this.props.Document[DataSym]; - const docList = DocListCast(targetDataDoc[this.annotationKey]); + const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]); const added = docs.filter(d => !docList.includes(d)); const effectiveAcl = GetEffectiveAcl(this.dataDoc); @@ -184,12 +184,12 @@ export function ViewBoxAnnotatableComponent

Doc.AddDocToList(targetDataDoc, this.annotationKey, doc)); + added.map(doc => Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc)); } else { added.map(doc => doc.context = this.props.Document); - (targetDataDoc[this.annotationKey] as List).push(...added); - targetDataDoc[this.annotationKey + "-lastModified"] = new DateField(new Date(Date.now())); + (targetDataDoc[annotationKey ?? this.annotationKey] as List).push(...added); + targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now())); } } } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index a12de0320..146b3cd37 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -205,7 +205,7 @@ export class CollectionTreeView extends CollectionSubView this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument?.(d, target, addDoc) || false; return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, - moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, this.props.ScreenToLocalTransform, + moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.styleProvider, returnTrue, this.props.ScreenToLocalTransform, this.outerXf, this.active, this.panelWidth, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick, this.onChildClick, this.props.treeViewSkipFields, true, this.whenActiveChanged, this.props.dontRegisterView || Cast(this.props.Document.dontRegisterChildViews, "boolean", null), this); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 297796b4b..dbd05f37a 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -52,6 +52,7 @@ export interface TreeViewProps { ScreenToLocalTransform: () => Transform; dontRegisterView?: boolean; styleProvider?: StyleProviderFunc | undefined; + layerProvider?: undefined | ((doc: Doc, assign?: boolean) => boolean); outerXf: () => { translateX: number, translateY: number }; treeView: CollectionTreeView; parentKey: string; @@ -327,7 +328,7 @@ export class TreeView extends React.Component { const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true); contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents), this.props.treeView, doc, undefined, key, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, + this.props.dropAction, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.layerProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView, this); } else { @@ -410,7 +411,7 @@ export class TreeView extends React.Component { {!docs ? (null) : TreeView.GetChildElements(docs, this.props.treeView, this.layoutDoc, this.dataDoc, expandKey, this.props.containingCollection, this.props.prevSibling, addDoc, remDoc, this.move, - StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.ScreenToLocalTransform, + StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.props.pinToPres, this.titleStyleProvider, this.props.layerProvider, this.props.ScreenToLocalTransform, this.props.outerXf, this.props.active, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, this.props.treeViewPreventOpen, [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenActiveChanged, this.props.dontRegisterView, this)} ; @@ -578,7 +579,7 @@ export class TreeView extends React.Component { DataDoc={undefined} scriptContext={this} styleProvider={this.titleStyleProvider} - layerProvider={undefined} + layerProvider={this.props.layerProvider} docViewPath={returnEmptyDoclist} treeViewDoc={this.props.treeView.props.Document} addDocument={undefined} @@ -671,7 +672,7 @@ export class TreeView extends React.Component { renderDepth={this.props.renderDepth + 1} rootSelected={returnTrue} styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider} - layerProvider={undefined} + layerProvider={this.props.layerProvider} docViewPath={this.props.treeView.props.docViewPath} docFilters={returnEmptyFilter} docRangeFilters={returnEmptyFilter} @@ -775,6 +776,7 @@ export class TreeView extends React.Component { addDocTab: (doc: Doc, where: string) => boolean, pinToPres: (document: Doc) => void, styleProvider: undefined | StyleProviderFunc, + layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean), screenToLocalXf: () => Transform, outerXf: () => { translateX: number, translateY: number }, active: (outsideReaction?: boolean) => boolean, @@ -838,6 +840,7 @@ export class TreeView extends React.Component { removeDoc={StrCast(containingCollection.freezeChildren).includes("remove") ? undefined : remove} addDocument={addDocument} styleProvider={styleProvider} + layerProvider={layerProvider} panelWidth={rowWidth} panelHeight={rowHeight} dontRegisterView={dontRegisterView} diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ce54bf57f..b4fbda9f4 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -105,7 +105,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp private _disposers: { [name: string]: IReactionDisposer } = {}; private _dropDisposer?: DragManager.DragDropDisposer; private _recordingStart: number = 0; - private _pause: boolean = false; private _ignoreScroll = false; @computed get _recording() { return this.dataDoc?.audioState === "recording"; } @@ -1649,6 +1648,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1); fitToBox = () => this.props.Document._fitToBox; + sidebarAddDocument = (doc: Doc | Doc[]) => this.addDocument(doc, this.SidebarKey); + sidebarMoveDocument = (doc: Doc | Doc[], targetCollection: Doc, addDocument: (doc: Doc | Doc[]) => boolean) => this.moveDocument(doc, targetCollection, addDocument, this.SidebarKey); + sidebarRemDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, this.SidebarKey); @computed get sidebarCollection() { const collectionProps: SubCollectionViewProps & collectionFreeformViewProps = { ...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit, @@ -1660,16 +1662,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp yMargin: 0, chromeStatus: "enabled", scaleField: this.SidebarKey + "-scale", - isAnnotationOverlay: true, + isAnnotationOverlay: false, fieldKey: this.SidebarKey, fitContentsToDoc: this.fitToBox, select: emptyFunction, active: this.annotationsActive, scaling: this.sidebarContentScaling, whenActiveChanged: this.whenActiveChanged, - removeDocument: this.removeDocument, - moveDocument: this.moveDocument, - addDocument: this.addDocument, + removeDocument: this.sidebarRemDocument, + moveDocument: this.sidebarMoveDocument, + addDocument: this.sidebarAddDocument, CollectionView: undefined, ScreenToLocalTransform: this.sidebarScreenToLocal, renderDepth: this.props.renderDepth + 1, -- cgit v1.2.3-70-g09d2 From 3410e107435410a5635a70f12ee05e2d874ff01c Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 24 Feb 2021 22:04:31 -0500 Subject: cleaned up DocumentView's contentView api to be more general. fixed screenToLocal for freeformView's whenb _fitToBox is set. moved webBox menu items out of CollectionMenu and into WebBox using the contentView API. --- src/client/views/collections/CollectionMenu.tsx | 57 +--------------------- .../collectionFreeForm/CollectionFreeFormView.scss | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 +++---- src/client/views/nodes/DocumentView.tsx | 17 +++---- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 47 ++++++++++++++++++ 7 files changed, 68 insertions(+), 77 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 423c94005..591b4161e 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -560,7 +560,6 @@ export class CollectionFreeFormViewChrome extends React.Component; } - - onWebUrlDrop = (e: React.DragEvent) => { - const { dataTransfer } = e; - const html = dataTransfer.getData("text/html"); - const uri = dataTransfer.getData("text/uri-list"); - const url = uri || html || this.webBoxUrl || ""; - const newurl = url.startsWith(window.location.origin) ? - url.replace(window.location.origin, this.webBoxUrl?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; - this.submitWebUrl(newurl); - e.stopPropagation(); - } - onWebUrlValueKeyDown = (e: React.KeyboardEvent) => { - e.key === "Enter" && this.submitWebUrl(this._keyInput.current!.value); - e.stopPropagation(); - } - submitWebUrl = (url: string) => this.selectedDocumentView?.ComponentView?.submitURL?.(url); - webUrlForward = () => this.selectedDocumentView?.ComponentView?.forward?.(); - webUrlBack = () => this.selectedDocumentView?.ComponentView?.back?.(); - - private _keyInput = React.createRef(); - - @computed get urlEditor() { - return ( -

e.preventDefault()} > - e.preventDefault()} - onKeyDown={this.onWebUrlValueKeyDown} - onClick={(e) => { - this._keyInput.current!.select(); - e.stopPropagation(); - }} - ref={this._keyInput} - /> -
- - - -
-
- ); - } - - @observable viewType = this.selectedDoc?._viewType; render() { @@ -811,9 +758,7 @@ export class CollectionFreeFormViewChrome extends React.Component : null} - {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText || this.props.isDoc ? (null) : - this.urlEditor - } + {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()} {!this.isText ? <> {this.drawButtons} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index a05c25c9b..eb0538c41 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -191,7 +191,7 @@ .collectionfreeformview-container { // touch action none means that the browser will handle none of the touch actions. this allows us to implement our own actions. touch-action: none; - + transform-origin: top left; .collectionfreeformview-placeholder { background: gray; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 96ef7af01..92c09ff3f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -69,7 +69,7 @@ type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSch const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema); export type collectionFreeformViewProps = { parentActive: (outsideReaction: boolean) => boolean; - forceScaling?: boolean; // whether to force scaling of content (needed by ImageBox) + annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) viewDefDivClick?: ScriptField; childPointerEvents?: boolean; scaleField?: string; @@ -157,10 +157,11 @@ export class CollectionFreeFormView extends CollectionSubView this.fitToContent || force ? this.fitToContentVals : undefined; freeformDocFilters = () => this._focusFilters || this.docFilters(); freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); + reverseNativeScaling = () => this.fitToContent ? true : false; panX = () => this.freeformData()?.panX ?? NumCast(this.Document._panX); panY = () => this.freeformData()?.panY ?? NumCast(this.Document._panY); zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1)); - contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)` + contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; getTransform = () => this.cachedGetTransform.copy(); getLocalTransform = () => this.cachedGetLocalTransform.copy(); getContainerTransform = () => this.cachedGetContainerTransform.copy(); @@ -1274,11 +1275,11 @@ export class CollectionFreeFormView extends CollectionSubView Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight); + toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight) @undoBatch @action - toggleLockTransform = () => this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true; + toggleLockTransform = () => this.layoutDoc._lockedTransform = this.layoutDoc._lockedTransform ? undefined : true onContextMenu = (e: React.MouseEvent) => { if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return; @@ -1462,7 +1463,7 @@ export class CollectionFreeFormView extends CollectionSubView {this.Document._freeformLOD && !this.props.active() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ? this.placeholder : this.marqueeView} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 5dfb3e99b..ec2c77a82 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -76,14 +76,13 @@ export type DocAfterFocusFunc = (notFocused: boolean) => Promise export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { - getAnchor?: () => Doc; + getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) scrollFocus?: (doc: Doc, smooth: boolean) => Opt; // returns the duration of the focus - setViewSpec?: (anchor: Doc, preview: boolean) => void; - back?: () => boolean; - forward?: () => boolean; - url?: () => string; - submitURL?: (url: string) => boolean; - freeformData?: (force?: boolean) => Opt<{ panX: number, panY: number, scale: number, bounds: { x: number, y: number, r: number, b: number } }>; + setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document + reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. + menuControls?: any; // controls to display in the top menu bar when the document is selected. + // this is kind of hacky since it's not really a generic interface. need to think about how to do this better (it's used to fit a tab's contents to view when shown in a lightbox and to setup the minimap) + freeformData?: (force?: boolean) => Opt<{ panX: number, panY: number, scale: number, bounds: { x: number, y: number, r: number, b: number } }>; // the content bounds, pan position and zoom scale of a content view (typically for FreeformViews) } export interface DocumentViewSharedProps { renderDepth: number; @@ -990,8 +989,8 @@ export class DocumentView extends React.Component { @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; } @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); } - @computed get nativeWidth() { return returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); } - @computed get nativeHeight() { return returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions) || 0); } + @computed get nativeWidth() { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions)); } + @computed get nativeHeight() { return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions) || 0); } @computed get nativeScaling() { if (this.nativeWidth && (this.layoutDoc?._fitWidth || this.props.PanelHeight() / this.nativeHeight > this.props.PanelWidth() / this.nativeWidth)) { return this.props.PanelWidth() / this.nativeWidth; // width-limited or fitWidth diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 728e5e02f..4c3031ae2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -381,7 +381,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent = React.createRef(); private _iframeIndicatorRef = React.createRef(); private _iframeDragRef = React.createRef(); + private _keyInput = React.createRef(); private _ignoreScroll = false; private _initialScroll: Opt; @observable private _marqueeing: number[] | undefined; @@ -262,6 +263,52 @@ export class WebBox extends ViewBoxAnnotatableComponent this.urlEditor; + onWebUrlDrop = (e: React.DragEvent) => { + const { dataTransfer } = e; + const html = dataTransfer.getData("text/html"); + const uri = dataTransfer.getData("text/uri-list"); + const url = uri || html || this._url || ""; + const newurl = url.startsWith(window.location.origin) ? + url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; + this.submitURL(newurl); + e.stopPropagation(); + } + onWebUrlValueKeyDown = (e: React.KeyboardEvent) => { + e.key === "Enter" && this.submitURL(this._keyInput.current!.value); + e.stopPropagation(); + } + + @computed get urlEditor() { + return ( +
e.preventDefault()} > + e.preventDefault()} + onKeyDown={this.onWebUrlValueKeyDown} + onClick={(e) => { + this._keyInput.current!.select(); + e.stopPropagation(); + }} + ref={this._keyInput} + /> +
+ + + +
+
+ ); + } + editToggleBtn() { return {`${this.props.Document.isAnnotating ? "Exit" : "Enter"} annotation mode`}
}>
Date: Thu, 25 Feb 2021 01:25:36 -0500 Subject: cleaned up DoccomponentView to just have general api fields. fixed lightbox to animate back properly & shinkwrap. --- src/client/views/LightboxView.tsx | 34 +++++++++------------- src/client/views/collections/TabDocView.tsx | 8 ++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +++++---- src/client/views/nodes/DocumentView.tsx | 5 ++-- 4 files changed, 28 insertions(+), 33 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index e967a5b07..1b21bd073 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -13,7 +13,7 @@ import { SelectionManager } from '../util/SelectionManager'; import { Transform } from '../util/Transform'; import { TabDocView } from './collections/TabDocView'; import "./LightboxView.scss"; -import { DocumentView } from './nodes/DocumentView'; +import { DocumentView, ViewAdjustment } from './nodes/DocumentView'; import { DefaultStyleProvider } from './StyleProvider'; interface LightboxViewProps { @@ -103,8 +103,7 @@ export class LightboxView extends React.Component { [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + "-annotations"]), ...(LightboxView._future ?? []) - ] - .sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow))); + ].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow))); } docFilters = () => LightboxView._docFilters || []; addDocTab = LightboxView.AddDocTab; @@ -113,8 +112,8 @@ export class LightboxView extends React.Component { const target = LightboxView._docTarget = LightboxView._future?.pop(); const docView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (docView && target) { - docView.focus(target, { willZoom: true, scale: 0.9 }); - if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target: LightboxView._docTarget }); + docView.focus(target, { originalTarget: target, willZoom: true, scale: 0.9 }); + if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target }); } else { if (!target && LightboxView.path.length) { const saved = LightboxView._savedState; @@ -149,16 +148,14 @@ export class LightboxView extends React.Component { return; } const { doc, target } = LightboxView._history?.lastElement(); - const docView = target && DocumentManager.Instance.getLightboxDocumentView(target); - if (docView && target) { - LightboxView._doc = doc; - LightboxView._docTarget = target || doc; - if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc); - docView.focus(target, { willZoom: true, scale: 0.9 }); - } else { - LightboxView._doc = doc; - LightboxView._docTarget = target || doc; - } + const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const focusSpeed = 1000; + doc._viewTransition = `transform ${focusSpeed}ms`; + LightboxView._doc = doc; + LightboxView._docTarget = target || doc; + docView?.focus(doc, { willZoom: true, scale: 0.9 }); + setTimeout(() => doc._viewTransition = undefined, focusSpeed); + 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; @@ -208,11 +205,8 @@ export class LightboxView extends React.Component { { LightboxView._docView = r !== null ? r : undefined; setTimeout(action(() => { - const vals = r?.ComponentView?.freeformData?.(); - if (vals && r) { - r.layoutDoc._panX = vals.panX; - r.layoutDoc._panY = vals.panY; - r.layoutDoc._viewScale = vals.scale; + if (r && LightboxView._docTarget === r.props.Document) { + r.ComponentView?.shrinkWrap?.(); } r && (LightboxView._docTarget = undefined); })); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 80ddf2f48..3f8794665 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -273,12 +273,10 @@ export class TabDocView extends React.Component { } @action focusFunc = (doc: Doc, options?: DocFocusOptions) => { - const vals = (!options?.originalTarget || options?.originalTarget === this._document) && this.view?.ComponentView?.freeformData?.(true); - if (vals && this._document) { + const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; + if (shrinkwrap && this._document) { const focusSpeed = 1000; - this._document._panX = vals.panX; - this._document._panY = vals.panY; - this._document._viewScale = vals.scale; + shrinkwrap(); this._document._viewTransition = `transform ${focusSpeed}ms`; setTimeout(action(() => { this._document!._viewTransition = undefined; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 92c09ff3f..dfca2ba07 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -120,9 +120,7 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); parentActive = (outsideReaction: boolean) => this.props.active(outsideReaction) || this.props.parentActive?.(outsideReaction) || this.backgroundActive || this.layoutDoc._viewType === CollectionViewType.Pile ? true : false; elementFunc = () => this._layoutElements; + shrinkWrap = () => { + const vals = this.fitToContentVals; + this.layoutDoc._panX = vals.bounds.cx; + this.layoutDoc._panY = vals.bounds.cy; + this.layoutDoc._viewScale = vals.scale; + } freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined; freeformDocFilters = () => this._focusFilters || this.docFilters(); freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); reverseNativeScaling = () => this.fitToContent ? true : false; - panX = () => this.freeformData()?.panX ?? NumCast(this.Document._panX); - panY = () => this.freeformData()?.panY ?? NumCast(this.Document._panY); + panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX); + panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY); zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1)); contentTransform = () => `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`; getTransform = () => this.cachedGetTransform.copy(); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ec2c77a82..01d799255 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -80,9 +80,8 @@ export interface DocComponentView { scrollFocus?: (doc: Doc, smooth: boolean) => Opt; // returns the duration of the focus setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. - menuControls?: any; // controls to display in the top menu bar when the document is selected. - // this is kind of hacky since it's not really a generic interface. need to think about how to do this better (it's used to fit a tab's contents to view when shown in a lightbox and to setup the minimap) - freeformData?: (force?: boolean) => Opt<{ panX: number, panY: number, scale: number, bounds: { x: number, y: number, r: number, b: number } }>; // the content bounds, pan position and zoom scale of a content view (typically for FreeformViews) + shrinkWrap?: () => boolean; // 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. } export interface DocumentViewSharedProps { renderDepth: number; -- cgit v1.2.3-70-g09d2 From c4c3764397eb1ab12c93ea3e07483b436e87d736 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 25 Feb 2021 11:50:54 -0500 Subject: overhaul of state preservation in lightboxview when going back/forward/reopening a doc/etc --- src/client/views/LightboxView.tsx | 101 +++++++++++++-------- .../collectionFreeForm/CollectionFreeFormView.tsx | 13 +-- src/client/views/nodes/DocumentView.tsx | 3 +- 3 files changed, 71 insertions(+), 46 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 1b21bd073..babc518ff 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, trace } from 'mobx'; import { observer } from 'mobx-react'; import "normalize.css"; import * as React from 'react'; @@ -28,32 +28,33 @@ export class LightboxView extends React.Component { @computed public static get LightboxDoc() { return this._doc; } @observable private static _doc: Opt; @observable private static _docTarget: Opt; - @observable private static _tourMap: Opt = []; // list of all tours available from the current target @observable private static _docFilters: string[] = []; // filters - private static _savedState: Opt<{ panX: Opt, panY: Opt, scale: Opt, transition: Opt }>; + @observable private static _tourMap: Opt = []; // list of all tours available from the current target + private static _savedState: Opt<{ panX: Opt, panY: Opt, scale: Opt }>; private static _history: Opt<{ doc: Doc, target?: Doc }[]> = []; private static _future: Opt = []; private static _docView: Opt; - static path: { doc: Opt, target: Opt, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt, saved: Opt<{ panX: Opt, panY: Opt, scale: Opt, transition: Opt }> }[] = []; + static path: { doc: Opt, target: Opt, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt, saved: Opt<{ panX: Opt, panY: Opt, scale: Opt }> }[] = []; @action public static SetLightboxDoc(doc: Opt, target?: Doc, future?: Doc[]) { + if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) { + this.LightboxDoc._panX = this._savedState.panX; + this.LightboxDoc._panY = this._savedState.panY; + this.LightboxDoc._viewScale = this._savedState.scale; + this.LightboxDoc._viewTransition = undefined; + } if (!doc) { this._docFilters && (this._docFilters.length = 0); - if (this.LightboxDoc) { - this.LightboxDoc._panX = this._savedState?.panX; - this.LightboxDoc._panY = this._savedState?.panY; - this.LightboxDoc._viewScale = this._savedState?.scale; - this.LightboxDoc._viewTransition = this._savedState?.transition; - } this._future = this._history = []; } else { TabDocView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : this._history = [{ doc, target }]; - this._savedState = { - panX: Cast(doc._panX, "number", null), - panY: Cast(doc._panY, "number", null), - scale: Cast(doc._viewScale, "number", null), - transition: Cast(doc._viewTransition, "string", null) - }; + if (doc !== LightboxView.LightboxDoc) { + this._savedState = { + panX: Cast(doc._panX, "number", null), + panY: Cast(doc._panY, "number", null), + scale: Cast(doc._viewScale, "number", null), + }; + } } if (future) { this._future = future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length); @@ -92,6 +93,7 @@ export class LightboxView extends React.Component { } // adds a cookie to the lightbox view - the cookie becomes part of a filter which will display any documents whose cookie metadata field matches this cookie + @action public static SetCookie(cookie: string) { if (this.LightboxDoc && cookie) { this._docFilters = (f => this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f])(`cookies:${cookie}:provide`); @@ -117,11 +119,11 @@ export class LightboxView extends React.Component { } else { if (!target && LightboxView.path.length) { const saved = LightboxView._savedState; - if (LightboxView.LightboxDoc) { - LightboxView.LightboxDoc._panX = saved?.panX; - LightboxView.LightboxDoc._panY = saved?.panY; - LightboxView.LightboxDoc._viewScale = saved?.scale; - LightboxView.LightboxDoc._viewTransition = saved?.transition; + if (LightboxView.LightboxDoc && saved) { + LightboxView.LightboxDoc._panX = saved.panX; + LightboxView.LightboxDoc._panY = saved.panY; + LightboxView.LightboxDoc._viewScale = saved.scale; + LightboxView.LightboxDoc._viewTransition = undefined; } const pop = LightboxView.path.pop(); if (pop) { @@ -149,12 +151,17 @@ export class LightboxView extends React.Component { } const { doc, target } = LightboxView._history?.lastElement(); const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); - const focusSpeed = 1000; - doc._viewTransition = `transform ${focusSpeed}ms`; - LightboxView._doc = doc; - LightboxView._docTarget = target || doc; - docView?.focus(doc, { willZoom: true, scale: 0.9 }); - setTimeout(() => doc._viewTransition = undefined, focusSpeed); + if (docView) { + LightboxView._docTarget = undefined; + const focusSpeed = 1000; + doc._viewTransition = `transform ${focusSpeed}ms`; + if (!target) docView.ComponentView?.shrinkWrap?.(); + else docView.focus(target, { willZoom: true, scale: 0.9 }); + setTimeout(() => doc._viewTransition = undefined, focusSpeed); + } + 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!); @@ -185,6 +192,8 @@ export class LightboxView extends React.Component { setTimeout(LightboxView.Next); } + future = () => LightboxView._future; + tourMap = () => LightboxView._tourMap; fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc; render() { let downx = 0, downy = 0; @@ -204,11 +213,13 @@ export class LightboxView extends React.Component { }}> { LightboxView._docView = r !== null ? r : undefined; - setTimeout(action(() => { - if (r && LightboxView._docTarget === r.props.Document) { - r.ComponentView?.shrinkWrap?.(); - } - r && (LightboxView._docTarget = 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 && targetView?.focus(target, { willZoom: true, scale: 0.9, instant: true }); + LightboxView._docTarget = undefined; })); })} Document={LightboxView.LightboxDoc} @@ -246,13 +257,25 @@ export class LightboxView extends React.Component { e.stopPropagation(); LightboxView.Next(); })} - {this.navBtn("50%", 0, 0, "chevron-down", - () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => { - e.stopPropagation(); - this.stepInto(); - }, - StrCast(LightboxView._tourMap?.lastElement()?.TourMap) - )} +
; } +} +interface LightboxTourBtnProps { + navBtn: (left: Opt, bottom: Opt, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => JSX.Element; + tourMap: () => Opt; + future: () => Opt; + stepInto: () => void; +} +@observer +export class LightboxTourBtn extends React.Component { + render() { + return this.props.navBtn("50%", 0, 0, "chevron-down", + () => LightboxView.LightboxDoc && this.props.future()?.length ? "" : "none", e => { + e.stopPropagation(); + this.props.stepInto(); + }, + StrCast(this.props.tourMap()?.lastElement()?.TourMap) + ) + } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index dfca2ba07..4040362d8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -914,7 +914,6 @@ export class CollectionFreeFormView extends CollectionSubView this._viewTransition = 0); } return resetView; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 01d799255..e58aaba65 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -71,6 +71,7 @@ export interface DocFocusOptions { scale?: number; // percent of containing frame to zoom into document 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) } export type DocAfterFocusFunc = (notFocused: boolean) => Promise; export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void; @@ -80,7 +81,7 @@ export interface DocComponentView { scrollFocus?: (doc: Doc, smooth: boolean) => Opt; // returns the duration of the focus setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height. However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling. - shrinkWrap?: () => boolean; // requests a document to display all of its contents with no white space. currently only implemented (needed?) for freeform views + 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. } export interface DocumentViewSharedProps { -- cgit v1.2.3-70-g09d2