diff options
| author | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-02-18 16:31:21 -0500 |
|---|---|---|
| committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2021-02-18 16:31:21 -0500 |
| commit | ec18ae95005b9adc674a1ed6c3e976aa987ed7d2 (patch) | |
| tree | ae991bbfad86ca6230a21c86b40c274e2cfe6607 /src/client/views/collections/collectionFreeForm | |
| parent | 546540013de0a7cb647f30f1fcb513ce52048b72 (diff) | |
| parent | 0d59f6bc23c755c4eab2503add28699f5a5b1992 (diff) | |
merging
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
3 files changed, 152 insertions, 79 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index edf473a90..afc1babeb 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -1,16 +1,12 @@ -import { Doc, Field, FieldResult, WidthSym, HeightSym } from "../../../../fields/Doc"; -import { NumCast, StrCast, Cast } from "../../../../fields/Types"; -import { ScriptBox } from "../../ScriptBox"; -import { CompileScript } from "../../../util/Scripting"; -import { ScriptField } from "../../../../fields/ScriptField"; -import { OverlayView, OverlayElementOptions } from "../../OverlayView"; -import { emptyFunction, aggregateBounds } from "../../../../Utils"; -import React = require("react"); +import { Doc, Field, FieldResult, HeightSym, WidthSym } from "../../../../fields/Doc"; import { Id, ToString } from "../../../../fields/FieldSymbols"; import { ObjectField } from "../../../../fields/ObjectField"; import { RefField } from "../../../../fields/RefField"; import { listSpec } from "../../../../fields/Schema"; +import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { aggregateBounds } from "../../../../Utils"; import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import React = require("react"); export interface ViewDefBounds { type: string; @@ -89,7 +85,8 @@ export function computerPassLayout( pivotDoc: Doc, childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], + engineProps: any ) { const docMap = new Map<string, PoolData>(); childPairs.forEach(({ layout, data }, i) => { @@ -110,7 +107,8 @@ export function computerStarburstLayout( pivotDoc: Doc, childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], + engineProps: any ) { const docMap = new Map<string, PoolData>(); const burstRadius = [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])]; @@ -137,16 +135,17 @@ export function computePivotLayout( pivotDoc: Doc, childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], + engineProps: any ) { const docMap = new Map<string, PoolData>(); const fieldKey = "data"; const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>(); let nonNumbers = 0; - const pivotFieldKey = toLabel(pivotDoc._pivotField); + const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField); childPairs.map(pair => { - const lval = pivotFieldKey === "#" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) : + const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) : Cast(pair.layout[pivotFieldKey], listSpec("string"), null); const num = toNumber(pair.layout[pivotFieldKey]); @@ -278,7 +277,8 @@ export function computeTimelineLayout( pivotDoc: Doc, childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] + viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], + engineProps?: any ) { const fieldKey = "data"; const pivotDateGroups = new Map<number, Doc[]>(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index bba0807a4..58288a7b1 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,22 +1,24 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx"; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; -import { Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from "../../../../fields/Doc"; +import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc"; import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas"; import { Id } from "../../../../fields/FieldSymbols"; import { InkData, InkField, InkTool } from "../../../../fields/InkField"; import { List } from "../../../../fields/List"; +import { ObjectField } from "../../../../fields/ObjectField"; import { RichTextField } from "../../../../fields/RichTextField"; -import { createSchema, makeInterface, listSpec } from "../../../../fields/Schema"; +import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema"; 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, returnOne, returnZero, setupMoveUpEvents, Utils, returnVal, returnTrue } from "../../../../Utils"; +import { aggregateBounds, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils"; import { CognitiveServices } from "../../../cognitive_services/CognitiveServices"; import { DocServer } from "../../../DocServer"; import { Docs, DocUtils } from "../../../documents/Documents"; import { DocumentType } from "../../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; import { DocumentManager } from "../../../util/DocumentManager"; import { DragManager, dropActionType } from "../../../util/DragManager"; import { HistoryUtil } from "../../../util/History"; @@ -30,13 +32,16 @@ import { undoBatch } from "../../../util/UndoManager"; import { COLLECTION_BORDER_WIDTH } from "../../../views/globalCssVariables.scss"; import { Timeline } from "../../animationtimeline/Timeline"; import { ContextMenu } from "../../ContextMenu"; +import { DocumentDecorations } from "../../DocumentDecorations"; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke"; -import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewProps } from "../../nodes/CollectionFreeFormDocumentView"; -import { DocumentLinksButton } from "../../nodes/DocumentLinksButton"; -import { DocumentViewProps, DocAfterFocusFunc, DocumentView } from "../../nodes/DocumentView"; +import { LightboxView } from "../../LightboxView"; +import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView"; +import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView"; +import { FieldViewProps } from "../../nodes/FieldView"; import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox"; import { pageSchema } from "../../nodes/ImageBox"; import { PresBox } from "../../nodes/PresBox"; +import { StyleLayers, StyleProp } from "../../StyleProvider"; import { CollectionDockingView } from "../CollectionDockingView"; import { CollectionSubView } from "../CollectionSubView"; import { CollectionViewType } from "../CollectionView"; @@ -45,11 +50,6 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso import "./CollectionFreeFormView.scss"; import { MarqueeView } from "./MarqueeView"; import React = require("react"); -import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; -import { StyleProp, StyleLayers } from "../../StyleProvider"; -import { DocumentDecorations } from "../../DocumentDecorations"; -import { FieldViewProps } from "../../nodes/FieldView"; -import { reset } from "colors"; export const panZoomSchema = createSchema({ _panX: "number", @@ -79,6 +79,7 @@ export type collectionFreeformViewProps = { 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; }; @observer @@ -114,10 +115,20 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @observable _clusterSets: (Doc[])[] = []; @observable _timelineRef = React.createRef<Timeline>(); @observable _marqueeRef = React.createRef<HTMLDivElement>(); + @observable _focusFilters: Opt<string[]>; // fields that get overriden by focus anchor + @observable _focusRangeFilters: Opt<string[]>; // fields that get overriden by focus anchor @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.active() || this.props.active()); } - @computed get fitToContentScaling() { return this.fitToContent ? NumCast(this.layoutDoc.fitToContentScaling, 1) : 1; } - @computed get fitToContent() { return (this.props.fitContentsToDoc || this.Document._fitToBox) && !this.isAnnotationOverlay; } + @computed get fitToContentVals() { + return { + panX: (this.contentBounds.x + this.contentBounds.r) / 2, + panY: (this.contentBounds.y + this.contentBounds.b) / 2, + scale: !this.childDocs.length ? 1 : + Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), + this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)) + } || undefined; + } + @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; } @computed get parentScaling() { return 1; } @computed get contentBounds() { return aggregateBounds(this._layoutElements.filter(e => 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); } @@ -125,17 +136,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P 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.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.x + this.contentBounds.r) / 2 : this.Document._panX || 0; - private panY = () => this.fitToContent && !this.props.isAnnotationOverlay ? (this.contentBounds.y + this.contentBounds.b) / 2 : this.Document._panY || 0; - private zoomScaling = () => { - const mult = this.fitToContentScaling / this.parentScaling; - if (this.fitToContent) { - const zs = !this.childDocs.length ? 1 : - Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)); - return mult * zs; - } - return mult * NumCast(this.Document[this.scaleFieldKey], 1); - } + 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)) / this.parentScaling; @computed get cachedCenteringShiftX(): number { const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling; return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / this.parentScaling / scaling; // shift so pan position is at center of window for non-overlay collections @@ -441,7 +444,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return; } this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY)); - if (e.button === 0 && (!e.shiftKey || this._hitCluster !== -1) && !e.altKey && !e.ctrlKey && this.props.active(true)) { + if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.active(true)) { document.removeEventListener("pointermove", this.onPointerMove); document.removeEventListener("pointerup", this.onPointerUp); document.addEventListener("pointermove", this.onPointerMove); @@ -606,12 +609,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P } onClick = (e: React.MouseEvent) => { - if (this.layoutDoc.targetScale && (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { - if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a double click - runInAction(() => DocumentLinksButton.StartLink = DocumentLinksButton.StartLinkView = undefined); - this.scaleAtPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); - e.stopPropagation(); - e.preventDefault(); + if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) { + if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a double click + if (e.shiftKey) { + if (this.layoutDoc.targetScale) { + this.scaleAtPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1); + } + e.stopPropagation(); + e.preventDefault(); + } } this._lastTap = Date.now(); } @@ -811,7 +817,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P @action onPointerWheel = (e: React.WheelEvent): void => { - if (this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode) return; + if (this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return; if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming e.stopPropagation(); } @@ -855,13 +861,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2; } } - if (!this.layoutDoc._lockedTransform || CurrentUserUtils.OverlayDocs.includes(this.Document)) { + if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) { this.Document._viewTransition = panType; const scale = this.getLocalTransform().inverse().Scale; const newPanX = Math.min((1 - 1 / scale) * this.nativeWidth, Math.max(0, panX)); const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : (1 - 1 / scale) * this.nativeHeight), Math.max(0, panY)); - this.Document._panX = this.isAnnotationOverlay ? newPanX : panX; - this.Document._panY = this.isAnnotationOverlay ? newPanY : panY; + !this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX); + !this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY); } } @@ -894,7 +900,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1]; } - focusDocument = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => { + focusDocument = (doc: Doc, options?: DocFocusOptions) => { const state = HistoryUtil.getState(); // TODO This technically isn't correct if type !== "doc", as @@ -910,37 +916,42 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P HistoryUtil.pushState(state); } } + const LightboxState = LightboxView.GetSavedState(this.props.Document); SelectionManager.DeselectAll(); if (this.props.Document.scrollHeight) { - this.props.focus(doc, undefined, undefined, afterFocus); + this.props.focus(doc, options); } else { - const xfToCollection = docTransform ?? Transform.Identity(); + const xfToCollection = options?.docTransform ?? Transform.Identity(); const layoutdoc = Doc.Layout(doc); - const savedState = { px: NumCast(this.Document._panX), py: NumCast(this.Document._panY), s: this.Document[this.scaleFieldKey], pt: this.Document._viewTransition }; + const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: this.Document[this.scaleFieldKey], transition: this.Document._viewTransition }; const newState = HistoryUtil.getState(); - const cantTransform = this.props.isAnnotationOverlay || this.rootDoc._isGroup; - const { px, py } = cantTransform ? savedState : this.setPanIntoView(layoutdoc, xfToCollection, willZoom ? scale || .75 : undefined); + const cantTransform = this.props.isAnnotationOverlay || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc); + const { panX, panY, scale } = cantTransform ? savedState : this.setPanIntoView(layoutdoc, xfToCollection, options?.willZoom ? options?.scale || .75 : undefined); if (!cantTransform) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection - newState.initializers![this.Document[Id]] = { panX: px, panY: py }; + newState.initializers![this.Document[Id]] = { panX: panX, panY: panY }; HistoryUtil.pushState(newState); } // focus on the document in the collection - const didMove = !doc.z && (px !== savedState.px || py !== savedState.py); + const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== undefined); const focusSpeed = didMove ? (doc.focusSpeed !== undefined ? Number(doc.focusSpeed) : 500) : 0; // glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active... - if (didMove) this.setPan(px, py, `transform ${focusSpeed}ms`, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow - Doc.BrushDoc(this.rootDoc); - !doc.hidden && Doc.linkFollowHighlight(doc); + if (didMove) { + this.setPan(panX, panY, `transform ${focusSpeed}ms`, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow + scale && (this.Document[this.scaleFieldKey] = scale); + } + Doc.BrushDoc(doc); + const startTime = Date.now(); // focus on this collection within its parent view. the parent view after focusing determines whether to reset the view change within the collection const endFocus = async (moved: boolean) => { doc.hidden && Doc.UnHighlightDoc(doc); - const resetView = afterFocus ? await afterFocus(moved) : false; + const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing; if (resetView) { - this.Document._panX = savedState.px; - this.Document._panY = savedState.py; - this.Document[this.scaleFieldKey] = savedState.s; - this.Document._viewTransition = savedState.pt; + const restoreState = LightboxView.LightboxDoc !== this.props.Document && LightboxState ? LightboxState : savedState; + this.Document._panX = restoreState.panX; + this.Document._panY = restoreState.panY; + this.Document[this.scaleFieldKey] = restoreState.scale; + this.Document._viewTransition = restoreState.transition; } return resetView; }; @@ -950,8 +961,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P : new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1); - this.props.focus(cantTransform ? doc : this.rootDoc, willZoom, scale, (didFocus: boolean) => - new Promise<boolean>(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), focusSpeed)), xf.transform(docTransform ?? Transform.Identity())); + + this.props.focus(cantTransform ? doc : this.rootDoc, { + ...options, docTransform: xf, + afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => + setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))) + }); } } @@ -961,17 +976,29 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y)); const pt2 = xf.transformPoint(NumCast(doc.x) + doc[WidthSym](), NumCast(doc.y) + doc[HeightSym]()); const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1] }; + const cx = NumCast(this.layoutDoc._panX); + const cy = NumCast(this.layoutDoc._panY); + const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 }; if (scale) { - this.Document[this.scaleFieldKey] = scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0])), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]); - return { px: (bounds.left + bounds.right) / 2, py: (bounds.top + bounds.bot) / 2 }; + const maxZoom = 2; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context + + return { + panX: (bounds.left + bounds.right) / 2, + panY: (bounds.top + bounds.bot) / 2, + scale: Math.min(maxZoom, scale * Math.min(this.props.PanelWidth() / Math.abs(pt2[0] - pt[0]), this.props.PanelHeight() / Math.abs(pt2[1] - pt[1]))) + }; + } else if ((screen.right - screen.left) < (bounds.right - bounds.left) || + (screen.bot - screen.top) < (bounds.bot - bounds.top)) { + return { + panX: (bounds.left + bounds.right) / 2, + panY: (bounds.top + bounds.bot) / 2, + scale: Math.min(this.props.PanelHeight() / (bounds.bot - bounds.top), this.props.PanelWidth() / (bounds.right - bounds.left)) / 1.1 + }; } else { - const cx = NumCast(this.layoutDoc._panX); - const cy = NumCast(this.layoutDoc._panY); - const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 }; return { - px: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right), - py: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot) + panX: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right), + panY: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot), }; } } @@ -1000,8 +1027,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P ScreenToLocalTransform: childLayout.z ? this.getTransformOverlay : this.getTransform, PanelWidth: childLayout[WidthSym], PanelHeight: childLayout[HeightSym], - docFilters: this.docFilters, - docRangeFilters: this.docRangeFilters, + docFilters: this.freeformDocFilters, + docRangeFilters: this.freeformRangeDocFilters, searchFilterDocs: this.searchFilterDocs, focus: this.focusDocument, styleProvider: this.getClusterColor, @@ -1099,9 +1126,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P pivotDoc: Doc, childPairs: { layout: Doc, data?: Doc }[], panelDim: number[], - viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[])) => ViewDefResult[] + viewDefsToJSX: ((views: ViewDefBounds[]) => ViewDefResult[]), + engineProps: any) => ViewDefResult[] ) { - return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); + return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX, this.props.engineProps); } doFreeformLayout(poolData: Map<string, PoolData>) { @@ -1179,9 +1207,42 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P return elements; } + freeformDocFilters = () => this._focusFilters || this.docFilters(); + freeformRangeDocFilters = () => this._focusRangeFilters || this.docRangeFilters(); + @action + setViewSpec = (anchor: Doc, preview: boolean) => { + if (preview) { + this._focusFilters = StrListCast(Doc.GetProto(anchor).docFilters); + this._focusRangeFilters = StrListCast(Doc.GetProto(anchor).docRangeFilters); + } else if (anchor.pivotField !== undefined) { + this.layoutDoc._docFilters = new List<string>(StrListCast(anchor.docFilters)); + this.layoutDoc._docRangeFilters = new List<string>(StrListCast(anchor.docRangeFilters)); + } + return 0; + } + + getAnchor = () => { + const anchor = Docs.Create.TextanchorDocument({ + title: StrCast(this.layoutDoc._viewType), + useLinkSmallAnchor: true, + hideLinkButton: true, + annotationOn: this.rootDoc + }); + const proto = Doc.GetProto(anchor); + proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType; + proto[ViewSpecPrefix + "_docFilters"] = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]); + if (Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), null) !== undefined) { + Cast(this.dataDoc[this.props.fieldKey + "-annotations"], listSpec(Doc), []).push(anchor); + } else { + this.dataDoc[this.props.fieldKey + "-annotations"] = new List<Doc>([anchor]); + } + return anchor; + } + freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined; @action componentDidMount() { super.componentDidMount?.(); + this.props.setContentView?.(this); this._layoutComputeReaction = reaction(() => this.doLayoutComputation, (elements) => this._layoutElements = elements || [], { fireImmediately: true, name: "doLayout" }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index f7fb2b83d..f5a60effe 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -132,6 +132,18 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque }); })(); e.stopPropagation(); + } else if (e.key === "f" && e.ctrlKey) { + e.preventDefault(); + const root = Docs.Create.FreeformDocument([], { title: "folder", _stayInCollection: true, system: true, isFolder: true }); + const folder = Docs.Create.TreeDocument([root], { title: "root", isFolder: true, treeViewType: "fileSystem", treeViewTruncateTitleWidth: 150 }); + Doc.GetProto(folder).isFolder = true; + folder.x = x; + folder.y = y; + folder._width = 200; + folder._height = 300; + this.props.addDocument?.(folder); + //setTimeout(() => SelectionManager.SelectDoc(DocumentManager.Instance.getDocumentView(slide)!, false)); + e.stopPropagation(); } else if (e.key === "b" && e.ctrlKey) { // e.preventDefault(); // navigator.clipboard.readText().then(text => { |
