From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 17 Apr 2024 12:27:21 -0400 Subject: restoring eslint - updates not complete yet --- src/client/views/nodes/FunctionPlotBox.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/FunctionPlotBox.tsx') diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index a86bdbd79..180c651fb 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -2,18 +2,18 @@ import functionPlot from 'function-plot'; import { computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { DocListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; +import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; -import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { PinProps, ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; -import { PinProps, PresBox } from './trails'; -import { LinkManager } from '../../util/LinkManager'; +import { PresBox } from './trails'; @observer export class FunctionPlotBox extends ViewBoxAnnotatableComponent() { @@ -89,7 +89,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent drop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.docDragData?.droppedDocuments.length) { const added = de.complete.docDragData.droppedDocuments.reduce((res, doc) => { - ///const ret = res && Doc.AddDocToList(this.dataDoc, this._props.fieldKey, doc); + // const ret = res && Doc.AddDocToList(this.dataDoc, this._props.fieldKey, doc); if (res) { const link = DocUtils.MakeLink(doc, this.Document, { link_relationship: 'function', link_description: 'input' }); link && this._props.addDocument?.(link); -- cgit v1.2.3-70-g09d2 From 776c9cd88fc0799426ced87f36cb215dfdc1854b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 30 Apr 2024 17:59:40 -0400 Subject: unwinding some import cycles. added PinFuncs and .from(dv:DocumentView) for CollectionFreeForm stiuff --- package-lock.json | 1 + package.json | 1 + src/client/util/SnappingManager.ts | 3 + src/client/views/DocComponent.tsx | 34 +---- src/client/views/DocumentDecorations.tsx | 11 +- src/client/views/InkingStroke.tsx | 6 +- src/client/views/MainView.tsx | 3 +- src/client/views/PinFuncs.ts | 139 +++++++++++++++++++++ .../views/collections/CollectionStackingView.tsx | 1 + .../views/collections/CollectionTimeView.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +-- src/client/views/nodes/AudioBox.tsx | 6 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 + src/client/views/nodes/ComparisonBox.tsx | 6 +- .../nodes/DataVizBox/components/Histogram.tsx | 5 +- .../nodes/DataVizBox/components/LineChart.tsx | 6 +- .../views/nodes/DataVizBox/components/PieChart.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 18 +-- src/client/views/nodes/FunctionPlotBox.tsx | 6 +- src/client/views/nodes/ImageBox.tsx | 8 +- src/client/views/nodes/LabelBox.tsx | 6 +- src/client/views/nodes/MapBox/AnimationUtility.ts | 1 - src/client/views/nodes/MapBox/MapBox.tsx | 7 +- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 6 +- src/client/views/nodes/PDFBox.tsx | 8 +- src/client/views/nodes/VideoBox.tsx | 6 +- src/client/views/nodes/WebBox.tsx | 6 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +- src/client/views/nodes/trails/PresBox.tsx | 121 ++---------------- src/client/views/nodes/trails/PresElementBox.tsx | 3 +- src/server/server_Initialization.ts | 1 - 32 files changed, 236 insertions(+), 229 deletions(-) create mode 100644 src/client/views/PinFuncs.ts (limited to 'src/client/views/nodes/FunctionPlotBox.tsx') diff --git a/package-lock.json b/package-lock.json index 80aef5232..c53a3219e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,6 +82,7 @@ "core-js": "^3.33.3", "cors": "^2.8.5", "css-loader": "^7.0.0", + "csstype": "^3.1.3", "csv-parser": "^3.0.0", "csv-stringify": "^6.4.4", "D": "^1.0.0", diff --git a/package.json b/package.json index d826c855e..0e7955d54 100644 --- a/package.json +++ b/package.json @@ -167,6 +167,7 @@ "core-js": "^3.33.3", "cors": "^2.8.5", "css-loader": "^7.0.0", + "csstype": "^3.1.3", "csv-parser": "^3.0.0", "csv-stringify": "^6.4.4", "D": "^1.0.0", diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 3da85191f..5cd6ecfe1 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -18,6 +18,7 @@ export class SnappingManager { @observable _horizSnapLines: number[] = []; @observable _vertSnapLines: number[] = []; @observable _exploreMode = false; + @observable _userPanned = false; private constructor() { SnappingManager._manager = this; @@ -43,6 +44,7 @@ export class SnappingManager { public static get IsResizing() { return this.Instance._isResizing; } // prettier-ignore public static get CanEmbed() { return this.Instance._canEmbed; } // prettier-ignore public static get ExploreMode() { return this.Instance._exploreMode; } // prettier-ignore + public static get UserPanned() { return this.Instance._userPanned; } // prettier-ignore public static SetShiftKey = (down: boolean) => runInAction(() => {this.Instance._shiftKey = down}); // prettier-ignore public static SetCtrlKey = (down: boolean) => runInAction(() => {this.Instance._ctrlKey = down}); // prettier-ignore public static SetMetaKey = (down: boolean) => runInAction(() => {this.Instance._metaKey = down}); // prettier-ignore @@ -52,6 +54,7 @@ export class SnappingManager { public static SetIsResizing = (docid?:string) => runInAction(() => {this.Instance._isResizing = docid}); // prettier-ignore public static SetCanEmbed = (embed:boolean) => runInAction(() => {this.Instance._canEmbed = embed}); // prettier-ignore public static SetExploreMode = (state:boolean) => runInAction(() => {this.Instance._exploreMode = state}); // prettier-ignore + public static TriggerUserPanned = () => runInAction(() => {this.Instance._userPanned = !this.Instance._userPanned}); // prettier-ignore public static userColor: string | undefined; public static userVariantColor: string | undefined; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index bbd902a96..391c2f694 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -11,43 +11,11 @@ import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util'; import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { PinProps } from './PinFuncs'; import { DocumentView, OpenWhere } from './nodes/DocumentView'; import { FieldViewProps, FocusViewOptions } from './nodes/FieldView'; // import { DocUtils } from '../documents/Documents'; -export interface pinDataTypes { - scrollable?: boolean; - dataviz?: number[]; - pannable?: boolean; - type_collection?: boolean; - inkable?: boolean; - filters?: boolean; - pivot?: boolean; - temporal?: boolean; - clippable?: boolean; - datarange?: boolean; - dataview?: boolean; - poslayoutview?: boolean; - dataannos?: boolean; - map?: boolean; -} - -export interface MarqueeViewBounds { - left: number; - top: number; - width: number; - height: number; -} -export interface PinProps { - audioRange?: boolean; - activeFrame?: number; - currentFrame?: number; - hidePresBox?: boolean; - pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected) - pinDocLayout?: boolean; // pin layout info (width/height/x/y) - pinAudioPlay?: boolean; // pin audio annotation - pinData?: pinDataTypes; -} /** * Shared interface among all viewBox'es (ie, react classes that render the contents of a Doc) * Many of these methods only make sense for specific viewBox'es, but they should be written to diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3083b9be0..1e6eb1aeb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -6,10 +6,11 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { FaUndo } from 'react-icons/fa'; import { lightOrDark, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; -import { Utils, numberValue, emptyFunction } from '../../Utils'; +import { Utils, emptyFunction, numberValue } from '../../Utils'; import { DateField } from '../../fields/DateField'; import { Doc, DocListCast, Field, FieldType, HierarchyMapping, ReverseHierarchyMap } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, DocData } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; @@ -31,11 +32,11 @@ import { ObservableReactComponent } from './ObservableReactComponent'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { Colors } from './global/globalEnums'; +import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { KeyValueBox } from './nodes/KeyValueBox'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; -import { Id } from '../../fields/FieldSymbols'; interface DocumentDecorationsProps { PanelWidth: number; @@ -447,7 +448,7 @@ export class DocumentDecorations extends ObservableReactComponent docView.CollectionFreeFormView?.dragStarting(false, false)); + SelectionManager.Views.forEach(docView => CollectionFreeFormDocumentView.from(docView)?.CollectionFreeFormView?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { @@ -700,7 +701,7 @@ export class DocumentDecorations extends ObservableReactComponent 135; - const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && seldocview.CollectionFreeFormDocumentView; // when do we want an object to not rotate? + const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && CollectionFreeFormDocumentView.from(seldocview); // when do we want an object to not rotate? const rotation = SelectionManager.Views.length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; // Radius constants @@ -771,7 +772,7 @@ export class DocumentDecorations extends ObservableReactComponent v.CollectionFreeFormDocumentView); + const freeformDoc = SelectionManager.Views.some(v => CollectionFreeFormDocumentView.from(v)); return (
() if (anchor) { anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), inkable: true } }, this.Document); return anchor; } return this.Document; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index be13b14f5..d3dd51ac6 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -76,6 +76,7 @@ import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; const _global = (window /* browser */ || global) /* node */ as any; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @@ -964,7 +965,7 @@ export class MainView extends ObservableReactComponent<{}> { } @computed get snapLines() { const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs.lastElement(); - const dragPar = dragged ? DocumentManager.Instance.getDocumentView(dragged)?.CollectionFreeFormView : undefined; + const dragPar = dragged ? CollectionFreeFormView.from(DocumentManager.Instance.getDocumentView(dragged)) : undefined; return !dragPar?.layoutDoc.freeform_snapLines ? null : (
diff --git a/src/client/views/PinFuncs.ts b/src/client/views/PinFuncs.ts new file mode 100644 index 000000000..3d998ecaf --- /dev/null +++ b/src/client/views/PinFuncs.ts @@ -0,0 +1,139 @@ +import { Doc, DocListCast } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; +import { Copy, Id } from '../../fields/FieldSymbols'; +import { List } from '../../fields/List'; +import { ObjectField } from '../../fields/ObjectField'; +import { NumCast, StrCast } from '../../fields/Types'; +import { SerializationHelper } from '../util/SerializationHelper'; + +export interface MarqueeViewBounds { + left: number; + top: number; + width: number; + height: number; +} +export interface pinDataTypes { + scrollable?: boolean; + dataviz?: number[]; + pannable?: boolean; + type_collection?: boolean; + inkable?: boolean; + filters?: boolean; + pivot?: boolean; + temporal?: boolean; + clippable?: boolean; + datarange?: boolean; + dataview?: boolean; + poslayoutview?: boolean; + dataannos?: boolean; + map?: boolean; +} +export interface PinProps { + audioRange?: boolean; + activeFrame?: number; + currentFrame?: number; + hidePresBox?: boolean; + pinViewport?: MarqueeViewBounds; // pin a specific viewport on a freeform view (use MarqueeView.CurViewBounds to compute if no region has been selected) + pinDocLayout?: boolean; // pin layout info (width/height/x/y) + pinAudioPlay?: boolean; // pin audio annotation + pinData?: pinDataTypes; +} + +/// copies values from the targetDoc (which is the prototype of the pinDoc) to +/// reserved fields on the pinDoc so that those values can be restored to the +/// target doc when navigating to it. +export function PinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) { + const pinDoc = pinDocIn; + pinDoc.presentation = true; + pinDoc.config = ''; + if (pinProps.pinDocLayout) { + pinDoc.config_pinLayout = true; + pinDoc.config_x = NumCast(targetDoc.x); + pinDoc.config_y = NumCast(targetDoc.y); + pinDoc.config_rotation = NumCast(targetDoc.rotation); + pinDoc.config_width = NumCast(targetDoc.width); + pinDoc.config_height = NumCast(targetDoc.height); + } + if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true; + if (pinProps.pinData) { + pinDoc.config_pinData = + pinProps.pinData.scrollable || + pinProps.pinData.temporal || + pinProps.pinData.pannable || + pinProps.pinData.type_collection || + pinProps.pinData.clippable || + pinProps.pinData.datarange || + pinProps.pinData.dataview || + pinProps.pinData.poslayoutview || + pinProps?.activeFrame !== undefined; + const fkey = Doc.LayoutFieldKey(targetDoc); + if (pinProps.pinData.dataview) { + pinDoc.config_usePath = targetDoc[fkey + '_usePath']; + pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data; + } + if (pinProps.pinData.dataannos) { + const fieldKey = Doc.LayoutFieldKey(targetDoc); + pinDoc.config_annotations = new List(DocListCast(targetDoc[DocData][fieldKey + '_annotations']).filter(doc => !doc.layout_unrendered)); + } + if (pinProps.pinData.inkable) { + pinDoc.config_fillColor = targetDoc.fillColor; + pinDoc.config_color = targetDoc.color; + pinDoc.config_width = targetDoc._width; + pinDoc.config_height = targetDoc._height; + } + if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop; + if (pinProps.pinData.clippable) { + const fieldKey = Doc.LayoutFieldKey(targetDoc); + pinDoc.config_clipWidth = targetDoc[fieldKey + '_clipWidth']; + } + if (pinProps.pinData.datarange) { + pinDoc.config_xRange = undefined; // targetDoc?.xrange; + pinDoc.config_yRange = undefined; // targetDoc?.yrange; + } + if (pinProps.pinData.map) { + // pinDoc.config_latitude = targetDoc?.latitude; + // pinDoc.config_longitude = targetDoc?.longitude; + pinDoc.config_map_zoom = targetDoc?.map_zoom; + pinDoc.config_map_type = targetDoc?.map_type; + // ... + } + if (pinProps.pinData.poslayoutview) + pinDoc.config_pinLayoutData = new List( + DocListCast(targetDoc[fkey] as ObjectField).map(d => + JSON.stringify({ + id: d[Id], + x: NumCast(d.x), + y: NumCast(d.y), + w: NumCast(d._width), + h: NumCast(d._height), + fill: StrCast(d._fillColor), + back: StrCast(d._backgroundColor), + data: SerializationHelper.Serialize(d.data instanceof ObjectField ? d.data[Copy]() : ''), + text: SerializationHelper.Serialize(d.text instanceof ObjectField ? d.text[Copy]() : ''), + }) + ) + ); + if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection; + if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField); + if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField; + if (pinProps.pinData.pannable) { + pinDoc.config_panX = NumCast(targetDoc._freeform_panX); + pinDoc.config_panY = NumCast(targetDoc._freeform_panY); + pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1); + } + if (pinProps.pinData.temporal) { + pinDoc.config_clipStart = targetDoc._layout_currentTimecode; + const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1); + pinDoc.config_clipEnd = NumCast(pinDoc.config_clipStart) + NumCast(targetDoc.clipEnd, duration); + } + } + if (pinProps?.pinViewport) { + // If pinWithView option set then update scale and x / y props of slide + const bounds = pinProps.pinViewport; + pinDoc.config_pinView = true; + pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1); + pinDoc.config_panX = bounds.left + bounds.width / 2; + pinDoc.config_panY = bounds.top + bounds.height / 2; + pinDoc.config_viewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); + } +} diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index c47fe915a..ad9960989 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,5 +1,6 @@ /* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// eslint-disable-next-line import/no-extraneous-dependencies import * as CSS from 'csstype'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index e1d2e3c40..b3760d4af 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -3,8 +3,8 @@ import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction } from '../../../Utils'; import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils'; +import { emptyFunction } from '../../../Utils'; import { Doc, Opt, StrListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; @@ -17,8 +17,8 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { FieldsDropdown } from '../FieldsDropdown'; +import { PinDocView } from '../PinFuncs'; import { DocumentView } from '../nodes/DocumentView'; -import { PresBox } from '../nodes/trails'; import { CollectionSubView } from './CollectionSubView'; import './CollectionTimeView.scss'; import { ViewDefBounds, computePivotLayout, computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines'; @@ -55,7 +55,7 @@ export class CollectionTimeView extends CollectionSubView() { title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any, annotationOn: this.Document, }); - PresBox.pinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document); + PinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document); if (addAsAnnotation) { // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index e98e444ed..e7b8237a5 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -26,9 +26,9 @@ import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { UndoManager, undoable } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; -import { PinProps } from '../DocComponent'; import { LightboxView } from '../LightboxView'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { PinProps, PinDocView } from '../PinFuncs'; import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; import { Colors } from '../global/globalEnums'; import { DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView'; @@ -426,7 +426,7 @@ export class TabDocView extends ObservableReactComponent { pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); - if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc); + if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc); if (!pinProps?.audioRange && duration !== undefined) { pinDoc.presentation_mediaStart = 'manual'; pinDoc.presentation_mediaStop = 'manual'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 69cb52233..653a01a04 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -38,7 +38,6 @@ import { Transform } from '../../../util/Transform'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; -import { PinProps } from '../../DocComponent'; import { GestureOverlay } from '../../GestureOverlay'; import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; @@ -47,13 +46,13 @@ import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp'; import { DocumentView, OpenWhere } from '../../nodes/DocumentView'; import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; -import { PresBox } from '../../nodes/trails/PresBox'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; import { TreeViewType } from '../CollectionTreeView'; import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid'; -import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI'; import { CollectionFreeFormClusters } from './CollectionFreeFormClusters'; +import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI'; import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines'; import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents'; import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; @@ -85,6 +84,9 @@ export class CollectionFreeFormView extends CollectionSubView(); + public static from(dv?: DocumentView) { + return CollectionFreeFormDocumentView.from(dv)?.CollectionFreeFormView; + } _oldWheel: any; _clusters = new CollectionFreeFormClusters(this); @@ -592,7 +594,7 @@ export class CollectionFreeFormView extends CollectionSubView { - PresBox.Instance?.pauseAutoPres(); + SnappingManager.TriggerUserPanned(); this.setPan(NumCast(this.Document[this.panXFieldKey]) - e.deltaX, NumCast(this.Document[this.panYFieldKey]) - e.deltaY, 0, true); }; @@ -600,7 +602,7 @@ export class CollectionFreeFormView extends CollectionSubView { const ctrlKey = e.ctrlKey && !e.shiftKey; const shiftKey = e.shiftKey && !e.ctrlKey; - PresBox.Instance?.pauseAutoPres(); + SnappingManager.TriggerUserPanned(); this.DocumentView?.().clearViewTransition(); const [dxi, dyi] = this.screenToFreeformContentsXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY); const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.ScreenToLocalBoxXf().Rotate); @@ -834,7 +836,7 @@ export class CollectionFreeFormView extends CollectionSubView { if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom - PresBox.Instance?.pauseAutoPres(); + SnappingManager.TriggerUserPanned(); if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return; e.stopPropagation(); const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight); @@ -1289,7 +1291,7 @@ export class CollectionFreeFormView extends CollectionSubView view.CollectionFreeFormView?.bringToFront(view.Document)); + SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function sendToBack() { - SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true)); + SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function datavizFromSchema() { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 9251dca6d..1f618135f 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -21,11 +21,11 @@ import { undoBatch } from '../../util/UndoManager'; import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { PinDocView, PinProps } from '../PinFuncs'; import './AudioBox.scss'; import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; -import { PresBox } from './trails'; /** * AudioBox @@ -159,7 +159,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { ) || this.Document : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true } }, this.Document); return anchor; }; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 7b1847ae4..9c4d748bd 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -38,6 +38,7 @@ interface freeFormProps { highlight?: boolean; transition?: string; } + export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { RenderCutoffProvider: (doc: Doc) => boolean; CollectionFreeFormView: CollectionFreeFormView; @@ -63,6 +64,9 @@ export class CollectionFreeFormDocumentView extends DocComponent (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames + public static from(dv?: DocumentView) { + return dv?._props.parent instanceof CollectionFreeFormDocumentView ? dv._props.parent : undefined; + } constructor(props: CollectionFreeFormDocumentViewProps & freeFormProps) { super(props); @@ -273,6 +277,7 @@ export class CollectionFreeFormDocumentView extends DocComponent ) : ( val.lower)).omit} // prettier-ignore DataTransition={this.DataTransition} diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index e6fd447ac..bc151f6a5 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -11,14 +11,14 @@ import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { undoBatch } from '../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { PinProps, PinDocView } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { KeyValueBox } from './KeyValueBox'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; -import { PresBox } from './trails'; @observer export class ComparisonBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -113,7 +113,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; /* addAsAnnotation && */ this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), clippable: true } }, this.Document); return anchor; } return this.Document; diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index f0ffdbdcf..79b3e9541 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -11,9 +11,8 @@ import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; import { undoable } from '../../../../util/UndoManager'; -import { PinProps } from '../../../DocComponent'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; -import { PresBox } from '../../trails'; +import { PinProps, PinDocView } from '../../../PinFuncs'; import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils'; import './Chart.scss'; @@ -117,7 +116,7 @@ export class Histogram extends ObservableReactComponent { const anchor = Docs.Create.ConfigDocument({ title: 'histogram doc selection' + this._currSelected, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); return anchor; }; diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 8105adf1e..1c3134185 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -10,12 +10,12 @@ import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; import { DocumentManager } from '../../../../util/DocumentManager'; import { undoable } from '../../../../util/UndoManager'; +import {} from '../../../DocComponent'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; -import { PresBox } from '../../trails'; +import { PinProps, PinDocView } from '../../../PinFuncs'; import { DataVizBox } from '../DataVizBox'; import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils'; import './Chart.scss'; -import { PinProps } from '../../../DocComponent'; export interface DataPoint { x: number; @@ -177,7 +177,7 @@ export class LineChart extends ObservableReactComponent { // title: 'line doc selection' + (this._currSelected?.x ?? ''), }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); anchor.config_dataVizSelection = this._currSelected ? new List([this._currSelected.x, this._currSelected.y]) : undefined; return anchor; }; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index f57070f92..ffb2f528a 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -12,9 +12,8 @@ import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; import { undoable } from '../../../../util/UndoManager'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; -import { PresBox } from '../../trails'; +import { PinProps, PinDocView } from '../../../PinFuncs'; import './Chart.scss'; -import { PinProps } from '../../../DocComponent'; export interface PieChartProps { Document: Doc; @@ -109,7 +108,7 @@ export class PieChart extends ObservableReactComponent { // title: 'piechart doc selection' + this._currSelected, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document); return anchor; }; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 626e3d899..a603de10b 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -22,7 +22,6 @@ import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; -import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocUtils, Docs } from '../../documents/Documents'; import { DictationManager } from '../../util/DictationManager'; @@ -46,7 +45,6 @@ import { FieldsDropdown } from '../FieldsDropdown'; import { GestureOverlay } from '../GestureOverlay'; import { LightboxView } from '../LightboxView'; import { AudioAnnoState, StyleProp } from '../StyleProvider'; -import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView'; import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'; import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; @@ -108,6 +106,8 @@ export interface DocumentViewProps extends FieldViewSharedProps { dragConfig?: (data: DragManager.DocumentDragData) => void; dragStarting?: () => void; dragEnding?: () => void; + + parent?: any; } @observer export class DocumentViewInternal extends DocComponent() { @@ -314,7 +314,7 @@ export class DocumentViewInternal extends DocComponent { - const options:FocusViewOptions = { pointFocus: {X: e.clientX, Y :e.clientY}, zoomTime: browseTransitionTime}; + const options: FocusViewOptions = { pointFocus: { X: e.clientX, Y: e.clientY }, zoomTime: browseTransitionTime }; if (!focused && this._docView) { this._docView .docViewPath() @@ -645,12 +645,6 @@ export class DocumentViewInternal extends DocComponent Doc.MakeMetadataFieldTemplate(this.Document, this._props.TemplateDataDocument), icon: 'concierge-bell' }); moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => { this.Document._chromeHidden = !this.Document._chromeHidden; }, icon: 'project-diagram' }); // prettier-ignore - - if (Cast(Doc.GetProto(this.Document).data, listSpec(Doc))) { - moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.Document }).then(console.log), icon: 'caret-square-right' }); - moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this.Document), icon: 'caret-square-right' }); - moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this.Document), icon: 'caret-square-right' }); - } moreItems.push({ description: 'Copy ID', event: () => ClientUtils.CopyText(Doc.globalServerPath(this.Document)), icon: 'fingerprint' }); } } @@ -1079,7 +1073,7 @@ export class DocumentViewInternal extends DocComponent CollectionFreeFormDocumentView }>() { +export class DocumentView extends DocComponent() { public static ROOT_DIV = 'documentView-effectsWrapper'; public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore public ContentRef = React.createRef(); @@ -1100,7 +1094,7 @@ export class DocumentView extends DocComponent. used by LinkBox's Xanchor to find the arrowhead locations. public DocUniqueId = DocumentView.UniquifyId(LightboxView.Contains(this), this.Document[Id]); - constructor(props: DocumentViewProps & { CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView }) { + constructor(props: DocumentViewProps) { super(props); makeObservable(this); } @@ -1227,8 +1221,6 @@ export class DocumentView extends DocComponent { diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 180c651fb..31faa7ac3 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -11,9 +11,9 @@ import { DocUtils, Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent } from '../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { PinProps, PinDocView } from '../PinFuncs'; import { FieldView, FieldViewProps } from './FieldView'; -import { PresBox } from './trails'; @observer export class FunctionPlotBox extends ViewBoxAnnotatableComponent() { @@ -40,7 +40,7 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent } getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), datarange: true } }, this.Document); anchor.config_xRange = new List(Array.from(this._plot.options.xAxis.domain)); anchor.config_yRange = new List(Array.from(this._plot.options.yAxis.domain)); if (addAsAnnotation) this.addDocument(anchor); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6d3d4be06..8bd5ab03d 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -21,18 +21,18 @@ import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; -import { ContextMenu } from '../ContextMenu'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; import './ImageBox.scss'; -import { PresBox } from './trails'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -102,7 +102,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !visibleAnchor } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !visibleAnchor } }, this.Document); return anchor; } return this.Document; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 9df928581..89270652c 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -10,12 +10,12 @@ import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxBaseComponent } from '../DocComponent'; +import { ViewBoxBaseComponent } from '../DocComponent'; +import { PinProps, PinDocView } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; import './LabelBox.scss'; -import { PresBox } from './trails'; @observer export class LabelBox extends ViewBoxBaseComponent() { @@ -96,7 +96,7 @@ export class LabelBox extends ViewBoxBaseComponent() { if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; // addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}) } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}) } }, this.Document); return anchor; } return anchor; diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts index 3a2679ecf..f4bae66bb 100644 --- a/src/client/views/nodes/MapBox/AnimationUtility.ts +++ b/src/client/views/nodes/MapBox/AnimationUtility.ts @@ -1,7 +1,6 @@ import * as turf from '@turf/turf'; import { Position } from '@turf/turf'; import * as d3 from 'd3'; -// eslint-disable-next-line import/no-extraneous-dependencies import { Feature, GeoJsonProperties, Geometry } from 'geojson'; import mapboxgl, { MercatorCoordinate } from 'mapbox-gl'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 6495cf440..60dad314f 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -6,7 +6,6 @@ import { Checkbox, FormControlLabel, TextField } from '@mui/material'; import * as turf from '@turf/turf'; import { IconButton, Size, Type } from 'browndash-components'; import * as d3 from 'd3'; -// eslint-disable-next-line import/no-extraneous-dependencies import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson'; import mapboxgl, { LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl'; import { IReactionDisposer, ObservableMap, action, autorun, computed, makeObservable, observable, runInAction } from 'mobx'; @@ -25,14 +24,14 @@ import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { PinProps, PinDocView } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; import { FormattedTextBox } from '../formattedText/FormattedTextBox'; -import { PresBox } from '../trails'; import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; import { MapAnchorMenu } from './MapAnchorMenu'; @@ -462,7 +461,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); return anchor; } return this.Document; diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index c19528af6..d899fcb9a 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -17,7 +17,8 @@ import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoable } from '../../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent } from '../../DocComponent'; +import { ViewBoxAnnotatableComponent } from '../../DocComponent'; +import { PinProps, PinDocView } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; @@ -26,7 +27,6 @@ import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; import { MapAnchorMenu } from '../MapBox/MapAnchorMenu'; import '../MapBox/MapBox.scss'; import { FormattedTextBox } from '../formattedText/FormattedTextBox'; -import { PresBox } from '../trails'; /** * MapBox architecture: @@ -487,7 +487,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent if (anchor) { if (!addAsAnnotation) anchor.backgroundColor = 'transparent'; addAsAnnotation && this.addDocument(anchor); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document); return anchor; } return this.Document; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 4d5013437..fbf5e018c 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -26,16 +26,16 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionStackingView } from '../collections/CollectionStackingView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { Colors } from '../global/globalEnums'; -import { CreateImage } from './WebBoxRenderer'; import { PDFViewer } from '../pdf/PDFViewer'; +import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; import { DocumentView, OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; import { ImageBox } from './ImageBox'; import './PDFBox.scss'; -import { PresBox } from './trails'; +import { CreateImage } from './WebBoxRenderer'; @observer export class PDFBox extends ViewBoxAnnotatableComponent() implements ViewBoxInterface { @@ -265,7 +265,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent() implem }); const visibleAnchor = this._pdfViewer?._getAnchor?.(this._pdfViewer.savedAnnotations(), true); const anchor = visibleAnchor ?? docAnchor(); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: true, pannable: true } }, this.Document); anchor.text = ele?.textContent ?? ''; anchor.text_html = ele?.innerHTML; addAsAnnotation && this.addDocument(anchor); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b8ec0081a..13ee3250e 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -25,14 +25,14 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; import { RecordingBox } from './RecordingBox'; -import { PresBox } from './trails'; import './VideoBox.scss'; /** @@ -357,7 +357,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl addAsAnnotation && marquee ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document); return anchor; }; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 725c5bab4..dfe237a86 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -27,19 +27,19 @@ import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { Annotation } from '../pdf/Annotation'; import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; +import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; import { StyleProp } from '../StyleProvider'; import { DocumentView, OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; import { LinkInfo } from './LinkDocPreview'; -import { PresBox } from './trails'; import './WebBox.scss'; const { CreateImage } = require('./WebBoxRenderer'); @@ -350,7 +350,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem y: NumCast(this.layoutDoc._layout_scrollTop), annotationOn: this.Document, }); - PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: !!pinProps?.pinData, pannable: true } }, this.Document); + PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), scrollable: !!pinProps?.pinData, pannable: true } }, this.Document); anchor.text = ele?.textContent ?? ''; anchor.text_html = ele?.innerHTML ?? this._selectionText; addAsAnnotation && this.addDocumentWrapper(anchor); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d659d5e3c..9bcf5027f 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -45,18 +45,18 @@ import { CollectionStackingView } from '../../collections/CollectionStackingView import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { styleFromLayoutString, StyleProp } from '../../StyleProvider'; import { mediaState } from '../AudioBox'; import { DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; import { LinkInfo } from '../LinkDocPreview'; -import { PresBox } from '../trails'; import { DashDocCommentView } from './DashDocCommentView'; import { DashDocView } from './DashDocView'; import { DashFieldView } from './DashFieldView'; @@ -252,7 +252,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent() { @@ -144,6 +145,10 @@ export class PresBox extends ViewBoxBaseComponent() { } componentDidMount() { + this._disposers.pause = reaction( + () => SnappingManager.UserPanned, + () => this.pauseAutoPres() + ); this._disposers.keyboard = reaction( () => this.selectedDoc, selected => { @@ -618,114 +623,6 @@ export class PresBox extends ViewBoxBaseComponent() { return undefined; } - /// copies values from the targetDoc (which is the prototype of the pinDoc) to - /// reserved fields on the pinDoc so that those values can be restored to the - /// target doc when navigating to it. - @action - static pinDocView(pinDocIn: Doc, pinProps: PinProps, targetDoc: Doc) { - const pinDoc = pinDocIn; - pinDoc.presentation = true; - pinDoc.config = ''; - if (pinProps.pinDocLayout) { - pinDoc.config_pinLayout = true; - pinDoc.config_x = NumCast(targetDoc.x); - pinDoc.config_y = NumCast(targetDoc.y); - pinDoc.config_rotation = NumCast(targetDoc.rotation); - pinDoc.config_width = NumCast(targetDoc.width); - pinDoc.config_height = NumCast(targetDoc.height); - } - if (pinProps.pinAudioPlay) pinDoc.presPlayAudio = true; - if (pinProps.pinData) { - pinDoc.config_pinData = - pinProps.pinData.scrollable || - pinProps.pinData.temporal || - pinProps.pinData.pannable || - pinProps.pinData.type_collection || - pinProps.pinData.clippable || - pinProps.pinData.datarange || - pinProps.pinData.dataview || - pinProps.pinData.poslayoutview || - pinProps?.activeFrame !== undefined; - const fkey = Doc.LayoutFieldKey(targetDoc); - if (pinProps.pinData.dataview) { - pinDoc.config_usePath = targetDoc[fkey + '_usePath']; - pinDoc.config_data = targetDoc[fkey] instanceof ObjectField ? (targetDoc[fkey] as ObjectField)[Copy]() : targetDoc.data; - } - if (pinProps.pinData.dataannos) { - const fieldKey = Doc.LayoutFieldKey(targetDoc); - pinDoc.config_annotations = new List(DocListCast(targetDoc[DocData][fieldKey + '_annotations']).filter(doc => !doc.layout_unrendered)); - } - if (pinProps.pinData.inkable) { - pinDoc.config_fillColor = targetDoc.fillColor; - pinDoc.config_color = targetDoc.color; - pinDoc.config_width = targetDoc._width; - pinDoc.config_height = targetDoc._height; - } - if (pinProps.pinData.scrollable) pinDoc.config_scrollTop = targetDoc._layout_scrollTop; - if (pinProps.pinData.clippable) { - const fieldKey = Doc.LayoutFieldKey(targetDoc); - pinDoc.config_clipWidth = targetDoc[fieldKey + '_clipWidth']; - } - if (pinProps.pinData.datarange) { - pinDoc.config_xRange = undefined; // targetDoc?.xrange; - pinDoc.config_yRange = undefined; // targetDoc?.yrange; - } - if (pinProps.pinData.map) { - // pinDoc.config_latitude = targetDoc?.latitude; - // pinDoc.config_longitude = targetDoc?.longitude; - pinDoc.config_map_zoom = targetDoc?.map_zoom; - pinDoc.config_map_type = targetDoc?.map_type; - // ... - } - if (pinProps.pinData.poslayoutview) - pinDoc.config_pinLayoutData = new List( - DocListCast(targetDoc[fkey] as ObjectField).map(d => - JSON.stringify({ - id: d[Id], - x: NumCast(d.x), - y: NumCast(d.y), - w: NumCast(d._width), - h: NumCast(d._height), - fill: StrCast(d._fillColor), - back: StrCast(d._backgroundColor), - data: SerializationHelper.Serialize(d.data instanceof ObjectField ? d.data[Copy]() : ''), - text: SerializationHelper.Serialize(d.text instanceof ObjectField ? d.text[Copy]() : ''), - }) - ) - ); - if (pinProps.pinData.type_collection) pinDoc.config_viewType = targetDoc._type_collection; - if (pinProps.pinData.filters) pinDoc.config_docFilters = ObjectField.MakeCopy(targetDoc.childFilters as ObjectField); - if (pinProps.pinData.pivot) pinDoc.config_pivotField = targetDoc._pivotField; - if (pinProps.pinData.pannable) { - pinDoc.config_panX = NumCast(targetDoc._freeform_panX); - pinDoc.config_panY = NumCast(targetDoc._freeform_panY); - pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1); - } - if (pinProps.pinData.temporal) { - pinDoc.config_clipStart = targetDoc._layout_currentTimecode; - const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], NumCast(targetDoc.config_clipStart) + 0.1); - pinDoc.config_clipEnd = NumCast(pinDoc.config_clipStart) + NumCast(targetDoc.clipEnd, duration); - } - } - if (pinProps?.pinViewport) { - // If pinWithView option set then update scale and x / y props of slide - const bounds = pinProps.pinViewport; - pinDoc.config_pinView = true; - pinDoc.config_viewScale = NumCast(targetDoc._freeform_scale, 1); - pinDoc.config_panX = bounds.left + bounds.width / 2; - pinDoc.config_panY = bounds.top + bounds.height / 2; - pinDoc.config_viewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); - } - } - - @action - static reversePin(pinDoc: Doc, targetDoc: Doc) { - // const fkey = Doc.LayoutFieldKey(targetDoc); - pinDoc.config_data = targetDoc.data; - - console.log(pinDoc.presData); - } - /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 5fa32ad12..cf78a45b7 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -22,6 +22,7 @@ import { TreeView } from '../../collections/TreeView'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; +import { PinDocView } from '../../PinFuncs'; import { StyleProp } from '../../StyleProvider'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; @@ -335,7 +336,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { updateCapturedViewContents = undoable( action((presTargetDoc: Doc, activeItem: Doc) => { const target = DocCast(presTargetDoc.annotationOn) ?? presTargetDoc; - PresBox.pinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target); + PinDocView(activeItem, { pinData: PresBox.pinDataTypes(target) }, target); }), 'updated captured view contents' ); diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 5e6c855c9..9183688c6 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -12,7 +12,6 @@ import * as passport from 'passport'; import * as request from 'request'; import * as webpack from 'webpack'; import * as wdm from 'webpack-dev-middleware'; -// eslint-disable-next-line import/no-extraneous-dependencies import * as whm from 'webpack-hot-middleware'; import * as zlib from 'zlib'; import * as config from '../../webpack.config'; -- cgit v1.2.3-70-g09d2 From 098deaa68c8b9bb781748fbe0c1bd0104bab3596 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 30 Apr 2024 23:35:18 -0400 Subject: unwinding more import loops by splitting up Documents.ts into DocUtils.ts and moving crate functions to <>Box functions --- .eslintrc.json | 2 +- .../apis/google_docs/GooglePhotosClientUtils.ts | 3 +- src/client/documents/DocFromField.ts | 50 + src/client/documents/DocUtils.ts | 883 ++++++++++++++ src/client/documents/Documents.ts | 1270 +------------------- src/client/util/CurrentUserUtils.ts | 8 +- src/client/util/DictationManager.ts | 5 +- src/client/util/DocumentManager.ts | 5 +- src/client/util/DragManager.ts | 21 +- src/client/util/GroupManager.tsx | 6 +- src/client/util/GroupMemberView.tsx | 4 +- src/client/util/LinkFollower.ts | 16 +- src/client/util/RTFMarkup.tsx | 22 +- src/client/util/ReplayMovements.ts | 2 +- src/client/util/SettingsManager.tsx | 17 +- src/client/util/SharingManager.tsx | 19 +- src/client/views/DashboardView.tsx | 47 +- src/client/views/DocComponent.tsx | 7 +- src/client/views/DocumentButtonBar.tsx | 7 +- src/client/views/DocumentDecorations.tsx | 5 +- src/client/views/GestureOverlay.tsx | 43 +- src/client/views/GlobalKeyHandler.ts | 11 +- src/client/views/InkStrokeProperties.ts | 3 +- src/client/views/InkingStroke.tsx | 79 +- src/client/views/LightboxView.tsx | 10 +- src/client/views/MainView.tsx | 10 +- src/client/views/MarqueeAnnotator.tsx | 8 +- src/client/views/PreviewCursor.tsx | 3 +- src/client/views/PropertiesButtons.tsx | 5 +- .../views/PropertiesDocBacklinksSelector.tsx | 2 +- src/client/views/PropertiesDocContextSelector.tsx | 3 +- src/client/views/PropertiesView.tsx | 5 +- src/client/views/SidebarAnnos.tsx | 5 +- src/client/views/TemplateMenu.tsx | 3 +- .../views/collections/CollectionCarousel3DView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 26 +- .../collections/CollectionMasonryViewFieldRow.tsx | 2 +- .../views/collections/CollectionNoteTakingView.tsx | 12 +- .../collections/CollectionNoteTakingViewColumn.tsx | 12 +- .../views/collections/CollectionPileView.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 12 +- .../views/collections/CollectionStackingView.tsx | 11 +- .../CollectionStackingViewFieldColumn.tsx | 10 +- src/client/views/collections/CollectionSubView.tsx | 12 +- .../views/collections/CollectionTreeView.tsx | 5 +- src/client/views/collections/CollectionView.tsx | 23 +- src/client/views/collections/TabDocView.tsx | 5 +- src/client/views/collections/TreeView.tsx | 8 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 17 +- .../collections/collectionFreeForm/MarqueeView.tsx | 9 +- .../collectionGrid/CollectionGridView.tsx | 5 +- .../collectionSchema/CollectionSchemaView.tsx | 6 +- .../collections/collectionSchema/SchemaRowBox.tsx | 4 +- src/client/views/global/globalScripts.ts | 6 +- src/client/views/linking/LinkMenuItem.tsx | 3 +- .../views/newlightbox/ButtonMenu/ButtonMenu.tsx | 2 +- src/client/views/newlightbox/NewLightboxView.tsx | 10 +- src/client/views/nodes/AudioBox.tsx | 11 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 6 +- src/client/views/nodes/ComparisonBox.tsx | 21 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 27 +- src/client/views/nodes/DocumentLinksButton.tsx | 4 +- src/client/views/nodes/DocumentView.tsx | 39 +- src/client/views/nodes/EquationBox.tsx | 18 +- src/client/views/nodes/FieldView.tsx | 27 +- src/client/views/nodes/FocusViewOptions.ts | 24 + src/client/views/nodes/FontIconBox/FontIconBox.tsx | 17 +- src/client/views/nodes/FunctionPlotBox.tsx | 11 +- src/client/views/nodes/ImageBox.tsx | 13 +- src/client/views/nodes/KeyValueBox.tsx | 10 +- src/client/views/nodes/KeyValuePair.tsx | 3 +- src/client/views/nodes/LabelBox.tsx | 12 +- src/client/views/nodes/LinkBox.tsx | 18 + src/client/views/nodes/LinkDocPreview.tsx | 3 +- src/client/views/nodes/LoadingBox.tsx | 6 + src/client/views/nodes/MapBox/MapBox.tsx | 16 +- src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx | 2 +- src/client/views/nodes/MapBox/MapPushpinBox.tsx | 7 + .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 11 +- src/client/views/nodes/OpenWhere.ts | 25 + src/client/views/nodes/PDFBox.tsx | 14 +- .../nodes/PhysicsBox/PhysicsSimulationBox.tsx | 383 +++--- .../views/nodes/RecordingBox/RecordingBox.tsx | 5 + src/client/views/nodes/ScreenshotBox.tsx | 10 +- src/client/views/nodes/ScriptingBox.tsx | 7 + src/client/views/nodes/VideoBox.tsx | 17 +- src/client/views/nodes/WebBox.tsx | 15 +- src/client/views/nodes/calendarBox/CalendarBox.tsx | 6 + .../views/nodes/formattedText/DashDocView.tsx | 5 +- .../views/nodes/formattedText/DashFieldView.tsx | 2 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 56 +- .../formattedText/ProsemirrorExampleTransfer.ts | 4 +- .../views/nodes/formattedText/RichTextRules.ts | 3 +- .../views/nodes/generativeFill/GenerativeFill.tsx | 5 +- src/client/views/nodes/trails/PresBox.tsx | 15 +- src/client/views/nodes/trails/PresElementBox.tsx | 7 +- src/client/views/pdf/Annotation.tsx | 2 +- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 3 +- src/client/views/pdf/PDFViewer.tsx | 11 +- src/client/views/search/SearchBox.tsx | 12 +- src/client/views/selectedDoc/SelectedDocView.tsx | 2 +- src/fields/Doc.ts | 76 ++ src/fields/RichTextUtils.ts | 3 +- src/fields/ScriptField.ts | 4 + src/mobile/MobileInterface.tsx | 3 +- 105 files changed, 1900 insertions(+), 1896 deletions(-) create mode 100644 src/client/documents/DocFromField.ts create mode 100644 src/client/documents/DocUtils.ts create mode 100644 src/client/views/nodes/FocusViewOptions.ts create mode 100644 src/client/views/nodes/OpenWhere.ts (limited to 'src/client/views/nodes/FunctionPlotBox.tsx') diff --git a/.eslintrc.json b/.eslintrc.json index 6d851a64a..2e4da56b8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -57,7 +57,7 @@ "react/destructuring-assignment": 0, "no-restricted-globals": ["error", "event"], "no-param-reassign": ["error", { "props": false }], - "import/no-cycle": 0, + // "import/no-cycle": 0, "no-alert": 0, "radix": "off" }, diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts index 1eb977813..fdc185a8e 100644 --- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts +++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts @@ -10,7 +10,8 @@ import { RichTextUtils } from '../../../fields/RichTextUtils'; import { Cast, ImageCast, StrCast } from '../../../fields/Types'; import { MediaItem, NewMediaItemResult } from '../../../server/apis/google/SharedTypes'; import { Networking } from '../../Network'; -import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents'; +import { Docs, DocumentOptions } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { FormattedTextBox } from '../../views/nodes/formattedText/FormattedTextBox'; import { GoogleAuthenticationManager } from '../GoogleAuthenticationManager'; diff --git a/src/client/documents/DocFromField.ts b/src/client/documents/DocFromField.ts new file mode 100644 index 000000000..1c0a9755b --- /dev/null +++ b/src/client/documents/DocFromField.ts @@ -0,0 +1,50 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { Doc, DocListCast } from '../../fields/Doc'; +import { InkField } from '../../fields/InkField'; +import { List } from '../../fields/List'; +import { AudioField, ImageField, PdfField, VideoField } from '../../fields/URLField'; +import { InkingStroke } from '../views/InkingStroke'; +import { CollectionView } from '../views/collections/CollectionView'; +import { AudioBox } from '../views/nodes/AudioBox'; +import { ImageBox } from '../views/nodes/ImageBox'; +import { PDFBox } from '../views/nodes/PDFBox'; +import { VideoBox } from '../views/nodes/VideoBox'; +import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; +import { Docs, DocumentOptions } from './Documents'; + +export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { + let created: Doc | undefined; + const field = target[fieldKey]; + const resolved = options ?? {}; + if (field instanceof ImageField) { + created = Docs.Create.ImageDocument(field.url.href, resolved); + created.layout = ImageBox.LayoutString(fieldKey); + } else if (field instanceof Doc) { + created = field; + } else if (field instanceof VideoField) { + created = Docs.Create.VideoDocument(field.url.href, resolved); + created.layout = VideoBox.LayoutString(fieldKey); + } else if (field instanceof PdfField) { + created = Docs.Create.PdfDocument(field.url.href, resolved); + created.layout = PDFBox.LayoutString(fieldKey); + } else if (field instanceof AudioField) { + created = Docs.Create.AudioDocument(field.url.href, resolved); + created.layout = AudioBox.LayoutString(fieldKey); + } else if (field instanceof InkField) { + created = Docs.Create.InkDocument(field.inkData, resolved); + created.layout = InkingStroke.LayoutString(fieldKey); + } else if (field instanceof List && field[0] instanceof Doc) { + created = Docs.Create.StackingDocument(DocListCast(field), resolved); + created.layout = CollectionView.LayoutString(fieldKey); + } else { + created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved }); + created.layout = FormattedTextBox.LayoutString(fieldKey); + } + if (created) { + created.title = fieldKey; + proto && created.proto && (created.proto = Doc.GetProto(proto)); + } + return created; +} diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts new file mode 100644 index 000000000..e93bbc41b --- /dev/null +++ b/src/client/documents/DocUtils.ts @@ -0,0 +1,883 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { saveAs } from 'file-saver'; +import * as JSZip from 'jszip'; +import { action, runInAction } from 'mobx'; +import { ClientUtils } from '../../ClientUtils'; +import * as JSZipUtils from '../../JSZipUtils'; +import { decycle } from '../../decycler/decycler'; +import { DateField } from '../../fields/DateField'; +import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, SetActiveAudioLinker, StrListCast } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; +import { InkDataFieldName, InkField } from '../../fields/InkField'; +import { List, ListFieldName } from '../../fields/List'; +import { ProxyField } from '../../fields/Proxy'; +import { RichTextField } from '../../fields/RichTextField'; +import { ComputedField, FollowLinkScript, ScriptField } from '../../fields/ScriptField'; +import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; +import { SharingPermissions } from '../../fields/util'; +import { Upload } from '../../server/SharedMediaTypes'; +import { DocServer } from '../DocServer'; +import { Networking } from '../Network'; +import { LinkManager } from '../util/LinkManager'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { SerializationHelper } from '../util/SerializationHelper'; +import { UndoManager, undoable } from '../util/UndoManager'; +import { ContextMenu } from '../views/ContextMenu'; +import { ContextMenuProps } from '../views/ContextMenuItem'; +import { FieldViewProps } from '../views/nodes/FieldView'; +import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; +import { LoadingBox } from '../views/nodes/LoadingBox'; +import { OpenWhere } from '../views/nodes/OpenWhere'; +import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; +import { DocumentType } from './DocumentTypes'; +import { Docs, DocumentOptions } from './Documents'; + +const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore + +const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); + +export namespace DocUtils { + function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean { + let value = valueIn; + const hasFunctionFilter = ClientUtils.HasFunctionFilter(value); + if (hasFunctionFilter) { + return hasFunctionFilter(StrCast(doc[key])); + } + if (key === LinkedTo) { + // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) + const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); + const matchLink = (val: string, anchor: Doc) => { + const linkedToExp = (val ?? '').split('='); + if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; + return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; + }; + // prettier-ignore + return (value === Doc.FilterNone && !allLinks.length) || + (value === Doc.FilterAny && !!allLinks.length) || + (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) || + matchLink(value,DocCast(link.link_anchor_2)) )); + } + if (typeof value === 'string') { + value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); + } + const fieldVal = doc[key]; + // prettier-ignore + if ((value === Doc.FilterAny && fieldVal !== undefined) || + (value === Doc.FilterNone && fieldVal === undefined)) { + return true; + } + const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings + if (vals.length) { + return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + } + return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring + } + /** + * @param docs + * @param childFilters + * @param childFiltersByRanges + * @param parentCollection + * Given a list of docs and childFilters, @returns the list of Docs that match those filters + */ + export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) { + if (!childFilters?.length && !childFiltersByRanges?.length) { + return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one + } + + const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields + childFilters.forEach(filter => { + const fields = filter.split(Doc.FilterSep); + const key = fields[0]; + const value = fields[1]; + const modifiers = fields[2]; + if (!filterFacets[key]) { + filterFacets[key] = {}; + } + filterFacets[key][value] = modifiers; + }); + + const filteredDocs = childFilters.length + ? childDocs.filter(d => { + if (d.z) return true; + // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter + if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { + return false; + } + const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]); + // eslint-disable-next-line no-restricted-syntax + for (const facetKey of facetKeys) { + const facet = filterFacets[facetKey]; + + // facets that match some value in the field of the document (e.g. some text field) + const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); + + // facets that have a check next to them + const checks = Object.keys(facet).filter(value => facet[value] === 'check'); + + // metadata facets that exist + const exists = Object.keys(facet).filter(value => facet[value] === 'exists'); + + // facets that unset metadata (a hack for making cookies work) + const unsets = Object.keys(facet).filter(value => facet[value] === 'unset'); + + // facets that specify that a field must not match a specific value + const xs = Object.keys(facet).filter(value => facet[value] === 'x'); + + if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; + const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); + const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); + const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; + const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; + const satisfiesMatchFacets = !matches.length + ? true + : matches.some(value => { + if (facetKey.startsWith('*')) { + // fields starting with a '*' are used to match families of related fields. ie, *modificationDate will match text_modificationDate, data_modificationDate, etc + const allKeys = Array.from(Object.keys(d)); + allKeys.push(...Object.keys(Doc.GetProto(d))); + const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); + return keys.some(key => Field.toString(d[key] as FieldType).includes(value)); + } + return Field.toString(d[facetKey] as FieldType).includes(value); + }); + // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria + if (parentCollection?.childFilters_boolean === 'OR') { + if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; + } + // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria + else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; + } + return parentCollection?.childFilters_boolean !== 'OR'; + }) + : childDocs; + const rangeFilteredDocs = filteredDocs.filter(d => { + for (let i = 0; i < childFiltersByRanges.length; i += 3) { + const key = childFiltersByRanges[i]; + const min = Number(childFiltersByRanges[i + 1]); + const max = Number(childFiltersByRanges[i + 2]); + const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); + if (val === undefined) { + // console.log("Should 'undefined' pass range filter or not?") + } else if (val < min || val > max) return false; + } + return true; + }); + return rangeFilteredDocs; + } + + export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; + + export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { + broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore + return DocUtils.ActiveRecordings.map(audio => { + const sourceDoc = getSourceDoc(); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); + }); + } + + SetActiveAudioLinker(MakeLinkToActiveAudio); + + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { + if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; + if (target.doc === Doc.UserDoc()) return undefined; + + const makeLink = action((linkDoc: Doc, showAt?: number[]) => { + if (showAt) { + LinkManager.Instance.currentLink = linkDoc; + + TaskCompletionBox.textDisplayed = 'Link Created'; + TaskCompletionBox.popupX = showAt[0]; + TaskCompletionBox.popupY = showAt[1] - 33; + TaskCompletionBox.taskCompleted = true; + + LinkDescriptionPopup.Instance.popupX = showAt[0]; + LinkDescriptionPopup.Instance.popupY = showAt[1]; + LinkDescriptionPopup.Instance.display = true; + + const rect = document.body.getBoundingClientRect(); + if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) { + LinkDescriptionPopup.Instance.popupX -= 190; + TaskCompletionBox.popupX -= 40; + } + if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) { + LinkDescriptionPopup.Instance.popupY -= 40; + TaskCompletionBox.popupY -= 40; + } + + setTimeout( + action(() => { + TaskCompletionBox.taskCompleted = false; + }), + 2500 + ); + } + return linkDoc; + }); + + const a = source.layout_unrendered ? 'link_anchor_1?.annotationOn' : 'link_anchor_1'; + const b = target.layout_unrendered ? 'link_anchor_2?.annotationOn' : 'link_anchor_2'; + + return makeLink( + Docs.Create.LinkDocument( + source, + target, + { + acl_Guest: SharingPermissions.Augment, + _acl_Guest: SharingPermissions.Augment, + title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, + link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, + link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, + link_displayLine: linkSettings.link_displayLine, + link_relationship: linkSettings.link_relationship, + link_description: linkSettings.link_description, + x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, + y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as any, + link_autoMoveAnchors: true, + _lockedPosition: true, + _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box) + _layout_showTitle: '', + // _layout_showCaption: 'link_description', + // _layout_showTitle: 'link_relationship', + }, + id + ), + showPopup + ); + } + + export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) { + scripts && + Object.keys(scripts).forEach(key => { + const script = scripts[key]; + if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { + (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { + self: Doc.name, + this: Doc.name, + dragData: Doc.DocDragDataName, + value: 'any', + _readOnly_: 'boolean', + scriptContext: 'any', + documentView: Doc.name, + heading: Doc.name, + checked: 'boolean', + containingTreeView: Doc.name, + altKey: 'boolean', + ctrlKey: 'boolean', + shiftKey: 'boolean', + }); + } + }); + funcs && + Object.keys(funcs) + .filter(key => !key.endsWith('-setter')) + .forEach(key => { + const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); + if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) { + const setFunc = Cast(funcs[key + '-setter'], 'string', null); + (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: Doc.DocDragDataName }, { _readOnly_: true }, setFunc) : undefined; + } + }); + return doc; + } + export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) { + if (doc) { + const compareValues = (val1: any, val2: any) => { + if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { + return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); + } + return val1 === val2; + }; + Object.entries(reqdOpts).forEach(pair => { + const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc); + if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) { + targetDoc[pair[0]] = pair[1]; + } + }); + items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item)); + items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item)); + } + return doc; + } + export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { + // eslint-disable-next-line no-return-assign + return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); + } + + /** + * + * @param type the type of file. + * @param path the path to the file. + * @param options the document options. + * @param overwriteDoc the placeholder loading doc. + * @returns + */ + export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { + let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; + if (type.indexOf('image') !== -1) { + ctor = Docs.Create.ImageDocument; + if (!options._width) options._width = 300; + } + if (type.indexOf('video') !== -1) { + ctor = Docs.Create.VideoDocument; + if (!options._width) options._width = 600; + if (!options._height) options._height = ((options._width as number) * 2) / 3; + } + if (type.indexOf('audio') !== -1) { + ctor = Docs.Create.AudioDocument; + } + if (type.indexOf('pdf') !== -1) { + ctor = Docs.Create.PdfDocument; + if (!options._width) options._width = 400; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; + } + if (type.indexOf('csv') !== -1) { + ctor = Docs.Create.DataVizDocument; + if (!options._width) options._width = 400; + if (!options._height) options._height = ((options._width as number) * 1200) / 927; + } + // TODO:al+glr + // if (type.indexOf("map") !== -1) { + // ctor = Docs.Create.MapDocument; + // if (!options._width) options._width = 800; + // if (!options._height) options._height = (options._width as number) * 3 / 4; + // } + if (type.indexOf('html') !== -1) { + if (path.includes(window.location.hostname)) { + const s = path.split('/'); + const id = s[s.length - 1]; + return DocServer.GetRefField(id).then(field => { + if (field instanceof Doc) { + const embedding = Doc.MakeEmbedding(field); + embedding.x = (options.x as number) || 0; + embedding.y = (options.y as number) || 0; + embedding._width = (options._width as number) || 300; + embedding._height = (options._height as number) || (options._width as number) || 300; + return embedding; + } + return undefined; + }); + } + ctor = Docs.Create.WebDocument; + // eslint-disable-next-line no-param-reassign + options = { ...options, _width: 400, _height: 512, title: path }; + } + + return ctor ? ctor(path, overwriteDoc ? { ...options, title: StrCast(overwriteDoc.title, path) } : options, overwriteDoc) : undefined; + } + + export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { + const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) + .map(dragDoc => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoable(() => { + const newDoc = DocUtils.copyDragFactory(dragDoc); + if (newDoc) { + newDoc.author = ClientUtils.CurrentUserEmail(); + newDoc.x = x; + newDoc.y = y; + Doc.SetSelectOnLoad(newDoc); + if (pivotField) { + newDoc[pivotField] = pivotValue; + } + docAdder?.(newDoc); + } + }, StrCast(dragDoc.title)), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + ContextMenu.Instance.addItem({ + description: 'Create document', + subitems: documentList, + icon: 'file', + }); + !simpleMenu && + ContextMenu.Instance.addItem({ + description: 'Styled Notes', + subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({ + description: ':' + StrCast(note.title), + event: undoable(() => { + const textDoc = Docs.Create.TextDocument('', { + _width: 200, + x, + y, + _layout_autoHeight: note._layout_autoHeight !== false, + title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1), + }); + textDoc.layout_fieldKey = 'layout_' + note.title; + textDoc[textDoc.layout_fieldKey] = note; + if (pivotField) { + textDoc[pivotField] = pivotValue; + } + docTextAdder(textDoc); + }, 'create quick note'), + icon: StrCast(note.icon) as IconProp, + })) as ContextMenuProps[], + icon: 'sticky-note', + }); + const userDocList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data) + .filter(btnDoc => !btnDoc.hidden) + .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) + .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) + .map(dragDoc => ({ + description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), + event: undoable(() => { + const newDoc = DocUtils.delegateDragFactory(dragDoc); + if (newDoc) { + newDoc.author = ClientUtils.CurrentUserEmail(); + newDoc.x = x; + newDoc.y = y; + Doc.SetSelectOnLoad(newDoc); + if (pivotField) { + newDoc[pivotField] = pivotValue; + } + docAdder?.(newDoc); + } + }, StrCast(dragDoc.title)), + icon: Doc.toIcon(dragDoc), + })) as ContextMenuProps[]; + ContextMenu.Instance.addItem({ + description: 'User Templates', + subitems: userDocList, + icon: 'file', + }); + } + + // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) + + /** + * Applies a template to a Doc and logs the action with the UndoManager + * If the template already exists and has been registered, it can be specified by it's signature name (e.g., 'icon' not 'layout_icon'). + * Alternatively, the signature can be omitted and the template can be provided. + * @param doc the Doc to apply the template to. + * @param creator a function that will create the template if it doesn't exist + * @param templateSignature the signature name for a template that has already been created and registered on the userDoc. (can be "" if template is provide) + * @param template the template to use (optional if templateSignature is provided) + * @returns doc + */ + export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', template?: Doc) { + const batch = UndoManager.StartBatch('makeCustomViewClicked'); + createCustomView(doc, creator, templateSignature || StrCast(template?.title), template); + batch.end(); + return doc; + } + export function findTemplate(templateName: string, type: string) { + let docLayoutTemplate: Opt; + const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); + const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); + const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data); + const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data); + const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data); + const allTemplates = iconViews + .concat(templBtns) + .concat(noteTypes) + .concat(userTypes) + .concat(clickFuncs) + .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) + .filter(doc => doc.isTemplateDoc); + // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized + // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc); + }); + !docLayoutTemplate && + allTemplates.forEach(tempDoc => { + StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc); + }); + return docLayoutTemplate; + } + export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { + const templateName = templateSignature.replace(/\(.*\)/, ''); + doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); + // eslint-disable-next-line no-param-reassign + docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type)); + + const customName = 'layout_' + templateSignature; + const _width = NumCast(doc._width); + const _height = NumCast(doc._height); + const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false }; + + if (docLayoutTemplate) { + if (docLayoutTemplate !== doc[customName]) { + Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); + } + } else { + let fieldTemplate: Opt; + if (doc.data instanceof RichTextField || typeof doc.data === 'string') { + fieldTemplate = Docs.Create.TextDocument('', options); + } else if (doc.data instanceof PdfField) { + fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); + } else if (doc.data instanceof VideoField) { + fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); + } else if (doc.data instanceof AudioField) { + fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); + } else if (doc.data instanceof ImageField) { + fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); + } + const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); + fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); + docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); + } + } + export function makeCustomView(doc: Doc, custom: boolean, layout: string) { + Doc.setNativeView(doc); + if (custom) { + makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); + } + } + export function iconify(doc: Doc) { + const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null); + DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); + if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', ''); + } + + export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { + runInAction(() => { + docList.forEach((doc, i) => { + const d = doc; + DocUtils.iconify(d); + d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size; + d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size; + d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + }); + }); + if (create) { + const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', _freeform_noZoom: true, x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, dragWhenActive: true, _layout_fitWidth: false }); + newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size; + newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size; + newCollection._width = newCollection._height = size * 2; + return newCollection; + } + return undefined; + } + export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { + const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); + if (!portalLink) { + DocUtils.MakeLink( + doc, + Docs.Create.FreeformDocument([], { + _width: NumCast(layoutDoc._width) + 10, + _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10), + _isLightbox: true, + _layout_fitWidth: true, + title: StrCast(doc.title) + ' [Portal]', + }), + { link_relationship: 'portal to:portal from' } + ); + } + doc.followLinkLocation = OpenWhere.lightbox; + doc.onClick = FollowLinkScript(); + } + + export function LeavePushpin(doc: Doc, annotationField: string) { + if (doc.followLinkToggle) return undefined; + const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); + const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); + if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { + const pushpin = Docs.Create.FontIconDocument({ + title: '', + annotationOn: Cast(doc.annotationOn, Doc, null), + followLinkToggle: true, + icon: 'map-pin', + x: Cast(doc.x, 'number', null), + y: Cast(doc.y, 'number', null), + backgroundColor: '#ACCEF7', + layout_hideAllLinks: true, + _width: 15, + _height: 15, + _xPadding: 0, + onClick: FollowLinkScript(), + _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), + }); + Doc.AddDocToList(context, annotationField, pushpin); + DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); + doc._timecodeToShow = undefined; + return pushpin; + } + return undefined; + } + + // /** + // * + // * @param dms Degree Minute Second format exif gps data + // * @param ref ref that determines negativity of decimal coordinates + // * @returns a decimal format of gps latitude / longitude + // */ + // function getDecimalfromDMS(dms?: number[], ref?: string) { + // if (dms && ref) { + // let degrees = dms[0] / dms[1]; + // let minutes = dms[2] / dms[3] / 60.0; + // let seconds = dms[4] / dms[5] / 3600.0; + + // if (['S', 'W'].includes(ref)) { + // degrees = -degrees; minutes = -minutes; seconds = -seconds + // } + // return (degrees + minutes + seconds).toFixed(5); + // } + // } + + function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { + let dd = degrees + minutes / 60 + seconds / (60 * 60); + if (direction === 'S' || direction === 'W') { + dd *= -1; + } // Don't do anything for N or E + return dd; + } + + export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) { + const proto = protoIn; + if (Upload.isImageInformation(result)) { + const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); + const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); + proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + if (NumCast(proto.data_nativeOrientation) >= 5) { + proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; + proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); + } + proto.data_exif = JSON.stringify(result.exifData?.data); + proto.data_contentSize = result.contentSize; + // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates + const latitude = result.exifData?.data?.GPSLatitude; + const latitudeDirection = result.exifData?.data?.GPSLatitudeRef; + const longitude = result.exifData?.data?.GPSLongitude; + const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; + if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { + proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); + proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); + } + } + } + + async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) { + if (result instanceof Error) { + alert(`Upload failed: ${result.message}`); + return; + } + const full = { ...options, _width: 400, title: name }; + const pathname = result.accessPaths.agnostic.client; + const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc); + if (doc) { + const proto = Doc.GetProto(doc); + proto.text = result.rawText; + !(result instanceof Error) && DocUtils.assignImageInfo(result, proto); + if (Upload.isVideoInformation(result)) { + proto.data_duration = result.duration; + } + if (overwriteDoc) { + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + generatedDocuments.push(doc); + } + } + + export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { + const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout); + const tbox = Docs.Create.TextDocument('', { + annotationOn, + backgroundColor, + x, + y, + title, + ...(defaultTextTemplate + ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance + : { + _width: width || 200, + _height: 35, + _layout_centered: BoolCast(Doc.UserDoc()._layout_centered), + _layout_fitWidth: true, + _layout_autoHeight: true, + }), + }); + + if (defaultTextTemplate) { + tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title); + Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template + tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values) + } + return tbox; + } + + export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { + const generatedDocuments: Doc[] = []; + Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { + const { + source: { newFilename, mimetype }, + result, + } = upfiles.lastElement(); + if ((result as any).message) { + if (overwriteDoc) { + overwriteDoc.isLoading = false; + overwriteDoc.loadingError = (result as any).message; + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); + }); + } + + /** + * uploadFilesToDocs will take in an array of Files, and creates documents for the + * new files. + * + * @param files an array of files that will be uploaded + * @param options options to use while uploading + * @returns + */ + export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { + const generatedDocuments: Doc[] = []; + + // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. + const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); + + const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); + upfiles.forEach(({ source: { newFilename, mimetype }, result }) => { + newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); + }); + return generatedDocuments; + } + + export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { + const generatedDocuments: Doc[] = []; + // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. + Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { + const { + source: { newFilename, mimetype }, + result, + } = upfiles.lastElement() ?? { source: { newFilename: '', mimetype: '' }, result: { message: 'upload failed' } }; + if ((result as any).message) { + if (overwriteDoc) { + overwriteDoc.loadingError = (result as any).message; + LoadingBox.removeCurrentlyLoading(overwriteDoc); + } + } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); + }); + } + + // copies the specified drag factory document + export function copyDragFactory(dragFactory: Doc) { + if (!dragFactory) return undefined; + const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true); + } + + return ndoc; + } + export function delegateDragFactory(dragFactory: Doc) { + const ndoc = Doc.MakeDelegateWithProto(dragFactory); + if (ndoc && dragFactory.dragFactory_count !== undefined) { + dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; + Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(); + } + return ndoc; + } + + export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { + const { clone, map, linkMap } = await Doc.MakeClone(doc); + const proms = new Set(); + function replacer(key: any, value: any) { + if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; + if (value?.__type === 'image') { + const extension = value.url.replace(/.*\./, ''); + proms.add(value.url.replace('.' + extension, '_o.' + extension)); + return SerializationHelper.Serialize(new ImageField(value.url)); + } + if (value?.__type === 'pdf') { + proms.add(value.url); + return SerializationHelper.Serialize(new PdfField(value.url)); + } + if (value?.__type === 'audio') { + proms.add(value.url); + return SerializationHelper.Serialize(new AudioField(value.url)); + } + if (value?.__type === 'video') { + proms.add(value.url); + return SerializationHelper.Serialize(new VideoField(value.url)); + } + if ( + value instanceof Doc || + value instanceof ScriptField || + value instanceof RichTextField || + value instanceof InkField || + value instanceof CsvField || + value instanceof WebField || + value instanceof DateField || + value instanceof ProxyField || + value instanceof ComputedField + ) { + return SerializationHelper.Serialize(value); + } + if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' }; + return value; + } + + const docs: { [id: string]: any } = {}; + const links: { [id: string]: any } = {}; + Array.from(map.entries()).forEach(f => { + docs[f[0]] = f[1]; + }); + Array.from(linkMap.entries()).forEach(l => { + links[l[0]] = l[1]; + }); + const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); + + const zip = new JSZip(); + let count = 0; + const promArr = Array.from(proms) + .filter(url => url?.startsWith('/files')) + .map(url => url.replace('/', '')); // window.location.origin)); + console.log(promArr.length); + if (!promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + } else + promArr.forEach((url, i) => { + // loading a file and add it in a zip file + JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => { + if (err) throw err; // or handle the error + // // Generate a directory within the Zip file structure + // const assets = zip.folder("assets"); + // assets.file(filename, data, {binary: true}); + const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%'); + zip.file(assetPathOnServer, data, { binary: true }); + console.log(' => ' + url); + if (++count === promArr.length) { + zip.file('docs.json', jsonDocs); + zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); + // const a = document.createElement("a"); + // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); + // a.href = url; + // a.download = `DocExport-${this.props.Document[Id]}.zip`; + // a.click(); + } + }); + }); + } +} + +ScriptingGlobals.add('Docs', Docs); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { + return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; +}); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function makeDelegate(proto: any) { + const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); + return d; +}); +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function generateLinkTitle(link: Doc) { + const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; + const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; + const relation = link.link_relationship || 'to'; + return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`; +}); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 0e7f911c7..a3fea1ce4 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,79 +1,27 @@ /* eslint-disable prefer-destructuring */ /* eslint-disable default-param-last */ /* eslint-disable no-use-before-define */ -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { saveAs } from 'file-saver'; -import * as JSZip from 'jszip'; -import { action, reaction, runInAction } from 'mobx'; +import { reaction } from 'mobx'; import { basename } from 'path'; import { ClientUtils, OmitKeys } from '../../ClientUtils'; -import * as JSZipUtils from '../../JSZipUtils'; -import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, StrListCast, updateCachedAcls } from '../../fields/Doc'; -import { DocData, Initializing } from '../../fields/DocSymbols'; -import { Id } from '../../fields/FieldSymbols'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, CreateLinkToActiveAudio, Doc, FieldType, Opt, updateCachedAcls } from '../../fields/Doc'; +import { Initializing } from '../../fields/DocSymbols'; import { HtmlField } from '../../fields/HtmlField'; -import { InkDataFieldName, InkField } from '../../fields/InkField'; -import { List, ListFieldName } from '../../fields/List'; -import { ProxyField } from '../../fields/Proxy'; +import { InkField } from '../../fields/InkField'; +import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { SchemaHeaderField } from '../../fields/SchemaHeaderField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { ScriptCast, StrCast } from '../../fields/Types'; import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from '../../fields/URLField'; -import { SharingPermissions, inheritParentAcls } from '../../fields/util'; +import { SharingPermissions } from '../../fields/util'; import { PointData } from '../../pen-gestures/GestureTypes'; -import { Upload } from '../../server/SharedMediaTypes'; import { DocServer } from '../DocServer'; -import { Networking } from '../Network'; -import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; -import { FollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SerializationHelper } from '../util/SerializationHelper'; -import { UndoManager, undoable } from '../util/UndoManager'; -import { ContextMenu } from '../views/ContextMenu'; -import { ContextMenuProps } from '../views/ContextMenuItem'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; -import { CollectionDockingView } from '../views/collections/CollectionDockingView'; -import { CollectionView } from '../views/collections/CollectionView'; -import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView'; -import { AudioBox, mediaState } from '../views/nodes/AudioBox'; -import { ComparisonBox } from '../views/nodes/ComparisonBox'; -import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox'; -import { OpenWhere } from '../views/nodes/DocumentView'; -import { EquationBox } from '../views/nodes/EquationBox'; -import { FieldViewProps } from '../views/nodes/FieldView'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; -import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox'; -import { ImageBox } from '../views/nodes/ImageBox'; -import { KeyValueBox } from '../views/nodes/KeyValueBox'; -import { LabelBox } from '../views/nodes/LabelBox'; -import { LinkBox } from '../views/nodes/LinkBox'; -import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; -import { LoadingBox } from '../views/nodes/LoadingBox'; -import { MapBox } from '../views/nodes/MapBox/MapBox'; -import { MapPushpinBox } from '../views/nodes/MapBox/MapPushpinBox'; -import { PDFBox } from '../views/nodes/PDFBox'; -import { PhysicsSimulationBox } from '../views/nodes/PhysicsBox/PhysicsSimulationBox'; -import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox'; -import { ScreenshotBox } from '../views/nodes/ScreenshotBox'; -import { ScriptingBox } from '../views/nodes/ScriptingBox'; -import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; -import { VideoBox } from '../views/nodes/VideoBox'; -import { WebBox } from '../views/nodes/WebBox'; -import { CalendarBox } from '../views/nodes/calendarBox/CalendarBox'; -import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox'; -import { PresBox } from '../views/nodes/trails/PresBox'; -import { PresElementBox } from '../views/nodes/trails/PresElementBox'; -import { SearchBox } from '../views/search/SearchBox'; import { CollectionViewType, DocumentType } from './DocumentTypes'; -const { DFLT_IMAGE_NATIVE_DIM } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore - -const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); class EmptyBox { public static LayoutString() { return ''; @@ -146,7 +94,7 @@ class DocInfo extends FInfo { } class DimInfo extends FInfo { fieldType? = FInfoFieldType.enumeration; - values? = [DimUnit.Pixel, DimUnit.Ratio]; + values? = []; // DimUnit.Pixel, DimUnit.Ratio]; readOnly = false; filterable = false; override searchable = () => false; @@ -202,7 +150,7 @@ type STRt = StrInfo | string; type LISTt = ListInfo | List; type DOCt = DocInfo | Doc; type RTFt = RtfInfo | RichTextField; -type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio; +type DIMt = DimInfo; // | typeof DimUnit.Pixel | typeof DimUnit.Ratio; type PEVt = PEInfo | 'none' | 'all'; type COLLt = CTypeInfo | CollectionViewType; type DROPt = DAInfo | dropActionType; @@ -245,6 +193,7 @@ export class DocumentOptions { _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)', false); _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)', false); + acl?: STRt = new StrInfo('unused except as a display category in KeyValueBox'); acl_Guest?: STRt = new StrInfo("permissions granted to users logged in as 'guest' (either view, or private)"); // public permissions _acl_Guest?: string; // public permissions type?: DTYPEt = new DTypeInfo('type of document', true); @@ -253,6 +202,7 @@ export class DocumentOptions { title?: STRt = new StrInfo('title of document', true); title_custom?: BOOLt = new BoolInfo('whether title is a default or has been intentionally set'); caption?: RichTextField; + systemIcon?: STRt = new StrInfo("name of icon to use to represent document's type"); author?: string; // STRt = new StrInfo('creator of document'); // bcz: don't change this. Otherwise, the userDoc's field Infos will have a FieldInfo assigned to its author field which will render it unreadable author_date?: DATEt = new DateInfo('date the document was created', true); annotationOn?: DOCt = new DocInfo('document annotated by this document', false); @@ -272,6 +222,12 @@ export class DocumentOptions { _lockedPosition?: BOOLt = new BoolInfo("lock the x,y coordinates of the document so that it can't be dragged"); _lockedTransform?: BOOLt = new BoolInfo('lock the freeform_panx,freeform_pany and scale parameters of the document so that it be panned/zoomed'); + dataViz_title?: string; + dataViz_line?: string; + dataViz_pie?: string; + dataViz_histogram?: string; + dataViz?: string; + layout?: string | Doc; // default layout string or template document layout_isSvg?: BOOLt = new BoolInfo('whether document decorations and other selections should handle pointerEvents for svg content or use doc bounding box'); layout_keyValue?: STRt = new StrInfo('layout definition for showing keyValue view of document', false); @@ -325,6 +281,7 @@ export class DocumentOptions { _text_fontSize?: string; _text_fontFamily?: string; _text_fontWeight?: string; + fontSize?: string; _pivotField?: string; // field key used to determine headings for sections in stacking, masonry, pivot views infoWindowOpen?: BOOLt = new BoolInfo('whether info window corresponding to pin is open (on MapDocuments)'); @@ -333,7 +290,7 @@ export class DocumentOptions { _label_maxFontSize?: NUMt = new NumInfo('maximum font size for labelBoxes', false); stroke_width?: NUMt = new NumInfo('width of an ink stroke', false); stroke_showLabel?: BOOLt = new BoolInfo('show label inside of stroke'); - mediaState?: STRt = new StrInfo(`status of audio/video media document: ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.Playing}`, false); + mediaState?: STRt = new StrInfo(`status of audio/video media document:`); // ${mediaState.PendingRecording}, ${mediaState.Recording}, ${mediaState.Paused}, ${mediaState.Playing}`, false); recording?: BOOLt = new BoolInfo('whether WebCam is recording or not'); slides?: DOCt = new DocInfo('presentation slide associated with video recording (bcz: should be renamed!!)'); autoPlayAnchors?: BOOLt = new BoolInfo('whether to play audio/video when an anchor is clicked in a stackedTimeline.'); @@ -403,6 +360,7 @@ export class DocumentOptions { // STOPPING HERE // freeform properties + freeform?: STRt = new StrInfo(''); _freeform_backgroundGrid?: BOOLt = new BoolInfo('whether background grid is shown on freeform collections'); _freeform_scale_min?: NUMt = new NumInfo('how far out a view can zoom (used by image/videoBoxes that are clipped'); _freeform_scale_max?: NUMt = new NumInfo('how far in a view can zoom (used by sidebar freeform views'); @@ -433,6 +391,7 @@ export class DocumentOptions { flexGap?: NUMt = new NumInfo('Linear view flex gap'); flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse'; + link?: string; link_description?: string; // added for links link_relationship?: string; // type of relatinoship a link represents link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors'); @@ -528,120 +487,12 @@ export namespace Docs { type PrototypeMap = Map; const defaultDataKey = 'data'; - const TemplateMap: TemplateMap = new Map([ - [ - DocumentType.RTF, - { - layout: { view: FormattedTextBox, dataField: 'text' }, - options: { - acl: '', - _height: 35, - _xMargin: 10, - _yMargin: 10, - layout_nativeDimEditable: true, - layout_reflowVertical: true, - layout_reflowHorizontal: true, - defaultDoubleClick: 'ignore', - systemIcon: 'BsFileEarmarkTextFill', - }, - }, - ], - [ - DocumentType.SEARCH, - { - layout: { view: SearchBox, dataField: defaultDataKey }, - options: { acl: '', _width: 400 }, - }, - ], - [ - DocumentType.IMG, - { - layout: { view: ImageBox, dataField: defaultDataKey }, - options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, - }, - ], - [ - DocumentType.WEB, - { - layout: { view: WebBox, dataField: defaultDataKey }, - options: { acl: '', _height: 300, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, - }, - ], + export const TemplateMap: TemplateMap = new Map([ [ - DocumentType.COL, - { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { - acl: '', - _layout_fitWidth: true, - freeform: '', - _freeform_panX: 0, - _freeform_panY: 0, - _freeform_scale: 1, - layout_nativeDimEditable: true, - layout_reflowHorizontal: true, - layout_reflowVertical: true, - systemIcon: 'BsFillCollectionFill', - }, - }, - ], - [ - DocumentType.KVP, - { - layout: { view: KeyValueBox, dataField: defaultDataKey }, - options: { acl: '', _layout_fitWidth: true, _height: 150 }, - }, - ], - [ - DocumentType.VID, - { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, - }, - ], - [ - DocumentType.AUDIO, - { - layout: { view: AudioBox, dataField: defaultDataKey }, - options: { acl: '', _height: 100, layout_fitWidth: true, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, - }, - ], - [ - DocumentType.REC, - { - layout: { view: VideoBox, dataField: defaultDataKey }, - options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, - }, - ], - [ - DocumentType.PDF, - { - layout: { view: PDFBox, dataField: defaultDataKey }, - options: { acl: '', _layout_curPage: 1, _layout_fitWidth: true, layout_nativeDimEditable: true, layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, - }, - ], - [ - DocumentType.MAP, - { - layout: { view: MapBox, dataField: defaultDataKey }, - options: { acl: '', map: '', _height: 600, _width: 800, layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, - }, - ], - [ - DocumentType.LINK, + DocumentType.GROUPDB, { - layout: { view: LinkBox, dataField: 'link' }, - options: { - acl: '', - childDontRegisterViews: true, - layout_hideLinkAnchors: true, - _height: 1, - _width: 1, - link: '', - link_description: '', - color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area - _dropPropertiesToRemove: new List(['onClick']), - }, + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { acl: '', title: 'Global Group Database' }, }, ], [ @@ -652,175 +503,18 @@ export namespace Docs { options: { acl: '', title: 'Global Script Database' }, }, ], - [ - DocumentType.SCRIPTING, - { - layout: { view: ScriptingBox, dataField: defaultDataKey }, - options: { acl: '', systemIcon: 'BsFileEarmarkCodeFill' }, - }, - ], - [ - DocumentType.LABEL, - { - layout: { view: LabelBox, dataField: 'title' }, - options: { acl: '', _singleLine: true, layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, - }, - ], - [ - DocumentType.EQUATION, - { - layout: { view: EquationBox, dataField: 'text' }, - options: { acl: '', fontSize: '14px', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, // systemIcon: 'BsSuperscript' + BsSubscript - }, - ], - [ - DocumentType.FUNCPLOT, - { - layout: { view: FunctionPlotBox, dataField: defaultDataKey }, - options: { acl: '', layout_reflowHorizontal: true, layout_reflowVertical: true, layout_nativeDimEditable: true }, - }, - ], - [ - DocumentType.BUTTON, - { - layout: { view: LabelBox, dataField: 'title' }, - options: { acl: '', layout_nativeDimEditable: true, layout_reflowHorizontal: true, layout_reflowVertical: true }, - }, - ], - [ - DocumentType.PRES, - { - layout: { view: PresBox, dataField: defaultDataKey }, - options: { acl: '', defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, - }, - ], - [ - DocumentType.FONTICON, - { - layout: { view: FontIconBox, dataField: 'icon' }, - options: { acl: '', defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, - }, - ], - [ - DocumentType.WEBCAM, - { - layout: { view: RecordingBox, dataField: defaultDataKey }, - options: { acl: '', systemIcon: 'BsFillCameraVideoFill' }, - }, - ], - [ - DocumentType.PRESELEMENT, - { - layout: { view: PresElementBox, dataField: defaultDataKey }, - options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, - }, - ], + [ DocumentType.CONFIG, - { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { acl: '', config: '', layout_hideLinkButton: true, layout_unrendered: true }, - }, - ], - [ - DocumentType.INK, - { - // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method - layout: { view: InkingStroke, dataField: 'stroke' }, - options: { - acl: '', - systemIcon: 'BsFillPencilFill', // - layout_nativeDimEditable: true, - layout_reflowVertical: true, - layout_reflowHorizontal: true, - layout_hideDecorationTitle: true, // don't show title when selected - fitWidth: false, - layout_isSvg: true, - }, - }, - ], - [ - DocumentType.SCREENSHOT, - { - layout: { view: ScreenshotBox, dataField: defaultDataKey }, - options: { acl: '', layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, - }, - ], - [ - DocumentType.COMPARISON, - { - data: '', - layout: { view: ComparisonBox, dataField: defaultDataKey }, - options: { - acl: '', - backgroundColor: 'gray', - dropAction: dropActionType.move, - waitForDoubleClickToClick: 'always', - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - systemIcon: 'BsLayoutSplit', - }, - }, - ], - [ - DocumentType.GROUPDB, { layout: { view: EmptyBox, dataField: defaultDataKey }, - options: { acl: '', title: 'Global Group Database' }, - }, - ], - [ - DocumentType.DATAVIZ, - { - layout: { view: DataVizBox, dataField: defaultDataKey }, - options: { - acl: '', - dataViz_title: '', - dataViz_line: '', - dataViz_pie: '', - dataViz_histogram: '', - dataViz: 'table', - _layout_fitWidth: true, - layout_reflowHorizontal: true, - layout_reflowVertical: true, - layout_nativeDimEditable: true, - }, - }, - ], - [ - DocumentType.LOADING, - { - layout: { view: LoadingBox, dataField: '' }, - options: { acl: '', _layout_fitWidth: true, _fitHeight: true, layout_nativeDimEditable: true }, - }, - ], - [ - DocumentType.SIMULATION, - { - data: '', - layout: { view: PhysicsSimulationBox, dataField: defaultDataKey, _width: 1000, _height: 800 }, - options: { acl: '', _height: 100, mass1: '', mass2: '', layout_nativeDimEditable: true, position: '', acceleration: '', pendulum: '', spring: '', wedge: '', simulation: '', review: '', systemIcon: 'BsShareFill' }, - }, - ], - [ - DocumentType.PUSHPIN, - { - layout: { view: MapPushpinBox, dataField: defaultDataKey }, - options: { acl: '' }, + options: { acl: '', config: '', layout_hideLinkButton: true, layout_unrendered: true }, }, ], [ DocumentType.MAPROUTE, { - layout: { view: CollectionView, dataField: defaultDataKey }, - options: { acl: '' }, - }, - ], - [ - DocumentType.CALENDAR, - { - layout: { view: CalendarBox, dataField: defaultDataKey }, + layout: { view: EmptyBox, dataField: defaultDataKey }, options: { acl: '' }, }, ], @@ -1005,7 +699,7 @@ export namespace Docs { } Doc.assign(viewDoc, viewProps, true, true); if (![DocumentType.LINK, DocumentType.CONFIG, DocumentType.LABEL].includes(viewDoc.type as any)) { - DocUtils.MakeLinkToActiveAudio(() => viewDoc); + CreateLinkToActiveAudio(() => viewDoc); } updateCachedAcls(dataDoc); updateCachedAcls(viewDoc); @@ -1039,7 +733,7 @@ export namespace Docs { */ // eslint-disable-next-line default-param-last export function ScriptingDocument(script: Opt | null, options: DocumentOptions = {}, fieldKey?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); + return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script || undefined, { ...options, layout: fieldKey ? `` /* ScriptingBox.LayoutString(fieldKey) */ : undefined }); } // eslint-disable-next-line default-param-last @@ -1309,34 +1003,6 @@ export namespace Docs { return ret; } - export type DocConfig = { - doc: Doc; - initialWidth?: number; - path?: Doc[]; - }; - - export function StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = 'row') { - const layoutConfig = { - content: [ - { - type: type, - content: [...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))], - }, - ], - }; - const doc = DockDocument( - configs.map(c => c.doc), - JSON.stringify(layoutConfig), - ClientUtils.CurrentUserEmail() === 'guest' ? options : { acl_Guest: SharingPermissions.View, ...options }, - id - ); - configs.forEach(c => { - Doc.SetContainer(c.doc, doc); - inheritParentAcls(doc, c.doc, false); - }); - return doc; - } - export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) { return InstanceFromProto(proto, undefined, options); } @@ -1346,879 +1012,3 @@ export namespace Docs { } } } - -export namespace DocUtils { - function matchFieldValue(doc: Doc, key: string, valueIn: any): boolean { - let value = valueIn; - const hasFunctionFilter = ClientUtils.HasFunctionFilter(value); - if (hasFunctionFilter) { - return hasFunctionFilter(StrCast(doc[key])); - } - if (key === LinkedTo) { - // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) - const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); - const matchLink = (val: string, anchor: Doc) => { - const linkedToExp = (val ?? '').split('='); - if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; - return Field.toScriptString(DocCast(anchor[linkedToExp[0]])) === linkedToExp[1]; - }; - // prettier-ignore - return (value === Doc.FilterNone && !allLinks.length) || - (value === Doc.FilterAny && !!allLinks.length) || - (allLinks.some(link => matchLink(value,DocCast(link.link_anchor_1)) || - matchLink(value,DocCast(link.link_anchor_2)) )); - } - if (typeof value === 'string') { - value = value.replace(`,${ClientUtils.noRecursionHack}`, ''); - } - const fieldVal = doc[key]; - // prettier-ignore - if ((value === Doc.FilterAny && fieldVal !== undefined) || - (value === Doc.FilterNone && fieldVal === undefined)) { - return true; - } - const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings - if (vals.length) { - return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring - } - return Field.toString(fieldVal as FieldType).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring - } - /** - * @param docs - * @param childFilters - * @param childFiltersByRanges - * @param parentCollection - * Given a list of docs and childFilters, @returns the list of Docs that match those filters - */ - export function FilterDocs(childDocs: Doc[], childFilters: string[], childFiltersByRanges: string[], parentCollection?: Doc) { - if (!childFilters?.length && !childFiltersByRanges?.length) { - return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one - } - - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - childFilters.forEach(filter => { - const fields = filter.split(Doc.FilterSep); - const key = fields[0]; - const value = fields[1]; - const modifiers = fields[2]; - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - }); - - const filteredDocs = childFilters.length - ? childDocs.filter(d => { - if (d.z) return true; - // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter - if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) { - return false; - } - const facetKeys = Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== ClientUtils.noDragDocsFilter.split(Doc.FilterSep)[0]); - // eslint-disable-next-line no-restricted-syntax - for (const facetKey of facetKeys) { - const facet = filterFacets[facetKey]; - - // facets that match some value in the field of the document (e.g. some text field) - const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match'); - - // facets that have a check next to them - const checks = Object.keys(facet).filter(value => facet[value] === 'check'); - - // metadata facets that exist - const exists = Object.keys(facet).filter(value => facet[value] === 'exists'); - - // facets that unset metadata (a hack for making cookies work) - const unsets = Object.keys(facet).filter(value => facet[value] === 'unset'); - - // facets that specify that a field must not match a specific value - const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - - if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; - const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; - const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; - const satisfiesMatchFacets = !matches.length - ? true - : matches.some(value => { - if (facetKey.startsWith('*')) { - // fields starting with a '*' are used to match families of related fields. ie, *modificationDate will match text_modificationDate, data_modificationDate, etc - const allKeys = Array.from(Object.keys(d)); - allKeys.push(...Object.keys(Doc.GetProto(d))); - const keys = allKeys.filter(key => key.includes(facetKey.substring(1))); - return keys.some(key => Field.toString(d[key] as FieldType).includes(value)); - } - return Field.toString(d[facetKey] as FieldType).includes(value); - }); - // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria - if (parentCollection?.childFilters_boolean === 'OR') { - if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true; - } - // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria - else if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false; - } - return parentCollection?.childFilters_boolean !== 'OR'; - }) - : childDocs; - const rangeFilteredDocs = filteredDocs.filter(d => { - for (let i = 0; i < childFiltersByRanges.length; i += 3) { - const key = childFiltersByRanges[i]; - const min = Number(childFiltersByRanges[i + 1]); - const max = Number(childFiltersByRanges[i + 2]); - const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null); - if (val === undefined) { - // console.log("Should 'undefined' pass range filter or not?") - } else if (val < min || val > max) return false; - } - return true; - }); - return rangeFilteredDocs; - } - - export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; - - export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore - return DocUtils.ActiveRecordings.map(audio => { - const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); - }); - } - - export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { - if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; - if (target.doc === Doc.UserDoc()) return undefined; - - const makeLink = action((linkDoc: Doc, showAt?: number[]) => { - if (showAt) { - LinkManager.Instance.currentLink = linkDoc; - - TaskCompletionBox.textDisplayed = 'Link Created'; - TaskCompletionBox.popupX = showAt[0]; - TaskCompletionBox.popupY = showAt[1] - 33; - TaskCompletionBox.taskCompleted = true; - - LinkDescriptionPopup.Instance.popupX = showAt[0]; - LinkDescriptionPopup.Instance.popupY = showAt[1]; - LinkDescriptionPopup.Instance.display = true; - - const rect = document.body.getBoundingClientRect(); - if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) { - LinkDescriptionPopup.Instance.popupX -= 190; - TaskCompletionBox.popupX -= 40; - } - if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) { - LinkDescriptionPopup.Instance.popupY -= 40; - TaskCompletionBox.popupY -= 40; - } - - setTimeout( - action(() => { - TaskCompletionBox.taskCompleted = false; - }), - 2500 - ); - } - return linkDoc; - }); - - const a = source.layout_unrendered ? 'link_anchor_1?.annotationOn' : 'link_anchor_1'; - const b = target.layout_unrendered ? 'link_anchor_2?.annotationOn' : 'link_anchor_2'; - - return makeLink( - Docs.Create.LinkDocument( - source, - target, - { - acl_Guest: SharingPermissions.Augment, - _acl_Guest: SharingPermissions.Augment, - title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, - link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, - link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, - link_displayLine: linkSettings.link_displayLine, - link_relationship: linkSettings.link_relationship, - link_description: linkSettings.link_description, - x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, - y: ComputedField.MakeFunction(`((this.${a}?.y||0)+(this.${b}?.y||0))/2`) as any, - link_autoMoveAnchors: true, - _lockedPosition: true, - _layout_showCaption: '', // removed since they conflict with showing a link with a LinkBox (ie, line, not comparison box) - _layout_showTitle: '', - // _layout_showCaption: 'link_description', - // _layout_showTitle: 'link_relationship', - }, - id - ), - showPopup - ); - } - - export function AssignScripts(doc: Doc, scripts?: { [key: string]: string | undefined }, funcs?: { [key: string]: string }) { - scripts && - Object.keys(scripts).forEach(key => { - const script = scripts[key]; - if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { - (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { - self: Doc.name, - this: Doc.name, - dragData: DragManager.DocumentDragData.name, - value: 'any', - _readOnly_: 'boolean', - scriptContext: 'any', - documentView: Doc.name, - heading: Doc.name, - checked: 'boolean', - containingTreeView: Doc.name, - altKey: 'boolean', - ctrlKey: 'boolean', - shiftKey: 'boolean', - }); - } - }); - funcs && - Object.keys(funcs) - .filter(key => !key.endsWith('-setter')) - .forEach(key => { - const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key])); - if (ScriptCast(cfield)?.script.originalScript !== funcs[key]) { - const setFunc = Cast(funcs[key + '-setter'], 'string', null); - (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = funcs[key] ? ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true }, setFunc) : undefined; - } - }); - return doc; - } - export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) { - if (doc) { - const compareValues = (val1: any, val2: any) => { - if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) { - return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v)); - } - return val1 === val2; - }; - Object.entries(reqdOpts).forEach(pair => { - const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc); - if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) { - targetDoc[pair[0]] = pair[1]; - } - }); - items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item)); - items && DocListCast(doc.data).forEach(item => Doc.IsSystem(item) && !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item)); - } - return doc; - } - export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) { - // eslint-disable-next-line no-return-assign - return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs); - } - - export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined { - let created: Doc | undefined; - const field = target[fieldKey]; - const resolved = options ?? {}; - if (field instanceof ImageField) { - created = Docs.Create.ImageDocument(field.url.href, resolved); - created.layout = ImageBox.LayoutString(fieldKey); - } else if (field instanceof Doc) { - created = field; - } else if (field instanceof VideoField) { - created = Docs.Create.VideoDocument(field.url.href, resolved); - created.layout = VideoBox.LayoutString(fieldKey); - } else if (field instanceof PdfField) { - created = Docs.Create.PdfDocument(field.url.href, resolved); - created.layout = PDFBox.LayoutString(fieldKey); - } else if (field instanceof AudioField) { - created = Docs.Create.AudioDocument(field.url.href, resolved); - created.layout = AudioBox.LayoutString(fieldKey); - } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(field.inkData, resolved); - created.layout = InkingStroke.LayoutString(fieldKey); - } else if (field instanceof List && field[0] instanceof Doc) { - created = Docs.Create.StackingDocument(DocListCast(field), resolved); - created.layout = CollectionView.LayoutString(fieldKey); - } else { - created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved }); - created.layout = FormattedTextBox.LayoutString(fieldKey); - } - if (created) { - created.title = fieldKey; - proto && created.proto && (created.proto = Doc.GetProto(proto)); - } - return created; - } - - /** - * - * @param type the type of file. - * @param path the path to the file. - * @param options the document options. - * @param overwriteDoc the placeholder loading doc. - * @returns - */ - export async function DocumentFromType(type: string, path: string, options: DocumentOptions, overwriteDoc?: Doc): Promise> { - let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined; - if (type.indexOf('image') !== -1) { - ctor = Docs.Create.ImageDocument; - if (!options._width) options._width = 300; - } - if (type.indexOf('video') !== -1) { - ctor = Docs.Create.VideoDocument; - if (!options._width) options._width = 600; - if (!options._height) options._height = ((options._width as number) * 2) / 3; - } - if (type.indexOf('audio') !== -1) { - ctor = Docs.Create.AudioDocument; - } - if (type.indexOf('pdf') !== -1) { - ctor = Docs.Create.PdfDocument; - if (!options._width) options._width = 400; - if (!options._height) options._height = ((options._width as number) * 1200) / 927; - } - if (type.indexOf('csv') !== -1) { - ctor = Docs.Create.DataVizDocument; - if (!options._width) options._width = 400; - if (!options._height) options._height = ((options._width as number) * 1200) / 927; - } - // TODO:al+glr - // if (type.indexOf("map") !== -1) { - // ctor = Docs.Create.MapDocument; - // if (!options._width) options._width = 800; - // if (!options._height) options._height = (options._width as number) * 3 / 4; - // } - if (type.indexOf('html') !== -1) { - if (path.includes(window.location.hostname)) { - const s = path.split('/'); - const id = s[s.length - 1]; - return DocServer.GetRefField(id).then(field => { - if (field instanceof Doc) { - const embedding = Doc.MakeEmbedding(field); - embedding.x = (options.x as number) || 0; - embedding.y = (options.y as number) || 0; - embedding._width = (options._width as number) || 300; - embedding._height = (options._height as number) || (options._width as number) || 300; - return embedding; - } - return undefined; - }); - } - ctor = Docs.Create.WebDocument; - // eslint-disable-next-line no-param-reassign - options = { ...options, _width: 400, _height: 512, title: path }; - } - - return ctor ? ctor(path, overwriteDoc ? { ...options, title: StrCast(overwriteDoc.title, path) } : options, overwriteDoc) : undefined; - } - - export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { - const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data) - .filter(btnDoc => !btnDoc.hidden) - .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc.title) - .map(dragDoc => ({ - description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable(() => { - const newDoc = DocUtils.copyDragFactory(dragDoc); - if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail(); - newDoc.x = x; - newDoc.y = y; - EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); - if (pivotField) { - newDoc[pivotField] = pivotValue; - } - docAdder?.(newDoc); - } - }, StrCast(dragDoc.title)), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; - ContextMenu.Instance.addItem({ - description: 'Create document', - subitems: documentList, - icon: 'file', - }); - !simpleMenu && - ContextMenu.Instance.addItem({ - description: 'Styled Notes', - subitems: DocListCast((Doc.UserDoc().template_notes as Doc).data).map(note => ({ - description: ':' + StrCast(note.title), - event: undoable(() => { - const textDoc = Docs.Create.TextDocument('', { - _width: 200, - x, - y, - _layout_autoHeight: note._layout_autoHeight !== false, - title: StrCast(note.title) + '#' + (note.embeddingCount = NumCast(note.embeddingCount) + 1), - }); - textDoc.layout_fieldKey = 'layout_' + note.title; - textDoc[textDoc.layout_fieldKey] = note; - if (pivotField) { - textDoc[pivotField] = pivotValue; - } - docTextAdder(textDoc); - }, 'create quick note'), - icon: StrCast(note.icon) as IconProp, - })) as ContextMenuProps[], - icon: 'sticky-note', - }); - const userDocList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[1]?.data) - .filter(btnDoc => !btnDoc.hidden) - .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)) - .filter(doc => doc && doc !== Doc.UserDoc().emptyTrail && doc !== Doc.UserDoc().emptyNote && doc.title) - .map(dragDoc => ({ - description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''), - event: undoable(() => { - const newDoc = DocUtils.delegateDragFactory(dragDoc); - if (newDoc) { - newDoc.author = ClientUtils.CurrentUserEmail(); - newDoc.x = x; - newDoc.y = y; - EquationBox.SelectOnLoad = newDoc[Id]; - if (newDoc.type === DocumentType.RTF) FormattedTextBox.SetSelectOnLoad(newDoc); - if (pivotField) { - newDoc[pivotField] = pivotValue; - } - docAdder?.(newDoc); - } - }, StrCast(dragDoc.title)), - icon: Doc.toIcon(dragDoc), - })) as ContextMenuProps[]; - ContextMenu.Instance.addItem({ - description: 'User Templates', - subitems: userDocList, - icon: 'file', - }); - } - - // applies a custom template to a document. the template is identified by it's short name (e.g, slideView not layout_slideView) - - /** - * Applies a template to a Doc and logs the action with the UndoManager - * If the template already exists and has been registered, it can be specified by it's signature name (e.g., 'icon' not 'layout_icon'). - * Alternatively, the signature can be omitted and the template can be provided. - * @param doc the Doc to apply the template to. - * @param creator a function that will create the template if it doesn't exist - * @param templateSignature the signature name for a template that has already been created and registered on the userDoc. (can be "" if template is provide) - * @param template the template to use (optional if templateSignature is provided) - * @returns doc - */ - export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', template?: Doc) { - const batch = UndoManager.StartBatch('makeCustomViewClicked'); - createCustomView(doc, creator, templateSignature || StrCast(template?.title), template); - batch.end(); - return doc; - } - export function findTemplate(templateName: string, type: string) { - let docLayoutTemplate: Opt; - const iconViews = DocListCast(Cast(Doc.UserDoc().template_icons, Doc, null)?.data); - const templBtns = DocListCast(Cast(Doc.UserDoc().template_buttons, Doc, null)?.data); - const noteTypes = DocListCast(Cast(Doc.UserDoc().template_notes, Doc, null)?.data); - const userTypes = DocListCast(Cast(Doc.UserDoc().template_user, Doc, null)?.data); - const clickFuncs = DocListCast(Cast(Doc.UserDoc().template_clickFuncs, Doc, null)?.data); - const allTemplates = iconViews - .concat(templBtns) - .concat(noteTypes) - .concat(userTypes) - .concat(clickFuncs) - .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc) - .filter(doc => doc.isTemplateDoc); - // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document. but type is not reliable and there could be other types of template searches so this should be generalized - // first try to find a template that matches the specific document type (_). otherwise, fallback to a general match on - !docLayoutTemplate && - allTemplates.forEach(tempDoc => { - StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc); - }); - !docLayoutTemplate && - allTemplates.forEach(tempDoc => { - StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc); - }); - return docLayoutTemplate; - } - export function createCustomView(doc: Doc, creator: Opt<(documents: Array, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) { - const templateName = templateSignature.replace(/\(.*\)/, ''); - doc.layout_fieldKey = 'layout_' + (templateSignature || (docLayoutTemplate?.title ?? '')); - // eslint-disable-next-line no-param-reassign - docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc.isGroup && doc.transcription ? 'transcription' : doc.type)); - - const customName = 'layout_' + templateSignature; - const _width = NumCast(doc._width); - const _height = NumCast(doc._height); - const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _layout_autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _layout_showSidebar: false }; - - if (docLayoutTemplate) { - if (docLayoutTemplate !== doc[customName]) { - Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined); - } - } else { - let fieldTemplate: Opt; - if (doc.data instanceof RichTextField || typeof doc.data === 'string') { - fieldTemplate = Docs.Create.TextDocument('', options); - } else if (doc.data instanceof PdfField) { - fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options); - } else if (doc.data instanceof VideoField) { - fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof AudioField) { - fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options); - } else if (doc.data instanceof ImageField) { - fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options); - } - const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) }); - fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate); - docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined); - } - } - export function makeCustomView(doc: Doc, custom: boolean, layout: string) { - Doc.setNativeView(doc); - if (custom) { - makeCustomViewClicked(doc, Docs.Create.StackingDocument, layout, undefined); - } - } - export function iconify(doc: Doc) { - const layoutFieldKey = Cast(doc.layout_fieldKey, 'string', null); - DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined); - if (layoutFieldKey && layoutFieldKey !== 'layout' && layoutFieldKey !== 'layout_icon') doc.deiconifyLayout = layoutFieldKey.replace('layout_', ''); - } - - export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) { - runInAction(() => { - docList.forEach((doc, i) => { - const d = doc; - DocUtils.iconify(d); - d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size - size; - d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size - size; - d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - }); - }); - if (create) { - const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', _freeform_noZoom: true, x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, dragWhenActive: true, _layout_fitWidth: false }); - newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size; - newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size; - newCollection._width = newCollection._height = size * 2; - return newCollection; - } - return undefined; - } - export function makeIntoPortal(doc: Doc, layoutDoc: Doc, allLinks: Doc[]) { - const portalLink = allLinks.find(d => d.link_anchor_1 === doc && d.link_relationship === 'portal to:portal from'); - if (!portalLink) { - DocUtils.MakeLink( - doc, - Docs.Create.FreeformDocument([], { - _width: NumCast(layoutDoc._width) + 10, - _height: Math.max(NumCast(layoutDoc._height), NumCast(layoutDoc._width) + 10), - _isLightbox: true, - _layout_fitWidth: true, - title: StrCast(doc.title) + ' [Portal]', - }), - { link_relationship: 'portal to:portal from' } - ); - } - doc.followLinkLocation = OpenWhere.lightbox; - doc.onClick = FollowLinkScript(); - } - - export function LeavePushpin(doc: Doc, annotationField: string) { - if (doc.followLinkToggle) return undefined; - const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); - if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { - const pushpin = Docs.Create.FontIconDocument({ - title: '', - annotationOn: Cast(doc.annotationOn, Doc, null), - followLinkToggle: true, - icon: 'map-pin', - x: Cast(doc.x, 'number', null), - y: Cast(doc.y, 'number', null), - backgroundColor: '#ACCEF7', - layout_hideAllLinks: true, - _width: 15, - _height: 15, - _xPadding: 0, - onClick: FollowLinkScript(), - _timecodeToShow: Cast(doc._timecodeToShow, 'number', null), - }); - Doc.AddDocToList(context, annotationField, pushpin); - DocUtils.MakeLink(pushpin, doc, { link_relationship: 'pushpin' }, ''); - doc._timecodeToShow = undefined; - return pushpin; - } - return undefined; - } - - // /** - // * - // * @param dms Degree Minute Second format exif gps data - // * @param ref ref that determines negativity of decimal coordinates - // * @returns a decimal format of gps latitude / longitude - // */ - // function getDecimalfromDMS(dms?: number[], ref?: string) { - // if (dms && ref) { - // let degrees = dms[0] / dms[1]; - // let minutes = dms[2] / dms[3] / 60.0; - // let seconds = dms[4] / dms[5] / 3600.0; - - // if (['S', 'W'].includes(ref)) { - // degrees = -degrees; minutes = -minutes; seconds = -seconds - // } - // return (degrees + minutes + seconds).toFixed(5); - // } - // } - - function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) { - let dd = degrees + minutes / 60 + seconds / (60 * 60); - if (direction === 'S' || direction === 'W') { - dd *= -1; - } // Don't do anything for N or E - return dd; - } - - export function assignImageInfo(result: Upload.FileInformation, protoIn: Doc) { - const proto = protoIn; - if (Upload.isImageInformation(result)) { - const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim); - const exifRotation = StrCast((result.exifData?.data as any)?.Orientation).toLowerCase(); - proto.data_nativeOrientation = result.exifData?.data?.image?.Orientation ?? (exifRotation.includes('rotate 90') || exifRotation.includes('rotate 270') ? 5 : undefined); - proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - if (NumCast(proto.data_nativeOrientation) >= 5) { - proto.data_nativeHeight = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim; - proto.data_nativeWidth = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight); - } - proto.data_exif = JSON.stringify(result.exifData?.data); - proto.data_contentSize = result.contentSize; - // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates - const latitude = result.exifData?.data?.GPSLatitude; - const latitudeDirection = result.exifData?.data?.GPSLatitudeRef; - const longitude = result.exifData?.data?.GPSLongitude; - const longitudeDirection = result.exifData?.data?.GPSLongitudeRef; - if (latitude !== undefined && longitude !== undefined && latitudeDirection !== undefined && longitudeDirection !== undefined) { - proto.latitude = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection); - proto.longitude = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); - } - } - } - - async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, overwriteDoc?: Doc) { - if (result instanceof Error) { - alert(`Upload failed: ${result.message}`); - return; - } - const full = { ...options, _width: 400, title: name }; - const pathname = result.accessPaths.agnostic.client; - const doc = await DocUtils.DocumentFromType(type, pathname, full, overwriteDoc); - if (doc) { - const proto = Doc.GetProto(doc); - proto.text = result.rawText; - !(result instanceof Error) && DocUtils.assignImageInfo(result, proto); - if (Upload.isVideoInformation(result)) { - proto.data_duration = result.duration; - } - if (overwriteDoc) { - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - generatedDocuments.push(doc); - } - } - - export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, annotationOn?: Doc, backgroundColor?: string) { - const defaultTextTemplate = DocCast(Doc.UserDoc().defaultTextLayout); - const tbox = Docs.Create.TextDocument('', { - annotationOn, - backgroundColor, - x, - y, - title, - ...(defaultTextTemplate - ? {} // if the new doc will inherit from a template, don't set any layout fields since that would block the inheritance - : { - _width: width || 200, - _height: 35, - _layout_centered: BoolCast(Doc.UserDoc()._layout_centered), - _layout_fitWidth: true, - _layout_autoHeight: true, - }), - }); - - if (defaultTextTemplate) { - tbox.layout_fieldKey = 'layout_' + StrCast(defaultTextTemplate.title); - Doc.GetProto(tbox)[StrCast(tbox.layout_fieldKey)] = defaultTextTemplate; // set the text doc's layout to render with the text template - tbox[DocData].proto = defaultTextTemplate; // and also set the text doc to inherit from the template (this allows the template to specify default field values) - } - return tbox; - } - - export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { - const generatedDocuments: Doc[] = []; - Networking.UploadYoutubeToServer(videoId, overwriteDoc?.[Id]).then(upfiles => { - const { - source: { newFilename, mimetype }, - result, - } = upfiles.lastElement(); - if ((result as any).message) { - if (overwriteDoc) { - overwriteDoc.isLoading = false; - overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); - }); - } - - /** - * uploadFilesToDocs will take in an array of Files, and creates documents for the - * new files. - * - * @param files an array of files that will be uploaded - * @param options options to use while uploading - * @returns - */ - export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { - const generatedDocuments: Doc[] = []; - - // These files do not have overwriteDocs, so we do not set the guid and let the client generate one. - const fileNoGuidPairs: Networking.FileGuidPair[] = files.map(file => ({ file })); - - const upfiles = await Networking.UploadFilesToServer(fileNoGuidPairs); - upfiles.forEach(({ source: { newFilename, mimetype }, result }) => { - newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options); - }); - return generatedDocuments; - } - - export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { - const generatedDocuments: Doc[] = []; - // Since this file has an overwriteDoc, we can set the client tracking guid to the overwriteDoc's guid. - Networking.UploadFilesToServer([{ file, guid: overwriteDoc[Id] }]).then(upfiles => { - const { - source: { newFilename, mimetype }, - result, - } = upfiles.lastElement() ?? { source: { newFilename: '', mimetype: '' }, result: { message: 'upload failed' } }; - if ((result as any).message) { - if (overwriteDoc) { - overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); - } - } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); - }); - } - - // copies the specified drag factory document - export function copyDragFactory(dragFactory: Doc) { - if (!dragFactory) return undefined; - const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true); - if (ndoc && dragFactory.dragFactory_count !== undefined) { - dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; - Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(), true); - } - - return ndoc; - } - export function delegateDragFactory(dragFactory: Doc) { - const ndoc = Doc.MakeDelegateWithProto(dragFactory); - if (ndoc && dragFactory.dragFactory_count !== undefined) { - dragFactory.dragFactory_count = NumCast(dragFactory.dragFactory_count) + 1; - Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory.dragFactory_count).toString(); - } - return ndoc; - } - - export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') { - const { clone, map, linkMap } = await Doc.MakeClone(doc); - const proms = new Set(); - function replacer(key: any, value: any) { - if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined; - if (value?.__type === 'image') { - const extension = value.url.replace(/.*\./, ''); - proms.add(value.url.replace('.' + extension, '_o.' + extension)); - return SerializationHelper.Serialize(new ImageField(value.url)); - } - if (value?.__type === 'pdf') { - proms.add(value.url); - return SerializationHelper.Serialize(new PdfField(value.url)); - } - if (value?.__type === 'audio') { - proms.add(value.url); - return SerializationHelper.Serialize(new AudioField(value.url)); - } - if (value?.__type === 'video') { - proms.add(value.url); - return SerializationHelper.Serialize(new VideoField(value.url)); - } - if ( - value instanceof Doc || - value instanceof ScriptField || - value instanceof RichTextField || - value instanceof InkField || - value instanceof CsvField || - value instanceof WebField || - value instanceof DateField || - value instanceof ProxyField || - value instanceof ComputedField - ) { - return SerializationHelper.Serialize(value); - } - if (value instanceof Array && key !== ListFieldName && key !== InkDataFieldName) return { fields: value, __type: 'list' }; - return value; - } - - const docs: { [id: string]: any } = {}; - const links: { [id: string]: any } = {}; - Array.from(map.entries()).forEach(f => { - docs[f[0]] = f[1]; - }); - Array.from(linkMap.entries()).forEach(l => { - links[l[0]] = l[1]; - }); - const jsonDocs = JSON.stringify({ id: clone[Id], docs, links }, decycle(replacer)); - - const zip = new JSZip(); - let count = 0; - const promArr = Array.from(proms) - .filter(url => url?.startsWith('/files')) - .map(url => url.replace('/', '')); // window.location.origin)); - console.log(promArr.length); - if (!promArr.length) { - zip.file('docs.json', jsonDocs); - zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); - } else - promArr.forEach((url, i) => { - // loading a file and add it in a zip file - JSZipUtils.getBinaryContent(window.location.origin + '/' + url, (err: any, data: any) => { - if (err) throw err; // or handle the error - // // Generate a directory within the Zip file structure - // const assets = zip.folder("assets"); - // assets.file(filename, data, {binary: true}); - const assetPathOnServer = promArr[i].replace(window.location.origin + '/', '').replace(/\//g, '%%%'); - zip.file(assetPathOnServer, data, { binary: true }); - console.log(' => ' + url); - if (++count === promArr.length) { - zip.file('docs.json', jsonDocs); - zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename)); - // const a = document.createElement("a"); - // const url = Utils.prepend(`/downloadId/${this.props.Document[Id]}`); - // a.href = url; - // a.download = `DocExport-${this.props.Document[Id]}.zip`; - // a.click(); - } - }); - }); - } -} - -ScriptingGlobals.add('Docs', Docs); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { - return dragFactory instanceof Doc ? (asDelegate ? DocUtils.delegateDragFactory(dragFactory) : DocUtils.copyDragFactory(dragFactory)) : dragFactory; -}); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function makeDelegate(proto: any) { - const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title }); - return d; -}); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function generateLinkTitle(link: Doc) { - const linkAnchor1title = link.link_anchor_1 && link.link_anchor_1 !== link ? Cast(link.link_anchor_1, Doc, null)?.title : ''; - const linkAnchor2title = link.link_anchor_2 && link.link_anchor_2 !== link ? Cast(link.link_anchor_2, Doc, null)?.title : ''; - const relation = link.link_relationship || 'to'; - return `${linkAnchor1title} (${relation}) ${linkAnchor2title}`; -}); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 00279e7e1..96f5172bc 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -8,28 +8,28 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { ScriptField } from "../../fields/ScriptField"; +import { FollowLinkScript, ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types"; import { WebField, nullAudio } from "../../fields/URLField"; import { SetCachedGroups, SharingPermissions } from "../../fields/util"; import { Gestures } from "../../pen-gestures/GestureTypes"; import { DocServer } from "../DocServer"; +import { DocUtils } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; -import { DocUtils, Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; +import { Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; import { DashboardView } from "../views/DashboardView"; import { OverlayView } from "../views/OverlayView"; import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView"; import { Colors } from "../views/global/globalEnums"; import { mediaState } from "../views/nodes/AudioBox"; -import { OpenWhere } from "../views/nodes/DocumentView"; import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox"; import { ImageBox } from "../views/nodes/ImageBox"; import { LabelBox } from "../views/nodes/LabelBox"; +import { OpenWhere } from "../views/nodes/OpenWhere"; import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { DragManager } from "./DragManager"; import { dropActionType } from "./DropActionTypes"; import { MakeTemplate } from "./DropConverter"; -import { FollowLinkScript } from "./LinkFollower"; import { LinkManager, UPDATE_SERVER_CACHE } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; import { ColorScheme } from "./SettingsManager"; diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 9026b368f..97ee628e2 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -4,6 +4,7 @@ import * as interpreter from 'words-to-numbers'; import type {} from '@types/dom-speech-recognition'; import { ClientUtils } from '../../ClientUtils'; import { Doc, Opt } from '../../fields/Doc'; +import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { listSpec } from '../../fields/Schema'; @@ -12,10 +13,10 @@ import { AudioField, ImageField } from '../../fields/URLField'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { DictationOverlay } from '../views/DictationOverlay'; -import { DocumentView, OpenWhere } from '../views/nodes/DocumentView'; +import { DocumentView } from '../views/nodes/DocumentView'; +import { OpenWhere } from '../views/nodes/OpenWhere'; import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; -import { DocData } from '../../fields/DocSymbols'; /** * This namespace provides a singleton instance of a manager that diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index a893fe627..8f0c2b0bf 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -10,10 +10,11 @@ import { GetEffectiveAcl } from '../../fields/util'; import { CollectionViewType } from '../documents/DocumentTypes'; import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../views/nodes/DocumentView'; -import { FocusViewOptions } from '../views/nodes/FieldView'; +import { DocumentView, DocumentViewInternal } from '../views/nodes/DocumentView'; +import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; import { KeyValueBox } from '../views/nodes/KeyValueBox'; import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; +import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 3890b7845..34f54be2e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -18,14 +18,13 @@ import { action, observable, runInAction } from 'mobx'; import { ClientUtils } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { DateField } from '../../fields/DateField'; -import { Doc, FieldType, Opt, StrListCast } from '../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, FieldType, Opt, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { PrefetchProxy } from '../../fields/Proxy'; import { ScriptField } from '../../fields/ScriptField'; import { ScriptCast } from '../../fields/Types'; -import { DocUtils, Docs } from '../documents/Documents'; -import { CollectionFreeFormDocumentView } from '../views/nodes/CollectionFreeFormDocumentView'; +import { Docs } from '../documents/Documents'; import { DocumentView } from '../views/nodes/DocumentView'; import { dropActionType } from './DropActionTypes'; import { ScriptingGlobals } from './ScriptingGlobals'; @@ -74,6 +73,7 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () } export namespace DragManager { + export const dragClassName = 'collectionFreeFormDocumentView-container'; let dragDiv: HTMLDivElement; let dragLabel: HTMLDivElement; export let StartWindowDrag: Opt<(e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => boolean>; @@ -139,11 +139,11 @@ export namespace DragManager { constructor(dragDoc: Doc[], dropAction?: dropActionType) { this.draggedDocuments = dragDoc; this.droppedDocuments = []; - this.draggedViews = []; this.offset = [0, 0]; this.dropAction = dropAction; } - draggedViews: DocumentView[]; + dragEnding?: () => void; + dragStarting?: () => void; draggedDocuments: Doc[]; droppedDocuments: Doc[]; treeViewDoc?: Doc; @@ -157,6 +157,7 @@ export namespace DragManager { removeDocument?: RemoveFunction; isDocDecorationMove?: boolean; // Flags that Document decorations are used to drag document which allows suppression of onDragStart scripts } + Doc.SetDocDragDataName(DocumentDragData.name); export class LinkDragData { constructor(dragView: DocumentView, linkSourceGetAnchor: () => Doc) { this.linkDragView = dragView; @@ -224,12 +225,12 @@ export namespace DragManager { export function StartDocumentDrag(eles: HTMLElement[], dragData: DocumentDragData, downX: number, downY: number, options?: DragOptions, onDropCompleted?: (e?: DragCompleteEvent) => any) { const addAudioTag = (dropDoc: any) => { dropDoc && !dropDoc.author_date && (dropDoc.author_date = new DateField()); - dropDoc instanceof Doc && DocUtils.MakeLinkToActiveAudio(() => dropDoc); + dropDoc instanceof Doc && CreateLinkToActiveAudio(() => dropDoc); return dropDoc; }; const finishDrag = async (e: DragCompleteEvent) => { const { docDragData } = e; - setTimeout(() => dragData.draggedViews.forEach(view => view.props.dragEnding?.())); + setTimeout(() => dragData.dragEnding?.()); onDropCompleted?.(e); // glr: optional additional function to be called - in this case with presentation trails if (docDragData && !docDragData.droppedDocuments.length) { docDragData.dropAction = dragData.userDropAction || dragData.dropAction; @@ -265,7 +266,7 @@ export namespace DragManager { }; dragData.draggedDocuments.map(d => d.dragFactory); // does this help? trying to make sure the dragFactory Doc is loaded StartDrag(eles, dragData, downX, downY, options, finishDrag); - dragData.draggedViews.forEach(view => view.props.dragStarting?.()); + dragData.dragStarting?.(); return true; } @@ -418,7 +419,7 @@ export namespace DragManager { // if the parent isn't a freeform view, then the element's width and height are presumed to match the acutal doc's dimensions (eg, dragging from import sidebar menu) let rotation: number | undefined; for (let parEle: HTMLElement | null | undefined = ele.parentElement; parEle; parEle = parEle?.parentElement) { - if (parEle.className === CollectionFreeFormDocumentView.CollectionFreeFormDocViewClassName) { + if (parEle.className === DragManager.dragClassName) { rotation = (rotation ?? 0) + Number(parEle.style.transform.replace(/.*rotate\(([-0-9.e]*)deg\).*/, '$1') || 0); } parEle = parEle.parentElement; @@ -630,7 +631,7 @@ export namespace DragManager { dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, () => cleanupDrag(false)); }; const cleanupDrag = action((undo: boolean) => { - (dragData as DocumentDragData).draggedViews?.forEach(view => view.props.dragEnding?.()); + (dragData as DocumentDragData).dragEnding?.(); hideDragShowOriginalElements(false); document.removeEventListener('pointermove', moveHandler, true); document.removeEventListener('pointerup', upHandler, true); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 8d84dbad8..e1a503ac9 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -19,8 +19,8 @@ import { ObservableReactComponent } from '../views/ObservableReactComponent'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './GroupManager.scss'; import { GroupMemberView } from './GroupMemberView'; -import { SettingsManager } from './SettingsManager'; import { SharingManager, User } from './SharingManager'; +import { SnappingManager } from './SnappingManager'; /** * Interface for options for the react-select component @@ -289,7 +289,7 @@ export class GroupManager extends ObservableReactComponent<{}> { */ private get groupCreationModal() { const contents = ( -
+

New Group @@ -384,7 +384,7 @@ export class GroupManager extends ObservableReactComponent<{}> { const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups; return ( -

+
{this.groupCreationModal} {this.currentGroup ? ( { const hasEditAccess = GroupManager.Instance.hasEditAccess(this.group); return !this.group ? null : ( -
+
containerDocContext.length && (dv.ComponentView as PresBox).PlayTrail(containerDocContext))) { - PresBox.OpenPresMinimized(target, [0, 0]); + if (!DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { + alert('OPEN Pres minimized'); + // PresBox.OpenPresMinimized(target, [0, 0]); } finished?.(); } else { @@ -138,9 +137,6 @@ ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) { return LinkFollower.FollowLink(undefined, doc, altKey) ? undefined : { select: true }; }); -export function FollowLinkScript() { - return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); -} export function IsFollowLinkScript(field: FieldResult) { return ScriptCast(field)?.script.originalScript.includes('return followLink('); } diff --git a/src/client/util/RTFMarkup.tsx b/src/client/util/RTFMarkup.tsx index 05fb849fd..248fda7e3 100644 --- a/src/client/util/RTFMarkup.tsx +++ b/src/client/util/RTFMarkup.tsx @@ -2,7 +2,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { MainViewModal } from '../views/MainViewModal'; -import { SettingsManager } from './SettingsManager'; +import { SnappingManager } from './SnappingManager'; @observer export class RTFMarkup extends React.Component<{}> { @@ -10,6 +10,10 @@ export class RTFMarkup extends React.Component<{}> { static Instance: RTFMarkup; @observable private isOpen = false; // whether the SharingManager modal is open or not + public setOpen = action((status: boolean) => { + this.isOpen = status; + }); + constructor(props: {}) { super(props); makeObservable(this); @@ -21,7 +25,7 @@ export class RTFMarkup extends React.Component<{}> { */ @computed get cheatSheet() { return ( -
+

(@wiki:phrase) {` display wikipedia page for entered text (terminate with carriage return)`} @@ -122,24 +126,14 @@ export class RTFMarkup extends React.Component<{}> { ); } - @action - public open = () => { - this.isOpen = true; - }; - - @action - public close = () => { - this.isOpen = false; - }; - render() { return ( this.setOpen(false)} /> ); } diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index 2c8fdf483..dcc6aabab 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -2,7 +2,7 @@ import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { Doc, IdToDoc } from '../../fields/Doc'; import { CollectionDockingView } from '../views/collections/CollectionDockingView'; import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; -import { OpenWhereMod } from '../views/nodes/DocumentView'; +import { OpenWhereMod } from '../views/nodes/OpenWhere'; import { VideoBox } from '../views/nodes/VideoBox'; import { DocumentManager } from './DocumentManager'; import { Movement, Presentation } from './TrackMovements'; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8347844f7..8cb97fe50 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -14,9 +14,7 @@ import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { Networking } from '../Network'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; -import { GestureOverlay } from '../views/GestureOverlay'; import { MainViewModal } from '../views/MainViewModal'; -import { FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; import { SnappingManager } from './SnappingManager'; @@ -256,25 +254,14 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> - { - FontIconBox.ShowIconLabels = !FontIconBox.ShowIconLabels; - }} - toggleStatus={FontIconBox.ShowIconLabels} - size={Size.XSMALL} - color={SettingsManager.userColor} - /> { - GestureOverlay.RecognizeGestures = !GestureOverlay.RecognizeGestures; + Doc.UserDoc().recognizeGestures = !Doc.UserDoc().recognizeGestures; }} - toggleStatus={GestureOverlay.RecognizeGestures} + toggleStatus={BoolCast(Doc.UserDoc().recognizeGestures)} size={Size.XSMALL} color={SettingsManager.userColor} /> diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 19a7948e6..b0140b73c 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -18,7 +18,6 @@ import { Id } from '../../fields/FieldSymbols'; import { StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, TraceMobx, distributeAcls, normalizeEmail } from '../../fields/util'; import { DocServer } from '../DocServer'; -import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; @@ -27,8 +26,8 @@ import { GroupManager, UserOptions } from './GroupManager'; import { GroupMemberView } from './GroupMemberView'; import { SearchUtil } from './SearchUtil'; import { SelectionManager } from './SelectionManager'; -import { SettingsManager } from './SettingsManager'; import './SharingManager.scss'; +import { SnappingManager } from './SnappingManager'; import { undoable } from './UndoManager'; export interface User { @@ -238,14 +237,14 @@ export class SharingManager extends React.Component<{}> { const permissions = uniform ? StrCast(targetDoc?.[groupKey]) : '-multiple-'; return !permissions ? null : ( -

+
{StrCast(group.title)}
  {group instanceof Doc ? ( } size={Size.XSMALL} - color={SettingsManager.userColor} + color={SnappingManager.userColor} onClick={action(() => { GroupManager.Instance.currentGroup = group; })} @@ -279,10 +278,10 @@ export class SharingManager extends React.Component<{}> {
-

+

window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')}> window.open('https://brown-dash.github.io/Dash-Documentation/features/collaboration/', '_blank')} />
@@ -290,10 +289,10 @@ export class SharingManager extends React.Component<{}> { {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}

-
-
{admin ? (
@@ -335,7 +334,7 @@ export class SharingManager extends React.Component<{}> {
-
@@ -514,7 +513,6 @@ export class SharingManager extends React.Component<{}> { setTimeout( action(() => { // this.copied = false; - DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500 @@ -528,7 +526,6 @@ export class SharingManager extends React.Component<{}> { runInAction(() => { this.targetDocView = target; this.targetDoc = targetDoc || target?.Document; - DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; this.permissions = SharingPermissions.Augment; this.upgradeNested = true; diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index c4855d2e7..d92a73b63 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -15,9 +15,10 @@ import { PrefetchProxy } from '../../fields/Proxy'; import { listSpec } from '../../fields/Schema'; import { ScriptField } from '../../fields/ScriptField'; import { Cast, ImageCast, StrCast } from '../../fields/Types'; -import { normalizeEmail } from '../../fields/util'; +import { SharingPermissions, inheritParentAcls, normalizeEmail } from '../../fields/util'; import { DocServer } from '../DocServer'; -import { DocUtils, Docs, DocumentOptions } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; +import { Docs, DocumentOptions } from '../documents/Documents'; import { dropActionType } from '../util/DropActionTypes'; import { HistoryUtil } from '../util/History'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; @@ -36,11 +37,51 @@ enum DashboardGroup { SharedDashboards, } +export type DocConfig = { + doc: Doc; + initialWidth?: number; + path?: Doc[]; +}; + // DashboardView is the view with the dashboard previews, rendered when the app first loads @observer export class DashboardView extends ObservableReactComponent<{}> { public static _urlState: HistoryUtil.DocUrl; + public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) { + return { + type: 'react-component', + component: 'DocumentFrameRenderer', + title: document.title, + width: width, + props: { + documentId: document[Id], + keyValue, + panelName, // name of tab that can be used to close or replace its contents + }, + }; + } + static StandardCollectionDockingDocument(configs: Array, options: DocumentOptions, id?: string, type: string = 'row') { + const layoutConfig = { + content: [ + { + type: type, + content: [...configs.map(config => DashboardView.makeDocumentConfig(config.doc, undefined, config.initialWidth))], + }, + ], + }; + const doc = Docs.Create.DockDocument( + configs.map(c => c.doc), + JSON.stringify(layoutConfig), + ClientUtils.CurrentUserEmail() === 'guest' ? options : { acl_Guest: SharingPermissions.View, ...options }, + id + ); + configs.forEach(c => { + Doc.SetContainer(c.doc, doc); + inheritParentAcls(doc, c.doc, false); + }); + return doc; + } constructor(props: any) { super(props); makeObservable(this); @@ -389,7 +430,7 @@ export class DashboardView extends ObservableReactComponent<{}> { const title = name || `Dashboard ${dashboardCount}`; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); + const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); Doc.AddDocToList(Doc.MyHeaderBar, 'data', freeformDoc, undefined, undefined, true); dashboardDoc.pane_count = 1; diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 391c2f694..a0e3d2ddd 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -12,8 +12,10 @@ import { DocumentType } from '../documents/DocumentTypes'; import { DragManager } from '../util/DragManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from './nodes/FieldView'; +import { DocumentView } from './nodes/DocumentView'; +import { FocusViewOptions } from './nodes/FocusViewOptions'; +import { FieldViewProps } from './nodes/FieldView'; +import { OpenWhere } from './nodes/OpenWhere'; // import { DocUtils } from '../documents/Documents'; /** @@ -41,6 +43,7 @@ export interface ViewBoxInterface { onClickScriptDisable?: () => 'never' | 'always'; // disable click scripts : never, always, or undefined = only when selected getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) + playTrail?: (docs: Doc[]) => void; playFrom?: (time: number, endTime?: number) => void; Pause?: () => void; // pause a media document (eg, audio/video) IsPlaying?: () => boolean; // is a media document playing diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index c5358dffe..dd744f272 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -14,7 +14,7 @@ import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from ' import { emptyFunction } from '../../Utils'; import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; -import { DocUtils } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; @@ -22,15 +22,16 @@ import { IsFollowLinkScript } from '../util/LinkFollower'; import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; import { UndoManager, undoBatch } from '../util/UndoManager'; -import { PinProps } from './DocComponent'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { PinProps } from './PinFuncs'; import { TemplateMenu } from './TemplateMenu'; import { TabDocView } from './collections/TabDocView'; import { Colors } from './global/globalEnums'; import { LinkPopup } from './linking/LinkPopup'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal, OpenWhere } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @observer diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 1e6eb1aeb..ef493fb69 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -33,9 +33,10 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { Colors } from './global/globalEnums'; import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; -import { DocumentView, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { ImageBox } from './nodes/ImageBox'; import { KeyValueBox } from './nodes/KeyValueBox'; +import { OpenWhereMod } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; interface DocumentDecorationsProps { @@ -448,7 +449,7 @@ export class DocumentDecorations extends ObservableReactComponent CollectionFreeFormDocumentView.from(docView)?.CollectionFreeFormView?.dragStarting(false, false)); + SelectionManager.Views.forEach(docView => CollectionFreeFormView.from(docView)?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index bf5b51a2d..0d1573ea3 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -4,17 +4,6 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Opt } from '../../fields/Doc'; -import { InkData, InkTool } from '../../fields/InkField'; -import { BoolCast, NumCast } from '../../fields/Types'; -import MobileInkOverlay from '../../mobile/MobileInkOverlay'; -import { Gestures } from '../../pen-gestures/GestureTypes'; -import { GestureUtils } from '../../pen-gestures/GestureUtils'; -import { MobileInkOverlayContent } from '../../server/Message'; -import { InteractionUtils } from '../util/InteractionUtils'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { Transform } from '../util/Transform'; -import './GestureOverlay.scss'; import { ActiveArrowEnd, ActiveArrowScale, @@ -24,12 +13,24 @@ import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, + Doc, + Opt, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, -} from './InkingStroke'; +} from '../../fields/Doc'; +import { InkData, InkTool } from '../../fields/InkField'; +import { NumCast } from '../../fields/Types'; +// import MobileInkOverlay from '../../mobile/MobileInkOverlay'; +import { Gestures } from '../../pen-gestures/GestureTypes'; +import { GestureUtils } from '../../pen-gestures/GestureUtils'; +// import { MobileInkOverlayContent } from '../../server/Message'; +import { InteractionUtils } from '../util/InteractionUtils'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { Transform } from '../util/Transform'; +import './GestureOverlay.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { DocumentView } from './nodes/DocumentView'; @@ -49,13 +50,6 @@ export class GestureOverlay extends ObservableReactComponent = undefined; @observable public SavedColor?: string = undefined; @observable public SavedWidth?: number = undefined; @@ -80,7 +74,7 @@ export class GestureOverlay extends ObservableReactComponent(); private _d1: Doc | undefined; @@ -158,7 +152,7 @@ export class GestureOverlay extends ObservableReactComponent 2 && GestureUtils.GestureRecognizer.Recognize([points]); let actionPerformed = false; - if (GestureOverlay.RecognizeGestures && result && result.Score > 0.7) { + if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) { switch (result.Name) { case Gestures.Line: case Gestures.Triangle: @@ -502,15 +496,10 @@ export class GestureOverlay extends ObservableReactComponent { - this.showMobileInkOverlay = content.enableOverlay; - }; - render() { return (
- {this.showMobileInkOverlay ? : null} + {/* {this.showMobileInkOverlay ? : null} */} {this.elements}
{}); nudge = (x: number, y: number, label: string) => { - const nudgeable = SelectionManager.Views.some(dv => dv.CollectionFreeFormDocumentView?.nudge); - nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => dv.CollectionFreeFormDocumentView?.nudge(x, y)), label); + const nudgeable = SelectionManager.Views.some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); + nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); return { stopPropagation: nudgeable, preventDefault: nudgeable }; }; @@ -105,7 +106,7 @@ export class KeyManager { case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { const selected = SelectionManager.Views; - const cv = selected.reduce((col, dv) => (!col || dv.CollectionFreeFormView === col ? dv.CollectionFreeFormView : undefined), undefined as undefined | CollectionFreeFormView); + const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView); cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs), 'grouping'); } break; @@ -205,7 +206,7 @@ export class KeyManager { switch (keyname) { case 'Æ’': case 'f': - UndoManager.RunInBatch(() => SelectionManager.Views?.[0]?.CollectionFreeFormDocumentView?.float(), 'float'); + UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(SelectionManager.Views?.[0])?.float(), 'float'); break; default: } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 98cf33c41..109a9cad4 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -13,6 +13,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; export class InkStrokeProperties { @@ -380,7 +381,7 @@ export class InkStrokeProperties { }; snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => { - const containingCollection = inkView.CollectionFreeFormView; + const containingCollection = CollectionFreeFormView.from(inkView); const containingDocView = containingCollection?.DocumentView?.(); containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index cf609d8f9..884512539 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -30,6 +30,7 @@ import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; import { Docs } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { InteractionUtils } from '../util/InteractionUtils'; import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; @@ -491,67 +492,17 @@ export class InkingStroke extends ViewBoxAnnotatableComponent() ); } } -export function ActiveInkPen(): Doc { - return Doc.UserDoc(); -} -export function ActiveInkColor(): string { - return StrCast(ActiveInkPen()?.activeInkColor, 'black'); -} -export function ActiveFillColor(): string { - return StrCast(ActiveInkPen()?.activeFillColor, ''); -} -export function ActiveIsInkMask(): boolean { - return BoolCast(ActiveInkPen()?.activeIsInkMask, false); -} -export function ActiveInkHideTextLabels(): boolean { - return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); -} -export function ActiveArrowStart(): string { - return StrCast(ActiveInkPen()?.activeArrowStart, ''); -} -export function ActiveArrowEnd(): string { - return StrCast(ActiveInkPen()?.activeArrowEnd, ''); -} -export function ActiveArrowScale(): number { - return NumCast(ActiveInkPen()?.activeArrowScale, 1); -} -export function ActiveDash(): string { - return StrCast(ActiveInkPen()?.activeDash, '0'); -} -export function ActiveInkWidth(): number { - return Number(ActiveInkPen()?.activeInkWidth); -} -export function ActiveInkBezierApprox(): string { - return StrCast(ActiveInkPen()?.activeInkBezier); -} - -export function SetActiveInkWidth(width: string): void { - !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); -} -export function SetActiveBezierApprox(bezier: string): void { - ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); -} -export function SetActiveInkColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeInkColor = value); -} -export function SetActiveIsInkMask(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); -} -export function SetActiveInkHideTextLabels(value: boolean) { - ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); -} -export function SetActiveFillColor(value: string) { - ActiveInkPen() && (ActiveInkPen().activeFillColor = value); -} -export function SetActiveArrowStart(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); -} -export function SetActiveArrowEnd(value: string) { - ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); -} -export function SetActiveArrowScale(value: number) { - ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); -} -export function SetActiveDash(dash: string): void { - !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); -} +Docs.Prototypes.TemplateMap.set(DocumentType.INK, { + // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method + layout: { view: InkingStroke, dataField: 'stroke' }, + options: { + acl: '', + systemIcon: 'BsFillPencilFill', // + _layout_nativeDimEditable: true, + _layout_reflowVertical: true, + _layout_reflowHorizontal: true, + layout_hideDecorationTitle: true, // don't show title when selected + _layout_fitWidth: false, + layout_isSvg: true, + }, +}); diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 7bf6bb9e5..020525ef8 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -9,11 +9,10 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, toList } from '../../fields/Types'; -import { DocUtils } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; @@ -27,7 +26,8 @@ import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { TabDocView } from './collections/TabDocView'; -import { DocumentView, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; interface LightboxViewProps { PanelWidth: number; @@ -88,7 +88,7 @@ export class LightboxView extends ObservableReactComponent { savedKeys.forEach(key => { this._savedState[key] = Doc.Get(doc, key, true); }); - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); this._history.push({ doc, target }); @@ -136,7 +136,7 @@ export class LightboxView extends ObservableReactComponent { const target = (this._docTarget = this._future.pop()); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); + const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (this._history.lastElement().target !== target) this._history.push({ doc: lightDoc, target }); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d3dd51ac6..aec553baa 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -11,7 +11,7 @@ import * as React from 'react'; import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { DocCast, StrCast, toList } from '../../fields/Types'; import { DocServer } from '../DocServer'; @@ -55,28 +55,28 @@ import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionMenu } from './collections/CollectionMenu'; import { TabDocView } from './collections/TabDocView'; import './collections/TreeView.scss'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu'; import { CollectionLinearView } from './collections/collectionLinear'; import { LinkMenu } from './linking/LinkMenu'; import { AudioBox } from './nodes/AudioBox'; import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp'; import { DocButtonState } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from './nodes/DocumentView'; import { ImageEditorData as ImageEditor } from './nodes/ImageBox'; import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup'; import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview'; import { DirectionsAnchorMenu } from './nodes/MapBox/DirectionsAnchorMenu'; import { MapAnchorMenu } from './nodes/MapBox/MapAnchorMenu'; +import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; import { TaskCompletionBox } from './nodes/TaskCompletedBox'; import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView'; -import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from './nodes/formattedText/RichTextMenu'; import GenerativeFill from './nodes/generativeFill/GenerativeFill'; import { PresBox } from './nodes/trails'; import { AnchorMenu } from './pdf/AnchorMenu'; import { GPTPopup } from './pdf/GPTPopup/GPTPopup'; import { TopBar } from './topbar/TopBar'; -import { CollectionFreeFormView } from './collections/collectionFreeForm'; const _global = (window /* browser */ || global) /* node */ as any; const { LEFT_MENU_WIDTH, TOPBAR_HEIGHT } = require('./global/globalCssVariables.module.scss'); // prettier-ignore @@ -242,7 +242,7 @@ export class MainView extends ObservableReactComponent<{}> { window.addEventListener('paste', KeyManager.Instance.paste as any); document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on - const id = FormattedTextBox.GetDocFromUrl(e.detail); + const id = GetDocFromUrl(e.detail); DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index ea73a53a9..06cae6d04 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -4,16 +4,16 @@ import * as React from 'react'; import { Doc, Opt } from '../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocData } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; +import { FollowLinkScript } from '../../fields/ScriptField'; import { NumCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { unimplementedFunction, Utils } from '../../Utils'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { DragManager } from '../util/DragManager'; -import { FollowLinkScript } from '../util/LinkFollower'; import { undoable, undoBatch, UndoManager } from '../util/UndoManager'; import './MarqueeAnnotator.scss'; import { DocumentView } from './nodes/DocumentView'; -import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ObservableReactComponent } from './ObservableReactComponent'; import { AnchorMenu } from './pdf/AnchorMenu'; @@ -197,7 +197,7 @@ export class MarqueeAnnotator extends ObservableReactComponent { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index fddb40624..034ade50b 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -3,7 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { lightOrDark, returnFalse } from '../../ClientUtils'; import { Doc, Opt } from '../../fields/Doc'; -import { DocUtils, Docs, DocumentOptions } from '../documents/Documents'; +import { Docs, DocumentOptions } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { ImageUtils } from '../util/Import & Export/ImageUtils'; import { Transform } from '../util/Transform'; import { UndoManager, undoBatch } from '../util/UndoManager'; diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index d83fea2a5..cbbf48c75 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -20,8 +20,8 @@ import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; +import { DocUtils } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocUtils } from '../documents/Documents'; import { IsFollowLinkScript } from '../util/LinkFollower'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; @@ -30,7 +30,8 @@ import { undoBatch, undoable } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; import './PropertiesButtons.scss'; import { Colors } from './global/globalEnums'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @observer diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index e079b5cde..a806995f2 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -12,7 +12,7 @@ import { SettingsManager } from '../util/SettingsManager'; import './PropertiesDocBacklinksSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { LinkMenu } from './linking/LinkMenu'; -import { OpenWhere } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 2d04f2fe3..722b1f90a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -11,7 +11,8 @@ import { DocFocusOrOpen } from '../util/DocumentManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PropertiesDocContextSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; +import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { DocView?: DocumentView; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index f4ded8367..ff5dcd1b8 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -14,7 +14,7 @@ import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Field, FieldType, FieldResult, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; +import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; @@ -41,9 +41,10 @@ import { PropertiesDocContextSelector } from './PropertiesDocContextSelector'; import { PropertiesSection } from './PropertiesSection'; import './PropertiesView.scss'; import { DefaultStyleProvider } from './StyleProvider'; -import { DocumentView, OpenWhere } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; +import { OpenWhere } from './nodes/OpenWhere'; import { PresBox, PresEffect, PresEffectDirection } from './nodes/trails'; const _global = (window /* browser */ || global) /* node */ as any; diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 6195dcde8..3648e613d 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -12,7 +12,8 @@ import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; import { DocCast, NumCast, StrCast } from '../../fields/Types'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocUtils, Docs } from '../documents/Documents'; +import { Docs } from '../documents/Documents'; +import { DocUtils } from '../documents/DocUtils'; import { LinkManager } from '../util/LinkManager'; import { SearchUtil } from '../util/SearchUtil'; import { Transform } from '../util/Transform'; @@ -83,7 +84,7 @@ export class SidebarAnnos extends ObservableReactComponent void) => { this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag'); - const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) }; + const config = dragDocs.length === 1 ? DashboardView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => DashboardView.makeDocumentConfig(doc)) }; const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config); this.tabDragStart(dragSource, finishDrag); dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }); @@ -139,7 +125,7 @@ export class CollectionDockingView extends CollectionSubView() { public static ReplaceTab(document: Doc, mods: OpenWhereMod, stack: any, panelName: string, addToSplit?: boolean, keyValue?: boolean): boolean { const instance = CollectionDockingView.Instance; if (!instance) return false; - const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue); + const newConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue); if (!panelName && stack) { const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config); const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout); @@ -180,7 +166,7 @@ export class CollectionDockingView extends CollectionSubView() { const instance = CollectionDockingView.Instance; const glayRoot = instance._goldenLayout.root; if (!instance) return false; - const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue); + const docContentConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue); CollectionDockingView.Instance._flush = CollectionDockingView.Instance._flush ?? UndoManager.StartBatch('Add Split'); setTimeout(CollectionDockingView.Instance.endUndoBatch, 100); @@ -461,7 +447,7 @@ export class CollectionDockingView extends CollectionSubView() { if (content) { const _width = DivWidth(content); const _height = DivHeight(content); - return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', iconFile => { + return UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', iconFile => { const proto = this.dataDoc; // Cast(img.proto, Doc, null)!; proto.thumb_nativeWidth = _width; proto.thumb_nativeHeight = _height; diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 8803f6f79..9a6f1e2eb 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -166,7 +166,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent { const key = this._props.pivotField; doc[key] = this.getValue(this._props.heading); - FormattedTextBox.SetSelectOnLoad(doc); + Doc.SetSelectOnLoad(doc); return this._props.addDocument?.(doc); }, this._props.addDocument, @@ -195,7 +197,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); + const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); if (created) { if (this._props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this._props.Document); diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 6ccbd6208..e02570d3e 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -8,11 +8,11 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { ScriptField } from '../../../fields/ScriptField'; import { NumCast, StrCast, toList } from '../../../fields/Types'; import { emptyFunction } from '../../../Utils'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { dropActionType } from '../../util/DropActionTypes'; import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { OpenWhere } from '../nodes/DocumentView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import './CollectionPileView.scss'; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 7adf44a5c..50a66aa41 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -13,7 +13,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; -import { ComputedField, ScriptField } from '../../../fields/ScriptField'; +import { ComputedField, FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; @@ -21,21 +21,23 @@ import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; -import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; +import { IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; -import { CollectionSubView } from './CollectionSubView'; import { LightboxView } from '../LightboxView'; import { AudioWaveform } from '../nodes/audio/AudioWaveform'; -import { DocumentView, OpenWhere } from '../nodes/DocumentView'; -import { FocusFuncType, FocusViewOptions, StyleProviderFuncType } from '../nodes/FieldView'; +import { DocumentView } from '../nodes/DocumentView'; +import { FocusFuncType, StyleProviderFuncType } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LabelBox } from '../nodes/LabelBox'; +import { OpenWhere } from '../nodes/OpenWhere'; import { VideoBox } from '../nodes/VideoBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; import './CollectionStackedTimeline.scss'; +import { CollectionSubView } from './CollectionSubView'; export type CollectionStackedTimelineProps = { Play: () => void; diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index ad9960989..8ae0f2832 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -15,8 +15,9 @@ import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { SettingsManager } from '../../util/SettingsManager'; @@ -28,8 +29,8 @@ import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView } from '../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; +import { FieldViewProps } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { StyleProp } from '../StyleProvider'; import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; @@ -296,7 +297,7 @@ export class CollectionStackingView extends CollectionSubView (NumCast(doc.heading) > prevHeading ? NumCast(doc.heading) : prevHeading), 0); const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3; newDoc.heading = heading; - FormattedTextBox.SetSelectOnLoad(newDoc); + Doc.SetSelectOnLoad(newDoc); FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' '; return this._props.addDocument?.(newDoc) || false; }; @@ -240,7 +242,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< const height = this._ele ? DivHeight(this._ele) : 0; DocUtils.addDocumentCreatorMenuItems( doc => { - FormattedTextBox.SetSelectOnLoad(doc); + Doc.SetSelectOnLoad(doc); return this._props.addDocument?.(doc); }, this._props.addDocument, @@ -255,7 +257,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< docItems.push({ description: ':' + fieldKey, event: () => { - const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); + const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document)); if (created) { if (this._props.Document.isTemplateDoc) { Doc.MakeMetadataFieldTemplate(created, this._props.Document); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index f3dedaedf..7c08aedb1 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import * as rp from 'request-promise'; import { ClientUtils, returnFalse } from '../../../ClientUtils'; import CursorField from '../../../fields/CursorField'; -import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc'; import { AclPrivate } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; @@ -15,8 +15,9 @@ import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; +import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents'; +import { Docs, DocumentOptions } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; @@ -26,7 +27,6 @@ import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldViewProps } from '../nodes/FieldView'; import { LoadingBox } from '../nodes/LoadingBox'; -import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; export interface CollectionViewProps extends React.PropsWithChildren { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) @@ -317,10 +317,10 @@ export function CollectionSubView() { const addDocument = (doc: Doc | Doc[]) => this.addDocument(doc); if (html) { - if (FormattedTextBox.IsFragment(html)) { - const href = FormattedTextBox.GetHref(html); + if (RTFIsFragment(html)) { + const href = GetHrefFromHTML(html); if (href) { - const docId = FormattedTextBox.GetDocFromUrl(href); + const docId = GetDocFromUrl(href); if (docId) { // prosemirror text containing link to dash document DocServer.GetRefField(docId).then(f => { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 365b5acfd..d015e73ad 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -12,7 +12,8 @@ import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, Utils } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; @@ -189,7 +190,7 @@ export class CollectionTreeView extends CollectionSubView 0 && prev) { - FormattedTextBox.SetSelectOnLoad(prev); + Doc.SetSelectOnLoad(prev); DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false); } return true; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index d6cbe0dab..7cadd072b 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -7,14 +7,15 @@ import { Doc, DocListCast } from '../../../fields/Doc'; import { ObjectField } from '../../../fields/ObjectField'; import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { CollectionViewType } from '../../documents/DocumentTypes'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; -import { OpenWhere } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { CollectionCalendarView } from './CollectionCalendarView'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; @@ -234,3 +235,19 @@ export class CollectionView extends ViewBoxAnnotatableComponent { const fieldKey = Doc.LayoutFieldKey(newParent); if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) { remove(child); - FormattedTextBox.SetSelectOnLoad(child); + Doc.SetSelectOnLoad(child); TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined; Doc.AddDocToList(newParent, fieldKey, child, addAfter, false); newParent.treeView_Open = true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 653a01a04..f55d5a23f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -9,7 +9,7 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, Opt } from '../../../../fields/Doc'; +import { ActiveInkWidth, Doc, DocListCast, Field, FieldType, Opt, SetActiveInkColor, SetActiveInkWidth } from '../../../../fields/Doc'; import { DocData, Height, Width } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField'; @@ -23,8 +23,9 @@ import { TraceMobx } from '../../../../fields/util'; import { Gestures, PointData } from '../../../../pen-gestures/GestureTypes'; import { GestureUtils } from '../../../../pen-gestures/GestureUtils'; import { aggregateBounds, emptyFunction, intersectRect, Utils } from '../../../../Utils'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; @@ -39,13 +40,15 @@ import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; -import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { InkingStroke } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp'; -import { DocumentView, OpenWhere } from '../../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; +import { DocumentView } from '../../nodes/DocumentView'; +import { FieldViewProps } from '../../nodes/FieldView'; +import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; import { StyleProp } from '../../StyleProvider'; import { CollectionSubView } from '../CollectionSubView'; @@ -285,7 +288,7 @@ export class CollectionFreeFormView extends CollectionSubView this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout); isAnyChildContentActive = () => this._props.isAnyChildContentActive(); addLiveTextBox = (newDoc: Doc) => { - FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed this.addDocument(newDoc); }; selectDocuments = (docs: Doc[]) => { @@ -1052,7 +1055,7 @@ export class CollectionFreeFormView extends CollectionSubView { const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 }); - FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed + Doc.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed Doc.AddDocToList(this.Document, this._props.fieldKey, text); this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY)))); }) diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index ee79812a1..406a7d626 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -13,7 +13,8 @@ import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { DocUtils, Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; @@ -26,7 +27,8 @@ import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; -import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView'; +import { FieldViewProps } from '../../nodes/FieldView'; +import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 61afe08cf..32b48e4d1 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -5,8 +5,8 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import { CgClose, CgLock, CgLockUnlock } from 'react-icons/cg'; import { FaExternalLinkAlt } from 'react-icons/fa'; -import { emptyFunction } from '../../../../Utils'; import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; import { Doc } from '../../../../fields/Doc'; import { BoolCast } from '../../../../fields/Types'; import { DragManager } from '../../../util/DragManager'; @@ -15,8 +15,8 @@ import { Transform } from '../../../util/Transform'; import { undoable } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; -import { OpenWhere } from '../../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; +import { OpenWhere } from '../../nodes/OpenWhere'; import { CollectionSchemaView } from './CollectionSchemaView'; import './CollectionSchemaView.scss'; import { SchemaTableCell } from './SchemaTableCell'; diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 231bac541..d17d4ff7c 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -1,7 +1,7 @@ import { Colors } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { aggregateBounds } from '../../../Utils'; -import { Doc, Opt } from '../../../fields/Doc'; +import { ActiveFillColor, ActiveInkColor, ActiveInkHideTextLabels, ActiveInkWidth, ActiveIsInkMask, Doc, Opt, SetActiveFillColor, SetActiveInkColor, SetActiveInkHideTextLabels, SetActiveInkWidth, SetActiveIsInkMask } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; @@ -13,7 +13,7 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { UndoManager, undoable } from '../../util/UndoManager'; import { GestureOverlay } from '../GestureOverlay'; -import { ActiveFillColor, ActiveInkColor, ActiveInkHideTextLabels, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveFillColor, SetActiveInkColor, SetActiveInkHideTextLabels, SetActiveInkWidth, SetActiveIsInkMask } from '../InkingStroke'; +import { InkingStroke } from '../InkingStroke'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocumentView } from '../nodes/DocumentView'; @@ -111,7 +111,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { if (NumCast(selected?.Document.z) >= 1) return true; return false; } - selected ? selected.CollectionFreeFormDocumentView?.float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); + selected ? CollectionFreeFormDocumentView.from(selected)?.float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); return undefined; }); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 278d30d6a..df3accb0d 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -20,8 +20,9 @@ import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { undoBatch } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { LinkInfo } from '../nodes/LinkDocPreview'; +import { OpenWhere } from '../nodes/OpenWhere'; import './LinkMenuItem.scss'; interface LinkMenuItemProps { diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index bce2b296f..72d81e9eb 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -5,7 +5,7 @@ import { InkTool } from '../../../../fields/InkField'; import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { OpenWhereMod } from '../../nodes/DocumentView'; +import { OpenWhereMod } from '../../nodes/OpenWhere'; import { NewLightboxView } from '../NewLightboxView'; import './ButtonMenu.scss'; import { IButtonMenu } from './utils'; diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index 6616abad1..dc6bf3e9c 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -6,10 +6,9 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { LinkManager } from '../../util/LinkManager'; import { SelectionManager } from '../../util/SelectionManager'; @@ -20,7 +19,8 @@ import { LightboxView } from '../LightboxView'; import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; import { TabDocView } from '../collections/TabDocView'; -import { DocumentView, OpenWhere } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { ExploreView } from './ExploreView'; import { IBounds, emptyBounds } from './ExploreView/utils'; import { NewLightboxHeader } from './Header'; @@ -108,7 +108,7 @@ export class NewLightboxView extends React.Component { Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); } else { - const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); + const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); // TabDocView.PinDoc(doc, { hidePresBox: true }); @@ -150,7 +150,7 @@ export class NewLightboxView extends React.Component { const target = (NewLightboxView._docTarget = this._future?.pop()); const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (targetDocView && target) { - const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); + const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target }); diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 1f618135f..4697491e0 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -13,7 +13,9 @@ import { ComputedField } from '../../../fields/ScriptField'; import { Cast, DateCast, NumCast } from '../../../fields/Types'; import { AudioField, nullAudio } from '../../../fields/URLField'; import { formatTime } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; @@ -24,8 +26,8 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; import './AudioBox.scss'; -import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; +import { OpenWhere } from './OpenWhere'; /** * AudioBox @@ -761,3 +763,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.AUDIO, { + layout: { view: AudioBox, dataField: 'data' }, + options: { acl: '', _height: 100, _layout_fitWidth: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, systemIcon: 'BsFillVolumeUpFill' }, +}); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 9c4d748bd..691d07e31 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -11,14 +11,16 @@ import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { DocumentManager } from '../../util/DocumentManager'; +import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; import { DocComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import './CollectionFreeFormDocumentView.scss'; -import { DocumentView, DocumentViewProps, OpenWhere } from './DocumentView'; +import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldViewProps } from './FieldView'; +import { OpenWhere } from './OpenWhere'; /// Ugh, typescript has no run-time way of iterating through the keys of an interface. so we need /// manaully keep this list of keys in synch wih the fields of the freeFormProps interface @@ -48,7 +50,7 @@ export class CollectionFreeFormDocumentView extends DocComponent() ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.COMPARISON, { + data: '', + layout: { view: ComparisonBox, dataField: 'data' }, + options: { + acl: '', + backgroundColor: 'gray', + dropAction: dropActionType.move, + waitForDoubleClickToClick: 'always', + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + systemIcon: 'BsLayoutSplit', + }, +}); diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 113a857c3..ecfdcc229 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -13,22 +13,26 @@ import { List } from '../../../../fields/List'; import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; import { TraceMobx } from '../../../../fields/util'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { DocumentType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; -import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; +import { ContextMenu } from '../../ContextMenu'; +import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { MarqueeAnnotator } from '../../MarqueeAnnotator'; +import { PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; -import { ContextMenu } from '../../ContextMenu'; export enum DataVizView { TABLE = 'table', @@ -522,3 +526,18 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.DATAVIZ, { + layout: { view: DataVizBox, dataField: 'data' }, + options: { + acl: '', + dataViz_title: '', + dataViz_line: '', + dataViz_pie: '', + dataViz_histogram: '', + dataViz: 'table', + _layout_fitWidth: true, + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + }, +}); diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 9b4e36509..977899589 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -10,13 +10,13 @@ import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; -import { PinProps } from '../DocComponent'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { PinProps } from '../PinFuncs'; import './DocumentLinksButton.scss'; import { DocumentView } from './DocumentView'; import { LinkDescriptionPopup } from './LinkDescriptionPopup'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a603de10b..e8d1e582e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -16,20 +16,20 @@ import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { PrefetchProxy } from '../../../fields/Proxy'; import { listSpec } from '../../../fields/Schema'; -import { ScriptField } from '../../../fields/ScriptField'; +import { FollowLinkScript, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; +import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { MakeTemplate, makeUserTemplateButton } from '../../util/DropConverter'; -import { FollowLinkScript } from '../../util/LinkFollower'; import { LinkManager, UPDATE_SERVER_CACHE } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SearchUtil } from '../../util/SearchUtil'; @@ -48,38 +48,14 @@ import { AudioAnnoState, StyleProp } from '../StyleProvider'; import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView'; import { DocumentLinksButton } from './DocumentLinksButton'; import './DocumentView.scss'; -import { FieldViewProps, FieldViewSharedProps, FocusViewOptions } from './FieldView'; +import { FieldViewProps, FieldViewSharedProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { KeyValueBox } from './KeyValueBox'; import { LinkAnchorBox } from './LinkAnchorBox'; +import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { PresEffect, PresEffectDirection } from './trails'; -export enum OpenWhereMod { - none = '', - left = 'left', - right = 'right', - top = 'top', - bottom = 'bottom', - keyvalue = 'keyValue', -} -export enum OpenWhere { - lightbox = 'lightbox', - add = 'add', - addLeft = 'add:left', - addRight = 'add:right', - addBottom = 'add:bottom', - close = 'close', - toggle = 'toggle', - toggleRight = 'toggle:right', - replace = 'replace', - replaceRight = 'replace:right', - replaceLeft = 'replace:left', - inParent = 'inParent', - inParentFromScreen = 'inParentFromScreen', - overlay = 'overlay', - addRightKeyvalue = 'add:right:keyValue', -} - export interface DocumentViewProps extends FieldViewSharedProps { hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected hideResizeHandles?: boolean; // whether to suppress resized handles on doc decorations when this document is selected @@ -292,7 +268,8 @@ export class DocumentViewInternal extends DocComponent docView.props.dragEnding?.(); + dragData.dragStarting = () => docView.props.dragStarting?.(); dragData.canEmbed = !!(this.Document.dragAction ?? this._props.dragAction); (this._props.dragConfig ?? this._componentView?.dragConfig)?.(dragData); DragManager.StartDocumentDrag( diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx index 9c216cba4..32d08fbe7 100644 --- a/src/client/views/nodes/EquationBox.tsx +++ b/src/client/views/nodes/EquationBox.tsx @@ -3,10 +3,12 @@ import { action, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DivHeight, DivWidth } from '../../../ClientUtils'; -import { Id } from '../../../fields/FieldSymbols'; +import { Doc } from '../../../fields/Doc'; import { NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { LightboxView } from '../LightboxView'; @@ -19,7 +21,6 @@ export class EquationBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); } - public static SelectOnLoad: string = ''; _ref: React.RefObject = React.createRef(); constructor(props: FieldViewProps) { @@ -29,12 +30,12 @@ export class EquationBox extends ViewBoxBaseComponent() { componentDidMount() { this._props.setContentViewBox?.(this); - if (EquationBox.SelectOnLoad === this.Document[Id] && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) { + if (Doc.SelectOnLoad === this.Document && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) { this._props.select(false); this._ref.current!.mathField.focus(); this.dataDoc.text === 'x' && this._ref.current!.mathField.select(); - EquationBox.SelectOnLoad = ''; + Doc.SetSelectOnLoad(undefined); } reaction( () => StrCast(this.dataDoc.text), @@ -69,7 +70,7 @@ export class EquationBox extends ViewBoxBaseComponent() { x: NumCast(this.layoutDoc.x), y: NumCast(this.layoutDoc.y) + _height + 10, }); - EquationBox.SelectOnLoad = nextEq[Id]; + Doc.SetSelectOnLoad(nextEq); this._props.addDocument?.(nextEq); e.stopPropagation(); } @@ -130,3 +131,8 @@ export class EquationBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.EQUATION, { + layout: { view: EquationBox, dataField: 'text' }, + options: { acl: '', fontSize: '14px', _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, layout_hideDecorationTitle: true, systemIcon: 'BsCalculatorFill' }, // systemIcon: 'BsSuperscript' + BsSubscript +}); diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index ab0850790..c6c77d8d2 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -10,29 +10,12 @@ import { ScriptField } from '../../../fields/ScriptField'; import { WebField } from '../../../fields/URLField'; import { dropActionType } from '../../util/DropActionTypes'; import { Transform } from '../../util/Transform'; -import { PinProps, ViewBoxInterface } from '../DocComponent'; -import { DocumentView, OpenWhere } from './DocumentView'; +import { ViewBoxInterface } from '../DocComponent'; +import { PinProps } from '../PinFuncs'; +import { DocumentView } from './DocumentView'; +import { FocusViewOptions } from './FocusViewOptions'; +import { OpenWhere } from './OpenWhere'; -export interface FocusViewOptions { - willPan?: boolean; // determines whether to pan to target document - willZoomCentered?: boolean; // determines whether to zoom in on target document. if zoomScale is 0, this just centers the document - zoomScale?: number; // percent of containing frame to zoom into document - zoomTime?: number; - didMove?: boolean; // whether a document was changed during the showDocument process - docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy - instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) - preview?: boolean; // whether changes should be previewed by the componentView or written to the document - effect?: Doc; // animation effect for focus // bcz: needs to be changed to something more generic than a Doc - noSelect?: boolean; // whether target should be selected after focusing - playAudio?: boolean; // whether to play audio annotation on focus - playMedia?: boolean; // whether to play start target videos - openLocation?: OpenWhere; // where to open a missing document - zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections - toggleTarget?: boolean; // whether to toggle target on and off - easeFunc?: 'linear' | 'ease'; // transition method for scrolling - pointFocus?: { X: number; Y: number }; // clientX and clientY coordinates to focus on instead of a document target (used by explore mode) - contextPath?: Doc[]; // path of inner documents that will also be focused -} export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt; // eslint-disable-next-line no-use-before-define export type StyleProviderFuncType = (doc: Opt, props: Opt, property: string) => any; diff --git a/src/client/views/nodes/FocusViewOptions.ts b/src/client/views/nodes/FocusViewOptions.ts new file mode 100644 index 000000000..bb0d2b03c --- /dev/null +++ b/src/client/views/nodes/FocusViewOptions.ts @@ -0,0 +1,24 @@ +import { Doc } from '../../../fields/Doc'; +import { Transform } from '../../util/Transform'; +import { OpenWhere } from './OpenWhere'; + +export interface FocusViewOptions { + willPan?: boolean; // determines whether to pan to target document + willZoomCentered?: boolean; // determines whether to zoom in on target document. if zoomScale is 0, this just centers the document + zoomScale?: number; // percent of containing frame to zoom into document + zoomTime?: number; + didMove?: boolean; // whether a document was changed during the showDocument process + docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom. when this happens the docTransform must accumulate all the transforms of each level of the hierarchy + instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) + preview?: boolean; // whether changes should be previewed by the componentView or written to the document + effect?: Doc; // animation effect for focus // bcz: needs to be changed to something more generic than a Doc + noSelect?: boolean; // whether target should be selected after focusing + playAudio?: boolean; // whether to play audio annotation on focus + playMedia?: boolean; // whether to play start target videos + openLocation?: OpenWhere; // where to open a missing document + zoomTextSelections?: boolean; // whether to display a zoomed overlay of anchor text selections + toggleTarget?: boolean; // whether to toggle target on and off + easeFunc?: 'linear' | 'ease'; // transition method for scrolling + pointFocus?: { X: number; Y: number }; // clientX and clientY coordinates to focus on instead of a document target (used by explore mode) + contextPath?: Doc[]; // path of inner documents that will also be focused +} diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 1b2aefbe2..d83690cdd 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -9,6 +9,7 @@ import { ClientUtils, returnTrue, setupMoveUpEvents } from '../../../../ClientUt import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; import { emptyFunction } from '../../../../Utils'; +import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -18,8 +19,8 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { SelectedDocView } from '../../selectedDoc'; import { StyleProp } from '../../StyleProvider'; -import { OpenWhere } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; +import { OpenWhere } from '../OpenWhere'; import './FontIconBox.scss'; import TrailsIcon from './TrailsIcon'; @@ -51,15 +52,6 @@ export class FontIconBox extends ViewBoxBaseComponent() { super(props); makeObservable(this); } - // - // This controls whether fontIconButtons will display labels under their icons or not - // - public static get ShowIconLabels() { - return BoolCast(Doc.UserDoc()._showLabel); - } - public static set ShowIconLabels(show: boolean) { - Doc.UserDoc()._showLabel = show; - } @observable noTooltip = false; @@ -397,3 +389,8 @@ export class FontIconBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.FONTICON, { + layout: { view: FontIconBox, dataField: 'icon' }, + options: { acl: '', defaultDoubleClick: 'ignore', waitForDoubleClickToClick: 'never', layout_hideContextMenu: true, layout_hideLinkButton: true, _width: 40, _height: 40 }, +}); diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index 31faa7ac3..f32d39aaf 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -7,12 +7,14 @@ import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocUtils, Docs } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; -import { PinProps, PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { FieldView, FieldViewProps } from './FieldView'; @observer @@ -138,3 +140,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.FUNCPLOT, { + layout: { view: FunctionPlotBox, dataField: 'data' }, + options: { acl: '', _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true }, +}); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 8bd5ab03d..d317f46bb 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -15,8 +15,9 @@ import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; @@ -30,9 +31,10 @@ import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; -import { OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; +import { OpenWhere } from './OpenWhere'; export class ImageEditorData { // eslint-disable-next-line no-use-before-define @@ -546,3 +548,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl input.click(); }; } + +Docs.Prototypes.TemplateMap.set(DocumentType.IMG, { + layout: { view: ImageBox, dataField: 'data' }, + options: { acl: '', freeform: '', systemIcon: 'BsFileEarmarkImageFill' }, +}); diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b8296ce51..46bb16e50 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -3,12 +3,13 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { returnAlways, returnTrue } from '../../../ClientUtils'; -import { Doc, Field, FieldType, FieldResult } from '../../../fields/Doc'; +import { Doc, Field, FieldResult, FieldType } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { DocCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { SetupDrag } from '../../util/DragManager'; import { CompiledScript } from '../../util/Scripting'; @@ -17,11 +18,11 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentIconContainer } from './DocumentIcon'; -import { OpenWhere } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { ImageBox } from './ImageBox'; import './KeyValueBox.scss'; import { KeyValuePair } from './KeyValuePair'; +import { OpenWhere } from './OpenWhere'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; export type KVPScript = { @@ -339,3 +340,8 @@ export class KeyValueBox extends ObservableReactComponent { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.KVP, { + layout: { view: KeyValueBox, dataField: 'data' }, + options: { acl: '', _layout_fitWidth: true, _height: 150 }, +}); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index a9aa017a1..878b0e54c 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -14,10 +14,11 @@ import { ContextMenu } from '../ContextMenu'; import { EditableView } from '../EditableView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; -import { OpenWhere, returnEmptyDocViewList } from './DocumentView'; +import { returnEmptyDocViewList } from './DocumentView'; import { KeyValueBox } from './KeyValueBox'; import './KeyValueBox.scss'; import './KeyValuePair.scss'; +import { OpenWhere } from './OpenWhere'; // Represents one row in a key value plane diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 89270652c..4dec3506c 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -5,13 +5,14 @@ import { Doc, DocListCast, Field, FieldType } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; -import { PinProps, PinDocView } from '../PinFuncs'; +import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; @@ -203,3 +204,12 @@ export class LabelBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.LABEL, { + layout: { view: LabelBox, dataField: 'title' }, + options: { acl: '', _singleLine: true, _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, +}); +Docs.Prototypes.TemplateMap.set(DocumentType.BUTTON, { + layout: { view: LabelBox, dataField: 'title' }, + options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, +}); diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index f01905ee1..559b1fcae 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -7,9 +7,12 @@ import { DashColor, lightOrDark, returnFalse } from '../../../ClientUtils'; import { FieldResult } from '../../../fields/Doc'; import { DocCss, DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; +import { List } from '../../../fields/List'; import { DocCast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { SnappingManager } from '../../util/SnappingManager'; import { ViewBoxBaseComponent } from '../DocComponent'; @@ -263,3 +266,18 @@ export class LinkBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.LINK, { + layout: { view: LinkBox, dataField: 'link' }, + options: { + acl: '', + childDontRegisterViews: true, + layout_hideLinkAnchors: true, + _height: 1, + _width: 1, + link: '', + link_description: '', + color: 'lightBlue', // lightblue is default color for linking dot and link documents text comment area + _dropPropertiesToRemove: new List(['onClick']), + }, +}); diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index a9cfe6c0e..0936acc15 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -19,9 +19,10 @@ import { SearchUtil } from '../../util/SearchUtil'; import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, OpenWhere } from './DocumentView'; +import { DocumentView } from './DocumentView'; import { StyleProviderFuncType } from './FieldView'; import './LinkDocPreview.scss'; +import { OpenWhere } from './OpenWhere'; interface LinkDocPreviewProps { linkDoc?: Doc; diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 501831bca..aa89398f3 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -10,6 +10,8 @@ import { DocumentManager } from '../../util/DocumentManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -89,3 +91,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.LOADING, { + layout: { view: LoadingBox, dataField: '' }, + options: { acl: '', _layout_fitWidth: true, _layout_nativeDimEditable: true }, +}); diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 60dad314f..50831f8ea 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -18,20 +18,21 @@ import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; -import { PinProps, PinDocView } from '../../PinFuncs'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; -import { FormattedTextBox } from '../formattedText/FormattedTextBox'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { fastSpeedIcon, mediumSpeedIcon, slowSpeedIcon } from './AnimationSpeedIcons'; import { AnimationSpeed, AnimationStatus, AnimationUtility } from './AnimationUtility'; import { MapAnchorMenu } from './MapAnchorMenu'; @@ -355,7 +356,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; const docView = this.DocumentView?.(); @@ -1360,3 +1361,8 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.MAP, { + layout: { view: MapBox, dataField: 'data' }, + options: { acl: '', map: '', _height: 600, _width: 800, _layout_reflowHorizontal: true, _layout_reflowVertical: true, _layout_nativeDimEditable: true, systemIcon: 'BsFillPinMapFill' }, +}); diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx index 6ccbbbe1c..c69cd8e89 100644 --- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx +++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx @@ -32,7 +32,7 @@ // addNoteClick = (e: React.PointerEvent) => { // setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => { // const newDoc = Docs.Create.TextDocument('Note', { _layout_autoHeight: true }); -// FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed +// Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed // Doc.AddDocToList(this.props.place, 'data', newDoc); // this._stack?.scrollToBottom(); // e.stopPropagation(); diff --git a/src/client/views/nodes/MapBox/MapPushpinBox.tsx b/src/client/views/nodes/MapBox/MapPushpinBox.tsx index 8ebc90157..f3dc44755 100644 --- a/src/client/views/nodes/MapBox/MapPushpinBox.tsx +++ b/src/client/views/nodes/MapBox/MapPushpinBox.tsx @@ -2,6 +2,8 @@ import * as React from 'react'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; import { MapBoxContainer } from '../MapboxMapBox/MapboxContainer'; +import { Docs } from '../../../documents/Documents'; +import { DocumentType } from '../../../documents/DocumentTypes'; /** * Map Pushpin doc class @@ -28,3 +30,8 @@ export class MapPushpinBox extends ViewBoxBaseComponent() { return
; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PUSHPIN, { + layout: { view: MapPushpinBox, dataField: 'data' }, + options: { acl: '' }, +}); diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index d899fcb9a..3b4ffd4bd 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -10,23 +10,24 @@ import { Doc, DocListCast, Field, LinkedTo, Opt } from '../../../../fields/Doc'; import { DocCss, Highlight } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; +import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { LinkManager } from '../../../util/LinkManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; -import { PinProps, PinDocView } from '../../PinFuncs'; +import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { MapAnchorMenu } from '../MapBox/MapAnchorMenu'; import '../MapBox/MapBox.scss'; -import { FormattedTextBox } from '../formattedText/FormattedTextBox'; /** * MapBox architecture: @@ -237,7 +238,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent const targetCreator = (annotationOn: Doc | undefined) => { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow'); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; const docView = this.DocumentView?.(); diff --git a/src/client/views/nodes/OpenWhere.ts b/src/client/views/nodes/OpenWhere.ts new file mode 100644 index 000000000..e2a5f1f2a --- /dev/null +++ b/src/client/views/nodes/OpenWhere.ts @@ -0,0 +1,25 @@ +export enum OpenWhereMod { + none = '', + left = 'left', + right = 'right', + top = 'top', + bottom = 'bottom', + keyvalue = 'keyValue', +} +export enum OpenWhere { + lightbox = 'lightbox', + add = 'add', + addLeft = 'add:left', + addRight = 'add:right', + addBottom = 'add:bottom', + close = 'close', + toggle = 'toggle', + toggleRight = 'toggle:right', + replace = 'replace', + replaceRight = 'replace:right', + replaceLeft = 'replace:left', + inParent = 'inParent', + inParentFromScreen = 'inParentFromScreen', + overlay = 'overlay', + addRightKeyvalue = 'add:right:keyValue', +} diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fbf5e018c..5d1874aca 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -16,8 +16,9 @@ import { Cast, FieldValue, ImageCast, NumCast, StrCast, toList } from '../../../ import { ImageField, PdfField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; import { SelectionManager } from '../../util/SelectionManager'; @@ -31,9 +32,11 @@ import { Colors } from '../global/globalEnums'; import { PDFViewer } from '../pdf/PDFViewer'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; -import { DocumentView, OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { DocumentView } from './DocumentView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { ImageBox } from './ImageBox'; +import { OpenWhere } from './OpenWhere'; import './PDFBox.scss'; import { CreateImage } from './WebBoxRenderer'; @@ -665,3 +668,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent() implem return pdfView ?? this.renderTitleBox; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PDF, { + layout: { view: PDFBox, dataField: 'data' }, + options: { acl: '', _layout_curPage: 1, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, systemIcon: 'BsFileEarmarkPdfFill' }, +}); diff --git a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx index ae674d604..f88eb3bca 100644 --- a/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx +++ b/src/client/views/nodes/PhysicsBox/PhysicsSimulationBox.tsx @@ -1,3 +1,11 @@ +/* eslint-disable camelcase */ +/* eslint-disable jsx-a11y/control-has-associated-label */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/no-array-index-key */ +/* eslint-disable react/jsx-props-no-spreading */ +/* eslint-disable no-return-assign */ import ArrowLeftIcon from '@mui/icons-material/ArrowLeft'; import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import PauseIcon from '@mui/icons-material/Pause'; @@ -13,13 +21,15 @@ import { NumListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { BoolCast, NumCast, StrCast } from '../../../../fields/Types'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; -import { FieldView, FieldViewProps } from './../FieldView'; +import { FieldView, FieldViewProps } from '../FieldView'; import './PhysicsSimulationBox.scss'; import InputField from './PhysicsSimulationInputField'; import questions from './PhysicsSimulationQuestions.json'; import tutorials from './PhysicsSimulationTutorial.json'; import Wall from './PhysicsSimulationWall'; import Weight from './PhysicsSimulationWeight'; +import { Docs } from '../../../documents/Documents'; +import { DocumentType } from '../../../documents/DocumentTypes'; interface IWallProps { length: number; @@ -204,7 +214,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent) { super.componentDidUpdate(prevProps); - if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax != this._props.PanelHeight()) { + if (this.xMax !== this._props.PanelWidth() * 0.6 || this.yMax !== this._props.PanelHeight()) { this.xMax = this._props.PanelWidth() * 0.6; this.yMax = this._props.PanelHeight(); this.setupSimulation(); @@ -219,16 +229,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - const simulationType = this.simulationType; + const { simulationType } = this; const mode = this.simulationMode; this.dataDoc.simulation_paused = true; - if (simulationType != 'Circular Motion') { + if (simulationType !== 'Circular Motion') { this.dataDoc.mass1_velocityXstart = 0; this.dataDoc.mass1_velocityYstart = 0; this.dataDoc.mass1_velocityX = 0; this.dataDoc.mass1_velocityY = 0; } - if (mode == 'Freeform') { + if (mode === 'Freeform') { this.dataDoc.simulation_showForceMagnitudes = true; // prettier-ignore switch (simulationType) { @@ -247,9 +257,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { let theta = this.wedgeAngle; - let index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45'); + const index = this.selectedQuestion.variablesForQuestionSetup.indexOf('theta - max 45'); if (index >= 0) { theta = NumListCast(this.dataDoc.questionVariables)[index]; } @@ -467,26 +480,26 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { let error: boolean = false; - let epsilon: number = 0.01; + const epsilon: number = 0.01; if (this.selectedQuestion) { for (let i = 0; i < this.selectedQuestion.answerParts.length; i++) { - if (this.selectedQuestion.answerParts[i] == 'force of gravity') { + if (this.selectedQuestion.answerParts[i] === 'force of gravity') { if (Math.abs(NumCast(this.dataDoc.review_GravityMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of gravity') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of gravity') { if (Math.abs(NumCast(this.dataDoc.review_GravityAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'normal force') { + } else if (this.selectedQuestion.answerParts[i] === 'normal force') { if (Math.abs(NumCast(this.dataDoc.review_NormalMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of normal force') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of normal force') { if (Math.abs(NumCast(this.dataDoc.review_NormalAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'force of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'force of static friction') { if (Math.abs(NumCast(this.dataDoc.review_StaticMagnitude) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'angle of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'angle of static friction') { if (Math.abs(NumCast(this.dataDoc.review_StaticAngle) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'coefficient of static friction') { + } else if (this.selectedQuestion.answerParts[i] === 'coefficient of static friction') { if (Math.abs(NumCast(this.dataDoc.coefficientOfStaticFriction) - this.selectedSolutions[i]) > epsilon) { error = true; } - } else if (this.selectedQuestion.answerParts[i] == 'wedge angle') { + } else if (this.selectedQuestion.answerParts[i] === 'wedge angle') { if (Math.abs(this.wedgeAngle - this.selectedSolutions[i]) > epsilon) { error = true; } @@ -539,7 +552,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent (this.dataDoc.simulation_paused = true), 3000); } - if (this.selectedQuestion.goal == 'noMovement') { + if (this.selectedQuestion.goal === 'noMovement') { this.dataDoc.noMovement = !error; } }; @@ -571,12 +584,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent(this.getAnswersToQuestion(question, vars)); - //this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset); + // this.dataDoc.simulation_reset = (!this.dataDoc.simulation_reset); }; // Default setup for uniform circular motion simulation @@ -610,8 +623,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - let xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; - let yPos = this.yMin + 200; + const xPos = (this.xMax + this.xMin) / 2 - this.mass1Radius; + const yPos = this.yMin + 200; this.dataDoc.mass1_positionYstart = yPos; this.dataDoc.mass1_positionXstart = xPos; this.dataDoc.mass1_positionY = this.getDisplayYPos(yPos); this.dataDoc.mass1_positionX = xPos; - let tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4)); + const tensionMag = (this.mass1 * Math.abs(this.gravity)) / (2 * Math.sin(Math.PI / 4)); const tensionForce1: IForce = { description: 'Tension', magnitude: tensionMag, @@ -891,7 +904,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - {this.simulationType == 'Pulley' && ( + {this.simulationType === 'Pulley' && (
- {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane') && + {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane') && this.wallPositions?.map((element, index) => )}
@@ -927,17 +940,17 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( (this.dataDoc.simulation_paused = false)}> )} - {!this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {!this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( (this.dataDoc.simulation_paused = true)}> )} - {this.dataDoc.simulation_paused && this.simulationMode != 'Tutorial' && ( + {this.dataDoc.simulation_paused && this.simulationMode !== 'Tutorial' && ( this._simReset++)}> @@ -974,15 +987,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {this.simulationMode == 'Review' && this.simulationType != 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType !== 'Inclined Plane' && (
-

- <>{this.simulationType} review problems in progress! -

+

{this.simulationType} review problems in progress!


)} - {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
{!this.dataDoc.hintDialogueOpen && ( )} - (this.dataDoc.hintDialogueOpen = false)}> + (this.dataDoc.hintDialogueOpen = false)}> Hints {this.selectedQuestion.hints?.map((hint: any, index: number) => ( @@ -1030,12 +1041,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of gravity') && ( @@ -1045,13 +1056,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('normal force') && ( @@ -1061,12 +1072,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of normal force') && ( @@ -1076,13 +1087,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('force of static friction') && ( @@ -1092,12 +1103,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('angle of static friction') && ( @@ -1107,13 +1118,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} {this.selectedQuestion.answerParts.includes('coefficient of static friction') && ( @@ -1127,7 +1138,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.changeWedgeBasedOnNewAngle(val); this.updateReviewForcesBasedOnAngle(val); }} - radianEquivalent={true} + radianEquivalent showIcon={BoolCast(this.dataDoc.simulation_showIcon)} correctValue={NumListCast(this.dataDoc.answers)[this.selectedQuestion.answerParts.indexOf('wedge angle')]} /> @@ -1158,7 +1169,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationMode == 'Tutorial' && ( + {this.simulationMode === 'Tutorial' && (

Problem

@@ -1180,7 +1191,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent + disabled={this.dataDoc.tutorial_stepNumber === 0}>
@@ -1204,8 +1215,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
- {(this.simulationType == 'One Weight' || this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') &&

Resources

} - {this.simulationType == 'One Weight' && ( + {(this.simulationType === 'One Weight' || this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') &&

Resources

} + {this.simulationType === 'One Weight' && ( )} - {this.simulationType == 'Inclined Plane' && ( + {this.simulationType === 'Inclined Plane' && ( )} - {this.simulationType == 'Pendulum' && ( + {this.simulationType === 'Pendulum' && (
)} - {this.simulationMode == 'Review' && this.simulationType == 'Inclined Plane' && ( + {this.simulationMode === 'Review' && this.simulationType === 'Inclined Plane' && (
)} - {this.simulationMode == 'Freeform' && ( + {this.simulationMode === 'Freeform' && (
- {this.simulationType == 'One Weight' && ( + {this.simulationType === 'One Weight' && ( (this.dataDoc.elasticCollisions = !this.dataDoc.elasticCollisions)} />} label="Make collisions elastic" @@ -1334,7 +1345,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - {(this.simulationType == 'Inclined Plane' || this.simulationType == 'Pendulum') && ( + {(this.simulationType === 'Inclined Plane' || this.simulationType === 'Pendulum') && ( (this.dataDoc.simulation_showComponentForces = !this.dataDoc.simulation_showComponentForces)} />} label="Show component force vectors" @@ -1351,80 +1362,80 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - Speed} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit={'x'} upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth={'5em'} /> - {this.dataDoc.simulation_paused && this.simulationType != 'Circular Motion' && ( + Speed} lowerBound={1} dataDoc={this.dataDoc} prop="simulation_speed" step={1} unit="x" upperBound={10} value={NumCast(this.dataDoc.simulation_speed, 2)} labelWidth="5em" /> + {this.dataDoc.simulation_paused && this.simulationType !== 'Circular Motion' && ( Gravity} lowerBound={-30} dataDoc={this.dataDoc} prop="gravity" step={0.01} - unit={'m/s2'} + unit="m/s2" upperBound={0} value={NumCast(this.dataDoc.simulation_gravity, -9.81)} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Pulley' && ( Mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass1" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass1 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && ( Red mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass1" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass1 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Pulley' && ( Blue mass} lowerBound={1} dataDoc={this.dataDoc} prop="mass2" step={0.1} - unit={'kg'} + unit="kg" upperBound={5} value={this.mass2 ?? 1} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.dataDoc.simulation_paused && this.simulationType == 'Circular Motion' && ( + {this.dataDoc.simulation_paused && this.simulationType === 'Circular Motion' && ( Rod length} lowerBound={100} dataDoc={this.dataDoc} prop="circularMotionRadius" step={5} - unit={'m'} + unit="m" upperBound={250} value={this.circularMotionRadius} effect={(val: number) => this.setupSimulation()} - labelWidth={'5em'} + labelWidth="5em" /> )} - {this.simulationType == 'Spring' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Spring' && this.dataDoc.simulation_paused && (
Spring stiffness} @@ -1432,13 +1443,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} radianEquivalent={false} - mode={'Freeform'} - labelWidth={'7em'} + mode="Freeform" + labelWidth="7em" /> Rest length} @@ -1452,7 +1463,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} radianEquivalent={false} mode="Freeform" - labelWidth={'7em'} + labelWidth="7em" /> Starting displacement} @@ -1470,11 +1481,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Inclined Plane' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Inclined Plane' && this.dataDoc.simulation_paused && (
θ} @@ -1482,16 +1493,16 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.changeWedgeBasedOnNewAngle(val); this._simReset++; })} - radianEquivalent={true} - mode={'Freeform'} - labelWidth={'2em'} + radianEquivalent + mode="Freeform" + labelWidth="2em" /> { @@ -1513,8 +1524,8 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent this._simReset++)} - mode={'Freeform'} - labelWidth={'2em'} + mode="Freeform" + labelWidth="2em" />
)} - {this.simulationType == 'Inclined Plane' && !this.dataDoc.simulation_paused && ( + {this.simulationType === 'Inclined Plane' && !this.dataDoc.simulation_paused && ( <> θ: {Math.round(this.wedgeAngle * 100) / 100}° ≈ {Math.round(((this.wedgeAngle * Math.PI) / 180) * 100) / 100} rad @@ -1546,12 +1557,12 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} - {this.simulationType == 'Pendulum' && !this.dataDoc.simulation_paused && ( + {this.simulationType === 'Pendulum' && !this.dataDoc.simulation_paused && ( θ: {Math.round(this.pendulumAngle * 100) / 100}° ≈ {Math.round(((this.pendulumAngle * Math.PI) / 180) * 100) / 100} rad )} - {this.simulationType == 'Pendulum' && this.dataDoc.simulation_paused && ( + {this.simulationType === 'Pendulum' && this.dataDoc.simulation_paused && (
Angle} @@ -1559,13 +1570,13 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { this.dataDoc.pendulum_angleStart = value; this.dataDoc.pendulum_lengthStart = this.dataDoc.pendulum_length; - if (this.simulationType == 'Pendulum') { + if (this.simulationType === 'Pendulum') { const mag = this.mass1 * Math.abs(this.gravity) * Math.cos((value * Math.PI) / 180); const forceOfTension: IForce = { @@ -1598,7 +1609,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent @@ -1612,7 +1623,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent { - if (this.simulationType == 'Pendulum') { + if (this.simulationType === 'Pendulum') { this.dataDoc.pendulum_angleStart = this.pendulumAngle; this.dataDoc.pendulum_lengthStart = value; this._simReset++; @@ -1627,11 +1638,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )}
- {this.simulationMode == 'Freeform' && ( + {this.simulationMode === 'Freeform' && ( - + @@ -1646,36 +1657,34 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent Position - {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( - + {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && ( + )}{' '} - {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && ( )}{' '} - {(!this.dataDoc.simulation_paused || this.simulationType == 'Inclined Plane' || this.simulationType == 'Circular Motion' || this.simulationType == 'Pulley') && ( + {(!this.dataDoc.simulation_paused || this.simulationType === 'Inclined Plane' || this.simulationType === 'Circular Motion' || this.simulationType === 'Pulley') && ( )}{' '} - {this.dataDoc.simulation_paused && this.simulationType != 'Inclined Plane' && this.simulationType != 'Circular Motion' && this.simulationType != 'Pulley' && ( + {this.dataDoc.simulation_paused && this.simulationType !== 'Inclined Plane' && this.simulationType !== 'Circular Motion' && this.simulationType !== 'Pulley' && ( @@ -1758,10 +1767,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent Velocity - {(!this.dataDoc.simulation_paused || (this.simulationType != 'One Weight' && this.simulationType != 'Circular Motion')) && ( + {(!this.dataDoc.simulation_paused || (this.simulationType !== 'One Weight' && this.simulationType !== 'Circular Motion')) && ( )}{' '} - {this.dataDoc.simulation_paused && (this.simulationType == 'One Weight' || this.simulationType == 'Circular Motion') && ( + {this.dataDoc.simulation_paused && (this.simulationType === 'One Weight' || this.simulationType === 'Circular Motion') && ( )}{' '} - {(!this.dataDoc.simulation_paused || this.simulationType != 'One Weight') && ( - - )}{' '} - {this.dataDoc.simulation_paused && this.simulationType == 'One Weight' && ( + {(!this.dataDoc.simulation_paused || this.simulationType !== 'One Weight') && }{' '} + {this.dataDoc.simulation_paused && this.simulationType === 'One Weight' && ( @@ -1822,14 +1827,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponentAcceleration @@ -1842,7 +1843,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
{this.simulationType == 'Pulley' ? 'Red Weight' : ''}{this.simulationType === 'Pulley' ? 'Red Weight' : ''} X Y
- <>{this.dataDoc.mass1_positionX} m - {this.dataDoc.mass1_positionX + ''} m { this.dataDoc.mass1_xChange = value; - if (this.simulationType == 'Suspension') { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = value + this.radius - x1rod; - let deltaX2 = x2rod - (value + this.radius); - let deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius; + if (this.simulationType === 'Suspension') { + const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + const deltaX1 = value + this.radius - x1rod; + const deltaX2 = x2rod - (value + this.radius); + const deltaY = this.getYPosFromDisplay(NumCast(this.dataDoc.mass1_positionY)) + this.radius; let dir1T = Math.PI - Math.atan(deltaY / deltaX1); let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); - let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); dir1T = (dir1T * 180) / Math.PI; dir2T = (dir2T * 180) / Math.PI; const tensionForce1: IForce = { @@ -1692,15 +1701,15 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent {`${NumCast(this.dataDoc.mass1_positionY)} m`} { this.dataDoc.mass1_yChange = value; - if (this.simulationType == 'Suspension') { - let x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; - let x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; - let deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod; - let deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius); - let deltaY = this.getYPosFromDisplay(value) + this.radius; + if (this.simulationType === 'Suspension') { + const x1rod = (this.xMax + this.xMin) / 2 - this.radius - this.yMin - 200; + const x2rod = (this.xMax + this.xMin) / 2 + this.yMin + 200 + this.radius; + const deltaX1 = NumCast(this.dataDoc.mass1_positionX) + this.radius - x1rod; + const deltaX2 = x2rod - (NumCast(this.dataDoc.mass1_positionX) + this.radius); + const deltaY = this.getYPosFromDisplay(value) + this.radius; let dir1T = Math.PI - Math.atan(deltaY / deltaX1); let dir2T = Math.atan(deltaY / deltaX2); - let tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); - let tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); + const tensionMag2 = (this.mass1 * Math.abs(this.gravity)) / ((-Math.cos(dir2T) / Math.cos(dir1T)) * Math.sin(dir1T) + Math.sin(dir2T)); + const tensionMag1 = (-tensionMag2 * Math.cos(dir2T)) / Math.cos(dir1T); dir1T = (dir1T * 180) / Math.PI; dir2T = (dir2T * 180) / Math.PI; const tensionForce1: IForce = { @@ -1741,7 +1750,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent {`${NumCast(this.dataDoc.mass1_velocityX)} m/s`} { this.dataDoc.mass1_velocityXstart = value; this._simReset++; })} - small={true} + small mode="Freeform" /> - <>{this.dataDoc.mass1_velocityY} m/s - {this.dataDoc.mass1_velocityY + ''} m/s { this.dataDoc.mass1_velocityYstart = -value; }} - small={true} + small mode="Freeform" /> - <> - {this.dataDoc.mass1_accelerationX} m/s2 - + {this.dataDoc.mass1_accelerationX + ''} m/s2 - <> - {this.dataDoc.mass1_accelerationY} m/s2 - + {this.dataDoc.mass1_accelerationY + ''} m/s2
)} - {this.simulationMode == 'Freeform' && this.simulationType == 'Pulley' && ( + {this.simulationMode === 'Freeform' && this.simulationType === 'Pulley' && ( @@ -1869,14 +1870,10 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponentAcceleration @@ -1890,7 +1887,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent )} - {this.simulationType != 'Pendulum' && this.simulationType != 'Spring' && ( + {this.simulationType !== 'Pendulum' && this.simulationType !== 'Spring' && (

Kinematic Equations

    @@ -1907,7 +1904,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Spring' && ( + {this.simulationType === 'Spring' && (

Harmonic Motion Equations: Spring

    @@ -1936,7 +1933,7 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent
)} - {this.simulationType == 'Pendulum' && ( + {this.simulationType === 'Pendulum' && (

Harmonic Motion Equations: Pendulum

    @@ -1959,11 +1956,11 @@ export class PhysicsSimulationBox extends ViewBoxAnnotatableComponent - + - - + +

    - {this.simulationType == 'Circular Motion' ? 'Z' : 'Y'} + {this.simulationType === 'Circular Motion' ? 'Z' : 'Y'}

    () ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SCREENSHOT, { + layout: { view: ScreenshotBox, dataField: 'data' }, + options: { acl: '', _layout_nativeDimEditable: true, systemIcon: 'BsCameraFill' }, +}); diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 5ebc50a1b..bc19d7ad1 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -21,6 +21,8 @@ import { OverlayView } from '../OverlayView'; import { FieldView, FieldViewProps } from './FieldView'; import { DocumentIconContainer } from './DocumentIcon'; import './ScriptingBox.scss'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; const _global = (window /* browser */ || global) /* node */ as any; const ReactTextareaAutocomplete = require('@webscopeio/react-textarea-autocomplete').default; @@ -845,3 +847,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent() ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SCRIPTING, { + layout: { view: ScriptingBox, dataField: 'data' }, + options: { acl: '', systemIcon: 'BsFileEarmarkCodeFill' }, +}); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 13ee3250e..16767d11e 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -10,14 +10,15 @@ import { DocData } from '../../../fields/DocSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; +import { FollowLinkScript } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { AudioField, ImageField, VideoField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { FollowLinkScript } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { undoBatch } from '../../util/UndoManager'; @@ -31,7 +32,8 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProvider'; import { DocumentView } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { RecordingBox } from './RecordingBox'; import './VideoBox.scss'; @@ -1162,3 +1164,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.VID, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _layout_currentTimecode: 0, systemIcon: 'BsFileEarmarkPlayFill' }, +}); +Docs.Prototypes.TemplateMap.set(DocumentType.REC, { + layout: { view: VideoBox, dataField: 'data' }, + options: { acl: '', _height: 100, backgroundColor: 'pink', systemIcon: 'BsFillMicFill' }, +}); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index dfe237a86..54f246b20 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -18,7 +18,9 @@ import { Cast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, stringHash } from '../../../Utils'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; @@ -37,9 +39,11 @@ import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; import { StyleProp } from '../StyleProvider'; -import { DocumentView, OpenWhere } from './DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from './FieldView'; +import { DocumentView } from './DocumentView'; +import { FieldView, FieldViewProps } from './FieldView'; +import { FocusViewOptions } from './FocusViewOptions'; import { LinkInfo } from './LinkDocPreview'; +import { OpenWhere } from './OpenWhere'; import './WebBox.scss'; const { CreateImage } = require('./WebBoxRenderer'); @@ -1242,3 +1246,8 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem ScriptingGlobals.add(function urlHash(url: string) { return stringHash(url); }); + +Docs.Prototypes.TemplateMap.set(DocumentType.WEB, { + layout: { view: WebBox, dataField: 'data' }, + options: { acl: '', _height: 300, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, +}); diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index 8b9e3cc5d..bd66941c3 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -7,6 +7,8 @@ import * as React from 'react'; import { dateRangeStrToDates } from '../../../../ClientUtils'; import { Doc } from '../../../../fields/Doc'; import { StrCast } from '../../../../fields/Types'; +import { DocumentType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; @@ -119,3 +121,7 @@ export class CalendarBox extends ViewBoxBaseComponent() { ); } } +Docs.Prototypes.TemplateMap.set(DocumentType.CALENDAR, { + layout: { view: CalendarBox, dataField: 'data' }, + options: { acl: '' }, +}); diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx index f311b3cdd..93371685d 100644 --- a/src/client/views/nodes/formattedText/DashDocView.tsx +++ b/src/client/views/nodes/formattedText/DashDocView.tsx @@ -9,11 +9,12 @@ import { Doc } from '../../../../fields/Doc'; import { Height, Width } from '../../../../fields/DocSymbols'; import { NumCast } from '../../../../fields/Types'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { Transform } from '../../../util/Transform'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../DocumentView'; -import { FocusViewOptions } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { FormattedTextBox } from './FormattedTextBox'; const horizPadding = 3; // horizontal padding to container to allow cursor to show up on either side. diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index dc388b22a..1c5ea2dd4 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -23,7 +23,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { SchemaTableCell } from '../../collections/collectionSchema/SchemaTableCell'; import { FilterPanel } from '../../FilterPanel'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { OpenWhere } from '../DocumentView'; +import { OpenWhere } from '../OpenWhere'; import './DashFieldView.scss'; import { FormattedTextBox } from './FormattedTextBox'; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9bcf5027f..ad6629fc9 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -15,7 +15,7 @@ import * as React from 'react'; import { BsMarkdownFill } from 'react-icons/bs'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; @@ -28,8 +28,9 @@ import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util'; import { emptyFunction, numberRange, unimplementedFunction, Utils } from '../../../../Utils'; import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; -import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { Docs } from '../../../documents/Documents'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; +import { DocUtils } from '../../../documents/DocUtils'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -54,9 +55,11 @@ import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; import { styleFromLayoutString, StyleProp } from '../../StyleProvider'; import { mediaState } from '../AudioBox'; -import { DocumentView, DocumentViewInternal, OpenWhere } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { DocumentView, DocumentViewInternal } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; import { LinkInfo } from '../LinkDocPreview'; +import { OpenWhere } from '../OpenWhere'; import { DashDocCommentView } from './DashDocCommentView'; import { DashDocView } from './DashDocView'; import { DashFieldView } from './DashFieldView'; @@ -182,26 +185,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn); - FormattedTextBox.SetSelectOnLoad(target); + Doc.SetSelectOnLoad(target); return target; }; @@ -992,7 +977,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent }); + helpItems.push({ description: `show markdown options`, event: () => RTFMarkup.Instance.setOpen(true), icon: }); !help && cm.addItem({ description: 'Help...', subitems: helpItems, icon: 'eye' }); }; @@ -1062,7 +1047,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent>(schema: S, props: any): KeyMa return true; }); bind('Cmd-?', () => { - RTFMarkup.Instance.open(); + RTFMarkup.Instance.setOpen(true); return true; }); bind('Cmd-e', (state: EditorState, dispatch: (tx: Transaction) => void) => { diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 72a2b706d..1ff862859 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -8,7 +8,8 @@ import { List } from '../../../../fields/List'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Utils } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; -import { Docs, DocUtils } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { CollectionViewType } from '../../../documents/DocumentTypes'; import { CollectionView } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 87b7a4069..d5fad296f 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -14,12 +14,13 @@ import { Doc, DocListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; import { NumCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; +import { Docs } from '../../../documents/Documents'; import { DocumentManager } from '../../../util/DocumentManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; -import { OpenWhereMod } from '../DocumentView'; import { ImageEditorData } from '../ImageBox'; +import { OpenWhereMod } from '../OpenWhere'; import './GenerativeFill.scss'; import Buttons from './GenerativeFillButtons'; import { BrushHandler } from './generativeFillUtils/BrushHandler'; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 4cf9e99fa..485ba7367 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -36,8 +36,10 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { Colors } from '../../global/globalEnums'; import { LightboxView } from '../../LightboxView'; import { pinDataTypes as dataTypes } from '../../PinFuncs'; -import { DocumentView, OpenWhere, OpenWhereMod } from '../DocumentView'; -import { FieldView, FieldViewProps, FocusViewOptions } from '../FieldView'; +import { DocumentView } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FocusViewOptions } from '../FocusViewOptions'; +import { OpenWhere, OpenWhereMod } from '../OpenWhere'; import { ScriptingBox } from '../ScriptingBox'; import './PresBox.scss'; import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums'; @@ -423,7 +425,7 @@ export class PresBox extends ViewBoxBaseComponent() { const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc); const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; if (context) { - const ffview = DocumentManager.Instance.getFirstDocumentView(context)?.CollectionFreeFormView; + const ffview = CollectionFreeFormView.from(DocumentManager.Instance.getFirstDocumentView(context)); if (ffview?.childDocs) { PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, frameTime); ffview.layoutDoc._currentFrame = NumCast(activeFrame); @@ -745,7 +747,7 @@ export class PresBox extends ViewBoxBaseComponent() { }; _exitTrail: Opt<() => void>; - PlayTrail = (docs: Doc[]) => { + playTrail = (docs: Doc[]) => { const savedStates = docs.map(doc => { switch (doc.type) { case DocumentType.COL: @@ -2688,3 +2690,8 @@ export class PresBox extends ViewBoxBaseComponent() { ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) { PresBox.NavigateToTarget(bestTarget, activeItem); }); + +Docs.Prototypes.TemplateMap.set(DocumentType.PRES, { + layout: { view: PresBox, dataField: 'data' }, + options: { acl: '', defaultDoubleClick: 'ignore', hideClickBehaviors: true, layout_hideLinkAnchors: true }, +}); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index cf78a45b7..af0ab3b53 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -12,7 +12,7 @@ import { List } from '../../../../fields/List'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; -import { CollectionViewType } from '../../../documents/DocumentTypes'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { SettingsManager } from '../../../util/SettingsManager'; @@ -606,3 +606,8 @@ export class PresElementBox extends ViewBoxBaseComponent() { return !(this.slideDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem; } } + +Docs.Prototypes.TemplateMap.set(DocumentType.PRESELEMENT, { + layout: { view: PresElementBox, dataField: 'data' }, + options: { acl: '', title: 'pres element template', _layout_fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: 'data' }, +}); diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index f1cd1a4f7..8b2b179d3 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -9,8 +9,8 @@ import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { undoable } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { OpenWhere } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; +import { OpenWhere } from '../nodes/OpenWhere'; import { AnchorMenu } from './AnchorMenu'; import './Annotation.scss'; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 56ff2959c..c1bfdf176 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -12,7 +12,8 @@ import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT'; -import { DocUtils, Docs } from '../../../documents/Documents'; +import { Docs } from '../../../documents/Documents'; +import { DocUtils } from '../../../documents/DocUtils'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { AnchorMenu } from '../AnchorMenu'; import './GPTPopup.scss'; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 22355bc57..15ac73f78 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -6,21 +6,22 @@ import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; import * as PDFJSViewer from 'pdfjs-dist/web/pdf_viewer.mjs'; import * as React from 'react'; -import { Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; import { DocData, Height } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, returnAll, returnFalse, returnNone, returnZero, smoothScroll } from '../../../ClientUtils'; -import { DocUtils } from '../../documents/Documents'; +import { DocUtils } from '../../documents/DocUtils'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; -import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView'; +import { FieldViewProps } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LinkInfo } from '../nodes/LinkDocPreview'; import { PDFBox } from '../nodes/PDFBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; @@ -331,7 +332,7 @@ export class PDFViewer extends ObservableReactComponent { this._ignoreScroll = false; if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio this._scrollTimer = setTimeout(() => { - DocUtils.MakeLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); + CreateLinkToActiveAudio(() => this._props.pdfBox.getAnchor(true)!, false); this._scrollTimer = undefined; }, 200); } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 384e6d654..5df934231 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -4,11 +4,14 @@ import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { ClientUtils } from '../../../ClientUtils'; import { Doc, DocListCastAsync, Field, FieldType } from '../../../fields/Doc'; import { DirectLinks, DocData } from '../../../fields/DocSymbols'; +import { Id } from '../../../fields/FieldSymbols'; import { DocCast, StrCast } from '../../../fields/Types'; +import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocUtils } from '../../documents/Documents'; +import { Docs } from '../../documents/Documents'; import { DocumentManager } from '../../util/DocumentManager'; import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; @@ -21,8 +24,6 @@ import { IRecommendation, Recommendation } from '../newlightbox/components'; import { fetchRecommendations } from '../newlightbox/utils'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './SearchBox.scss'; -import { Id } from '../../../fields/FieldSymbols'; -import { ClientUtils } from '../../../ClientUtils'; const DAMPENING_FACTOR = 0.9; const MAX_ITERATIONS = 25; @@ -520,3 +521,8 @@ export class SearchBox extends ViewBoxBaseComponent() { ); } } + +Docs.Prototypes.TemplateMap.set(DocumentType.SEARCH, { + layout: { view: SearchBox, dataField: 'data' }, + options: { acl: '', _width: 400 }, +}); diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx index 7ad7b2927..6ed3b240d 100644 --- a/src/client/views/selectedDoc/SelectedDocView.tsx +++ b/src/client/views/selectedDoc/SelectedDocView.tsx @@ -8,7 +8,7 @@ import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; import { DocumentManager } from '../../util/DocumentManager'; import { SettingsManager } from '../../util/SettingsManager'; -import { FocusViewOptions } from '../nodes/FieldView'; +import { FocusViewOptions } from '../nodes/FocusViewOptions'; export interface SelectedDocViewProps { selectedDocs: Doc[]; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 9083d6ca3..5fd053eef 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -174,6 +174,49 @@ export function updateCachedAcls(doc: Doc) { return undefined; } +export function ActiveInkPen(): Doc { return Doc.UserDoc(); } // prettier-ignore +export function ActiveInkColor(): string { return StrCast(ActiveInkPen()?.activeInkColor, 'black'); } // prettier-ignore +export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } // prettier-ignore +export function ActiveIsInkMask(): boolean { return BoolCast(ActiveInkPen()?.activeIsInkMask, false); } // prettier-ignore +export function ActiveInkHideTextLabels(): boolean { return BoolCast(ActiveInkPen().activeInkHideTextLabels, false); } // prettier-ignore +export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } // prettier-ignore +export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ''); } // prettier-ignore +export function ActiveArrowScale(): number { return NumCast(ActiveInkPen()?.activeArrowScale, 1); } // prettier-ignore +export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, '0'); } // prettier-ignore +export function ActiveInkWidth(): number { return Number(ActiveInkPen()?.activeInkWidth); } // prettier-ignore +export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } // prettier-ignore + +export function SetActiveInkWidth(width: string): void { + !isNaN(parseInt(width)) && ActiveInkPen() && (ActiveInkPen().activeInkWidth = width); +} +export function SetActiveBezierApprox(bezier: string): void { + ActiveInkPen() && (ActiveInkPen().activeInkBezier = isNaN(parseInt(bezier)) ? '' : bezier); +} +export function SetActiveInkColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeInkColor = value); +} +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} +export function SetActiveInkHideTextLabels(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeInkHideTextLabels = value); +} +export function SetActiveFillColor(value: string) { + ActiveInkPen() && (ActiveInkPen().activeFillColor = value); +} +export function SetActiveArrowStart(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowStart = value); +} +export function SetActiveArrowEnd(value: string) { + ActiveInkPen() && (ActiveInkPen().activeArrowEnd = value); +} +export function SetActiveArrowScale(value: number) { + ActiveInkPen() && (ActiveInkPen().activeArrowScale = value); +} +export function SetActiveDash(dash: string): void { + !isNaN(parseInt(dash)) && ActiveInkPen() && (ActiveInkPen().activeDash = dash); +} + @scriptingGlobal @Deserializable('Doc', updateCachedAcls, ['id']) export class Doc extends RefField { @@ -420,6 +463,16 @@ export class Doc extends RefField { // eslint-disable-next-line no-redeclare export namespace Doc { + // eslint-disable-next-line import/no-mutable-exports + export let SelectOnLoad: Doc | undefined; + export function SetSelectOnLoad(doc: Doc | undefined) { + SelectOnLoad = doc; + } + // eslint-disable-next-line import/no-mutable-exports + export let DocDragDataName: string = ''; + export function SetDocDragDataName(name: string) { + DocDragDataName = name; + } export function SetContainer(doc: Doc, container: Doc) { if (container !== Doc.MyRecentlyClosed) { doc.embedContainer = container; @@ -1605,6 +1658,29 @@ export namespace Doc { } } +export function RTFIsFragment(html: string) { + return html.indexOf('data-pm-slice') !== -1; +} +export function GetHrefFromHTML(html: string): string { + const parser = new DOMParser(); + const parsedHtml = parser.parseFromString(html, 'text/html'); + if (parsedHtml.body.childNodes.length === 1 && parsedHtml.body.childNodes[0].childNodes.length === 1 && (parsedHtml.body.childNodes[0].childNodes[0] as any).href) { + return (parsedHtml.body.childNodes[0].childNodes[0] as any).href; + } + return ''; +} +export function GetDocFromUrl(url: string) { + return url.startsWith(document.location.origin) ? new URL(url).pathname.split('doc/').lastElement() : ''; // docId +} + +let activeAudioLinker: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[]; +export function SetActiveAudioLinker(func: (f: () => Doc | undefined, broadcast?: boolean) => (Doc | undefined)[]) { + activeAudioLinker = func; +} +export function CreateLinkToActiveAudio(func: () => Doc | undefined, broadcast?: boolean) { + return activeAudioLinker(func, broadcast); +} + export function IdToDoc(id: string) { return DocCast(DocServer.GetCachedRefField(id)); } diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index a2cc47a43..5eb60a2f8 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -12,7 +12,8 @@ import { DocServer } from '../client/DocServer'; import { Networking } from '../client/Network'; import { GoogleApiClientUtils } from '../client/apis/google_docs/GoogleApiClientUtils'; import { GooglePhotos } from '../client/apis/google_docs/GooglePhotosClientUtils'; -import { DocUtils, Docs } from '../client/documents/Documents'; +import { Docs } from '../client/documents/Documents'; +import { DocUtils } from '../client/documents/DocUtils'; import { FormattedTextBox } from '../client/views/nodes/formattedText/FormattedTextBox'; import { schema } from '../client/views/nodes/formattedText/schema_rts'; import { Doc, Opt } from './Doc'; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 8a3787768..5e5f5527f 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -277,6 +277,10 @@ export class ComputedField extends ScriptField { } } +export function FollowLinkScript() { + return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); +} + ScriptingGlobals.add( // eslint-disable-next-line prefer-arrow-callback function setIndexVal(list: any[], index: number, value: any) { diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx index b355e70a7..55a85ed1b 100644 --- a/src/mobile/MobileInterface.tsx +++ b/src/mobile/MobileInterface.tsx @@ -127,6 +127,7 @@ import './AudioUpload.scss'; import { Uploader } from './ImageUpload'; import './ImageUpload.scss'; import './MobileInterface.scss'; +import { DashboardView } from '../client/views/DashboardView'; library.add( ...[ @@ -583,7 +584,7 @@ export class MobileInterface extends React.Component { }; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row'); + const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row'); const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`); -- cgit v1.2.3-70-g09d2 From dd08c20ec6df3fad6ecd6b16c787f10b0c23feb4 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 2 May 2024 00:39:31 -0400 Subject: lots more dependency cycle unwinding. --- src/client/documents/DocUtils.ts | 36 ++-- src/client/documents/Documents.ts | 4 +- src/client/util/BranchingTrailManager.tsx | 5 +- src/client/util/CalendarManager.tsx | 20 +- src/client/util/CaptureManager.tsx | 9 +- src/client/util/CurrentUserUtils.ts | 24 +-- src/client/util/DictationManager.ts | 44 ++++- src/client/util/DocumentManager.ts | 85 +++----- src/client/util/DragManager.ts | 13 -- src/client/util/GroupManager.tsx | 2 +- src/client/util/HypothesisUtils.ts | 10 +- .../util/Import & Export/DirectoryImportBox.tsx | 1 - src/client/util/LinkFollower.ts | 31 ++- src/client/util/LinkManager.ts | 9 +- src/client/util/PingManager.ts | 4 +- src/client/util/ReplayMovements.ts | 6 +- src/client/util/Scripting.ts | 9 +- src/client/util/SelectionManager.ts | 29 +-- src/client/util/SettingsManager.tsx | 20 +- src/client/util/SharingManager.tsx | 12 +- src/client/util/SnappingManager.ts | 16 +- src/client/views/AntimodeMenu.tsx | 8 +- src/client/views/ComponentDecorations.tsx | 4 +- src/client/views/DocComponent.tsx | 2 + src/client/views/DocViewUtils.ts | 21 ++ src/client/views/DocumentButtonBar.tsx | 29 +-- src/client/views/DocumentDecorations.tsx | 117 ++++++----- src/client/views/FieldsDropdown.tsx | 22 +-- src/client/views/FilterPanel.tsx | 8 +- src/client/views/GestureOverlay.tsx | 6 +- src/client/views/GlobalKeyHandler.ts | 78 +++++--- src/client/views/InkControlPtHandles.tsx | 6 +- src/client/views/InkStrokeProperties.ts | 3 +- src/client/views/InkTranscription.tsx | 1 - src/client/views/InkingStroke.tsx | 2 +- src/client/views/LightboxView.tsx | 36 ++-- src/client/views/Main.tsx | 12 +- src/client/views/MainView.tsx | 85 ++++---- src/client/views/MarqueeAnnotator.tsx | 1 - src/client/views/OverlayView.tsx | 5 +- src/client/views/PropertiesButtons.tsx | 29 ++- .../views/PropertiesDocBacklinksSelector.tsx | 8 +- src/client/views/PropertiesDocContextSelector.tsx | 3 +- src/client/views/PropertiesView.tsx | 50 +++-- src/client/views/ScriptBox.tsx | 8 +- src/client/views/ScriptingRepl.tsx | 16 +- src/client/views/SidebarAnnos.tsx | 8 +- src/client/views/StyleProp.ts | 24 +++ src/client/views/StyleProvider.tsx | 105 ++++------ .../views/collections/CollectionCarousel3DView.tsx | 5 +- .../views/collections/CollectionCarouselView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 18 +- src/client/views/collections/CollectionMenu.tsx | 32 ++- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionPileView.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 16 +- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/CollectionSubView.tsx | 11 +- .../views/collections/CollectionTimeView.tsx | 3 +- .../views/collections/CollectionTreeView.tsx | 17 +- .../views/collections/CollectionTreeViewType.ts | 5 + src/client/views/collections/TabDocView.tsx | 216 +++++++++++---------- src/client/views/collections/TreeView.tsx | 15 +- .../CollectionFreeFormClusters.ts | 31 ++- .../CollectionFreeFormInfoUI.tsx | 10 +- .../CollectionFreeFormPannableContents.tsx | 9 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 88 ++++----- .../collections/collectionFreeForm/MarqueeView.tsx | 15 +- .../collectionLinear/CollectionLinearView.tsx | 10 +- .../collectionMulticolumn/MulticolumnResizer.tsx | 2 +- .../collectionMulticolumn/MultirowResizer.tsx | 2 +- .../collectionSchema/CollectionSchemaView.tsx | 28 +-- .../collectionSchema/SchemaTableCell.tsx | 8 +- src/client/views/global/globalScripts.ts | 58 +++--- src/client/views/linking/LinkMenuGroup.tsx | 4 +- src/client/views/linking/LinkMenuItem.tsx | 31 ++- .../views/newlightbox/ButtonMenu/ButtonMenu.tsx | 3 +- src/client/views/newlightbox/NewLightboxView.tsx | 27 ++- .../components/Recommendation/Recommendation.tsx | 12 +- src/client/views/nodes/AudioBox.tsx | 25 +-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 41 ++-- src/client/views/nodes/ComparisonBox.tsx | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 3 +- .../nodes/DataVizBox/components/LineChart.tsx | 6 +- .../views/nodes/DataVizBox/components/PieChart.tsx | 2 +- .../views/nodes/DataVizBox/components/TableBox.tsx | 9 +- src/client/views/nodes/DocumentContentsView.tsx | 13 +- src/client/views/nodes/DocumentIcon.tsx | 11 +- src/client/views/nodes/DocumentLinksButton.tsx | 13 -- src/client/views/nodes/DocumentView.tsx | 203 +++++++++---------- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 38 ++-- src/client/views/nodes/FunctionPlotBox.tsx | 7 +- src/client/views/nodes/ImageBox.tsx | 6 +- src/client/views/nodes/KeyValueBox.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 2 +- src/client/views/nodes/LinkAnchorBox.scss | 34 ---- src/client/views/nodes/LinkAnchorBox.tsx | 118 ----------- src/client/views/nodes/LinkBox.tsx | 5 +- src/client/views/nodes/LinkDescriptionPopup.tsx | 3 +- src/client/views/nodes/LinkDocPreview.tsx | 26 ++- src/client/views/nodes/LoadingBox.tsx | 21 +- .../views/nodes/MapBox/DirectionsAnchorMenu.tsx | 4 +- src/client/views/nodes/MapBox/MapAnchorMenu.tsx | 4 +- src/client/views/nodes/MapBox/MapBox.tsx | 6 +- src/client/views/nodes/MapBox/MapBox2.tsx | 6 +- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 6 +- src/client/views/nodes/PDFBox.tsx | 6 +- .../views/nodes/RecordingBox/RecordingBox.tsx | 16 +- src/client/views/nodes/ScreenshotBox.tsx | 11 +- src/client/views/nodes/VideoBox.tsx | 28 ++- src/client/views/nodes/WebBox.tsx | 5 +- .../views/nodes/formattedText/DashFieldView.tsx | 5 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 45 ++--- .../formattedText/ProsemirrorExampleTransfer.ts | 4 +- .../views/nodes/formattedText/RichTextMenu.tsx | 17 +- .../views/nodes/formattedText/SummaryView.tsx | 5 +- .../views/nodes/generativeFill/GenerativeFill.tsx | 8 +- src/client/views/nodes/trails/PresBox.tsx | 58 +++--- src/client/views/nodes/trails/PresElementBox.tsx | 11 +- src/client/views/pdf/AnchorMenu.tsx | 6 +- src/client/views/pdf/Annotation.tsx | 6 +- src/client/views/pdf/PDFViewer.tsx | 6 +- src/client/views/search/SearchBox.tsx | 31 ++- src/client/views/selectedDoc/SelectedDocView.tsx | 14 +- src/client/views/topbar/TopBar.tsx | 8 +- src/fields/Doc.ts | 19 +- src/fields/ScriptField.ts | 1 - src/fields/documentSchemas.ts | 1 - src/mobile/MobileInterface.tsx | 9 +- src/server/SharedMediaTypes.ts | 5 + 130 files changed, 1254 insertions(+), 1461 deletions(-) create mode 100644 src/client/views/DocViewUtils.ts create mode 100644 src/client/views/StyleProp.ts create mode 100644 src/client/views/collections/CollectionTreeViewType.ts delete mode 100644 src/client/views/nodes/LinkAnchorBox.scss delete mode 100644 src/client/views/nodes/LinkAnchorBox.tsx (limited to 'src/client/views/nodes/FunctionPlotBox.tsx') diff --git a/src/client/documents/DocUtils.ts b/src/client/documents/DocUtils.ts index 0a47e0359..0c9fe0315 100644 --- a/src/client/documents/DocUtils.ts +++ b/src/client/documents/DocUtils.ts @@ -9,7 +9,7 @@ import { ClientUtils } from '../../ClientUtils'; import * as JSZipUtils from '../../JSZipUtils'; import { decycle } from '../../decycler/decycler'; import { DateField } from '../../fields/DateField'; -import { Doc, DocListCast, Field, FieldType, LinkedTo, Opt, SetActiveAudioLinker, StrListCast } from '../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, LinkedTo, Opt, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkDataFieldName, InkField } from '../../fields/InkField'; @@ -29,9 +29,7 @@ import { SerializationHelper } from '../util/SerializationHelper'; import { UndoManager, undoable } from '../util/UndoManager'; import { ContextMenu } from '../views/ContextMenu'; import { ContextMenuProps } from '../views/ContextMenuItem'; -import { FieldViewProps } from '../views/nodes/FieldView'; import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; -import { LoadingBox } from '../views/nodes/LoadingBox'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import { DocumentType } from './DocumentTypes'; @@ -50,7 +48,7 @@ export namespace DocUtils { } if (key === LinkedTo) { // links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("...")) - const allLinks = LinkManager.Instance.getAllRelatedLinks(doc); + const allLinks = Doc.Links(doc); const matchLink = (val: string, anchor: Doc) => { const linkedToExp = (val ?? '').split('='); if (linkedToExp.length === 1) return Field.toScriptString(anchor) === val; @@ -131,7 +129,7 @@ export namespace DocUtils { if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true; const failsNotEqualFacets = !xs.length ? false : xs.some(value => matchFieldValue(d, facetKey, value)); const satisfiesCheckFacets = !checks.length ? true : checks.some(value => matchFieldValue(d, facetKey, value)); - const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : LinkManager.Instance.getAllRelatedLinks(d).length; + const satisfiesExistsFacets = !exists.length ? true : facetKey !== LinkedTo ? d[facetKey] !== undefined : Doc.Links(d).length; const satisfiesUnsetsFacets = !unsets.length ? true : d[facetKey] === undefined; const satisfiesMatchFacets = !matches.length ? true @@ -170,19 +168,7 @@ export namespace DocUtils { return rangeFilteredDocs; } - export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; - - export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { - broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore - return DocUtils.ActiveRecordings.map(audio => { - const sourceDoc = getSourceDoc(); - return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_displayLine: false, link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); - }); - } - - SetActiveAudioLinker(MakeLinkToActiveAudio); - - export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string; link_displayLine?: boolean }, id?: string, showPopup?: number[]) { + export function MakeLink(source: Doc, target: Doc, linkSettings: { link_relationship?: string; link_description?: string }, id?: string, showPopup?: number[]) { if (!linkSettings.link_relationship) linkSettings.link_relationship = target.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link'; if (target.doc === Doc.UserDoc()) return undefined; @@ -232,7 +218,6 @@ export namespace DocUtils { title: ComputedField.MakeFunction('generateLinkTitle(this)') as any, link_anchor_1_useSmallAnchor: source.useSmallAnchor ? true : undefined, link_anchor_2_useSmallAnchor: target.useSmallAnchor ? true : undefined, - link_displayLine: linkSettings.link_displayLine, link_relationship: linkSettings.link_relationship, link_description: linkSettings.link_description, x: ComputedField.MakeFunction(`((this.${a}?.x||0)+(this.${b}?.x||0))/2`) as any, @@ -256,7 +241,6 @@ export namespace DocUtils { const script = scripts[key]; if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && script) { (key.startsWith('_') ? doc : Doc.GetProto(doc))[key] = ScriptField.MakeScript(script, { - self: Doc.name, this: Doc.name, dragData: Doc.DocDragDataName, value: 'any', @@ -579,7 +563,7 @@ export namespace DocUtils { export function LeavePushpin(doc: Doc, annotationField: string) { if (doc.followLinkToggle) return undefined; const context = Cast(doc.embedContainer, Doc, null) ?? Cast(doc.annotationOn, Doc, null); - const hasContextAnchor = LinkManager.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); + const hasContextAnchor = Doc.Links(doc).some(l => (l.link_anchor_2 === doc && Cast(l.link_anchor_1, Doc, null)?.annotationOn === context) || (l.link_anchor_1 === doc && Cast(l.link_anchor_2, Doc, null)?.annotationOn === context)); if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ title: '', @@ -673,7 +657,7 @@ export namespace DocUtils { proto.data_duration = result.duration; } if (overwriteDoc) { - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } generatedDocuments.push(doc); } @@ -717,7 +701,7 @@ export namespace DocUtils { if (overwriteDoc) { overwriteDoc.isLoading = false; overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } } else newFilename && processFileupload(generatedDocuments, newFilename, mimetype ?? '', result, options, overwriteDoc); }); @@ -755,7 +739,7 @@ export namespace DocUtils { if ((result as any).message) { if (overwriteDoc) { overwriteDoc.loadingError = (result as any).message; - LoadingBox.removeCurrentlyLoading(overwriteDoc); + Doc.removeCurrentlyLoading(overwriteDoc); } } else newFilename && mimetype && processFileupload(generatedDocuments, newFilename, mimetype, result, options, overwriteDoc); }); @@ -868,6 +852,10 @@ export function FollowLinkScript() { return ScriptField.MakeScript('return followLink(this,altKey)', { altKey: 'boolean' }); } +export function IsFollowLinkScript(field: FieldResult) { + return ScriptCast(field)?.script.originalScript.includes('return followLink('); +} + ScriptingGlobals.add('Docs', Docs); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc, asDelegate?: boolean) { diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a3fea1ce4..1c18c0d84 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -19,7 +19,6 @@ import { SharingPermissions } from '../../fields/util'; import { PointData } from '../../pen-gestures/GestureTypes'; import { DocServer } from '../DocServer'; import { dropActionType } from '../util/DropActionTypes'; -import { LinkManager } from '../util/LinkManager'; import { CollectionViewType, DocumentType } from './DocumentTypes'; class EmptyBox { @@ -394,7 +393,6 @@ export class DocumentOptions { link?: string; link_description?: string; // added for links link_relationship?: string; // type of relatinoship a link represents - link_displayLine?: BOOLt = new BoolInfo('whether a link line should be dipslayed between the two link anchors'); link_displayArrow?: BOOLt = new BoolInfo("whether to display link's directional arrowhead"); link_anchor_1?: DOCt = new DocInfo('start anchor of a link'); link_anchor_2?: DOCt = new DocInfo('end anchor of a link'); @@ -810,7 +808,7 @@ export namespace Docs { 'link' ); - LinkManager.Instance.addLink(linkDoc); + Doc.AddLink(linkDoc); return linkDoc; } diff --git a/src/client/util/BranchingTrailManager.tsx b/src/client/util/BranchingTrailManager.tsx index 28c00644f..119d103c5 100644 --- a/src/client/util/BranchingTrailManager.tsx +++ b/src/client/util/BranchingTrailManager.tsx @@ -6,8 +6,8 @@ import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { OverlayView } from '../views/OverlayView'; +import { DocumentView } from '../views/nodes/DocumentView'; import { PresBox } from '../views/nodes/trails'; -import { DocumentManager } from './DocumentManager'; @observer export class BranchingTrailManager extends React.Component { @@ -46,7 +46,6 @@ export class BranchingTrailManager extends React.Component { // hi.overlayY = 100; // Doc.AddToMyOverlay(hi); - console.log(DocumentManager._overlayViews); }; @action setSlideHistoryStack = action((newArr: String[]) => { @@ -103,7 +102,7 @@ export class BranchingTrailManager extends React.Component { this.setSlideHistoryStack(newStack); removed.forEach(info => this.containsSet.delete(info.toString())); - DocumentManager.Instance.showDocument(targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(targetDoc, { willZoomCentered: true }); if (this.slideHistoryStack.length === 0) { Doc.UserDoc().isBranchingMode = false; } diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index 8e3936d34..77cf80151 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -13,15 +13,12 @@ import { Doc, DocListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { StrCast } from '../../fields/Types'; import { Docs } from '../documents/Documents'; -import { DictationOverlay } from '../views/DictationOverlay'; import { MainViewModal } from '../views/MainViewModal'; import { ObservableReactComponent } from '../views/ObservableReactComponent'; import { DocumentView } from '../views/nodes/DocumentView'; import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox'; import './CalendarManager.scss'; -import { DocumentManager } from './DocumentManager'; -import { SelectionManager } from './SelectionManager'; -import { SettingsManager } from './SettingsManager'; +import { SnappingManager } from './SnappingManager'; // import 'react-date-range/dist/styles.css'; // import 'react-date-range/dist/theme/default.css'; @@ -85,11 +82,9 @@ export class CalendarManager extends ObservableReactComponent<{}> { }; public open = (target?: DocumentView, targetDoc?: Doc) => { - console.log('hi'); runInAction(() => { this.targetDoc = targetDoc || target?.Document; this.targetDocView = target; - DictationOverlay.Instance.hasActiveModal = true; this.isOpen = this.targetDoc !== undefined; }); }; @@ -99,7 +94,6 @@ export class CalendarManager extends ObservableReactComponent<{}> { TaskCompletionBox.taskCompleted = false; setTimeout( action(() => { - DictationOverlay.Instance.hasActiveModal = false; this.targetDoc = undefined; }), 500 @@ -137,7 +131,7 @@ export class CalendarManager extends ObservableReactComponent<{}> { // TODO: Make undoable private addToCalendar = () => { - const docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; // doc to add to calendar console.log(targetDoc); @@ -190,14 +184,14 @@ export class CalendarManager extends ObservableReactComponent<{}> { private focusOn = (contents: string) => { const title = this.targetDoc ? StrCast(this.targetDoc.title) : ''; - const docs = SelectionManager.Views.length > 1 ? SelectionManager.Views.map(docView => docView.Document) : [this.targetDoc]; + const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.Document) : [this.targetDoc]; return ( { if (this.targetDoc && this.targetDocView && docs.length === 1) { - DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { @@ -251,17 +245,17 @@ export class CalendarManager extends ObservableReactComponent<{}> { @computed get calendarInterface() { - const docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); const targetDoc = this.layoutDocAcls ? docs[0] : docs[0]?.[DocData]; return (

    -

    +

    {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}

    diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx index a03c80f2a..4fd934774 100644 --- a/src/client/util/CaptureManager.tsx +++ b/src/client/util/CaptureManager.tsx @@ -10,8 +10,7 @@ import { DocCast, StrCast } from '../../fields/Types'; import { LightboxView } from '../views/LightboxView'; import { MainViewModal } from '../views/MainViewModal'; import './CaptureManager.scss'; -import { LinkManager } from './LinkManager'; -import { SelectionManager } from './SelectionManager'; +import { DocumentView } from '../views/nodes/DocumentView'; @observer export class CaptureManager extends React.Component<{}> { @@ -56,7 +55,7 @@ export class CaptureManager extends React.Component<{}> { const doc = this._document; const order: JSX.Element[] = []; if (doc) { - LinkManager.Links(doc).forEach((l, i) => + Doc.Links(doc).forEach((l, i) => order.push(
    {i}
    @@ -88,8 +87,8 @@ export class CaptureManager extends React.Component<{}> {
    { - const selected = SelectionManager.Views.slice(); - SelectionManager.DeselectAll(); + const selected = DocumentView.Selected(); + DocumentView.DeselectAll(); selected.map(dv => dv.props.removeDocument?.(dv.Document)); this.close(); }}> diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 15c8fec5a..ffc1304bf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,4 +1,4 @@ -import { observable, reaction, runInAction } from "mobx"; +import { reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; @@ -19,7 +19,8 @@ import { CollectionViewType, DocumentType } from "../documents/DocumentTypes"; import { Docs, DocumentOptions, FInfo, FInfoFieldType } from "../documents/Documents"; import { DashboardView } from "../views/DashboardView"; import { OverlayView } from "../views/OverlayView"; -import { CollectionTreeView, TreeViewType } from "../views/collections/CollectionTreeView"; +import { CollectionTreeView } from "../views/collections/CollectionTreeView"; +import { TreeViewType } from "../views/collections/CollectionTreeViewType"; import { Colors } from "../views/global/globalEnums"; import { mediaState } from "../views/nodes/AudioBox"; import { ButtonType, FontIconBox } from "../views/nodes/FontIconBox/FontIconBox"; @@ -30,8 +31,9 @@ import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox"; import { DragManager } from "./DragManager"; import { dropActionType } from "./DropActionTypes"; import { MakeTemplate } from "./DropConverter"; -import { LinkManager, UPDATE_SERVER_CACHE } from "./LinkManager"; +import { UPDATE_SERVER_CACHE } from "./LinkManager"; import { ScriptingGlobals } from "./ScriptingGlobals"; +import { SelectionManager } from "./SelectionManager"; import { ColorScheme } from "./SettingsManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; @@ -937,14 +939,13 @@ pie title Minerals in my tap water DocUtils.AssignDocField(doc, "globalScriptDatabase", () => Docs.Prototypes.MainScriptDocument(), {}); DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "My Header Bar", isSystem: true, _chromeHidden:true, layout_maxShown: 10, childLayoutFitWidth:false, childDocumentsActive:false, dropAction: dropActionType.move}); // drop down panel at top of dashboard for stashing documents - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards) - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs) - Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed) - - Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org") + SelectionManager.DeselectAll(); // this forces SelectionManager implementation to copy over to DocumentView's API. This also triggers the LinkManager to be created + + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyDashboards); + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MySharedDocs); + Doc.AddDocToList(Doc.MyFilesystem, undefined, Doc.MyRecentlyClosed); - // eslint-disable-next-line no-new - new LinkManager(); + Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org"); DocServer.CacheNeedsUpdate() && setTimeout(UPDATE_SERVER_CACHE, 2500); setInterval(UPDATE_SERVER_CACHE, 120000); @@ -969,12 +970,11 @@ pie title Minerals in my tap water }); } - @observable public static ServerVersion: string = ';' public static async loadCurrentUser() { return rp.get(ClientUtils.prepend("/getCurrentUser")).then(async response => { if (response) { const result: { version: string, userDocumentId: string, sharingDocumentId: string, linkDatabaseId: string, email: string, cacheDocumentIds: string, resolvedPorts: string } = JSON.parse(response); - runInAction(() => { CurrentUserUtils.ServerVersion = result.version; }); + runInAction(() => { SnappingManager.SetServerVersion(result.version); }); ClientUtils.SetCurrentUserEmail(result.email); resolvedPorts = result.resolvedPorts as any; DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts?.socket, result.email); diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 97ee628e2..bc9fe813f 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -10,12 +10,13 @@ import { RichTextField } from '../../fields/RichTextField'; import { listSpec } from '../../fields/Schema'; import { Cast, CastCtor } from '../../fields/Types'; import { AudioField, ImageField } from '../../fields/URLField'; +import { AudioAnnoState } from '../../server/SharedMediaTypes'; +import { Networking } from '../Network'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; import { DictationOverlay } from '../views/DictationOverlay'; import { DocumentView } from '../views/nodes/DocumentView'; import { OpenWhere } from '../views/nodes/OpenWhere'; -import { SelectionManager } from './SelectionManager'; import { UndoManager } from './UndoManager'; /** @@ -244,7 +245,7 @@ export namespace DictationManager { export const execute = async (phrase: string) => UndoManager.RunInBatch(async () => { console.log('PHRASE: ' + phrase); - const targets = SelectionManager.Views; + const targets = DocumentView.Selected(); if (!targets || !targets.length) { return undefined; } @@ -395,4 +396,43 @@ export namespace DictationManager { }, ]; } + export function recordAudioAnnotation(dataDoc: Doc, field: string, onRecording?: (stop: () => void) => void, onEnd?: () => void) { + let gumStream: any; + let recorder: any; + navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { + let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null); + if (audioTextAnnos) audioTextAnnos.push(''); + else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List(['']); + DictationManager.Controls.listen({ + interimHandler: value => { audioTextAnnos[audioTextAnnos.length - 1] = value; }, // prettier-ignore + continuous: { indefinite: false }, + }).then(results => { + if (results && [DictationManager.Controls.Infringed].includes(results)) { + DictationManager.Controls.stop(); + } + onEnd?.(); + }); + + gumStream = stream; + recorder = new MediaRecorder(stream); + recorder.ondataavailable = async (e: any) => { + const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); + if (!(result instanceof Error)) { + const audioField = new AudioField(result.accessPaths.agnostic.client); + const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null); + if (audioAnnos) audioAnnos.push(audioField); + else dataDoc[field + '_audioAnnotations'] = new List([audioField]); + } + }; + recorder.start(); + const stopFunc = () => { + recorder.stop(); + DictationManager.Controls.stop(/* false */); + dataDoc.audioAnnoState = AudioAnnoState.stopped; + gumStream.getAudioTracks()[0].stop(); + }; + if (onRecording) onRecording(stopFunc); + else setTimeout(stopFunc, 5000); + }); + } } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 8f0c2b0bf..5bcac7330 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -1,23 +1,17 @@ import { Howl } from 'howler'; import { action, computed, makeObservable, observable, ObservableSet, observe } from 'mobx'; import { Doc, Opt } from '../../fields/Doc'; -import { AclAdmin, AclEdit, Animation, DocData } from '../../fields/DocSymbols'; +import { Animation, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { AudioField } from '../../fields/URLField'; -import { GetEffectiveAcl } from '../../fields/util'; import { CollectionViewType } from '../documents/DocumentTypes'; -import { TabDocView } from '../views/collections/TabDocView'; import { LightboxView } from '../views/LightboxView'; import { DocumentView, DocumentViewInternal } from '../views/nodes/DocumentView'; import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; -import { KeyValueBox } from '../views/nodes/KeyValueBox'; -import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; -import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; type childIterator = { viewSpec: Opt; childDocView: Opt; focused: boolean; contextPath: Doc[] }; export class DocumentManager { @@ -29,10 +23,9 @@ export class DocumentManager { } // global holds all of the nodes (regardless of which collection they're in) - @observable _documentViews = new Set(); - @observable.shallow public CurrentlyLoading: Doc[] = []; + @observable private _documentViews = new Set(); @computed public get DocumentViews() { - return Array.from(this._documentViews).filter(view => !(view.ComponentView instanceof KeyValueBox) && (!LightboxView.LightboxDoc || LightboxView.Contains(view))); + return Array.from(this._documentViews).filter(view => (!view.ComponentView?.dontRegisterView?.() && !LightboxView.LightboxDoc) || LightboxView.Contains(view)); } public AddDocumentView(dv: DocumentView) { this._documentViews.add(dv); @@ -44,7 +37,19 @@ export class DocumentManager { // private constructor so no other class can create a nodemanager private constructor() { makeObservable(this); - observe(this.CurrentlyLoading, change => { + + DocumentView.allViews = () => this.DocumentViews; + DocumentView.addView = this.AddView; + DocumentView.removeView = this.RemoveView; + DocumentView.showDocument = this.showDocument; + DocumentView.showDocumentView = this.showDocumentView; + DocumentView.linkCommonAncestor = DocumentManager.LinkCommonAncestor; + DocumentView.addViewRenderedCb = this.AddViewRenderedCb; + DocumentView.getFirstDocumentView = this.getFirstDocumentView; + DocumentView.getDocumentView = this.getDocumentView; + DocumentView.getContextPath = DocumentManager.GetContextPath; + DocumentView.getLightboxDocumentView = this.getLightboxDocumentView; + observe(Doc.CurrentlyLoading, change => { // watch CurrentlyLoading-- when something is loaded, it's removed from the list and we have to update its icon if it were iconified since LoadingBox icons are different than the media they become switch (change.type as any) { case 'update': @@ -92,17 +97,12 @@ export class DocumentManager { @action public AddView = (view: DocumentView) => { - if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && - !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - this.AddDocumentView(view); - this.callAddViewFuncs(view); - } // prettier-ignore + this.AddDocumentView(view); + this.callAddViewFuncs(view); }; public RemoveView = action((view: DocumentView) => { - if (!view._props.LayoutTemplateString?.includes(KeyValueBox.name) && !view._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - this.DeleteDocumentView(view); - } - SelectionManager.DeselectView(view); + this.DeleteDocumentView(view); + DocumentView.DeselectView(view); }); // gets all views @@ -203,6 +203,11 @@ export class DocumentManager { } static _overlayViews = new ObservableSet(); + /** + * Find the nearest common ancestor collection that contains a link's source and target + * @param linkDoc + * @returns common ancestor DocumentView + */ public static LinkCommonAncestor(linkDoc: Doc) { const getAnchor = (which: number) => { const anch = DocCast(linkDoc['link_anchor_' + which]); @@ -245,11 +250,8 @@ export class DocumentManager { Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, targetDoc); const docContextPath = DocumentManager.GetContextPath(targetDoc, true); if (docContextPath.some(doc => doc.hidden)) options.toggleTarget = false; - const tabView = Array.from(TabDocView._allTabs).find(view => view._document === docContextPath[0]); - if (!tabView?._activated && tabView?._document) { - options.toggleTarget = false; - TabDocView.Activate(tabView?._document); - } + if (DocumentView.activateTabView(docContextPath[0])) options.toggleTarget = false; + const rootContextView = docContextPath.length && (await new Promise(res => { @@ -260,7 +262,7 @@ export class DocumentManager { return; } options.didMove = true; - (!LightboxView.LightboxDoc && docContextPath.some(doc => TabDocView.Activate(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); + (!LightboxView.LightboxDoc && docContextPath.some(doc => DocumentView.activateTabView(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight); this.AddViewRenderedCb(docContextPath[0], dv => res(dv)); })); if (options.openLocation === OpenWhere.lightbox) { @@ -355,33 +357,4 @@ export class DocumentManager { } } } -// eslint-disable-next-line default-param-last -export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) { - let doc = docIn; - const options = optionsIn; - const func = () => { - const cv = DocumentManager.Instance.getDocumentView(containingDoc); - const dv = DocumentManager.Instance.getDocumentView(doc, cv); - if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) { - DocumentManager.Instance.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document)); - } else { - const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc)); - const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; - options.toggleTarget = undefined; - DocumentManager.Instance.showDocument(showDoc, options, () => DocumentManager.Instance.showDocument(doc, { ...options, openLocation: undefined })).then(() => { - const cvFound = DocumentManager.Instance.getDocumentView(containingDoc); - const dvFound = DocumentManager.Instance.getDocumentView(doc, cvFound); - dvFound && Doc.linkFollowHighlight(dvFound.Document); - }); - } - }; - if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) { - doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!; - } - if (doc.hidden) { - doc.hidden = false; - options.toggleTarget = false; - setTimeout(func); - } else func(); -} -ScriptingGlobals.add(DocFocusOrOpen); +setTimeout(() => DocumentManager.Instance); diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 34f54be2e..fda505420 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -27,8 +27,6 @@ import { ScriptCast } from '../../fields/Types'; import { Docs } from '../documents/Documents'; import { DocumentView } from '../views/nodes/DocumentView'; import { dropActionType } from './DropActionTypes'; -import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; @@ -648,14 +646,3 @@ export namespace DragManager { document.addEventListener('pointerup', upHandler, true); } } - -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { - if (readOnly) { - return SelectionManager.Views.some(dv => dv.Document.keepZWhenDragged); - } - SelectionManager.Views.forEach(dv => { - dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged; - }); - return undefined; -}); diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index e1a503ac9..5701a22c0 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -77,7 +77,7 @@ export class GroupManager extends ObservableReactComponent<{}> { */ @action open = () => { - // SelectionManager.DeselectAll(); + // DocumentView.DeselectAll(); this.isOpen = true; this.populateUsers(); }; diff --git a/src/client/util/HypothesisUtils.ts b/src/client/util/HypothesisUtils.ts index dd18b2533..5a6522dc8 100644 --- a/src/client/util/HypothesisUtils.ts +++ b/src/client/util/HypothesisUtils.ts @@ -6,8 +6,6 @@ import { WebField } from '../../fields/URLField'; import { Docs } from '../documents/Documents'; import { DocumentLinksButton } from '../views/nodes/DocumentLinksButton'; import { DocumentView } from '../views/nodes/DocumentView'; -import { DocumentManager } from './DocumentManager'; -import { SelectionManager } from './SelectionManager'; export namespace Hypothesis { /** @@ -25,7 +23,7 @@ export namespace Hypothesis { * Search for a WebDocument whose url field matches the given uri, return undefined if not found */ export const findWebDoc = async (uri: string) => { - const currentDoc = SelectionManager.Docs.lastElement(); + const currentDoc = DocumentView.Selected().lastElement()?.Document; if (currentDoc && Cast(currentDoc.data, WebField)?.url.href === uri) return currentDoc; // always check first whether the currently selected doc is the annotation's source, only use Search otherwise const results: Doc[] = []; @@ -39,7 +37,7 @@ export namespace Hypothesis { // }) // ); - const onScreenResults = results.filter(doc => DocumentManager.Instance.getFirstDocumentView(doc)); + const onScreenResults = results.filter(doc => DocumentView.getFirstDocumentView(doc)); return onScreenResults.length ? onScreenResults[0] : results.length ? results[0] : undefined; // prioritize results that are currently on the screen }; @@ -65,7 +63,7 @@ export namespace Hypothesis { DocumentLinksButton.AnnotationId = annotationId; DocumentLinksButton.AnnotationUri = annotationUri; }); - const endLinkView = DocumentManager.Instance.getFirstDocumentView(sourceDoc); + const endLinkView = DocumentView.getFirstDocumentView(sourceDoc); const rect = document.body.getBoundingClientRect(); const x = rect.x + rect.width / 2; const y = 250; @@ -182,7 +180,7 @@ export namespace Hypothesis { bubbles: true, }) ); - const targetView: Opt = DocumentManager.Instance.getFirstDocumentView(target); + const targetView: Opt = DocumentView.getFirstDocumentView(target); const position = targetView?.screenToViewTransform().inverse().transformPoint(0, 0); targetView && position && simulateMouseClick(targetView.ContentDiv!, position[0], position[1], position[0], position[1], false); }, 300); diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx index 398ba3c04..baa0786b7 100644 --- a/src/client/util/Import & Export/DirectoryImportBox.tsx +++ b/src/client/util/Import & Export/DirectoryImportBox.tsx @@ -17,7 +17,6 @@ // import { DocumentType } from '../../documents/DocumentTypes'; // import { Networking } from '../../Network'; // import { FieldView, FieldViewProps } from '../../views/nodes/FieldView'; -// import { DocumentManager } from '../DocumentManager'; // import './DirectoryImportBox.scss'; // import ImportMetadataEntry, { keyPlaceholder, valuePlaceholder } from './ImportMetadataEntry'; // import * as React from 'react'; diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts index a6f6122ab..9a0edcfec 100644 --- a/src/client/util/LinkFollower.ts +++ b/src/client/util/LinkFollower.ts @@ -1,14 +1,12 @@ import { action, runInAction } from 'mobx'; -import { Doc, DocListCast, FieldResult, FieldType, Opt } from '../../fields/Doc'; -import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; +import { DocumentView } from '../views/nodes/DocumentView'; import { FocusViewOptions } from '../views/nodes/FocusViewOptions'; import { OpenWhere } from '../views/nodes/OpenWhere'; import { PresBox } from '../views/nodes/trails'; -import { DocumentManager } from './DocumentManager'; -import { LinkManager } from './LinkManager'; import { ScriptingGlobals } from './ScriptingGlobals'; -import { SelectionManager } from './SelectionManager'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; /* @@ -24,6 +22,9 @@ import { UndoManager } from './UndoManager'; * - user defined kvps */ export class LinkFollower { + public static Init() { + DocumentView.FollowLink = LinkFollower.FollowLink; + } // follows a link - if the target is on screen, it highlights/pans to it. // if the target isn't onscreen, then it will open up the target in the lightbox, or in place // depending on the followLinkLocation property of the source (or the link itself as a fallback); @@ -42,9 +43,9 @@ export class LinkFollower { }; public static traverseLink(link: Opt, sourceDoc: Doc, finished?: () => void, traverseBacklink?: boolean) { - const getView = (doc: Doc) => DocumentManager.Instance.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); + const getView = (doc: Doc) => DocumentView.getFirstDocumentView(DocCast(doc.layout_unrendered ? doc.annotationOn : doc)); const isAnchor = (source: Doc, anchor: Doc) => Doc.AreProtosEqual(anchor, source) || Doc.AreProtosEqual(anchor.annotationOn as Doc, source); - const linkDocs = link ? [link] : LinkManager.Links(sourceDoc); + const linkDocs = link ? [link] : Doc.Links(sourceDoc); const fwdLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_1 as Doc)); // link docs where 'sourceDoc' is link_anchor_1 const backLinks = linkDocs.filter(l => isAnchor(sourceDoc, l.link_anchor_2 as Doc)); // link docs where 'sourceDoc' is link_anchor_2 const fwdLinkWithoutTargetView = fwdLinks.find(l => !getView(DocCast(l.link_anchor_2))); @@ -68,7 +69,7 @@ export class LinkFollower { ? linkDoc.link_anchor_2 : linkDoc.link_anchor_1 ) as Doc; - const srcAnchor: Doc = LinkManager.getOppositeAnchor(linkDoc, target) ?? sourceDoc; + const srcAnchor: Doc = Doc.getOppositeAnchor(linkDoc, target) ?? sourceDoc; if (target) { const doFollow = (canToggle?: boolean) => { const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle); @@ -87,14 +88,14 @@ export class LinkFollower { zoomTextSelections: BoolCast(srcAnchor.followLinkZoomText), }; if (target.type === DocumentType.PRES) { - const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail - SelectionManager.DeselectAll(); - if (!DocumentManager.Instance.AddViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { + const containerDocContext = DocumentView.getContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail + DocumentView.DeselectAll(); + if (!DocumentView.addViewRenderedCb(target, dv => containerDocContext.length && dv.ComponentView?.playTrail?.(containerDocContext))) { PresBox.OpenPresMinimized(target, [0, 0]); } finished?.(); } else { - DocumentManager.Instance.showDocument(target, options, allFinished); + DocumentView.showDocument(target, options, allFinished); } }; let movedTarget = false; @@ -133,10 +134,6 @@ export class LinkFollower { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function followLink(doc: Doc, altKey: boolean) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); return LinkFollower.FollowLink(undefined, doc, altKey) ? undefined : { select: true }; }); - -export function IsFollowLinkScript(field: FieldResult) { - return ScriptCast(field)?.script.originalScript.includes('return followLink('); -} diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index dbd13f978..56d5dce4e 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -30,8 +30,8 @@ export class LinkManager { @observable.shallow userLinkDBs: Doc[] = []; @observable public currentLink: Opt = undefined; @observable public currentLinkAnchor: Opt = undefined; - public static get Instance() { - return LinkManager._instance; + public static get Instance(): LinkManager { + return Doc.UserDoc() ? LinkManager._instance ?? new LinkManager() : (undefined as any as LinkManager); } public static Links(doc: Doc | undefined) { @@ -47,10 +47,13 @@ export class LinkManager { this.userLinkDBs.push(linkDb); }; public static AutoKeywords = 'keywords:Usages'; - constructor() { + private constructor() { makeObservable(this); LinkManager._instance = this; Doc.AddLink = this.addLink; + Doc.DeleteLink = this.deleteLink; + Doc.Links = LinkManager.Links; + Doc.getOppositeAnchor = LinkManager.getOppositeAnchor; this.createlink_relationshipLists(); // since this is an action, not a reaction, we get only one shot to add this link to the Anchor docs // Thus make sure all promised values are resolved from link -> link.proto -> link.link_anchor_[1,2] -> link.link_anchor_[1,2].proto diff --git a/src/client/util/PingManager.ts b/src/client/util/PingManager.ts index e5e69c5ac..255e9cee0 100644 --- a/src/client/util/PingManager.ts +++ b/src/client/util/PingManager.ts @@ -1,6 +1,6 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { Networking } from '../Network'; -import { CurrentUserUtils } from './CurrentUserUtils'; +import { SnappingManager } from './SnappingManager'; export class PingManager { // create static instance and getter for global use @@ -32,7 +32,7 @@ export class PingManager { try { const res = await Networking.PostToServer('/ping', { date: new Date() }); runInAction(() => { - CurrentUserUtils.ServerVersion = res.message; + SnappingManager.SetServerVersion(res.message); }); !this.IsBeating && this.setIsBeating(true); } catch { diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts index dcc6aabab..530fcf211 100644 --- a/src/client/util/ReplayMovements.ts +++ b/src/client/util/ReplayMovements.ts @@ -4,8 +4,8 @@ import { CollectionDockingView } from '../views/collections/CollectionDockingVie import { CollectionFreeFormView } from '../views/collections/collectionFreeForm'; import { OpenWhereMod } from '../views/nodes/OpenWhere'; import { VideoBox } from '../views/nodes/VideoBox'; -import { DocumentManager } from './DocumentManager'; import { Movement, Presentation } from './TrackMovements'; +import { DocumentView } from '../views/nodes/DocumentView'; export class ReplayMovements { private timers: NodeJS.Timeout[] | null; @@ -106,7 +106,7 @@ export class ReplayMovements { // returns undefined if the docView isn't open on the screen getCollectionFFView = (doc: Doc) => { - const isInView = DocumentManager.Instance.getDocumentView(doc); + const isInView = DocumentView.getDocumentView(doc); return isInView?.ComponentView as CollectionFreeFormView; }; @@ -118,7 +118,7 @@ export class ReplayMovements { } // console.log('openTab', docId, doc); CollectionDockingView.AddSplit(doc, OpenWhereMod.right); - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); // BUG - this returns undefined if the doc is already open return docView?.ComponentView as CollectionFreeFormView; }; diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 8df579e75..6948469cc 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -5,8 +5,8 @@ // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' // eslint-disable-next-line node/no-unpublished-import -import * as ts from 'typescript'; import * as typescriptlib from '!!raw-loader!./type_decls.d'; +import * as ts from 'typescript'; import { Doc, FieldType } from '../../fields/Doc'; import { RefField } from '../../fields/RefField'; import { ScriptField } from '../../fields/ScriptField'; @@ -195,7 +195,6 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp if (found) return found as CompiledScript; const { requiredType = '', addReturn = false, params = {}, capturedVariables = {}, typecheck = true } = options; if (options.params && !options.params.this) options.params.this = Doc.name; - if (options.params && !options.params.self) options.params.self = Doc.name; if (options.globals) { ScriptingGlobals.setScriptingGlobals(options.globals); } @@ -266,9 +265,3 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp !signature.includes('XXX') && ScriptField._scriptFieldCache.set(script + ':' + signature, result as CompiledScript); return result; } - -ScriptingGlobals.add(CompileScript); -// eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function runScript(doc: Doc, script: ScriptField) { - return script?.script.run({ this: doc }).result; -}); diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts index 7e8f42de9..0b942116c 100644 --- a/src/client/util/SelectionManager.ts +++ b/src/client/util/SelectionManager.ts @@ -1,6 +1,5 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import { Doc, Opt } from '../../fields/Doc'; -import { DocViews } from '../../fields/DocSymbols'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast, DocCast } from '../../fields/Types'; @@ -24,6 +23,13 @@ export class SelectionManager { private constructor() { SelectionManager._manager = this; makeObservable(this); + DocumentView.DeselectAll = SelectionManager.DeselectAll; + DocumentView.DeselectView = SelectionManager.DeselectView; + DocumentView.SelectView = SelectionManager.SelectView; + DocumentView.SelectedDocs = SelectionManager.Docs; + DocumentView.Selected = SelectionManager.Views; + DocumentView.SelectSchemaDoc = SelectionManager.SelectSchemaViewDoc; + DocumentView.SelectedSchemaDoc = () => this.SelectedSchemaDocument; } @action @@ -54,8 +60,10 @@ export class SelectionManager { public static DeselectAll = (except?: Doc): void => { const found = this.Instance.SelectedViews.find(dv => dv.Document === except); runInAction(() => { - LinkManager.Instance.currentLink = undefined; - LinkManager.Instance.currentLinkAnchor = undefined; + if (LinkManager.Instance) { + LinkManager.Instance.currentLink = undefined; + LinkManager.Instance.currentLinkAnchor = undefined; + } this.Instance.SelectedSchemaDocument = undefined; }); this.Instance.SelectedViews.forEach(dv => { @@ -68,19 +76,18 @@ export class SelectionManager { if (found) this.SelectView(found, false); }; - public static IsSelected = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected); - public static get Views() { return this.Instance.SelectedViews; } // prettier-ignore - public static get SelectedSchemaDoc() { return this.Instance.SelectedSchemaDocument; } // prettier-ignore - public static get Docs() { return this.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore + public static Views() { return SelectionManager.Instance.SelectedViews; } // prettier-ignore + public static get SelectedSchemaDoc() { return SelectionManager.Instance.SelectedSchemaDocument; } // prettier-ignore + public static Docs() { return SelectionManager.Instance.SelectedViews.map(dv => dv.Document).filter(doc => doc?._type_collection !== CollectionViewType.Docking); } // prettier-ignore } // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function SelectedDocType(type: string, expertMode: boolean, checkContext?: boolean) { if (Doc.noviceMode && expertMode) return false; if (type === 'tab') { - return SelectionManager.Views.lastElement()?._props.renderDepth === 0; + return DocumentView.Selected().lastElement()?._props.renderDepth === 0; } - const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(SelectionManager.SelectedSchemaDoc ?? SelectionManager.Docs.lastElement()); + const selected = (sel => (checkContext ? DocCast(sel?.embedContainer) : sel))(DocumentView.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement()); return selected?.type === type || selected?.type_collection === type || !type; }); // eslint-disable-next-line prefer-arrow-callback @@ -109,8 +116,6 @@ ScriptingGlobals.add(function redo() { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) { - const docs = SelectionManager.Views.map(dv => dv.Document).filter( - d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)) - ); + const docs = SelectionManager.Docs().filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP && (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null))); return docs.length ? new List(docs) : prevValue; }); diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 8cb97fe50..d3c10f9f4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { BsGoogle } from 'react-icons/bs'; import { FaFillDrip, FaPalette } from 'react-icons/fa'; import { ClientUtils, addStyleSheet, addStyleSheetRule } from '../../ClientUtils'; -import { Doc, Opt } from '../../fields/Doc'; +import { Doc } from '../../fields/Doc'; import { DashVersion } from '../../fields/DocSymbols'; import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { DocServer } from '../DocServer'; @@ -17,7 +17,7 @@ import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager import { MainViewModal } from '../views/MainViewModal'; import { GroupManager } from './GroupManager'; import './SettingsManager.scss'; -import { SnappingManager } from './SnappingManager'; +import { SnappingManager, freeformScrollMode } from './SnappingManager'; import { undoable } from './UndoManager'; export enum ColorScheme { @@ -28,11 +28,6 @@ export enum ColorScheme { Cupcake = 'Cupcake', } -export enum freeformScrollMode { - Pan = 'pan', - Zoom = 'zoom', -} - @observer export class SettingsManager extends React.Component<{}> { // eslint-disable-next-line no-use-before-define @@ -44,17 +39,15 @@ export class SettingsManager extends React.Component<{}> { @observable private _curr_password = ''; @observable private _new_password = ''; @observable private _new_confirm = ''; - @observable private _lastPressedSidebarBtn: Opt = undefined; // bcz: this is a hack to handle highlighting buttons in the leftpanel menu .. need to find a cleaner approach @observable private _activeTab = 'Accounts'; @observable private _isOpen = false; - @observable public propertiesWidth: number = 0; - private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)); public closeMgr = action(() => { this._isOpen = false; }); + // eslint-disable-next-line react/no-unused-class-component-methods public openMgr = action(() => { this._isOpen = true; }); @@ -154,9 +147,6 @@ export class SettingsManager extends React.Component<{}> { SnappingManager.SettingsStyle = SettingsManager._settingsStyle; } - public get LastPressedBtn() { return this._lastPressedSidebarBtn; } // prettier-ignore - public set LastPressedBtn(state:Doc|undefined) { this._lastPressedSidebarBtn = state; } // prettier-ignore - @computed public static get userColor() { return StrCast(Doc.UserDoc().userColor); } @@ -287,7 +277,7 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> - { toggleStatus={BoolCast(Doc.UserDoc().showLinkLines)} size={Size.XSMALL} color={SettingsManager.userColor} - /> + /> */} { const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users; const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList; - let docs = SelectionManager.Views.length < 2 ? [this.targetDoc] : SelectionManager.Views.map(docView => docView.Document); + let docs = DocumentView.Selected().length < 2 ? [this.targetDoc] : DocumentView.Selected().map(docView => docView.Document); if (this.myDocAcls) { const newDocs: Doc[] = []; @@ -442,7 +440,7 @@ export class SharingManager extends React.Component<{}> { const { user, sharingDoc } = recipient; const target = targetDoc || this.targetDoc!; const acl = `acl_${normalizeEmail(user.email)}`; - const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [target] : DocumentView.Selected().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); if (permission !== SharingPermissions.None) { @@ -460,7 +458,7 @@ export class SharingManager extends React.Component<{}> { const target = targetDoc || this.targetDoc!; const acl = `acl_${normalizeEmail(StrCast(group.title))}`; - const docs = SelectionManager.Views.length < 2 ? [target] : SelectionManager.Views.map(docView => docView.Document); + const docs = DocumentView.Selected().length < 2 ? [target] : DocumentView.Selected().map(docView => docView.Document); docs.map(doc => (this.layoutDocAcls || doc.dockingConfig ? doc : Doc.GetProto(doc))).forEach(doc => { distributeAcls(acl, permission as SharingPermissions, doc, undefined, this.upgradeNested ? true : undefined); @@ -643,14 +641,14 @@ export class SharingManager extends React.Component<{}> { private focusOn = (contents: string) => { const title = this.targetDoc ? StrCast(this.targetDoc.title) : ''; - const docs = SelectionManager.Views.length > 1 ? SelectionManager.Views.map(docView => docView.props.Document) : [this.targetDoc]; + const docs = DocumentView.Selected().length > 1 ? DocumentView.Selected().map(docView => docView.props.Document) : [this.targetDoc]; return ( { if (this.targetDoc && this.targetDocView && docs.length === 1) { - DocumentManager.Instance.showDocument(this.targetDoc, { willZoomCentered: true }); + DocumentView.showDocument(this.targetDoc, { willZoomCentered: true }); } }} onPointerEnter={action(() => { diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts index 5cd6ecfe1..6789c2ab8 100644 --- a/src/client/util/SnappingManager.ts +++ b/src/client/util/SnappingManager.ts @@ -1,5 +1,9 @@ import { observable, action, runInAction, makeObservable } from 'mobx'; +export enum freeformScrollMode { + Pan = 'pan', + Zoom = 'zoom', +} export class SnappingManager { // eslint-disable-next-line no-use-before-define private static _manager: SnappingManager; @@ -19,6 +23,9 @@ export class SnappingManager { @observable _vertSnapLines: number[] = []; @observable _exploreMode = false; @observable _userPanned = false; + @observable _serverVersion: string = ''; + @observable _lastBtnId: string = ''; + @observable _propertyWid: number = 0; private constructor() { SnappingManager._manager = this; @@ -45,6 +52,10 @@ export class SnappingManager { public static get CanEmbed() { return this.Instance._canEmbed; } // prettier-ignore public static get ExploreMode() { return this.Instance._exploreMode; } // prettier-ignore public static get UserPanned() { return this.Instance._userPanned; } // prettier-ignore + public static get ServerVersion() { return this.Instance._serverVersion; } // prettier-ignore + public static get LastPressedBtn() { return this.Instance._lastBtnId; } // prettier-ignore + public static get PropertiesWidth(){ return this.Instance._propertyWid; } // prettier-ignore + public static SetShiftKey = (down: boolean) => runInAction(() => {this.Instance._shiftKey = down}); // prettier-ignore public static SetCtrlKey = (down: boolean) => runInAction(() => {this.Instance._ctrlKey = down}); // prettier-ignore public static SetMetaKey = (down: boolean) => runInAction(() => {this.Instance._metaKey = down}); // prettier-ignore @@ -54,7 +65,10 @@ export class SnappingManager { public static SetIsResizing = (docid?:string) => runInAction(() => {this.Instance._isResizing = docid}); // prettier-ignore public static SetCanEmbed = (embed:boolean) => runInAction(() => {this.Instance._canEmbed = embed}); // prettier-ignore public static SetExploreMode = (state:boolean) => runInAction(() => {this.Instance._exploreMode = state}); // prettier-ignore - public static TriggerUserPanned = () => runInAction(() => {this.Instance._userPanned = !this.Instance._userPanned}); // prettier-ignore + public static TriggerUserPanned = () => runInAction(() => {this.Instance._userPanned = !this.Instance._userPanned}); // prettier-ignore + public static SetServerVersion = (version:string) =>runInAction(() => {this.Instance._serverVersion = version}); // prettier-ignore + public static SetLastPressedBtn = (id:string) =>runInAction(() => {this.Instance._lastBtnId = id}); // prettier-ignore + public static SetPropertiesWidth= (wid:number) =>runInAction(() => {this.Instance._propertyWid = wid}); // prettier-ignore public static userColor: string | undefined; public static userVariantColor: string | undefined; diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx index b1eb730fa..303672d90 100644 --- a/src/client/views/AntimodeMenu.tsx +++ b/src/client/views/AntimodeMenu.tsx @@ -1,6 +1,6 @@ import { action, makeObservable, observable, runInAction } from 'mobx'; import * as React from 'react'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import './AntimodeMenu.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -157,7 +157,7 @@ export abstract class AntimodeMenu extends Observab left: this._left, top: this._top, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -183,7 +183,7 @@ export abstract class AntimodeMenu extends Observab height: 'inherit', width: 200, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, @@ -206,7 +206,7 @@ export abstract class AntimodeMenu extends Observab left: this._left, top: this._top, opacity: this._opacity, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, transitionProperty: this._transitionProperty, transitionDuration: this._transitionDuration, transitionDelay: this._transitionDelay, diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx index 64b8a8446..929b549e0 100644 --- a/src/client/views/ComponentDecorations.tsx +++ b/src/client/views/ComponentDecorations.tsx @@ -1,7 +1,7 @@ import { observer } from 'mobx-react'; import * as React from 'react'; -import { SelectionManager } from '../util/SelectionManager'; import './ComponentDecorations.scss'; +import { DocumentView } from './nodes/DocumentView'; @observer export class ComponentDecorations extends React.Component<{ boundsTop: number; boundsLeft: number }, { value: string }> { @@ -9,7 +9,7 @@ export class ComponentDecorations extends React.Component<{ boundsTop: number; b static Instance: ComponentDecorations; render() { - const seldoc = SelectionManager.Views.lastElement(); + const seldoc = DocumentView.Selected().lastElement(); return seldoc?.ComponentView?.componentUI?.(this.props.boundsLeft, this.props.boundsTop) ?? null; } } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index a0e3d2ddd..97ff346e4 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -60,6 +60,8 @@ export interface ViewBoxInterface { ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; search?: (str: string, bwd?: boolean, clear?: boolean) => boolean; + dontRegisterView?: () => boolean; // KeyValueBox's don't want to register their views + isUnstyledView?: () => boolean; // SchemaView and KeyValue are untyled -- not tiles, no opacity } /** * DocComponent returns a React base class used by Doc views with accessors for unpacking the Document,layoutDoc, and dataDoc's diff --git a/src/client/views/DocViewUtils.ts b/src/client/views/DocViewUtils.ts new file mode 100644 index 000000000..1f5f29c7e --- /dev/null +++ b/src/client/views/DocViewUtils.ts @@ -0,0 +1,21 @@ +/* eslint-disable prefer-destructuring */ +/* eslint-disable default-param-last */ +/* eslint-disable no-use-before-define */ +import { runInAction } from 'mobx'; +import { Doc, SetActiveAudioLinker } from '../../fields/Doc'; +import { DocUtils } from '../documents/DocUtils'; +import { FieldViewProps } from './nodes/FieldView'; + +export namespace DocViewUtils { + export const ActiveRecordings: { props: FieldViewProps; getAnchor: (addAsAnnotation: boolean) => Doc }[] = []; + + export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { + broadcastEvent && runInAction(() => { Doc.RecordingEvent += 1; }); // prettier-ignore + return ActiveRecordings.map(audio => { + const sourceDoc = getSourceDoc(); + return sourceDoc && DocUtils.MakeLink(sourceDoc, audio.getAnchor(true) || audio.props.Document, { link_relationship: 'recording annotation:linked recording', link_description: 'recording timeline' }); + }); + } + + SetActiveAudioLinker(MakeLinkToActiveAudio); +} diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index dd744f272..487868169 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -14,23 +14,21 @@ import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from ' import { emptyFunction } from '../../Utils'; import { Doc } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; +import { DictationManager } from '../util/DictationManager'; import { DragManager } from '../util/DragManager'; import { dropActionType } from '../util/DropActionTypes'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { SelectionManager } from '../util/SelectionManager'; import { SharingManager } from '../util/SharingManager'; import { UndoManager, undoBatch } from '../util/UndoManager'; import './DocumentButtonBar.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PinProps } from './PinFuncs'; import { TemplateMenu } from './TemplateMenu'; -import { TabDocView } from './collections/TabDocView'; import { Colors } from './global/globalEnums'; import { LinkPopup } from './linking/LinkPopup'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; +import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; @@ -206,7 +204,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( .views() .filter(v => v) .map(dv => dv!.Document); - TabDocView.PinDoc(docs, { + DocumentView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: pinLayoutView, pinData: { dataview: pinContentView }, @@ -221,7 +219,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); }; return !targetDoc ? null : ( - {`Pin Document ${SelectionManager.Views.length > 1 ? 'multiple documents' : ''} to Trail`}
    }> + {`Pin Document ${DocumentView.Selected().length > 1 ? 'multiple documents' : ''} to Trail`}
    }>
    { @@ -229,7 +227,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( .views() .filter(v => v) .map(dv => dv!.Document); - TabDocView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + DocumentView.PinDoc(docs, { pinAudioPlay: true, pinDocLayout: e.shiftKey, pinData: { dataview: e.altKey }, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); e.stopPropagation(); }}>
    @@ -299,7 +297,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( this._props.views().map( view => view && - DocumentViewInternal.recordAudioAnnotation( + DictationManager.recordAudioAnnotation( view.dataDoc, view.LayoutFieldKey, stopFunc => { @@ -381,7 +379,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( } openContextMenu = (e: PointerEvent) => { - let child = SelectionManager.Views[0].ContentDiv!.children[0]; + let child = DocumentView.Selected()[0].ContentDiv!.children[0]; while (child.children.length) { const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string'); if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break; @@ -442,14 +440,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
    {this._showLinkPopup ? (
    - { - link.link_displayLine = !IsFollowLinkScript(this._props.views().lastElement()?.Document.onClick); - }} - linkCreateAnchor={() => this._props.views().lastElement()?.ComponentView?.getAnchor?.(true)} - linkFrom={() => this._props.views().lastElement()?.Document} - /> + this._props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this._props.views().lastElement()?.Document} />
    ) : (
    {this.linkButton}
    @@ -457,7 +448,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( {DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== doc ?
    {this.endLinkButton}
    : null}
    {this.templateButton}
    - {!SelectionManager.Views?.some(v => v.allLinks.length) ? null :
    {this.followLinkButton}
    } + {!DocumentView.Selected().some(v => v.allLinks.length) ? null :
    {this.followLinkButton}
    }
    {this.pinButton}
    {this.recordButton}
    {this.calendarButton}
    diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index ef493fb69..432b02782 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -17,9 +17,7 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl } from '../../fields/util'; import { DocumentType } from '../documents/DocumentTypes'; import { Docs } from '../documents/Documents'; -import { DocumentManager } from '../util/DocumentManager'; import { DragManager } from '../util/DragManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { UndoManager } from '../util/UndoManager'; @@ -81,14 +79,14 @@ export class DocumentDecorations extends ObservableReactComponent center.x+x || this.Bounds.r < center.x+x || this.Bounds.y > center.y+y || this.Bounds.b < center.y+y ))); @@ -110,7 +108,7 @@ export class DocumentDecorations extends ObservableReactComponent dv._props.renderDepth > 0) .map(dv => dv.getBounds) .reduce((bounds, rect) => !rect ? bounds @@ -127,7 +125,7 @@ export class DocumentDecorations extends ObservableReactComponent#')) { - SelectionManager.Docs.forEach(doc => { + DocumentView.SelectedDocs().forEach(doc => { doc[DocData].onViewMounted = ScriptField.MakeScript(`updateTagsCollection(this)`); }); } @@ -135,11 +133,11 @@ export class DocumentDecorations extends ObservableReactComponent titleFieldKey && - SelectionManager.Views.forEach(d => { + DocumentView.Selected().forEach(dv => { if (titleFieldKey === 'title') { - d.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); + dv.dataDoc.title_custom = !this._accumulatedTitle.startsWith('-'); } - KeyValueBox.SetField(d.Document, titleFieldKey, this._accumulatedTitle); + KeyValueBox.SetField(dv.Document, titleFieldKey, this._accumulatedTitle); }), 'edit title' ); @@ -154,7 +152,7 @@ export class DocumentDecorations extends ObservableReactComponent { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); + const effectiveLayoutAcl = GetEffectiveAcl(DocumentView.Selected()[0].Document); if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) { setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, emptyFunction); e.stopPropagation(); @@ -162,7 +160,7 @@ export class DocumentDecorations extends ObservableReactComponent { - const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document); + const effectiveLayoutAcl = GetEffectiveAcl(DocumentView.SelectedDocs()[0]); if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) { setupMoveUpEvents( this, @@ -170,7 +168,7 @@ export class DocumentDecorations extends ObservableReactComponent this.onBackgroundMove(true, moveEv), emptyFunction, action(() => { - const selected = SelectionManager.Views.length === 1 ? SelectionManager.Docs[0] : undefined; + const selected = DocumentView.SelectedDocs().length === 1 ? DocumentView.SelectedDocs()[0] : undefined; !this._editingTitle && (this._accumulatedTitle = this._titleControlString.startsWith('$') ? (selected && Field.toKeyValueString(selected, this._titleControlString.substring(1))) || '-unset-' : this._titleControlString); this._editingTitle = true; this._keyinput.current && setTimeout(this._keyinput.current.focus); @@ -186,19 +184,16 @@ export class DocumentDecorations extends ObservableReactComponent { - const dragDocView = SelectionManager.Views[0]; + const dragDocView = DocumentView.Selected()[0]; const effectiveLayoutAcl = GetEffectiveAcl(dragDocView.Document); if (effectiveLayoutAcl !== AclAdmin && effectiveLayoutAcl !== AclEdit && effectiveLayoutAcl !== AclAugment) { return false; } const containers = new Set(); - SelectionManager.Views.forEach(v => containers.add(DocCast(v.Document.embedContainer))); + DocumentView.Selected().forEach(v => containers.add(DocCast(v.Document.embedContainer))); if (containers.size > 1) return false; const { left, top } = dragDocView.getBounds || { left: 0, top: 0 }; - const dragData = new DragManager.DocumentDragData( - SelectionManager.Views.map(dv => dv.Document), - dragDocView._props.dropAction - ); + const dragData = new DragManager.DocumentDragData(DocumentView.SelectedDocs(), dragDocView._props.dropAction); dragData.offset = dragDocView.screenToContentsTransform().transformDirection(e.x - left, e.y - top); dragData.moveDocument = dragDocView._props.moveDocument; dragData.removeDocument = dragDocView._props.removeDocument; @@ -206,7 +201,7 @@ export class DocumentDecorations extends ObservableReactComponent dv.ContentDiv!), + DocumentView.Selected().map(dv => dv.ContentDiv!), dragData, e.x, e.y, @@ -223,7 +218,7 @@ export class DocumentDecorations extends ObservableReactComponent { - const views = SelectionManager.Views.filter(v => v && v._props.renderDepth > 0); + const views = DocumentView.Selected().filter(v => v && v._props.renderDepth > 0); if (forceDeleteOrIconify === false && this._iconifyBatch) return; this._deleteAfterIconify = !!(forceDeleteOrIconify || this._iconifyBatch); let iconifyingCount = views.length; @@ -239,7 +234,7 @@ export class DocumentDecorations extends ObservableReactComponent { - setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [SelectionManager.Views.lastElement().Document]) ?? false, emptyFunction, this.onMaximizeClick, false, false); + setupMoveUpEvents(this, e, () => DragManager.StartWindowDrag?.(e, [DocumentView.SelectedDocs().lastElement()]) ?? false, emptyFunction, this.onMaximizeClick, false, false); e.stopPropagation(); }; onMaximizeClick = (e: any): void => { - const selectedDocs = SelectionManager.Views; + const selectedDocs = DocumentView.SelectedDocs(); if (selectedDocs.length) { if (e.ctrlKey) { // open an embedding in a new tab with Ctrl Key - CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0].Document), OpenWhereMod.right); + CollectionDockingView.AddSplit(Doc.BestEmbedding(selectedDocs[0]), OpenWhereMod.right); } else if (e.shiftKey) { // open centered in a new workspace with Shift Key - const embedding = Doc.MakeEmbedding(selectedDocs[0].Document); + const embedding = Doc.MakeEmbedding(selectedDocs[0]); embedding.embedContainer = undefined; embedding.x = -NumCast(embedding._width) / 2; embedding.y = -NumCast(embedding._height) / 2; CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([embedding], { title: 'Tab for ' + embedding.title }), OpenWhereMod.right); } else if (e.altKey) { // open same document in new tab - CollectionDockingView.ToggleSplit(selectedDocs[0].Document, OpenWhereMod.right); + CollectionDockingView.ToggleSplit(selectedDocs[0], OpenWhereMod.right); } else { - let openDoc = selectedDocs[0].Document; + let openDoc = selectedDocs[0]; if (openDoc.layout_fieldKey === 'layout_icon') { openDoc = Doc.GetEmbeddings(openDoc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc); Doc.deiconifyView(openDoc); } - LightboxView.Instance.SetLightboxDoc( - openDoc, - undefined, - selectedDocs.slice(1).map(view => view.Document) - ); + LightboxView.Instance.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1)); } } - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); }; onIconifyClick = (): void => { - SelectionManager.Views.forEach(dv => dv?.iconify()); - SelectionManager.DeselectAll(); + DocumentView.Selected().forEach(dv => dv?.iconify()); + DocumentView.DeselectAll(); }; - onSelectContainerDocClick = () => SelectionManager.Views?.[0]?.containerViewPath?.().lastElement()?.select(false); + onSelectContainerDocClick = () => DocumentView.Selected()?.[0]?.containerViewPath?.().lastElement()?.select(false); /** * sets up events when user clicks on the border radius editor */ @action onRadiusDown = (e: React.PointerEvent): void => { - SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement()?.[Id]); + SnappingManager.SetIsResizing(DocumentView.SelectedDocs().lastElement()?.[Id]); this._isRounding = true; this._resizeUndo = UndoManager.StartBatch('DocDecs set radius'); setupMoveUpEvents( @@ -314,7 +305,7 @@ export class DocumentDecorations extends ObservableReactComponent { + DocumentView.SelectedDocs().forEach(doc => { const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2); const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box doc._layout_borderRounding = `${radius}px`; @@ -339,7 +330,7 @@ export class DocumentDecorations extends ObservableReactComponent UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => Doc.toggleLockedPosition(doc)), 'toggleBackground') + () => UndoManager.RunInBatch(() => DocumentView.Selected().forEach(dv => Doc.toggleLockedPosition(dv.Document)), 'toggleBackground') ); e.stopPropagation(); }; @@ -356,7 +347,7 @@ export class DocumentDecorations extends ObservableReactComponent { this._isRotating = true; - const seldocview = SelectionManager.Views[0]; + const seldocview = DocumentView.Selected()[0]; setupMoveUpEvents( this, e, @@ -374,11 +365,11 @@ export class DocumentDecorations extends ObservableReactComponent i.ComponentView instanceof InkingStroke); + const selectedInk = DocumentView.Selected().filter(i => i.ComponentView instanceof InkingStroke); const centerPoint = this.rotCenter.slice(); const infos = new Map(); - const seldocview = SelectionManager.Views[0]; - SelectionManager.Views.forEach(dv => { + const seldocview = DocumentView.Selected()[0]; + DocumentView.Selected().forEach(dv => { const accumRot = (NumCast(dv.Document._rotation) / 180) * Math.PI; const localRotCtr = dv.screenToViewTransform().transformPoint(rcScreen.X, rcScreen.Y); const localRotCtrOffset = [localRotCtr[0] - NumCast(dv.Document.width) / 2, localRotCtr[1] - NumCast(dv.Document.height) / 2]; @@ -387,7 +378,7 @@ export class DocumentDecorations extends ObservableReactComponent { - SelectionManager.Views.forEach( + DocumentView.Selected().forEach( action(dv => { const { unrotatedDocPos, startRotCtr, accumRot } = infos.get(dv.Document)!; const endRotCtr = Utils.rotPt(startRotCtr.x, startRotCtr.y, isAbs ? angle : accumRot + angle); @@ -437,7 +428,7 @@ export class DocumentDecorations extends ObservableReactComponent { - SnappingManager.SetIsResizing(SelectionManager.Docs.lastElement()?.[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them + SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction); e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); @@ -449,7 +440,7 @@ export class DocumentDecorations extends ObservableReactComponent CollectionFreeFormView.from(docView)?.dragStarting(false, false)); + DocumentView.Selected().forEach(docView => CollectionFreeFormView.from(docView)?.dragStarting(false, false)); }; projectDragToAspect = (e: PointerEvent, docView: DocumentView, fixedAspect: number) => { @@ -467,7 +458,7 @@ export class DocumentDecorations extends ObservableReactComponent { - const first = SelectionManager.Views[0]; + const first = DocumentView.Selected()[0]; const effectiveAcl = GetEffectiveAcl(first.Document); if (!(effectiveAcl === AclAdmin || effectiveAcl === AclEdit || effectiveAcl === AclAugment)) return false; if (!first) return false; @@ -484,10 +475,10 @@ export class DocumentDecorations extends ObservableReactComponent { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) this._interactionLock = true; this._snapPt = thisPt; - e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); - const hasFixedAspect = SelectionManager.Docs.some(this.hasFixedAspect); + e.ctrlKey && (DocumentView.Selected().forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions())); + const hasFixedAspect = DocumentView.Selected().map(dv => dv.Document).some(this.hasFixedAspect); const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y}; - SelectionManager.Views.forEach(docView => + DocumentView.Selected().forEach(docView => this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore await new Promise(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore @@ -526,7 +517,7 @@ export class DocumentDecorations extends ObservableReactComponent DocumentManager.Instance.getDocumentView(member, docView)!) + .map(member => DocumentView.getDocumentView(member, docView)!) .forEach(member => this.resizeView(member, refPt, scale, opts)); doc.xPadding = NumCast(doc.xPadding) * scale.x; doc.yPadding = NumCast(doc.yPadding) * scale.y; @@ -607,7 +598,7 @@ export class DocumentDecorations extends ObservableReactComponent { + DocumentView.Selected().forEach(view => { NumCast(view.Document._height) < 20 && (view.layoutDoc._layout_autoHeight = true); }); // need to change points for resize, or else rotation/control points will fail. @@ -626,18 +617,18 @@ export class DocumentDecorations extends ObservableReactComponent 1 ? '-multiple-' : '-unset-'; + return DocumentView.Selected().length > 1 ? '-multiple-' : '-unset-'; } @computed get rotCenter() { - const lastView = SelectionManager.Views.lastElement(); + const lastView = DocumentView.Selected().lastElement(); if (lastView) { const invXf = lastView.screenToContentsTransform().inverse(); const seldoc = lastView.layoutDoc; @@ -649,7 +640,7 @@ export class DocumentDecorations extends ObservableReactComponent { @@ -678,7 +669,7 @@ export class DocumentDecorations extends ObservableReactComponent docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || + DocumentView.Selected().some(docView => docView.Document._dragOnlyWithinContainer || docView.Document.isGroup || docView.Document.layout_hideOpenButton) || this._isRounding || this._isRotating; const hideDeleteButton = @@ -688,7 +679,7 @@ export class DocumentDecorations extends ObservableReactComponent { + DocumentView.Selected().some(docView => { const collectionAcl = docView.containerViewPath?.()?.lastElement() ? GetEffectiveAcl(docView.containerViewPath?.().lastElement().dataDoc) : AclEdit; return collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.Document) !== AclAdmin; }); @@ -703,7 +694,7 @@ export class DocumentDecorations extends ObservableReactComponent 135; const useRotation = !hideResizers && seldocview.Document.type !== DocumentType.EQUATION && CollectionFreeFormDocumentView.from(seldocview); // when do we want an object to not rotate? - const rotation = SelectionManager.Views.length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; + const rotation = DocumentView.Selected().length === 1 ? seldocview.screenToContentsTransform().inverse().RotateDeg : 0; // Radius constants const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView; @@ -773,7 +764,7 @@ export class DocumentDecorations extends ObservableReactComponent CollectionFreeFormDocumentView.from(v)); + const freeformDoc = DocumentView.Selected().some(v => CollectionFreeFormDocumentView.from(v)); return (
    - SelectionManager.Views} /> + DocumentView.Selected()} />
    )}
    diff --git a/src/client/views/FieldsDropdown.tsx b/src/client/views/FieldsDropdown.tsx index 3cb7848c2..0ea0ebd83 100644 --- a/src/client/views/FieldsDropdown.tsx +++ b/src/client/views/FieldsDropdown.tsx @@ -13,7 +13,7 @@ import Select from 'react-select'; import { Doc } from '../../fields/Doc'; import { DocOptions, FInfo } from '../documents/Documents'; import { SearchUtil } from '../util/SearchUtil'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import './FilterPanel.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -68,36 +68,36 @@ export class FieldsDropdown extends ObservableReactComponent ({ ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, }), placeholder: (baseStyles /* , state */) => ({ ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, + color: SnappingManager.userColor, + background: SnappingManager.userBackgroundColor, }), input: (baseStyles /* , state */) => ({ ...baseStyles, padding: 0, margin: 0, - color: SettingsManager.userColor, + color: SnappingManager.userColor, background: 'transparent', }), option: (baseStyles, state) => ({ ...baseStyles, - color: SettingsManager.userColor, - background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, + color: SnappingManager.userColor, + background: !state.isFocused ? SnappingManager.userBackgroundColor : SnappingManager.userVariantColor, }), menuList: (baseStyles /* , state */) => ({ ...baseStyles, - backgroundColor: SettingsManager.userBackgroundColor, + backgroundColor: SnappingManager.userBackgroundColor, }), }} placeholder={typeof this._props.placeholder === 'string' ? this._props.placeholder : this._props.placeholder?.()} diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index ed9fc8dfb..c97edd7f0 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -11,12 +11,12 @@ import { Doc, DocListCast, Field, FieldType, LinkedTo, StrListCast } from '../.. import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; import { RichTextField } from '../../fields/RichTextField'; -import { DocumentManager } from '../util/DocumentManager'; import { SearchUtil } from '../util/SearchUtil'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import { undoable } from '../util/UndoManager'; import { FieldsDropdown } from './FieldsDropdown'; import './FilterPanel.scss'; +import { DocumentView } from './nodes/DocumentView'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -40,7 +40,7 @@ export class FilterPanel extends ObservableReactComponent { return this._props.Document; } @computed get targetDocChildKey() { - const targetView = DocumentManager.Instance.getFirstDocumentView(this.Document); + const targetView = DocumentView.getFirstDocumentView(this.Document); return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data'; } @computed get targetDocChildren() { @@ -299,7 +299,7 @@ export class FilterPanel extends ObservableReactComponent { .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader) ?.split(Doc.FilterSep)[1] } - style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }} + style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }} onBlur={undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')} onKeyDown={e => e.key === 'Enter' && undoable(() => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')()} /> diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 0d1573ea3..7246f0ba0 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -65,8 +65,6 @@ export class GestureOverlay extends ObservableReactComponent { - GestureOverlay.DownDocView = undefined; + DocumentView.DownDocView = undefined; if (this._points.length > 1) { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); @@ -388,7 +386,7 @@ export class GestureOverlay extends ObservableReactComponent KeyControlInfo; export class KeyManager { - public static Instance = new KeyManager(); + // eslint-disable-next-line no-use-before-define + public static Instance: KeyManager; private router = new Map(); constructor() { + KeyManager.Instance = this; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; // SHIFT CONTROL ALT META @@ -46,6 +48,16 @@ export class KeyManager { this.router.set(isMac ? '0100' : '0010', this.alt); this.router.set(isMac ? '1001' : '1100', this.ctrl_shift); this.router.set('1000', this.shift); + + window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); + window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); + window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); + window.removeEventListener('keydown', KeyManager.Instance.handle); + window.addEventListener('keydown', KeyManager.Instance.handle); + window.removeEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('keyup', KeyManager.Instance.unhandle); + window.addEventListener('paste', KeyManager.Instance.paste as any); } public unhandle = action((/* e: KeyboardEvent */) => { @@ -89,8 +101,8 @@ export class KeyManager { private handleGreedy = action((/* keyname: string */) => {}); nudge = (x: number, y: number, label: string) => { - const nudgeable = SelectionManager.Views.some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); - nudgeable && UndoManager.RunInBatch(() => SelectionManager.Views.map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); + const nudgeable = DocumentView.Selected().some(dv => CollectionFreeFormDocumentView.from(dv)?.nudge); + nudgeable && UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => CollectionFreeFormDocumentView.from(dv)?.nudge(x, y)), label); return { stopPropagation: nudgeable, preventDefault: nudgeable }; }; @@ -98,16 +110,16 @@ export class KeyManager { switch (keyname) { case 'u': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const ungroupings = SelectionManager.Views; + const ungroupings = DocumentView.Selected(); undoable(() => () => ungroupings.forEach(dv => { dv.layoutDoc.group = undefined; }), 'ungroup'); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - const selected = SelectionManager.Views; + const selected = DocumentView.Selected(); const cv = selected.reduce((col, dv) => (!col || CollectionFreeFormView.from(dv) === col ? CollectionFreeFormView.from(dv) : undefined), undefined as undefined | CollectionFreeFormView); - cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, SelectionManager.Docs), 'grouping'); + cv && undoable(() => cv._marqueeViewRef.current?.collection(e, true, DocumentView.SelectedDocs()), 'grouping'); } break; case ' ': @@ -133,7 +145,7 @@ export class KeyManager { doDeselect = !ContextMenu.Instance.closeMenu(); } if (doDeselect) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); LightboxView.Instance.SetLightboxDoc(undefined); } // DictationManager.Controls.stop(); @@ -153,7 +165,7 @@ export class KeyManager { if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { if (LightboxView.LightboxDoc) { LightboxView.Instance.SetLightboxDoc(undefined); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } else if (!window.getSelection()?.toString()) DocumentDecorations.Instance.onCloseClick(true); return { stopPropagation: true, preventDefault: true }; } @@ -179,15 +191,15 @@ export class KeyManager { case 'arrowdown': return this.nudge(0, 10, 'nudge down'); case 'u' : if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { - UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => {doc.group = undefined}), 'unggroup'); - SelectionManager.DeselectAll(); + UndoManager.RunInBatch(() => DocumentView.Selected().map(dv => dv.Document).forEach(doc => {doc.group = undefined}), 'unggroup'); + DocumentView.DeselectAll(); } break; case 'g': if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') { const randomGroup = random(0, 1000); - UndoManager.RunInBatch(() => SelectionManager.Docs.forEach(doc => {doc.group = randomGroup}), 'group'); - SelectionManager.DeselectAll(); + UndoManager.RunInBatch(() => DocumentView.Selected().forEach(dv => {dv.Document.group = randomGroup}), 'group'); + DocumentView.DeselectAll(); } break; default: @@ -206,7 +218,7 @@ export class KeyManager { switch (keyname) { case 'Æ’': case 'f': - UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(SelectionManager.Views?.[0])?.float(), 'float'); + UndoManager.RunInBatch(() => CollectionFreeFormDocumentView.from(DocumentView.Selected()?.[0])?.float(), 'float'); break; default: } @@ -259,8 +271,8 @@ export class KeyManager { } break; case 'f': - if (SelectionManager.Views.length === 1 && SelectionManager.Views[0].ComponentView?.search) { - SelectionManager.Views[0].ComponentView?.search?.('', false, false); + if (DocumentView.Selected().length === 1 && DocumentView.Selected()[0].ComponentView?.search) { + DocumentView.Selected()[0].ComponentView?.search?.('', false, false); } else { const searchBtn = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MySearcher); if (searchBtn) { @@ -279,14 +291,14 @@ export class KeyManager { break; case 'y': if (Doc.ActivePage !== 'home') { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.Redo(); } stopPropagation = false; break; case 'z': if (Doc.ActivePage !== 'home') { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.Undo(); } stopPropagation = false; @@ -302,11 +314,17 @@ export class KeyManager { preventDefault = false; break; case 'x': - if (SelectionManager.Views.length) { + if (DocumentView.Selected().length) { const bds = DocumentDecorations.Instance.Bounds; - const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); - const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':'); - SelectionManager.Views.length && navigator.clipboard.writeText(text); + const pt = DocumentView.Selected()[0] // + .screenToViewTransform() + .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); + const text = + `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + // + DocumentView.Selected() + .map(dv => dv.Document[Id]) + .join(':'); // prettier-ignore + DocumentView.Selected().length && navigator.clipboard.writeText(text); DocumentDecorations.Instance.onCloseClick(true); stopPropagation = false; preventDefault = false; @@ -315,9 +333,15 @@ export class KeyManager { case 'c': if ((document.activeElement as any)?.type !== 'text' && !AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) { const bds = DocumentDecorations.Instance.Bounds; - const pt = SelectionManager.Views[0].screenToViewTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); - const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views.map(dv => dv.Document[Id]).join(':'); - SelectionManager.Views.length && navigator.clipboard.writeText(text); + const pt = DocumentView.Selected()[0] + .screenToViewTransform() + .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2); // prettier-ignore + const text = + `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + + DocumentView.SelectedDocs() + .map(doc => doc[Id]) + .join(':'); // prettier-ignore + DocumentView.Selected().length && navigator.clipboard.writeText(text); stopPropagation = false; } preventDefault = false; @@ -336,7 +360,7 @@ export class KeyManager { if (!plain) return; const clone = plain.startsWith('__DashCloneId('); const docids = plain.split(':'); // hack! docids[0] is the top left of the selection rectangle - const addDocument = SelectionManager.Views.lastElement()?.ComponentView?.addDocument; + const addDocument = DocumentView.Selected().lastElement()?.ComponentView?.addDocument; if (addDocument && (plain.startsWith('__DashDocId(') || clone)) { Doc.Paste(docids.slice(1), clone, addDocument); } diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx index b4fe44733..a49923ffb 100644 --- a/src/client/views/InkControlPtHandles.tsx +++ b/src/client/views/InkControlPtHandles.tsx @@ -7,7 +7,6 @@ import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast } from '../../fields/Types'; import { returnFalse, setupMoveUpEvents } from '../../ClientUtils'; -import { SelectionManager } from '../util/SelectionManager'; import { UndoManager } from '../util/UndoManager'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; @@ -15,6 +14,7 @@ import { InkStrokeProperties } from './InkStrokeProperties'; import { SnappingManager } from '../util/SnappingManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { PointData } from '../../pen-gestures/GestureTypes'; +import { DocumentView } from './nodes/DocumentView'; export interface InkControlProps { inkDoc: Doc; @@ -224,8 +224,8 @@ export class InkEndPtHandles extends ObservableReactComponent { const v1n = { X: v1.X / v1len, Y: v1.Y / v1len }; const v2n = { X: v2.X / v2len, Y: v2.Y / v2len }; const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y); - InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, moveEv.shiftKey); - InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call + InkStrokeProperties.Instance.stretchInk(DocumentView.Selected(), scaling, p2, v1n, moveEv.shiftKey); + InkStrokeProperties.Instance.rotateInk(DocumentView.Selected(), angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call return false; }), action(() => { diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 109a9cad4..3920ecc2a 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -9,7 +9,6 @@ import { Cast, NumCast } from '../../fields/Types'; import { PointData } from '../../pen-gestures/GestureTypes'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; -import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; @@ -386,7 +385,7 @@ export class InkStrokeProperties { containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { - const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView); + const testInkView = DocumentView.getDocumentView(doc, containingDocView); const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.Document ? this.excludeSelfSnapSegs(ink, controlIndex) : []); if (snapped && snapped.distance < snapData.distance) { const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 7e2b334af..1ed8de1be 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -6,7 +6,6 @@ // import { Cast, DateCast, NumCast } from '../../fields/Types'; // import { aggregateBounds } from '../../Utils'; // import { DocumentType } from '../documents/DocumentTypes'; -// import { DocumentManager } from '../util/DocumentManager'; // import { CollectionFreeFormView } from './collections/collectionFreeForm'; // import { InkingStroke } from './InkingStroke'; // import './InkTranscription.scss'; diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 884512539..62fc73c78 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -44,7 +44,7 @@ import { InkTangentHandles } from './InkTangentHandles'; import { FieldView, FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { PinDocView, PinProps } from './PinFuncs'; -import { StyleProp } from './StyleProvider'; +import { StyleProp } from './StyleProp'; const { INK_MASK_SIZE } = require('./global/globalCssVariables.module.scss'); // prettier-ignore diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 020525ef8..12d899388 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -13,10 +13,6 @@ import { CreateLinkToActiveAudio, Doc, DocListCast, FieldResult, Opt } from '../ import { Id } from '../../fields/FieldSymbols'; import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, toList } from '../../fields/Types'; -import { DocumentManager } from '../util/DocumentManager'; -import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; -import { SettingsManager } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { Transform } from '../util/Transform'; import { GestureOverlay } from './GestureOverlay'; @@ -24,8 +20,6 @@ import './LightboxView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; -import { TabDocView } from './collections/TabDocView'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere'; @@ -90,7 +84,7 @@ export class LightboxView extends ObservableReactComponent { }); const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); + DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); this._history.push({ doc, target }); } else { this._future = []; @@ -98,14 +92,14 @@ export class LightboxView extends ObservableReactComponent { Doc.ActiveTool = InkTool.None; SnappingManager.SetExploreMode(false); } - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); if (future) { this._future.push( ...(this._doc ? [this._doc] : []), ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length) + .sort((a, b) => Doc.Links(a).length - Doc.Links(b).length) ); } this._doc = doc; @@ -134,11 +128,11 @@ export class LightboxView extends ObservableReactComponent { const lightDoc = this._doc; if (!lightDoc) return; const target = (this._docTarget = this._future.pop()); - const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); + const targetDocView = target && DocumentView.getLightboxDocumentView(target); if (targetDocView && target) { const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (this._history.lastElement().target !== target) this._history.push({ doc: lightDoc, target }); } else if (!target && this._path.length) { savedKeys.forEach(key => { @@ -157,10 +151,10 @@ export class LightboxView extends ObservableReactComponent { return; } const { doc, target } = this._history.lastElement(); - const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const docView = DocumentView.getLightboxDocumentView(target || doc); if (docView) { this._docTarget = target; - target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + target && DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { this.SetLightboxDoc(doc, target); } @@ -178,8 +172,8 @@ export class LightboxView extends ObservableReactComponent { if (this._docTarget) { const fieldKey = Doc.LayoutFieldKey(this._docTarget); const contents = [...DocListCast(this._docTarget[fieldKey]), ...DocListCast(this._docTarget[fieldKey + '_annotations'])]; - const links = LinkManager.Links(this._docTarget) - .map(link => LinkManager.getOppositeAnchor(link, this._docTarget!)!) + const links = Doc.Links(this._docTarget) + .map(link => Doc.getOppositeAnchor(link, this._docTarget!)!) .filter(doc => doc); this.SetLightboxDoc(this._docTarget, undefined, contents.length ? contents : links); } @@ -220,7 +214,7 @@ export class LightboxView extends ObservableReactComponent {
    { e.stopPropagation(); click(); @@ -237,8 +231,8 @@ export class LightboxView extends ObservableReactComponent {
    } @@ -252,7 +246,7 @@ export class LightboxView extends ObservableReactComponent { return !this._doc ? null : (
    { downx = e.clientX; downy = e.clientY; @@ -266,7 +260,7 @@ export class LightboxView extends ObservableReactComponent { width: this.lightboxWidth(), height: this.lightboxHeight(), clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`, - background: SettingsManager.userBackgroundColor, + background: SnappingManager.userBackgroundColor, }}> { removeDocument={undefined} whenChildContentsActiveChanged={emptyFunction} addDocTab={this.AddDocTab} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} focus={emptyFunction} /> diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 01f3b032e..b6dfed687 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -8,14 +8,17 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { AssignAllExtensions } from '../../extensions/Extensions'; import { FieldLoader } from '../../fields/FieldLoader'; +import { BranchingTrailManager } from '../util/BranchingTrailManager'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; import { PingManager } from '../util/PingManager'; import { ReplayMovements } from '../util/ReplayMovements'; import { TrackMovements } from '../util/TrackMovements'; +import { KeyManager } from './GlobalKeyHandler'; +import { MainView } from './MainView'; import { CollectionView } from './collections/CollectionView'; +import { CollectionFreeFormInfoUI } from './collections/collectionFreeForm/CollectionFreeFormInfoUI'; import './global/globalScripts'; -import { MainView } from './MainView'; -import { BranchingTrailManager } from '../util/BranchingTrailManager'; +import { LinkFollower } from '../util/LinkFollower'; dotenv.config(); @@ -58,6 +61,11 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; new ReplayMovements(); new BranchingTrailManager({}); new PingManager(); + new KeyManager(); + + // iniitialize plugin apis + CollectionFreeFormInfoUI.Init(); + LinkFollower.Init(); root.render(); }, 0); })(); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index aec553baa..2c7d3d32a 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -12,7 +12,8 @@ import '../../../node_modules/browndash-components/dist/styles/global.min.css'; import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc'; -import { DocData } from '../../fields/DocSymbols'; +import { DocData, DocViews } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; import { DocCast, StrCast, toList } from '../../fields/Types'; import { DocServer } from '../DocServer'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; @@ -29,7 +30,6 @@ import { Hypothesis } from '../util/HypothesisUtils'; import { UPDATE_SERVER_CACHE } from '../util/LinkManager'; import { RTFMarkup } from '../util/RTFMarkup'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SelectionManager } from '../util/SelectionManager'; import { ServerStats } from '../util/ServerStats'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; @@ -42,7 +42,6 @@ import { DashboardView } from './DashboardView'; import { DictationOverlay } from './DictationOverlay'; import { DocumentDecorations } from './DocumentDecorations'; import { GestureOverlay } from './GestureOverlay'; -import { KeyManager } from './GlobalKeyHandler'; import { LightboxView } from './LightboxView'; import './MainView.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; @@ -160,7 +159,7 @@ export class MainView extends ObservableReactComponent<{}> { leftMenuHeight = () => this._dashUIHeight; leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; leftMenuFlyoutHeight = () => this._dashUIHeight; - propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SettingsManager.Instance?.propertiesWidth || 0)); + propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SnappingManager.PropertiesWidth || 0)); propertiesHeight = () => this._dashUIHeight; mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth(); mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight(); @@ -169,7 +168,7 @@ export class MainView extends ObservableReactComponent<{}> { // Utils.TraceConsoleLog(); reaction( // when a multi-selection occurs, remove focus from all active elements to allow keyboad input to go only to global key manager to act upon selection - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur() ); const scriptTag = document.createElement('script'); @@ -231,37 +230,29 @@ export class MainView extends ObservableReactComponent<{}> { tag.src = 'https://www.youtube.com/iframe_api'; const firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); - window.removeEventListener('keydown', KeyManager.Instance.handleModifiers, true); - window.addEventListener('keydown', KeyManager.Instance.handleModifiers, true); - window.removeEventListener('keyup', KeyManager.Instance.unhandleModifiers); - window.addEventListener('keyup', KeyManager.Instance.unhandleModifiers); - window.removeEventListener('keydown', KeyManager.Instance.handle); - window.addEventListener('keydown', KeyManager.Instance.handle); - window.removeEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('keyup', KeyManager.Instance.unhandle); - window.addEventListener('paste', KeyManager.Instance.paste as any); document.addEventListener('dash', (e: any) => { // event used by chrome plugin to tell Dash which document to focus on const id = GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.showDocument(doc, { willPan: false }) : null)); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentView.showDocument(doc, { willPan: false }) : null)); }); document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener); this.initEventListeners(); } componentWillUnMount() { - window.removeEventListener('keyup', KeyManager.Instance.unhandle); - window.removeEventListener('keydown', KeyManager.Instance.handle); - window.removeEventListener('pointerdown', this.globalPointerDown, true); - window.removeEventListener('pointermove', this.globalPointerMove, true); - window.removeEventListener('pointerup', this.globalPointerClick, true); - window.removeEventListener('paste', KeyManager.Instance.paste as any); - document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); + // window.removeEventListener('keyup', KeyManager.Instance.unhandle); + // window.removeEventListener('keydown', KeyManager.Instance.handle); + // window.removeEventListener('pointerdown', this.globalPointerDown, true); + // window.removeEventListener('pointermove', this.globalPointerMove, true); + // window.removeEventListener('pointerup', this.globalPointerClick, true); + // window.removeEventListener('paste', KeyManager.Instance.paste as any); + // document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); } constructor(props: any) { super(props); makeObservable(this); + CollectionDockingView.setTabJSXComponent(TabDocView); DocumentViewInternal.addDocTabFunc = MainView.addDocTabFunc_impl; MainView.Instance = this; DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any); @@ -592,7 +583,7 @@ export class MainView extends ObservableReactComponent<{}> { if (!e.cancelBubble) { const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join(); if (pathstr?.includes('libraryFlyout')) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); } } }, @@ -719,14 +710,14 @@ export class MainView extends ObservableReactComponent<{}> { this, e, action(() => { - SettingsManager.Instance.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX); - return !SettingsManager.Instance.propertiesWidth; + SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - e.clientX)); + return !SnappingManager.PropertiesWidth; }), action(() => { - SettingsManager.Instance.propertiesWidth < 5 && (SettingsManager.Instance.propertiesWidth = 0); + SnappingManager.PropertiesWidth < 5 && SnappingManager.SetPropertiesWidth(0); }), action(() => { - SettingsManager.Instance.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0; + SnappingManager.SetPropertiesWidth(this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0); }), false ); @@ -776,7 +767,7 @@ export class MainView extends ObservableReactComponent<{}> { Document={this._sidebarContent.proto || this._sidebarContent} addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} containerViewPath={returnEmptyDoclist} styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider} removeDocument={returnFalse} @@ -799,7 +790,7 @@ export class MainView extends ObservableReactComponent<{}> { @computed get leftMenuPanel() { return ( -
    +
    { {this.flyout}
    - +
    {this.dockingContent} @@ -864,11 +855,11 @@ export class MainView extends ObservableReactComponent<{}> { className={`mainView-propertiesDragger${this.propertiesWidth() < 10 ? '-minified' : ''}`} key="props" onPointerDown={this.onPropertiesPointerDown} - style={{ background: SettingsManager.userVariantColor, right: this.propertiesWidth() - 1 }}> - + style={{ background: SnappingManager.userVariantColor, right: this.propertiesWidth() - 1 }}> +
    )} -
    +
    @@ -911,11 +902,11 @@ export class MainView extends ObservableReactComponent<{}> { // setTimeout(action(() => (this._leftMenuFlyoutWidth += 0.5))); this._sidebarContent.proto = DocCast(button.target); - SettingsManager.Instance.LastPressedBtn = button; + SnappingManager.SetLastPressedBtn(button[Id]); }); closeFlyout = action(() => { - SettingsManager.Instance.LastPressedBtn = undefined; + SnappingManager.SetLastPressedBtn(''); this._panelContent = 'none'; this._sidebarContent.proto = undefined; this._leftMenuFlyoutWidth = 0; @@ -933,7 +924,7 @@ export class MainView extends ObservableReactComponent<{}> { @computed get docButtons() { return !Doc.MyDockedBtns ? null : ( -
    +
    { ); } @computed get snapLines() { - const dragged = DragManager.docsBeingDragged.lastElement() ?? SelectionManager.Docs.lastElement(); - const dragPar = dragged ? CollectionFreeFormView.from(DocumentManager.Instance.getDocumentView(dragged)) : undefined; + const dragged = DragManager.docsBeingDragged.lastElement() ?? DocumentView.SelectedDocs().lastElement(); + const dragPar = dragged ? CollectionFreeFormView.from(Array.from(dragged[DocViews]).lastElement()) : undefined; return !dragPar?.layoutDoc.freeform_snapLines ? null : (
    @@ -1010,14 +1001,22 @@ export class MainView extends ObservableReactComponent<{}> { ); } + togglePropertiesFlyout = () => { + if (MainView.Instance.propertiesWidth() > 0) { + SnappingManager.SetPropertiesWidth(0); + } else { + SnappingManager.SetPropertiesWidth(300); + } + }; + lightboxMaxBorder = [200, 50]; render() { return (
    (ele => { @@ -1067,7 +1066,7 @@ export class MainView extends ObservableReactComponent<{}> { case 'dashboard': default: return (<>
    - +
    {this.mainDashboardArea} ); @@ -1086,7 +1085,7 @@ export class MainView extends ObservableReactComponent<{}> { {/* */} {this.snapLines} - + {LightboxView.LightboxDoc ? null : } diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index 0f13de9e5..a917a7d57 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -227,7 +227,6 @@ export class MarqueeAnnotator extends ObservableReactComponent }; render() { - return LightboxView.LightboxDoc ? null : ( + return (
    {this._props.overlayOptions.title || 'Untitled'} @@ -181,7 +180,7 @@ export class OverlayView extends ObservableReactComponent<{}> { docScreenToLocalXf = computedFn((doc: Doc) => () => new Transform(-NumCast(doc.overlayX), -NumCast(doc.overlayY), 1)); @computed get overlayDocs() { - return Doc.MyOverlayDocs.filter(d => !LightboxView.LightboxDoc || d.type === DocumentType.PRES).map(d => { + return Doc.MyOverlayDocs.map(d => { let [offsetx, offsety] = [0, 0]; const dref = React.createRef(); const onPointerMove = action((e: PointerEvent, down: number[]) => { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index cbbf48c75..edf6df2b9 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -20,11 +20,8 @@ import { DocData } from '../../fields/DocSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, ScriptCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; -import { DocUtils } from '../documents/DocUtils'; +import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { undoBatch, undoable } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; @@ -40,13 +37,13 @@ export class PropertiesButtons extends React.Component<{}, {}> { @observable public static Instance: PropertiesButtons; @computed get selectedDoc() { - return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.Document; + return DocumentView.SelectedSchemaDoc() || DocumentView.Selected().lastElement()?.Document; } @computed get selectedLayoutDoc() { - return SelectionManager.SelectedSchemaDoc || SelectionManager.Views.lastElement()?.layoutDoc; + return DocumentView.SelectedSchemaDoc() || DocumentView.Selected().lastElement()?.layoutDoc; } @computed get selectedTabView() { - return !SelectionManager.SelectedSchemaDoc && SelectionManager.Views.lastElement()?.topMost; + return !DocumentView.SelectedSchemaDoc() && DocumentView.Selected().lastElement()?.topMost; } @computed get titleButton() { @@ -164,7 +161,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { // on => `${on ? 'Set' : 'Remove'} lightbox flag`, // on => 'window-restore', // onClick => { - // SelectionManager.Views.forEach(dv => { + // DocumentView.Selected().forEach(dv => { // const containerDoc = dv.Document; // //containerDoc.followAllLinks = // // containerDoc.noShadow = @@ -320,14 +317,14 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch handlePerspectiveChange = (e: any) => { this.selectedDoc && (this.selectedDoc._type_collection = e.target.value); - SelectionManager.Views.forEach(docView => { + DocumentView.Selected().forEach(docView => { docView.layoutDoc._type_collection = e.target.value; }); }; @computed get onClickVal() { const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); if (followLoc === OpenWhere.lightbox && !linkedToLightboxView()) return 'linkInPlace'; if (linkButton && followLoc === OpenWhere.addRight) return 'linkOnRight'; @@ -381,7 +378,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { @undoBatch @action handleOptionChange = (onClick: string) => { - SelectionManager.Views.forEach(docView => { + DocumentView.Selected().forEach(docView => { const linkButton = IsFollowLinkScript(docView.Document.onClick); docView.noOnClick(); switch (onClick) { @@ -417,7 +414,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { const click = () => this.handleOptionChange(value[0]); const linkButton = IsFollowLinkScript(this.selectedDoc.onClick); const followLoc = this.selectedDoc._followLinkLocation; - const linkedToLightboxView = () => LinkManager.Links(this.selectedDoc).some(link => LinkManager.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); + const linkedToLightboxView = () => Doc.Links(this.selectedDoc).some(link => Doc.getOppositeAnchor(link, this.selectedDoc)?._isLightbox); let active = false; // prettier-ignore @@ -451,7 +448,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { } @undoBatch editOnClickScript = () => { - if (SelectionManager.Views.length) SelectionManager.Views.forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick')); + if (DocumentView.Selected().length) DocumentView.Selected().forEach(dv => DocUtils.makeCustomViewClicked(dv.Document, undefined, 'onClick')); else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick'); }; @@ -472,8 +469,8 @@ export class PropertiesButtons extends React.Component<{}, {}> { fillWidth toggleType={ToggleType.BUTTON} onClick={undoable(() => { - if (SelectionManager.Views.length > 1) { - SelectionManager.Views.forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property)); + if (DocumentView.Selected().length > 1) { + DocumentView.Selected().forEach(dv => (onClick ?? onPropToggle)(dv, dv.Document, property)); } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property); }, property)} /> @@ -482,7 +479,7 @@ export class PropertiesButtons extends React.Component<{}, {}> { render() { const layoutField = this.selectedDoc?.[Doc.LayoutFieldKey(this.selectedDoc)]; - const isText = SelectionManager.Views.lastElement()?.ComponentView instanceof FormattedTextBox; + const isText = DocumentView.Selected().lastElement()?.ComponentView instanceof FormattedTextBox; const isInk = this.selectedDoc?.layout_isSvg; const isImage = layoutField instanceof ImageField; const isMap = this.selectedDoc?.type === DocumentType.MAP; diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx index a806995f2..edb55f341 100644 --- a/src/client/views/PropertiesDocBacklinksSelector.tsx +++ b/src/client/views/PropertiesDocBacklinksSelector.tsx @@ -7,12 +7,12 @@ import { Doc } from '../../fields/Doc'; import { Cast } from '../../fields/Types'; import { DocumentType } from '../documents/DocumentTypes'; import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import './PropertiesDocBacklinksSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { LinkMenu } from './linking/LinkMenu'; import { OpenWhere } from './nodes/OpenWhere'; +import { DocumentView } from './nodes/DocumentView'; type PropertiesDocBacklinksSelectorProps = { Document: Doc; @@ -25,7 +25,7 @@ type PropertiesDocBacklinksSelectorProps = { export class PropertiesDocBacklinksSelector extends React.Component { getOnClick = action((link: Doc) => { const linkAnchor = this.props.Document; - const other = LinkManager.getOppositeAnchor(link, linkAnchor); + const other = Doc.getOppositeAnchor(link, linkAnchor); const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other; LinkManager.Instance.currentLink = link; if (otherdoc) { @@ -36,10 +36,10 @@ export class PropertiesDocBacklinksSelector extends React.Component {this.props.hideTitle ? null :

    Contexts:

    } - +
    ); } diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx index 722b1f90a..7465c727a 100644 --- a/src/client/views/PropertiesDocContextSelector.tsx +++ b/src/client/views/PropertiesDocContextSelector.tsx @@ -7,11 +7,10 @@ import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Id } from '../../fields/FieldSymbols'; import { Cast, StrCast } from '../../fields/Types'; -import { DocFocusOrOpen } from '../util/DocumentManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import './PropertiesDocContextSelector.scss'; import { CollectionDockingView } from './collections/CollectionDockingView'; -import { DocumentView } from './nodes/DocumentView'; +import { DocFocusOrOpen, DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; type PropertiesDocContextSelectorProps = { diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index ff5dcd1b8..7c16e0ddb 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -23,10 +23,8 @@ import { ComputedField } from '../../fields/ScriptField'; import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { GetEffectiveAcl, SharingPermissions, normalizeEmail } from '../../fields/util'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { LinkManager } from '../util/LinkManager'; -import { SelectionManager } from '../util/SelectionManager'; import { SettingsManager } from '../util/SettingsManager'; import { SharingManager } from '../util/SharingManager'; import { Transform } from '../util/Transform'; @@ -40,7 +38,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector import { PropertiesDocContextSelector } from './PropertiesDocContextSelector'; import { PropertiesSection } from './PropertiesSection'; import './PropertiesView.scss'; -import { DefaultStyleProvider } from './StyleProvider'; +import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider'; import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; @@ -66,6 +64,12 @@ export class PropertiesView extends ObservableReactComponent { + this.CloseAll(); + this.openFilters = true; + }) + ); } @computed get MAX_EMBED_HEIGHT() { @@ -73,7 +77,7 @@ export class PropertiesView extends ObservableReactComponent(reqdKeys); - const docs: Doc[] = SelectionManager.Views.length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views.map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); + const docs: Doc[] = + DocumentView.Selected().length < 2 // + ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] + : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); docs.forEach(doc => Object.keys(doc) .filter(filter) @@ -237,7 +242,12 @@ export class PropertiesView extends ObservableReactComponent { - const docs = SelectionManager.Views.length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc!] : SelectionManager.Views.map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); + const docs = + DocumentView.Selected().length < 2 && this.selectedDoc + ? [this.layoutFields + ? Doc.Layout(this.selectedDoc) // + : this.dataDoc!] + : DocumentView.Selected().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc)); // prettier-ignore docs.forEach(doc => { if (value.indexOf(':') !== -1) { const newVal = value[0].toUpperCase() + value.substring(1, value.length); @@ -295,13 +305,13 @@ export class PropertiesView extends ObservableReactComponent counter++); + Doc.Links(selAnchor).forEach(() => counter++); return counter; } @computed get layoutPreview() { - if (SelectionManager.Views.length > 1) { + if (DocumentView.Selected().length > 1) { return '-- multiple selected --'; } if (this.selectedDoc) { @@ -348,7 +358,7 @@ export class PropertiesView extends ObservableReactComponent { - const docs = SelectionManager.Views.length < 2 ? [this.selectedDoc] : SelectionManager.Views.map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); + const docs = DocumentView.Selected().length < 2 ? [this.selectedDoc] : DocumentView.Selected().map(dv => (this.layoutDocAcls ? dv.layoutDoc : dv.dataDoc)); SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs, this.layoutDocAcls); }; @@ -462,7 +472,7 @@ export class PropertiesView extends ObservableReactComponent docView.Document); + const docs = DocumentView.Selected().length < 2 && this.selectedDoc ? [this.selectedDoc] : DocumentView.SelectedDocs(); const target = docs[0]; const showAdmin = GetEffectiveAcl(target) === AclAdmin; @@ -580,7 +590,7 @@ export class PropertiesView extends ObservableReactComponent(); - SelectionManager.Views.forEach(dv => titles.add(StrCast(dv.Document.title))); + DocumentView.Selected().forEach(dv => titles.add(StrCast(dv.Document.title))); const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title); return (
    @@ -631,8 +641,8 @@ export class PropertiesView extends ObservableReactComponent { - if (SelectionManager.Views.length > 1) { - SelectionManager.Views.map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); + if (DocumentView.Selected().length > 1) { + DocumentView.Selected().map(dv => Doc.SetInPlace(dv.Document, 'title', value, true)); } else if (this.dataDoc) { if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true); else KeyValueBox.SetField(this.dataDoc, 'title', value as string, true); @@ -1365,14 +1375,14 @@ export class PropertiesView extends ObservableReactComponent any = val => val) => { @@ -1435,7 +1445,7 @@ export class PropertiesView extends ObservableReactComponent diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx index 8de359e07..9c36e6d26 100644 --- a/src/client/views/ScriptBox.tsx +++ b/src/client/views/ScriptBox.tsx @@ -122,14 +122,14 @@ export class ScriptBox extends React.Component { if (!text) { doc[DocData][fieldKey] = undefined; } else { - const script = CompileScript(text, { + const compScript = CompileScript(text, { params: { this: Doc.name, ...contextParams }, typecheck: false, editable: true, transformer: DocumentIconContainer.getTransformer(), }); - if (!script.compiled) { - onError(script.errors.map(error => error.messageText).join('\n')); + if (!compScript.compiled) { + onError(compScript.errors.map(error => error.messageText).join('\n')); return; } @@ -143,7 +143,7 @@ export class ScriptBox extends React.Component { div.innerHTML = 'button'; params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, () => {}, clientX, clientY); - doc[DocData][fieldKey] = new ScriptField(script); + doc[DocData][fieldKey] = new ScriptField(compScript); overlayDisposer(); } }} diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index ba2e22b3b..1a2eb460f 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -5,15 +5,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocumentManager } from '../util/DocumentManager'; import { CompileScript, Transformer, ts } from '../util/Scripting'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { SettingsManager } from '../util/SettingsManager'; +import { SnappingManager } from '../util/SnappingManager'; import { undoable } from '../util/UndoManager'; import { ObservableReactComponent } from './ObservableReactComponent'; import { OverlayView } from './OverlayView'; import './ScriptingRepl.scss'; import { DocumentIconContainer } from './nodes/DocumentIcon'; +import { DocumentView } from './nodes/DocumentView'; interface replValueProps { scrollToBottom: () => void; @@ -169,7 +169,7 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { case 'Enter': { e.stopPropagation(); const docGlobals: { [name: string]: any } = {}; - DocumentManager.Instance.DocumentViews.forEach((dv, i) => { + DocumentView.allViews().forEach((dv, i) => { docGlobals[`d${i}`] = dv.Document; }); const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals); @@ -262,13 +262,13 @@ export class ScriptingRepl extends ObservableReactComponent<{}> { render() { return (
    -
    +
    {this.commands.map(({ command, result }, i) => ( -
    -
    +
    +
    {command ||
    }
    -
    +
    @@ -276,7 +276,7 @@ export class ScriptingRepl extends ObservableReactComponent<{}> {
    data.split(':')[0]) @@ -187,8 +185,8 @@ export class SidebarAnnos extends ObservableReactComponent 'title'; setHeightCallback = (height: number) => this._props.setHeight?.(height + this.filtersHeight()); sortByLinkAnchorY = (a: Doc, b: Doc) => { - const ay = LinkManager.Links(a).length && DocCast(LinkManager.Links(a)[0].link_anchor_1).y; - const by = LinkManager.Links(b).length && DocCast(LinkManager.Links(b)[0].link_anchor_1).y; + const ay = Doc.Links(a).length && DocCast(Doc.Links(a)[0].link_anchor_1).y; + const by = Doc.Links(b).length && DocCast(Doc.Links(b)[0].link_anchor_1).y; return NumCast(ay) - NumCast(by); }; render() { diff --git a/src/client/views/StyleProp.ts b/src/client/views/StyleProp.ts new file mode 100644 index 000000000..dd5b98cfe --- /dev/null +++ b/src/client/views/StyleProp.ts @@ -0,0 +1,24 @@ +export enum StyleProp { + TreeViewIcon = 'treeView_Icon', + TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items + DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view + Opacity = 'opacity', // opacity of the document view + BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views + BorderRounding = 'borderRounding', // border radius of the document view + Color = 'color', // foreground color of Document view items + BackgroundColor = 'backgroundColor', // background color of a document view + FillColor = 'fillColor', // fill color of an ink stroke or shape + WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note + PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified + Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background + HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that + ShowCaption = 'layout_showCaption', + TitleHeight = 'titleHeight', // Height of Title area + ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix) + BorderPath = 'customBorder', // border path for document view + FontColor = 'fontColor', // color o tet + FontSize = 'fontSize', // size of text font + FontFamily = 'fontFamily', // font family of text + FontWeight = 'fontWeight', // font weight of text + Highlighting = 'highlighting', // border highlighting +} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 6d4f69e6e..3ecfc8ba3 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -13,53 +13,20 @@ import { FaFilter } from 'react-icons/fa'; import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { DocViews } from '../../fields/DocSymbols'; +import { Id } from '../../fields/FieldSymbols'; +import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; +import { AudioAnnoState } from '../../server/SharedMediaTypes'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; -import { IsFollowLinkScript } from '../util/LinkFollower'; -import { LinkManager } from '../util/LinkManager'; -import { SettingsManager } from '../util/SettingsManager'; +import { IsFollowLinkScript } from '../documents/DocUtils'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; -import { CollectionSchemaView } from './collections/collectionSchema/CollectionSchemaView'; import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocFocusOrOpen, DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; -import { KeyValueBox } from './nodes/KeyValueBox'; -import { PropertiesView } from './PropertiesView'; +import { StyleProp } from './StyleProp'; import './StyleProvider.scss'; -import { ScriptField } from '../../fields/ScriptField'; - -export enum StyleProp { - TreeViewIcon = 'treeView_Icon', - TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items - DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view - Opacity = 'opacity', // opacity of the document view - BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views - BorderRounding = 'borderRounding', // border radius of the document view - Color = 'color', // foreground color of Document view items - BackgroundColor = 'backgroundColor', // background color of a document view - FillColor = 'fillColor', // fill color of an ink stroke or shape - WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note - PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified - Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background - HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that - ShowCaption = 'layout_showCaption', - TitleHeight = 'titleHeight', // Height of Title area - ShowTitle = 'layout_showTitle', // whether to display a title on a Document (optional :hover suffix) - BorderPath = 'customBorder', // border path for document view - FontColor = 'fontColor', // color o tet - FontSize = 'fontSize', // size of text font - FontFamily = 'fontFamily', // font family of text - FontWeight = 'fontWeight', // font weight of text - Highlighting = 'highlighting', // border highlighting -} - -export enum AudioAnnoState { - stopped = 'stopped', - playing = 'playing', -} function toggleLockedPosition(doc: Doc) { UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground'); @@ -80,7 +47,7 @@ export function styleFromLayoutString(doc: Doc, props: FieldViewProps, scale: nu const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; const replacer = (match: any, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value - ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ this: doc, self: doc, scale }).result?.toString() ?? ''; + ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: doc, scale }).result?.toString() ?? ''; divKeys.forEach((prop: string) => { const p = (props as any)[prop]; typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); @@ -98,6 +65,11 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { } ${ph * inset}`; } +let _filterOpener: () => void; +export function SetFilterOpener(func: () => void) { + _filterOpener = func; +} + // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab // export function DefaultStyleProvider(doc: Opt, props: Opt, property: string): any { @@ -124,7 +96,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt, props: Opt = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; const backColor = backgroundCol(); @@ -243,7 +214,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt, props: Opt, props: Opt, props: Opt, props: Opt ); const paint = () => !doc?.onPaint ? null : ( -
    togglePaintView(e, doc, props)}> +
    togglePaintView(e, doc, props)}>
    ); const filter = () => { - const dashView = untracked(() => DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard)); + const dashView = untracked(() => DocumentView.getDocumentView(Doc.ActiveDashboard)); const showFilterIcon = StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length ? 'green' // #18c718bd' //'hasFilter' @@ -357,17 +328,11 @@ export function DefaultStyleProvider(doc: Opt, props: Opt
    } closeOnSelect - setSelectedVal={ - action((dv) => { - (dv as any).select(false); - (SettingsManager.Instance.propertiesWidth = 250); - setTimeout(action(() => { - if (PropertiesView.Instance) { - PropertiesView.Instance.CloseAll(); - PropertiesView.Instance.openFilters = true; - } - })); - }) + setSelectedVal={((dv: DocumentView) => { + dv.select(false); + SnappingManager.SetPropertiesWidth(250); + _filterOpener?.(); + }) as any // Dropdown assumes values are strings or numbers.. } size={Size.XSMALL} width={15} @@ -375,14 +340,14 @@ export function DefaultStyleProvider(doc: Opt, props: Opt StrListCast(dv?.Document.childFilters).length || StrListCast(dv?.Document.childRangeFilters).length) .map(dv => ({ text: StrCast(dv?.Document.title), val: dv as any, - style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor}, + style: {color:SnappingManager.userColor, background:SnappingManager.userBackgroundColor}, } as IListItemProps)) } />
    @@ -395,7 +360,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt{StrListCast(doc[fieldKey + 'audioAnnotations_text']).lastElement()}
    }> -
    DocumentManager.Instance.getFirstDocumentView(doc)?.playAnnotation()}> +
    DocumentView.getFirstDocumentView(doc)?.playAnnotation()}>
    @@ -415,7 +380,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt void) { - const color = SettingsManager.userColor; + const color = SnappingManager.userColor; return ( { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % this.carouselItems.length; }; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index fda320077..9d3657995 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -11,7 +11,7 @@ import { Doc, Opt } from '../../../fields/Doc'; import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index b98aceb16..fc9e2e39b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -16,27 +16,33 @@ import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import * as GoldenLayout from '../../goldenLayout'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { undoable, undoBatch, UndoManager } from '../../util/UndoManager'; import { DashboardView } from '../DashboardView'; import { LightboxView } from '../LightboxView'; +import { DocumentView } from '../nodes/DocumentView'; import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; import { OverlayView } from '../OverlayView'; import { ScriptingRepl } from '../ScriptingRepl'; import { UndoStack } from '../UndoStack'; import './CollectionDockingView.scss'; import { CollectionSubView } from './CollectionSubView'; -import { TabDocView } from './TabDocView'; const _global = (window /* browser */ || global) /* node */ as any; @observer export class CollectionDockingView extends CollectionSubView() { + static tabClass: JSX.Element | null = null; + /** + * Configure golden layout to render its documents using the specified React component + * @param ele - typically would be set to TabDocView + */ + static setTabJSXComponent(ele: any) { + this.tabClass = ele; + } // eslint-disable-next-line no-use-before-define @observable public static Instance: CollectionDockingView | undefined = undefined; @@ -295,7 +301,7 @@ export class CollectionDockingView extends CollectionSubView() { glay.on('tabCreated', this.tabCreated); glay.on('tabDestroyed', this.tabDestroyed); glay.on('stackCreated', this.stackCreated); - glay.registerComponent('DocumentFrameRenderer', TabDocView); + glay.registerComponent('DocumentFrameRenderer', CollectionDockingView.tabClass); glay.container = this._containerRef.current; glay.init(); glay.root.layoutManager.on('itemDropped', this.tabItemDropped); @@ -431,8 +437,8 @@ export class CollectionDockingView extends CollectionSubView() { } else { const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement); const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget); - if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) { - SelectionManager.SelectView(DocumentManager.Instance.getFirstDocumentView(map.DashDoc), false); + if (map?.DashDoc && DocumentView.getFirstDocumentView(map.DashDoc)) { + DocumentView.SelectView(DocumentView.getFirstDocumentView(map.DashDoc), false); } } } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index f945a7aa4..e53071584 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -21,13 +21,11 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch } from '../../util/UndoManager'; import { AntimodeMenu } from '../AntimodeMenu'; import { EditableView } from '../EditableView'; -import { MainView } from '../MainView'; import { DefaultStyleProvider } from '../StyleProvider'; import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView'; import './CollectionMenu.scss'; @@ -38,6 +36,7 @@ interface CollectionMenuProps { panelWidth: () => number; toggleTopBar: () => void; topBarHeight: () => number; + togglePropertiesFlyout: () => void; } @observer @@ -59,7 +58,7 @@ export class CollectionMenu extends AntimodeMenu { componentDidMount() { reaction( - () => SelectionManager.Views.lastElement(), + () => DocumentView.Selected().lastElement(), view => view && this.SetSelection(view) ); } @@ -77,15 +76,6 @@ export class CollectionMenu extends AntimodeMenu { } }; - @action - toggleProperties = () => { - if (MainView.Instance.propertiesWidth() > 0) { - SettingsManager.Instance.propertiesWidth = 0; - } else { - SettingsManager.Instance.propertiesWidth = 300; - } - }; - buttonBarXf = () => { if (!this._docBtnRef.current) return Transform.Identity(); const { scale, translateX, translateY } = ClientUtils.GetScreenTransform(this._docBtnRef.current); @@ -128,15 +118,15 @@ export class CollectionMenu extends AntimodeMenu { render() { const headerIcon = this.props.topBarHeight() > 0 ? 'angle-double-up' : 'angle-double-down'; const headerTitle = this.props.topBarHeight() > 0 ? 'Close Header Bar' : 'Open Header Bar'; - const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; - const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties'; + const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; + const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Properties' : 'Open Properties'; const hardCodedButtons = (
    0} icon={} @@ -145,9 +135,9 @@ export class CollectionMenu extends AntimodeMenu { 0} + color={SnappingManager.userColor} + onClick={this._props.togglePropertiesFlyout} + toggleStatus={SnappingManager.PropertiesWidth > 0} icon={} tooltip={propTitle} /> @@ -159,7 +149,7 @@ export class CollectionMenu extends AntimodeMenu {
    {this.contMenuButtons} @@ -245,7 +235,7 @@ export class CollectionViewBaseChrome extends React.Component { this.target._freeform_panX = 0; this.target._freeform_panY = 0; diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index e940a1aef..3f9eed1d6 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -27,7 +27,7 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import './CollectionNoteTakingView.scss'; import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn'; import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider'; diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index e02570d3e..5b3f625db 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -10,13 +10,13 @@ import { NumCast, StrCast, toList } from '../../../fields/Types'; import { emptyFunction } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { OpenWhere } from '../nodes/OpenWhere'; import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import './CollectionPileView.scss'; import { CollectionSubView } from './CollectionSubView'; +import { DocumentView } from '../nodes/DocumentView'; @observer export class CollectionPileView extends CollectionSubView() { @@ -150,7 +150,7 @@ export class CollectionPileView extends CollectionSubView() { @undoBatch onClick = (e: React.MouseEvent) => { if (e.button === 0) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); this.toggleStarburst(); e.stopPropagation(); } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 48d24c910..f9b123bb6 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -19,11 +19,8 @@ import { ImageField } from '../../../fields/URLField'; import { emptyFunction, formatTime } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; -import { FollowLinkScript } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; +import { FollowLinkScript, IsFollowLinkScript } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; -import { IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower'; -import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -87,7 +84,6 @@ export class CollectionStackedTimeline extends CollectionSubView> => new Promise>(res => { if (doc.hidden) options.didMove = !(doc.hidden = false); - const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -296,7 +292,7 @@ export class CollectionStackedTimeline extends CollectionSubView 15 && !this.IsTrimming) { const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true); - setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false)); + setTimeout(() => DocumentView.getDocumentView(anchor)?.select(false)); } (!isClick || !wasSelecting) && (this._markerEnd = undefined); this._timelineWrapper && (this._timelineWrapper.style.cursor = ''); @@ -481,7 +477,7 @@ export class CollectionStackedTimeline extends CollectionSubView { if (IsFollowLinkScript(anchorDoc.onClick)) { - LinkFollower.FollowLink(undefined, anchorDoc, false); + DocumentView.FollowLink(undefined, anchorDoc, false); } const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05; const endTime = this.anchorEnd(anchorDoc); @@ -761,12 +757,12 @@ class StackedTimelineAnchor extends ObservableReactComponent NumCast(this._props.mark[this._props.startTag]) && time < NumCast(this._props.mark[this._props.endTag]) && this._lastTimecode < NumCast(this._props.mark[this._props.startTag]) - 1e-5 ) { - LinkFollower.FollowLink(undefined, this._props.mark, false); + DocumentView.FollowLink(undefined, this._props.mark, false); } this._lastTimecode = time; } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 8ae0f2832..3f12a281e 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -31,7 +31,7 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 7c08aedb1..a4708bed5 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -21,12 +21,11 @@ import { Docs, DocumentOptions } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldViewProps } from '../nodes/FieldView'; -import { LoadingBox } from '../nodes/LoadingBox'; +import { DocumentView } from '../nodes/DocumentView'; export interface CollectionViewProps extends React.PropsWithChildren { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) @@ -377,7 +376,7 @@ export function CollectionSubView() { } }); } else { - const srcWeb = SelectionManager.Views.lastElement(); + const srcWeb = DocumentView.Selected().lastElement(); const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0]; const reg = new RegExp(ClientUtils.prepend(''), 'g'); const modHtml = srcUrl ? html.replace(reg, srcUrl) : html; @@ -386,7 +385,7 @@ export function CollectionSubView() { Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text; addDocument(htmlDoc); if (srcWeb) { - const iframe = SelectionManager.Views[0].ContentDiv?.getElementsByTagName('iframe')?.[0]; + const iframe = DocumentView.Selected()[0].ContentDiv?.getElementsByTagName('iframe')?.[0]; const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any; if (focusNode) { const anchor = srcWeb?.ComponentView?.getAnchor?.(true); @@ -496,13 +495,13 @@ export function CollectionSubView() { if (typeof files === 'string') { const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); - LoadingBox.addCurrentlyLoading(loading); + Doc.addCurrentlyLoading(loading); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { generatedDocuments.push( ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); - LoadingBox.addCurrentlyLoading(loading); + Doc.addCurrentlyLoading(loading); DocUtils.uploadFileToDoc(file, {}, loading); return loading; }) diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx index b3760d4af..0369e4a2a 100644 --- a/src/client/views/collections/CollectionTimeView.tsx +++ b/src/client/views/collections/CollectionTimeView.tsx @@ -12,7 +12,6 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; @@ -257,7 +256,7 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou action(() => { const filterVals = bounds.payload as string[]; filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, 'check')); - const pivotView = DocumentManager.Instance.getDocumentView(pivotDoc); + const pivotView = DocumentView.getDocumentView(pivotDoc); if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) { if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) { // eslint-disable-next-line prefer-destructuring diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d015e73ad..c1247f5b0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -14,11 +14,9 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction, Utils } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -27,10 +25,11 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { EditableView } from '../EditableView'; import { DocumentView } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView } from './CollectionSubView'; import './CollectionTreeView.scss'; +import { TreeViewType } from './CollectionTreeViewType'; import { TreeView } from './TreeView'; const _global = (window /* browser */ || global) /* node */ as any; @@ -49,12 +48,6 @@ export type collectionTreeViewProps = { hierarchyIndex?: number[]; }; -export enum TreeViewType { - outline = 'outline', - fileSystem = 'fileSystem', - default = 'default', -} - @observer export class CollectionTreeView extends CollectionSubView>() { public static AddTreeFunc = 'addTreeFolder(this.embedContainer)'; @@ -183,7 +176,7 @@ export class CollectionTreeView extends CollectionSubView !docs.includes(v)); - if (docs.some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll(); + if (docs.some(doc => DocumentView.Selected().some(dv => Doc.AreProtosEqual(dv.Document, doc)))) DocumentView.DeselectAll(); if (result.length !== value.length) { if (docIn instanceof Doc) { const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(docIn); @@ -191,7 +184,7 @@ export class CollectionTreeView extends CollectionSubView 0 && prev) { Doc.SetSelectOnLoad(prev); - DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false); + DocumentView.getDocumentView(prev, this.DocumentView?.())?.select(false); } return true; } @@ -468,7 +461,7 @@ export class CollectionTreeView extends CollectionSubView e.stopPropagation()} - onClick={() => (!this.layoutDoc.forceActive ? this._props.select(false) : SelectionManager.DeselectAll())} + onClick={() => (!this.layoutDoc.forceActive ? this._props.select(false) : DocumentView.DeselectAll())} onDrop={this.onTreeDrop}>
      {this.treeViewElements}
    diff --git a/src/client/views/collections/CollectionTreeViewType.ts b/src/client/views/collections/CollectionTreeViewType.ts new file mode 100644 index 000000000..53b88160e --- /dev/null +++ b/src/client/views/collections/CollectionTreeViewType.ts @@ -0,0 +1,5 @@ +export enum TreeViewType { + outline = 'outline', + fileSystem = 'fileSystem', + default = 'default', +} diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 2d900bb73..2d8b2564d 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -9,7 +9,7 @@ import * as ReactDOM from 'react-dom/client'; import { ClientUtils, DashColor, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; +import { DocData, DocViews } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { FieldId } from '../../../fields/RefField'; @@ -18,10 +18,8 @@ import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types'; import { DocServer } from '../../DocServer'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { UndoManager, undoable } from '../../util/UndoManager'; @@ -29,7 +27,8 @@ import { DashboardView } from '../DashboardView'; import { LightboxView } from '../LightboxView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; +import { DefaultStyleProvider } from '../StyleProvider'; import { Colors } from '../global/globalEnums'; import { DocumentView, returnEmptyDocViewList } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; @@ -194,9 +193,101 @@ export class TabDocView extends ObservableReactComponent { _mainCont: HTMLDivElement | null = null; _tabReaction: IReactionDisposer | undefined; + /** + * Adds a document to the presentation view + * */ + @action + public static PinDoc(docIn: Doc | Doc[], pinProps: PinProps) { + const docs = toList(docIn); + + const batch = UndoManager.StartBatch('Pin doc to pres trail'); + const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); + + if (!Doc.ActivePresentation) { + Doc.AddDocToList(Doc.MyTrails, 'data', curPres); + Doc.ActivePresentation = curPres; + } + + docs.forEach(doc => { + // Edge Case 1: Cannot pin document to itself + if (doc === curPres) { + alert('Cannot pin presentation document to itself'); + return; + } + const anchorDoc = DocumentView.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps); + const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({}); + const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc); + pinDoc.title = doc.title + ' - Slide'; + pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data + pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; + pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000; + pinDoc.presentation_groupWithUp = false; + Doc.SetContainer(pinDoc, curPres); + // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time + pinDoc.treeView = ''; // not really needed, but makes key value pane look better + pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area + pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. + pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field + pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view + pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header + const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); + + if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc); + if (!pinProps?.audioRange && duration !== undefined) { + pinDoc.presentation_mediaStart = 'manual'; + pinDoc.presentation_mediaStop = 'manual'; + } + if (pinProps?.activeFrame !== undefined) { + pinDoc.config_activeFrame = pinProps?.activeFrame; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presentation_movement = PresMovement.Pan; + } + if (pinProps?.currentFrame !== undefined) { + pinDoc.config_currentFrame = pinProps?.currentFrame; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presentation_movement = PresMovement.Pan; + } + if (pinDoc.stroke_isInkMask) { + pinDoc.presentation_hideAfter = true; + pinDoc.presentation_hideBefore = true; + pinDoc.presentation_movement = PresMovement.None; + } + if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true; + Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement()); + PresBox.Instance?.clearSelectedArray(); + pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); // Update selected array + }); + if ( + // open the presentation trail if it's not already opened + !Array.from(CollectionDockingView.Instance?.tabMap ?? []) + .map(d => d.DashDoc) + .includes(curPres) + ) { + if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres); + CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); + setTimeout(() => DocumentView.showDocument(docs.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things + } + setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs + } + + static Activate = (tabDoc: Doc) => { + const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); + tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) + return tab !== undefined; + }; + // static ActivateTabView(doc: Doc) { + // const tabView = Array.from(TabDocView._allTabs).find(view => view._document === doc); + // if (!tabView?._activated && tabView?._document) { + // TabDocView.Activate(tabView?._document); + // return tabView; + // } + // return undefined; + // } constructor(props: any) { super(props); makeObservable(this); + DocumentView.activateTabView = TabDocView.Activate; + DocumentView.PinDoc = TabDocView.PinDoc; } @observable _activated: boolean = false; @@ -205,8 +296,14 @@ export class TabDocView extends ObservableReactComponent { @observable _hovering = false; @observable _isActive: boolean = false; @observable _isAnyChildContentActive = false; + public static IsSelected = (doc?: Doc) => { + if (Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected)) { + return true; + } + return false; + }; @computed get _isUserActivated() { - return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive; + return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } get _isContentActive() { return this._isUserActivated || this._hovering; @@ -279,12 +376,12 @@ export class TabDocView extends ObservableReactComponent { returnFalse, action(clickEv => { if (this.view) { - SelectionManager.SelectView(this.view, false); + DocumentView.SelectView(this.view, false); const child = getChild(); simulateMouseClick(child, clickEv.clientX, clickEv.clientY + 30, clickEv.screenX, clickEv.screenY + 30); } else { this._activated = true; - setTimeout(() => this.view && SelectionManager.SelectView(this.view, false)); + setTimeout(() => this.view && DocumentView.SelectView(this.view, false)); } }) ); @@ -350,7 +447,7 @@ export class TabDocView extends ObservableReactComponent { // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected titleEle.onpointerdown = action((e: any) => { if (e.target.className !== 'lm_iconWrap') { - if (this.view) SelectionManager.SelectView(this.view, false); + if (this.view) DocumentView.SelectView(this.view, false); else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); titleEle.lastClick = Date.now(); @@ -358,7 +455,7 @@ export class TabDocView extends ObservableReactComponent { } }); tab._disposers.selectionDisposer = reaction( - () => SelectionManager.IsSelected(this._document), + () => TabDocView.IsSelected(this._document), action(selected => { if (selected) this._activated = true; if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) { @@ -382,89 +479,12 @@ export class TabDocView extends ObservableReactComponent { .off('click') // unbind the current click handler .click(() => { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); UndoManager.RunInBatch(() => tab.contentItem.remove(), 'delete tab'); }); } }; - /** - * Adds a document to the presentation view - * */ - @action - public static PinDoc(docIn: Doc | Doc[], pinProps: PinProps) { - const docs = toList(docIn); - - const batch = UndoManager.StartBatch('Pin doc to pres trail'); - const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); - - if (!Doc.ActivePresentation) { - Doc.AddDocToList(Doc.MyTrails, 'data', curPres); - Doc.ActivePresentation = curPres; - } - - docs.forEach(doc => { - // Edge Case 1: Cannot pin document to itself - if (doc === curPres) { - alert('Cannot pin presentation document to itself'); - return; - } - const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps); - const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({}); - const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc); - pinDoc.title = doc.title + ' - Slide'; - pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data - pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom; - pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000; - pinDoc.presentation_groupWithUp = false; - Doc.SetContainer(pinDoc, curPres); - // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time - pinDoc.treeView = ''; // not really needed, but makes key value pane look better - pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area - pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. - pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field - pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view - pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header - const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null); - - if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc); - if (!pinProps?.audioRange && duration !== undefined) { - pinDoc.presentation_mediaStart = 'manual'; - pinDoc.presentation_mediaStop = 'manual'; - } - if (pinProps?.activeFrame !== undefined) { - pinDoc.config_activeFrame = pinProps?.activeFrame; - pinDoc.title = doc.title + ' (move)'; - pinDoc.presentation_movement = PresMovement.Pan; - } - if (pinProps?.currentFrame !== undefined) { - pinDoc.config_currentFrame = pinProps?.currentFrame; - pinDoc.title = doc.title + ' (move)'; - pinDoc.presentation_movement = PresMovement.Pan; - } - if (pinDoc.stroke_isInkMask) { - pinDoc.presentation_hideAfter = true; - pinDoc.presentation_hideBefore = true; - pinDoc.presentation_movement = PresMovement.None; - } - if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true; - Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement()); - PresBox.Instance?.clearSelectedArray(); - pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); // Update selected array - }); - if ( - // open the presentation trail if it's not already opened - !Array.from(CollectionDockingView.Instance?.tabMap ?? []) - .map(d => d.DashDoc) - .includes(curPres) - ) { - if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres); - CollectionDockingView.AddSplit(curPres, OpenWhereMod.right); - setTimeout(() => DocumentManager.Instance.showDocument(docs.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things - } - setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs - } - componentDidMount() { new _global.ResizeObserver( action((entries: any) => { @@ -484,12 +504,12 @@ export class TabDocView extends ObservableReactComponent { } componentDidUpdate(prevProps: Readonly) { super.componentDidUpdate(prevProps); - this._view && DocumentManager.Instance.AddView(this._view); + this._view && DocumentView.addView(this._view); } componentWillUnmount() { this._tabReaction?.(); - this._view && DocumentManager.Instance.RemoveView(this._view); + this._view && DocumentView.removeView(this._view); runInAction(() => TabDocView._allTabs.delete(this)); this._props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged); @@ -503,7 +523,7 @@ export class TabDocView extends ObservableReactComponent { private onActiveContentItemChanged(contentItem: any) { if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) { this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab; - if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => SelectionManager.SelectView(this._view, false)); + if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => DocumentView.SelectView(this._view, false)); !this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one. } } @@ -517,7 +537,7 @@ export class TabDocView extends ObservableReactComponent { // lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => { const docs = toList(docsIn); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); const whereFields = location.split(':'); const keyValue = whereFields.includes(OpenWhereMod.keyvalue); const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none; @@ -527,7 +547,7 @@ export class TabDocView extends ObservableReactComponent { switch (whereFields[0]) { case undefined: case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) { - const lightboxView = !docs[0].annotationOn && DocCast(docs[0].embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(docs[0].embedContainer)) : undefined; + const lightboxView = !docs[0].annotationOn && DocCast(docs[0].embedContainer) ? DocumentView.getFirstDocumentView(DocCast(docs[0].embedContainer)) : undefined; const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)]; if (lightboxView && (!data || data instanceof List)) { lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List(docs); @@ -543,7 +563,7 @@ export class TabDocView extends ObservableReactComponent { }; remDocTab = (doc: Doc | Doc[]) => { if (doc === this._document) { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); CollectionDockingView.CloseSplit(this._document); return true; } @@ -551,11 +571,7 @@ export class TabDocView extends ObservableReactComponent { }; getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame); - static Activate = (tabDoc: Doc) => { - const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue); - tab?.header.parent.setActiveContentItem(tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) - return tab !== undefined; - }; + @action focusFunc = () => { if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { @@ -586,7 +602,7 @@ export class TabDocView extends ObservableReactComponent { { - this._lastView && DocumentManager.Instance.RemoveView(this._lastView); + this._lastView && DocumentView.removeView(this._lastView); this._view = r; this._lastView = this._view; })} @@ -635,7 +651,7 @@ export class TabDocView extends ObservableReactComponent { this._mainCont = ref; if (this._mainCont) { if (this._lastTab) { - this._view && DocumentManager.Instance.RemoveView(this._view); + this._view && DocumentView.removeView(this._view); } this._lastTab = this.tab; (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 2f5af0564..969b98d91 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -21,24 +21,23 @@ import { TraceMobx } from '../../../fields/util'; import { DocUtils } from '../../documents/DocUtils'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { LinkManager } from '../../util/LinkManager'; import { SettingsManager } from '../../util/SettingsManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { UndoManager, undoBatch, undoable } from '../../util/UndoManager'; import { EditableView } from '../EditableView'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView'; import { KeyValueBox } from '../nodes/KeyValueBox'; import { OpenWhere } from '../nodes/OpenWhere'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { CollectionTreeView, TreeViewType } from './CollectionTreeView'; +import { CollectionTreeView } from './CollectionTreeView'; +import { TreeViewType } from './CollectionTreeViewType'; import { CollectionView } from './CollectionView'; import { TreeSort } from './TreeSort'; import './TreeView.scss'; @@ -208,7 +207,7 @@ export class TreeView extends ObservableReactComponent { const ind = DocListCast(this.dataDoc[key]).indexOf(docs.lastElement()); const res = docs.reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true); - res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false); + res && ind > 0 && DocumentView.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false); return res; }; @@ -379,7 +378,7 @@ export class TreeView extends ObservableReactComponent { const bulletData = bullet[DocData]; bulletData.title = ComputedField.MakeFunction('this.text?.Text'); bulletData.data = new List([]); - DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.()); + DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.()); return bullet; } @@ -823,7 +822,7 @@ export class TreeView extends ObservableReactComponent { @computed get validExpandViewTypes() { const annos = () => (DocListCast(this.Document[this.fieldKey + '_annotations']).length && !this.treeView.dashboardMode ? 'annotations' : ''); - const links = () => (LinkManager.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : ''); + const links = () => (Doc.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : ''); const data = () => (this.childDocs || this.treeView.dashboardMode ? this.fieldKey : ''); const embeddings = () => (this.treeView.dashboardMode ? '' : 'embeddings'); const fields = () => (Doc.noviceMode ? '' : 'fields'); @@ -1210,7 +1209,7 @@ export class TreeView extends ObservableReactComponent { className={`treeView-container${this._props.isContentActive() ? '-active' : ''}`} ref={this.createTreeDropTarget} onDrop={this.onTreeDrop} - // onPointerDown={e => this._props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document + // onPointerDown={e => this._props.isContentActive(true) && DocumentView.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document // onKeyDown={this.onKeyDown} >
  • diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts index 6fc295e27..26a52cd2a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts @@ -1,17 +1,16 @@ import { action, observable } from 'mobx'; +import { CollectionFreeFormView } from '.'; +import { intersectRect } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { intersectRect } from '../../../../Utils'; import { DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SelectionManager } from '../../../util/SelectionManager'; +import { StyleProp } from '../../StyleProp'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; +import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; -import { StyleProp } from '../../StyleProvider'; import './CollectionFreeFormView.scss'; -import { CollectionFreeFormView } from '.'; export class CollectionFreeFormClusters { private _view: CollectionFreeFormView; @@ -44,7 +43,7 @@ export class CollectionFreeFormClusters { const h = NumCast(doc1Layout._height) + clusterDistance; return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 }); } - pickCluster(probe: number[]) { + handlePointerDown(probe: number[]) { this._hitCluster = this.childLayoutPairs .map(pair => pair.layout) .reduce((cluster, cd) => { @@ -62,13 +61,13 @@ export class CollectionFreeFormClusters { return this._hitCluster; } - tryDragCluster(e: PointerEvent) { + tryToDrag(e: PointerEvent) { const cluster = this._hitCluster; if (cluster !== -1) { const ptsParent = e; if (ptsParent) { const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster); - const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView)!); + const clusterDocs = eles.map(ele => DocumentView.getDocumentView(ele, this.DocumentView)!); const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 }; const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined); de.moveDocument = this.viewMoveDocument; @@ -87,7 +86,7 @@ export class CollectionFreeFormClusters { return false; } - initClusters() { + initLayout() { if (this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length) { return this.updateClusters(true); } @@ -97,11 +96,11 @@ export class CollectionFreeFormClusters { updateClusters(useClusters: boolean) { this.Document._freeform_useClusters = useClusters; this._clusterSets.length = 0; - this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c)); + this.childLayoutPairs.map(pair => pair.layout).map(c => this.addDocument(c)); } @action - updateClusterDocs(docs: Doc[]) { + addDocuments(docs: Doc[]) { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.Document._freeform_useClusters) { const docFirst = docs[0]; @@ -143,12 +142,12 @@ export class CollectionFreeFormClusters { this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc); }); } - childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child)); + childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.addDocument(child)); } } @action - updateCluster = (doc: Doc) => { + addDocument = (doc: Doc) => { const childLayouts = this.childLayoutPairs.map(pair => pair.layout); if (this.Document._freeform_useClusters) { this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)); @@ -188,7 +187,7 @@ export class CollectionFreeFormClusters { const cluster = NumCast(doc?.layout_cluster); if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) { if (this._clusterSets.length <= cluster) { - setTimeout(() => doc && this.updateCluster(doc)); + setTimeout(() => doc && this.addDocument(doc)); } else { // choose a cluster color from a palette const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; @@ -212,9 +211,9 @@ export class CollectionFreeFormClusters { return styleProp; }; - trySelectCluster = (addToSel: boolean) => { + tryToSelect = (addToSel: boolean) => { if (addToSel && this._hitCluster !== -1) { - !addToSel && SelectionManager.DeselectAll(); + !addToSel && DocumentView.DeselectAll(); const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster); this.selectDocuments(eles); return true; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx index 65a529d62..5d8373fc7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx @@ -4,13 +4,13 @@ import * as React from 'react'; import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc'; import { InkTool } from '../../../../fields/InkField'; import { StrCast } from '../../../../fields/Types'; -import { LinkManager } from '../../../util/LinkManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { TopBar } from '../../topbar/TopBar'; import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState'; import './CollectionFreeFormView.scss'; import { DocData } from '../../../../fields/DocSymbols'; +import { CollectionFreeFormView } from './CollectionFreeFormView'; export interface CollectionFreeFormInfoUIProps { Document: Doc; @@ -21,6 +21,12 @@ export interface CollectionFreeFormInfoUIProps { @observer export class CollectionFreeFormInfoUI extends ObservableReactComponent { + public static Init() { + CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => ( + // + + )); + } _firstDocPos = { x: 0, y: 0 }; constructor(props: any) { @@ -63,7 +69,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent DocumentLinksButton.StartLink; const linkUnstart = () => !DocumentLinksButton.StartLink; - const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length; + const numDocLinks = () => Doc.Links(firstDoc())?.length; const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView; const activeTool = () => Doc.ActiveTool; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx index 90977d955..65a2fe0aa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx @@ -3,9 +3,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { ScriptField } from '../../../../fields/ScriptField'; -import { PresBox } from '../../nodes/trails/PresBox'; -import './CollectionFreeFormView.scss'; import { ObservableReactComponent } from '../../ObservableReactComponent'; +import './CollectionFreeFormView.scss'; export interface CollectionFreeFormPannableContentsProps { Document: Doc; @@ -20,12 +19,16 @@ export interface CollectionFreeFormPannableContentsProps { @observer export class CollectionFreeFormPannableContents extends ObservableReactComponent { + static _overlayPlugin: ((fform: Doc) => React.JSX.Element) | null = null; + public static SetOverlayPlugin(plugin: ((fform: Doc) => React.JSX.Element) | null) { + CollectionFreeFormPannableContents._overlayPlugin = plugin; + } constructor(props: CollectionFreeFormPannableContentsProps) { super(props); makeObservable(this); } @computed get presPaths() { - return this._props.showPresPaths() ? PresBox.Instance.pathLines(this._props.Document) : null; + return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null; } // rectangle highlight used when following trail/link to a region of a collection that isn't a document showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) => diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f55d5a23f..6d901119e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -26,15 +26,12 @@ import { aggregateBounds, emptyFunction, intersectRect, Utils } from '../../../. import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocUtils } from '../../../documents/DocUtils'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { ReplayMovements } from '../../../util/ReplayMovements'; import { CompileScript } from '../../../util/Scripting'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { freeformScrollMode } from '../../../util/SettingsManager'; -import { SnappingManager } from '../../../util/SnappingManager'; +import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; import { Timeline } from '../../animationtimeline/Timeline'; @@ -50,12 +47,11 @@ import { FocusViewOptions } from '../../nodes/FocusViewOptions'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { OpenWhere } from '../../nodes/OpenWhere'; import { PinDocView, PinProps } from '../../PinFuncs'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { CollectionSubView } from '../CollectionSubView'; -import { TreeViewType } from '../CollectionTreeView'; +import { TreeViewType } from '../CollectionTreeViewType'; import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid'; import { CollectionFreeFormClusters } from './CollectionFreeFormClusters'; -import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI'; import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines'; import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents'; import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors'; @@ -87,8 +83,9 @@ export class CollectionFreeFormView extends CollectionSubView(); - public static from(dv?: DocumentView) { - return CollectionFreeFormDocumentView.from(dv)?.CollectionFreeFormView; + public static from(dv?: DocumentView): CollectionFreeFormView | undefined { + const parent = CollectionFreeFormDocumentView.from(dv)?._props.parent; + return parent instanceof CollectionFreeFormView ? parent : undefined; } _oldWheel: any; @@ -227,25 +224,6 @@ export class CollectionFreeFormView extends CollectionSubView { - CollectionFreeFormDocumentView.animFields.forEach(val => { - const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); - }); - CollectionFreeFormDocumentView.animStringFields.forEach(val => { - const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); - }); - CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => { - const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any); - }); - }); - return newTimer; - } changeKeyFrame = (back = false) => { const currentFrame = Cast(this.Document._currentFrame, 'number', null); if (currentFrame === undefined) { @@ -256,7 +234,7 @@ export class CollectionFreeFormView extends CollectionSubView { - SelectionManager.DeselectAll(); - docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true)); + DocumentView.DeselectAll(); + docs.map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && DocumentView.SelectView(dv, true)); }; addDocument = (newBox: Doc | Doc[]) => { let retVal = false; @@ -301,7 +279,7 @@ export class CollectionFreeFormView extends CollectionSubView void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv)); + const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv)); findDoc(dv => res(dv)); }); @@ -459,7 +437,7 @@ export class CollectionFreeFormView extends CollectionSubView { - if (this._clusters.tryDragCluster(e)) { + if (this._clusters.tryToDrag(e)) { e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over return true; } @@ -675,7 +653,7 @@ export class CollectionFreeFormView extends CollectionSubView DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())) + .map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())) .filter(inkView => inkView?.ComponentView instanceof InkingStroke) .map(inkView => inkView!) .map(inkView => ({ inkViewBounds: inkView.getBounds, inkStroke: inkView.ComponentView as InkingStroke, inkView })) @@ -776,7 +754,7 @@ export class CollectionFreeFormView extends CollectionSubView doc.type === DocumentType.INK && !doc.dontIntersect) .forEach(doc => { - const otherInk = DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke; + const otherInk = DocumentView.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke; const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt)); @@ -1078,17 +1056,18 @@ export class CollectionFreeFormView extends CollectionSubView this.childDocs; closeInfo = action(() => { Doc.IsInfoUIDisabled = true }); // prettier-ignore - infoUI = () => (Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth ? null : ); + static _infoUI: ((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) | null = null; + static SetInfoUICreator(func: (doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) { + CollectionFreeFormView._infoUI = func; + } + infoUI = () => + Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth + ? null // + : CollectionFreeFormView._infoUI?.(this.Document, this.layoutDoc, this.childDocsFunc, this.closeInfo) || null; componentDidMount() { this._props.setContentViewBox?.(this); @@ -1551,9 +1537,7 @@ export class CollectionFreeFormView extends CollectionSubView intersectRect(docDims(doc), rect); const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to - activeDocs - .filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)) - .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); + activeDocs.filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)).forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited)); const horizLines: number[] = []; const vertLines: number[] = []; @@ -1654,7 +1638,7 @@ export class CollectionFreeFormView extends CollectionSubView 0 ? undefined : this.nudge} addDocTab={this.addDocTab} slowLoadDocuments={this.slowLoadDocuments} - trySelectCluster={this._clusters.trySelectCluster} + trySelectCluster={this._clusters.tryToSelect} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} addDocument={this.addDocument} @@ -1744,22 +1728,22 @@ export class CollectionFreeFormView extends CollectionSubView selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.())); return undefined; }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function pinWithView(pinContent: boolean) { - SelectionManager.Views.forEach(view => + DocumentView.Selected().forEach(view => view._props.pinToPres(view.Document, { currentFrame: Cast(view.Document.currentFrame, 'number', null), pinData: { @@ -1772,16 +1756,16 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function bringToFront() { - SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document)); + DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function sendToBack() { - SelectionManager.Views.forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true)); + DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true)); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function datavizFromSchema() { // creating a dataviz doc to represent the schema table - SelectionManager.Views.forEach(viewIn => { + DocumentView.Selected().forEach(viewIn => { const view = viewIn; if (!view.layoutDoc.schema_columnKeys) { view.layoutDoc.schema_columnKeys = new List(['title', 'type', 'author', 'author_date']); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 3ab04f403..b96444024 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -17,15 +17,14 @@ import { CognitiveServices } from '../../../cognitive_services/CognitiveServices import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs, DocumentOptions } from '../../../documents/Documents'; -import { SelectionManager } from '../../../util/SelectionManager'; -import { freeformScrollMode } from '../../../util/SettingsManager'; -import { SnappingManager } from '../../../util/SnappingManager'; +import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { MarqueeViewBounds } from '../../PinFuncs'; import { PreviewCursor } from '../../PreviewCursor'; +import { DocumentView } from '../../nodes/DocumentView'; import { OpenWhere } from '../../nodes/OpenWhere'; import { pasteImageBitmap } from '../../nodes/WebBoxRenderer'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -175,7 +174,7 @@ export class MarqueeView extends ObservableReactComponent | KeyboardEvent | undefined, hide?: boolean) => { const selected = this.marqueeSelect(false); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); selected.forEach(doc => { hide ? (doc.hidden = true) : this._props.removeDocument?.(doc); }); @@ -380,7 +379,7 @@ export class MarqueeView extends ObservableReactComponent { const selected = this.marqueeSelect(false); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); selected.forEach(d => this._props.removeDocument?.(d)); const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2)!; this._props.addDocument?.(newCollection); @@ -390,7 +389,7 @@ export class MarqueeView extends ObservableReactComponent ); getCurrentlyPlayingUI = () => - !CollectionStackedTimeline.CurrentlyPlaying?.length ? null : ( + !DocumentView.CurrentlyPlaying?.length ? null : ( Currently playing: - {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => ( + {DocumentView.CurrentlyPlaying.map((clip, i) => ( <> - DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}> - {clip.Document.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')} + DocumentView.showDocument(clip.Document, { willZoomCentered: true })}> + {clip.Document.title + (i === DocumentView.CurrentlyPlaying.length - 1 ? ' ' : ',')} clip.ComponentView?.TogglePause?.()} />{' '} clip.ComponentView?.Pause?.()} />{' '} diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index 49ba85524..931e2c5e0 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { UndoManager } from '../../../util/UndoManager'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { StyleProviderFuncType } from '../../nodes/FieldView'; import { DimUnit } from './CollectionMulticolumnView'; diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index ad77c327d..cff0a8b4c 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { UndoManager } from '../../../util/UndoManager'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { StyleProviderFuncType } from '../../nodes/FieldView'; import { DimUnit } from './CollectionMultirowView'; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 406a7d626..b30954ffd 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -15,16 +15,15 @@ import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { DefaultStyleProvider, StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; +import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; @@ -92,12 +91,12 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get _selectedDocs() { - const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); + const selected = DocumentView.SelectedDocs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)); if (!selected.length) { // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window) - const childOfSchemaDoc = SelectionManager.Docs.find(sel => DocumentManager.GetContextPath(sel, true).includes(this.Document)); + const childOfSchemaDoc = DocumentView.SelectedDocs().find(sel => DocumentView.getContextPath(sel, true).includes(this.Document)); if (childOfSchemaDoc) { - const contextPath = DocumentManager.GetContextPath(childOfSchemaDoc, true); + const contextPath = DocumentView.getContextPath(childOfSchemaDoc, true); return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected } } @@ -175,6 +174,7 @@ export class CollectionSchemaView extends CollectionSubView() { document.removeEventListener('keydown', this.onKeyDown); } + isUnstyledView = returnTrue; // used by style provide via ViewBoxInterface rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc); @action @@ -190,7 +190,7 @@ export class CollectionSchemaView extends CollectionSubView() { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[lastIndex + 1]; if (this._selectedDocs.includes(newDoc)) { - SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); } else { this.addDocToSelection(newDoc, e.shiftKey); this._selectedCell && (this._selectedCell[0] = newDoc); @@ -209,7 +209,7 @@ export class CollectionSchemaView extends CollectionSubView() { if (firstIndex > 0 && firstIndex < this.childDocs.length) { !e.shiftKey && this.clearSelection(); const newDoc = this.sortedDocs.docs[firstIndex - 1]; - if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc)); + if (this._selectedDocs.includes(newDoc)) DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc)); else { this.addDocToSelection(newDoc, e.shiftKey); this._selectedCell && (this._selectedCell[0] = newDoc); @@ -416,12 +416,12 @@ export class CollectionSchemaView extends CollectionSubView() { @action addDocToSelection = (doc: Doc, extendSelection: boolean) => { - const rowDocView = DocumentManager.Instance.getDocumentView(doc); - if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection); + const rowDocView = DocumentView.getDocumentView(doc); + if (rowDocView) DocumentView.SelectView(rowDocView, extendSelection); }; @action - clearSelection = () => SelectionManager.DeselectAll(); + clearSelection = () => DocumentView.DeselectAll(); selectRows = (doc: Doc, lastSelected: Doc) => { const index = this.rowIndex(doc); @@ -476,9 +476,9 @@ export class CollectionSchemaView extends CollectionSubView() { this.dataDoc[this.fieldKey ?? 'data'] = new List([...removed, ...draggedDocs, ...pushedDocs]); this.clearSelection(); draggedDocs.forEach(doc => { - const draggedView = DocumentManager.Instance.getFirstDocumentView(doc); - if (draggedView) DocumentManager.Instance.RemoveView(draggedView); - DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true)); + const draggedView = DocumentView.getFirstDocumentView(doc); + if (draggedView) DocumentView.removeView(draggedView); + DocumentView.addViewRenderedCb(doc, dv => dv.select(true)); }); return true; } diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index dfef3aa48..ee6987e89 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -19,9 +19,7 @@ import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { FInfo, FInfoFieldType } from '../../../documents/Documents'; -import { DocFocusOrOpen } from '../../../util/DocumentManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, undoable } from '../../../util/UndoManager'; @@ -29,7 +27,7 @@ import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; -import { returnEmptyDocViewList } from '../../nodes/DocumentView'; +import { DocFocusOrOpen, returnEmptyDocViewList } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; @@ -300,8 +298,8 @@ export class SchemaDateCell extends ObservableReactComponent} size={Size.XSMALL} type={Type.TERT} - color={SettingsManager.userColor} - background={SettingsManager.userBackgroundColor} + color={SnappingManager.userColor} + background={SnappingManager.userBackgroundColor} popup={
    diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index e75b01ab6..c595681b7 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -10,7 +10,6 @@ import { Gestures } from '../../../pen-gestures/GestureTypes'; import { DocumentType } from '../../documents/DocumentTypes'; import { LinkManager } from '../../util/LinkManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { UndoManager, undoable } from '../../util/UndoManager'; import { GestureOverlay } from '../GestureOverlay'; import { InkingStroke } from '../InkingStroke'; @@ -26,14 +25,14 @@ import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function IsNoneSelected() { - return SelectionManager.Views.length <= 0; + return DocumentView.Selected().length <= 0; }, 'are no document selected'); // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { - if (getSelected) return SelectionManager.Docs; - const selected = SelectionManager.Docs.lastElement(); + if (getSelected) return DocumentView.SelectedDocs(); + const selected = DocumentView.SelectedDocs().lastElement(); selected ? (selected._type_collection = view) : console.log('[FontIconBox.tsx] changeView failed'); return undefined; }); @@ -41,7 +40,7 @@ ScriptingGlobals.add(function setView(view: string, getSelected: boolean) { // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { - const selectedViews = SelectionManager.Views; + const selectedViews = DocumentView.Selected(); if (Doc.ActiveTool !== InkTool.None) { if (checkResult) { return ActiveFillColor(); @@ -71,7 +70,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b } }); } else { - const selected = SelectionManager.Docs.length ? SelectionManager.Docs : LinkManager.Instance.currentLink ? [LinkManager.Instance.currentLink] : []; + const selected = DocumentView.SelectedDocs().length ? DocumentView.SelectedDocs() : LinkManager.Instance.currentLink ? [LinkManager.Instance.currentLink] : []; if (checkResult) { return selected.lastElement()?._backgroundColor ?? 'transparent'; } @@ -90,10 +89,10 @@ ScriptingGlobals.add(function setDefaultTemplate(checkResult?: boolean) { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { if (checkResult) { - return SelectionManager.Views.length ? StrCast(SelectionManager.Docs.lastElement().layout_headingColor) : Doc.SharingDoc().headingColor; + return DocumentView.Selected().length ? StrCast(DocumentView.SelectedDocs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor; } - if (SelectionManager.Views.length) { - SelectionManager.Docs.forEach(doc => { + if (DocumentView.Selected().length) { + DocumentView.SelectedDocs().forEach(doc => { doc[DocData].layout_headingColor = color === 'transparent' ? undefined : color; doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title'); }); @@ -108,7 +107,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { - const selected = SelectionManager.Views.length ? SelectionManager.Views[0] : undefined; + const selected = DocumentView.Selected().length ? DocumentView.Selected()[0] : undefined; if (checkResult) { if (NumCast(selected?.Document.z) >= 1) return true; return false; @@ -119,7 +118,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); // prettier-ignore const map: Map<'center' |'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', { waitForRender?: boolean, checkResult: (doc:Doc) => any; setDoc: (doc:Doc, dv:DocumentView) => void;}> = new Map([ ['grid', { @@ -153,7 +152,7 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' return map.get(attr)?.checkResult(selected); } const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; - SelectionManager.Views.map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv)); + DocumentView.Selected().map(dv => map.get(attr)?.setDoc(dv.layoutDoc, dv)); setTimeout(() => batch.end(), 100); return undefined; }); @@ -347,7 +346,7 @@ ScriptingGlobals.add(setActiveTool, 'sets the active ink tool mode'); // toggle: Set overlay status of selected document // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', value: any, checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement() ?? Doc.UserDoc(); + const selected = DocumentView.SelectedDocs().lastElement() ?? Doc.UserDoc(); // prettier-ignore const map: Map<'inkMask' | 'labels' | 'fillColor' | 'strokeWidth' | 'strokeColor', { checkResult: () => any; setInk: (doc: Doc) => void; setMode: () => void }> = new Map([ ['inkMask', { @@ -381,7 +380,20 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil return map.get(option)?.checkResult(); } map.get(option)?.setMode(); - SelectionManager.Docs.filter(doc => doc._layout_isSvg).map(doc => map.get(option)?.setInk(doc)); + DocumentView.SelectedDocs() + .filter(doc => doc._layout_isSvg) + .map(doc => map.get(option)?.setInk(doc)); + return undefined; +}); + +// eslint-disable-next-line prefer-arrow-callback +ScriptingGlobals.add(function toggleRaiseOnDrag(readOnly?: boolean) { + if (readOnly) { + return DocumentView.Selected().some(dv => dv.Document.keepZWhenDragged); + } + DocumentView.Selected().forEach(dv => { + dv.Document.keepZWhenDragged = !dv.Document.keepZWhenDragged; + }); return undefined; }); @@ -390,7 +402,7 @@ ScriptingGlobals.add(function setInkProperty(option: 'inkMask' | 'labels' | 'fil * */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { - const selected = SelectionManager.Views.lastElement(); + const selected = DocumentView.Selected().lastElement(); if (selected?.Document.type === DocumentType.WEB) { if (checkResult) { return StrCast(selected.Document.data, Cast(selected.Document.data, WebField, null)?.url?.href); @@ -401,7 +413,7 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as WebBox; if (checkResult) { return selected?.forward(checkResult) ? undefined : 'lightGray'; } @@ -410,24 +422,24 @@ ScriptingGlobals.add(function webForward(checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function webBack() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as WebBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as WebBox; selected?.back(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function videoSnapshot() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as VideoBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as VideoBox; selected?.Snapshot(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function imageSetPixelSize() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as ImageBox; selected?.setNativeSize(); }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function imageRotate90() { - const selected = SelectionManager.Views.lastElement()?.ComponentView as ImageBox; + const selected = DocumentView.Selected().lastElement()?.ComponentView as ImageBox; selected?.rotate(); }); @@ -436,7 +448,7 @@ ScriptingGlobals.add(function imageRotate90() { * */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); if (checkResult && selected) { const result: boolean = NumCast(selected.schema_previewWidth) > 0; if (result) return Colors.MEDIUM_BLUE; @@ -453,7 +465,7 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) { - const selected = SelectionManager.Docs.lastElement(); + const selected = DocumentView.SelectedDocs().lastElement(); if (checkResult && selected) { return NumCast(selected._schema_singleLine) > 0 ? Colors.MEDIUM_BLUE : 'transparent'; } @@ -468,7 +480,7 @@ ScriptingGlobals.add(function toggleSingleLineSchema(checkResult?: boolean) { */ // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { - SelectionManager.Docs.forEach(doc => { doc._text_fontFamily = key; }); // prettier-ignore + DocumentView.SelectedDocs().forEach(doc => { doc._text_fontFamily = key; }); // prettier-ignore const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc())?.fontFamily); diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx index f99a18db2..cd735318e 100644 --- a/src/client/views/linking/LinkMenuGroup.tsx +++ b/src/client/views/linking/LinkMenuGroup.tsx @@ -8,7 +8,6 @@ import { Doc, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { Cast, DocCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; -import { LinkManager } from '../../util/LinkManager'; import { DocumentView } from '../nodes/DocumentView'; import './LinkMenu.scss'; import { LinkMenuItem } from './LinkMenuItem'; @@ -59,8 +58,7 @@ export class LinkMenuGroup extends React.Component { ? this.props.docView._props.LayoutTemplateString?.includes('link_anchor_1') ? DocCast(linkDoc.link_anchor_2) : DocCast(linkDoc.link_anchor_1) - : LinkManager.getOppositeAnchor(linkDoc, sourceDoc) || - LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null)?.annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null)); + : Doc.getOppositeAnchor(linkDoc, sourceDoc) || Doc.getOppositeAnchor(linkDoc, Cast(linkDoc.link_anchor_2, Doc, null)?.annotationOn === sourceDoc ? Cast(linkDoc.link_anchor_2, Doc, null) : Cast(linkDoc.link_anchor_1, Doc, null)); return !destDoc || !sourceDoc ? null : ( LinkManager.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; + const draggedDocs = (specificLinks || LinkManager.Links(sourceDoc)).map(link => Doc.getOppositeAnchor(link, sourceDoc)).filter(l => l) as Doc[]; if (draggedDocs.length) { const moddrag: Doc[] = []; @@ -81,7 +78,7 @@ export class LinkMenuItem extends ObservableReactComponent { onIconDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, returnFalse, returnFalse, () => { - const ancestor = DocumentManager.LinkCommonAncestor(this._props.linkDoc); + const ancestor = DocumentView.linkCommonAncestor(this._props.linkDoc); if (!ancestor?.ComponentView?.removeDocument?.(this._props.linkDoc)) { ancestor?.ComponentView?.addDocument?.(this._props.linkDoc); } @@ -105,14 +102,14 @@ export class LinkMenuItem extends ObservableReactComponent { Doc.ActivePresentation = trail; DocumentViewInternal.addDocTabFunc(trail, OpenWhere.replaceRight); } else { - SelectionManager.SelectView(this._props.docView, false); + DocumentView.SelectView(this._props.docView, false); LinkManager.Instance.currentLink = this._props.linkDoc === LinkManager.Instance.currentLink ? undefined : this._props.linkDoc; LinkManager.Instance.currentLinkAnchor = LinkManager.Instance.currentLink ? this.sourceAnchor : undefined; - if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) { + if ((SnappingManager.PropertiesWidth ?? 0) < 100) { setTimeout( action(() => { - SettingsManager.Instance.propertiesWidth = 250; + SnappingManager.SetPropertiesWidth(250); }) ); } @@ -126,10 +123,12 @@ export class LinkMenuItem extends ObservableReactComponent { this, e, moveEv => { - const eleClone: any = this._drag.current!.cloneNode(true); - eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`; - StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]); - this._props.clearLinkEditor?.(); + const eleClone: any = this._drag.current?.cloneNode(true); + if (eleClone) { + eleClone.style.transform = `translate(${moveEv.x}px, ${moveEv.y}px)`; + StartLinkTargetsDrag(eleClone, this._props.docView, moveEv.x, moveEv.y, this._props.sourceDoc, [this._props.linkDoc]); + this._props.clearLinkEditor?.(); + } return true; }, emptyFunction, @@ -146,13 +145,13 @@ export class LinkMenuItem extends ObservableReactComponent { : undefined; if (focusDoc) this._props.docView._props.focus(focusDoc, { instant: true }); - LinkFollower.FollowLink(this._props.linkDoc, this._props.sourceDoc, false); + DocumentView.FollowLink(this._props.linkDoc, this._props.sourceDoc, false); } } ); }; - deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this._props.linkDoc)))); + deleteLink = (e: React.PointerEvent): void => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Doc.DeleteLink?.(this._props.linkDoc)))); @observable _hover = false; docView = () => this._props.docView; render() { @@ -181,7 +180,7 @@ export class LinkMenuItem extends ObservableReactComponent { style={{ fontSize: this._hover ? 'larger' : undefined, fontWeight: this._hover ? 'bold' : undefined, - background: LinkManager.Instance.currentLink === this._props.linkDoc ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor, + background: LinkManager.Instance.currentLink === this._props.linkDoc ? SnappingManager.userVariantColor : SnappingManager.userBackgroundColor, }}>
    { onClick={e => { e.stopPropagation(); CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); NewLightboxView.SetNewLightboxDoc(undefined); }}>
    { } } public static AddDocTab = (docsIn: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => { - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); const doc = toList(docsIn).lastElement(); return ( doc && @@ -110,8 +105,8 @@ export class NewLightboxView extends React.Component { } else { const l = CreateLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); - // TabDocView.PinDoc(doc, { hidePresBox: true }); + DocumentView.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.()); + // DocumentView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { this._savedState = { @@ -130,7 +125,7 @@ export class NewLightboxView extends React.Component { ...future .slice() .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)) - .sort((a, b) => LinkManager.Links(a).length - LinkManager.Links(b).length), + .sort((a, b) => Doc.Links(a).length - Doc.Links(b).length), ]; } this._doc = doc; @@ -148,11 +143,11 @@ export class NewLightboxView extends React.Component { @action public static Next() { const doc = NewLightboxView._doc!; const target = (NewLightboxView._docTarget = this._future?.pop()); - const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target); + const targetDocView = target && DocumentView.getLightboxDocumentView(target); if (targetDocView && target) { const l = CreateLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.(true) || target).lastElement(); l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen'); - DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target }); } else if (!target && NewLightboxView.path.length) { const saved = NewLightboxView._savedState; @@ -182,10 +177,10 @@ export class NewLightboxView extends React.Component { return; } const { doc, target } = NewLightboxView._history?.lastElement(); - const docView = DocumentManager.Instance.getLightboxDocumentView(target || doc); + const docView = DocumentView.getLightboxDocumentView(target || doc); if (docView) { NewLightboxView._docTarget = target; - target && DocumentManager.Instance.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); + target && DocumentView.showDocument(target, { willZoomCentered: true, zoomScale: 0.9 }); } else { NewLightboxView.SetNewLightboxDoc(doc, target); } @@ -277,7 +272,7 @@ export class NewLightboxView extends React.Component { removeDocument={undefined} whenChildContentsActiveChanged={emptyFunction} addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} + pinToPres={DocumentView.PinDoc} focus={emptyFunction} /> @@ -316,8 +311,8 @@ export class NewLightboxView extends React.Component { if (coll) { const fieldKey = Doc.LayoutFieldKey(coll); const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])]; - const links = LinkManager.Links(coll) - .map(link => LinkManager.getOppositeAnchor(link, coll)) + const links = Doc.Links(coll) + .map(link => Doc.getOppositeAnchor(link, coll)) .filter(doc => doc) .map(doc => doc!); NewLightboxView.SetNewLightboxDoc(coll, undefined, contents.length ? contents : links); diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx index 23027808f..375408d01 100644 --- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx +++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { IRecommendation } from './utils'; -import './Recommendation.scss'; -import { getType } from '../../utils'; import { FaEyeSlash } from 'react-icons/fa'; -import { NewLightboxView } from '../../NewLightboxView'; -import { DocumentManager } from '../../../../util/DocumentManager'; import { Doc } from '../../../../../fields/Doc'; import { Docs } from '../../../../documents/Documents'; +import { DocumentView } from '../../../nodes/DocumentView'; +import { NewLightboxView } from '../../NewLightboxView'; +import { getType } from '../../utils'; +import './Recommendation.scss'; +import { IRecommendation } from './utils'; export const Recommendation = (props: IRecommendation) => { const { title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId } = props; @@ -17,7 +17,7 @@ export const Recommendation = (props: IRecommendation) => { onClick={() => { let doc: Doc | null = null; if (source == 'Dash' && docId) { - const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement(); + const docView = DocumentView.getDocumentViewsById(docId).lastElement(); if (docView) { doc = docView.Document; } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 4697491e0..9deed4de4 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -18,14 +18,15 @@ import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; import { DragManager } from '../../util/DragManager'; -import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { DocViewUtils } from '../DocViewUtils'; import { PinDocView, PinProps } from '../PinFuncs'; import './AudioBox.scss'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { OpenWhere } from './OpenWhere'; @@ -99,7 +100,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { return this._props.PanelHeight() < 50; } // used to collapse timeline when node is shrunk @computed get links() { - return LinkManager.Links(this.dataDoc); + return Doc.Links(this.dataDoc); } @computed get mediaState() { return this.dataDoc.mediaState as mediaState; @@ -218,9 +219,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { @action removeCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (CollectionStackedTimeline.CurrentlyPlaying && docView) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); - index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); + if (DocumentView.CurrentlyPlaying && docView) { + const index = DocumentView.CurrentlyPlaying.indexOf(docView); + index !== -1 && DocumentView.CurrentlyPlaying.splice(index, 1); } }; @@ -228,11 +229,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { @action addCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (!CollectionStackedTimeline.CurrentlyPlaying) { - CollectionStackedTimeline.CurrentlyPlaying = []; + if (!DocumentView.CurrentlyPlaying) { + DocumentView.CurrentlyPlaying = []; } - if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) { - CollectionStackedTimeline.CurrentlyPlaying.push(docView); + if (docView && DocumentView.CurrentlyPlaying.indexOf(docView) === -1) { + DocumentView.CurrentlyPlaying.push(docView); } }; @@ -251,7 +252,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { this._stream = await navigator.mediaDevices.getUserMedia({ audio: true }); this._recorder = new MediaRecorder(this._stream); this.dataDoc[this.fieldKey + '_recordingStart'] = new DateField(); - DocUtils.ActiveRecordings.push(this); + DocViewUtils.ActiveRecordings.push(this); this._recorder.ondataavailable = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); if (!(result instanceof Error)) { @@ -278,8 +279,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent() { this.dataDoc[this.fieldKey + '_duration'] = (now - this._recordStart - this._pausedTime) / 1000; this.mediaState = mediaState.Paused; this._stream?.getAudioTracks()[0].stop(); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); } }; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 691d07e31..685a5aca4 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -5,18 +5,16 @@ import { OmitKeys } from '../../../ClientUtils'; import { numberRange } from '../../../Utils'; import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { TransitionTimer } from '../../../fields/DocSymbols'; +import { InkField } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; -import { SelectionManager } from '../../util/SelectionManager'; import { DocComponent } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { StyleProp } from '../StyleProp'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; import { FieldViewProps } from './FieldView'; @@ -43,7 +41,8 @@ interface freeFormProps { export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { RenderCutoffProvider: (doc: Doc) => boolean; - CollectionFreeFormView: CollectionFreeFormView; + isAnyChildContentActive: () => boolean; + parent: any; } @observer export class CollectionFreeFormDocumentView extends DocComponent() { @@ -66,7 +65,7 @@ export class CollectionFreeFormDocumentView extends DocComponent (Doc.LayoutFieldKey(doc) ? [Doc.LayoutFieldKey(doc)] : []); // fields that are configured to be animatable using animation frames - public static from(dv?: DocumentView) { + public static from(dv?: DocumentView): CollectionFreeFormDocumentView | undefined { return dv?._props.parent instanceof CollectionFreeFormDocumentView ? dv._props.parent : undefined; } @@ -120,7 +119,6 @@ export class CollectionFreeFormDocumentView extends DocComponent this.Transition || StrCast(this.Document.dataTransition); // prettier-ignore RenderCutoffProvider = this.props.RenderCutoffProvider; // needed for type checking @@ -185,13 +183,32 @@ export class CollectionFreeFormDocumentView extends DocComponent { + this.animFields.forEach(val => { + const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); + }); + this.animStringFields.forEach(val => { + const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); + }); + this.animDataFields(doc).forEach(val => { + const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any); + }); + }); + return newTimer; + } public static setupKeyframes(docs: Doc[], currTimecode: number, makeAppear: boolean = false) { docs.forEach(doc => { if (doc.appearFrame === undefined) doc.appearFrame = currTimecode; @@ -220,7 +237,7 @@ export class CollectionFreeFormDocumentView extends DocComponent SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, containerDocView), false), 0); + setTimeout(() => DocumentView.SelectView(DocumentView.getDocumentView(topDoc, containerDocView), false), 0); } }; @@ -255,7 +272,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { - if (this.CollectionFreeFormView.isAnyChildContentActive()) return undefined; + if (this._props.isAnyChildContentActive()) return undefined; const backColor = this.BackgroundColor; const isGroup = this.dataDoc.isGroup && (!backColor || backColor === 'transparent'); return isGroup ? (this._props.isDocumentActive?.() ? 'group' : this._props.isGroupActive?.() ? 'child' : 'inactive') : this._props.isGroupActive?.() ? 'child' : undefined; diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index c42ca4468..6ae6bb228 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -15,7 +15,7 @@ import { dropActionType } from '../../util/DropActionTypes'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import './ComparisonBox.scss'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index ecfdcc229..15187b4e4 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -16,7 +16,6 @@ import { TraceMobx } from '../../../../fields/util'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; @@ -254,7 +253,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im this.toggleSidebar(); } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @computed get sidebarWidthPercent() { diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 1c3134185..bc35ab8c8 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -8,7 +8,6 @@ import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; -import { DocumentManager } from '../../../../util/DocumentManager'; import { undoable } from '../../../../util/UndoManager'; import {} from '../../../DocComponent'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; @@ -16,6 +15,7 @@ import { PinProps, PinDocView } from '../../../PinFuncs'; import { DataVizBox } from '../DataVizBox'; import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils'; import './Chart.scss'; +import { DocumentView } from '../../DocumentView'; export interface DataPoint { x: number; @@ -71,7 +71,7 @@ export class LineChart extends ObservableReactComponent { } @computed get parentViz() { return DocCast(this._props.Document.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links + // return LinkManager.Links(this._props.Document) // out of all links // .filter(link => { // return link.link_anchor_1 == this._props.Document.dataViz_parentViz; // }) // get links where this chart doc is the target of the link @@ -80,7 +80,7 @@ export class LineChart extends ObservableReactComponent { @computed get incomingHighlited() { // return selected x and y axes // otherwise, use the selection of whatever is linked to us - const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; + const incomingVizBox = DocumentView.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox; const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows); return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor } diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index ffb2f528a..ef6d1d412 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -84,7 +84,7 @@ export class PieChart extends ObservableReactComponent { @computed get parentViz() { return DocCast(this._props.Document.dataViz_parentViz); - // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links + // return LinkManager.Links(this._props.Document) // out of all links // .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 8038b2cd4..5cd77e274 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -7,6 +7,7 @@ import * as React from 'react'; import { ClientUtils, setupMoveUpEvents } from '../../../../../ClientUtils'; import { emptyFunction } from '../../../../../Utils'; import { Doc, Field, NumListCast } from '../../../../../fields/Doc'; +import { DocData } from '../../../../../fields/DocSymbols'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; import { Cast, DocCast } from '../../../../../fields/Types'; @@ -152,11 +153,9 @@ export class TableBox extends ObservableReactComponent { DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { dragComplete: completeEv => { if (!completeEv.aborted && completeEv.annoDragData && completeEv.annoDragData.linkSourceDoc && completeEv.annoDragData.dropDocument && completeEv.linkDocument) { - completeEv.linkDocument.link_displayLine = true; - completeEv.linkDocument.link_matchEmbeddings = true; - completeEv.linkDocument.link_displayArrow = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; + completeEv.linkDocument[DocData].link_matchEmbeddings = true; + completeEv.linkDocument[DocData].stroke_startMarker = true; + this._props.docView?.()?._props.addDocument?.(completeEv.linkDocument); } }, }); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index e902d1792..ec9db8480 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -31,7 +31,6 @@ import { FunctionPlotBox } from './FunctionPlotBox'; import { ImageBox } from './ImageBox'; import { KeyValueBox } from './KeyValueBox'; import { LabelBox } from './LabelBox'; -import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkBox } from './LinkBox'; import { LoadingBox } from './LoadingBox'; import { MapBox } from './MapBox/MapBox'; @@ -87,18 +86,18 @@ interface HTMLtagProps { export class HTMLtag extends React.Component { click = () => { const clickScript = (this.props as any).onClick as Opt; - clickScript?.script.run({ this: this.props.Document, self: this.props.Document, scale: this.props.scaling }); + clickScript?.script.run({ this: this.props.Document, scale: this.props.scaling }); }; onInput = (e: React.FormEvent) => { const onInputScript = (this.props as any).onInput as Opt; - onInputScript?.script.run({ this: this.props.Document, self: this.props.Document, value: (e.target as any).textContent }); + onInputScript?.script.run({ this: this.props.Document, value: (e.target as any).textContent }); }; render() { const style: { [key: string]: any } = {}; const divKeys = OmitKeys(this.props, ['children', 'dragStarting', 'dragEnding', 'htmltag', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit; const replacer = (match: any, expr: string) => // bcz: this executes a script to convert a property expression string: { script } into a value - (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.Document, this: this.props.Document, scale: this.props.scaling }).result as string) || ''; + (ScriptField.MakeFunction(expr, { this: Doc.name, scale: 'number' })?.script.run({ this: this.props.Document, scale: this.props.scaling }).result as string) || ''; Object.keys(divKeys).forEach((prop: string) => { const p = (this.props as any)[prop] as string; style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer); @@ -121,7 +120,6 @@ export class DocumentContentsView extends ObservableReactComponent{content}< as in {this.title} - const replacer = (match: any, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; + const replacer = (match: any, prefix: string, expr: string, postfix: string) => prefix + ((ScriptField.MakeFunction(expr, { this: Doc.name })?.script.run({ this: this._props.Document }).result as string) || '') + postfix; layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer); // replace HTML with corresponding HTML tag as in: becomes @@ -205,7 +203,7 @@ export class DocumentContentsView extends ObservableReactComponent { makeObservable(this); } - static get DocViews() { - return LightboxView.LightboxDoc ? DocumentManager.Instance.DocumentViews.filter(v => LightboxView.Contains(v)) : DocumentManager.Instance.DocumentViews; - } render() { const { view } = this._props; const { left, top, right } = view.getBounds || { left: 0, top: 0, right: 0, bottom: 0 }; @@ -69,7 +64,7 @@ export class DocumentIconContainer extends React.Component { const match = node.text.match(/d([0-9]+)/); if (match) { const m = parseInt(match[1]); - const doc = DocumentIcon.DocViews[m].Document; + const doc = DocumentView.allViews()[m].Document; usedDocuments.add(m); return factory.createIdentifier(`idToDoc("${doc[Id]}")`); } @@ -81,7 +76,7 @@ export class DocumentIconContainer extends React.Component { return ts.visitNode(root, visit); }, getVars() { - const docs = DocumentIcon.DocViews; + const docs = DocumentView.allViews(); const capturedVariables: { [name: string]: FieldType } = {}; usedDocuments.forEach(index => { capturedVariables[`d${index}`] = docs.length > index ? docs[index].Document : `d${index}`; @@ -91,6 +86,6 @@ export class DocumentIconContainer extends React.Component { }; } render() { - return DocumentIcon.DocViews.map((dv, i) => ); + return DocumentView.allViews().map((dv, i) => ); } } diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 977899589..0c5156339 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -8,11 +8,8 @@ import * as React from 'react'; import { StopEvent, returnFalse, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; -import { DocData } from '../../../fields/DocSymbols'; -import { StrCast } from '../../../fields/Types'; import { DocUtils } from '../../documents/DocUtils'; import { DragManager } from '../../util/DragManager'; -import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkManager } from '../../util/LinkManager'; import { UndoManager, undoBatch } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; @@ -169,16 +166,6 @@ export class DocumentLinksButton extends ObservableReactComponent (link.link_matchEmbeddings ? link.link_anchor_1 === this.Document : Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.Document)) || (link.link_matchEmbeddings ? link.link_anchor_2 === this.Document : Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.Document)) || @@ -197,11 +193,11 @@ export class DocumentViewInternal extends DocComponent !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document); + return Doc.Links(this.Document).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document); } @computed get filteredLinks() { - return DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []).filter(d => d.link_displayLine || Doc.UserDoc().showLinkLines); + return DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []); } componentWillUnmount() { @@ -259,7 +255,7 @@ export class DocumentViewInternal extends DocComponent dv.ContentDiv); + const views = DocumentView.Selected().filter(dv => dv.ContentDiv); const selected = views.length > 1 && views.some(dv => dv.Document === this.Document) ? views : [docView]; const dragData = new DragManager.DocumentDragData(selected.map(dv => dv.Document)); const screenXf = docView.screenToViewTransform(); @@ -289,8 +285,8 @@ export class DocumentViewInternal extends DocComponent { const browseTransitionTime = 500; - SelectionManager.DeselectAll(); - DocumentManager.Instance.showDocument(this.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { + DocumentView.DeselectAll(); + DocumentView.showDocument(this.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => { const options: FocusViewOptions = { pointFocus: { X: e.clientX, Y: e.clientY }, zoomTime: browseTransitionTime }; if (!focused && this._docView) { this._docView @@ -327,7 +323,7 @@ export class DocumentViewInternal extends DocComponent this.onDoubleClickHandler.script.run(scriptProps, console.log).result?.select && this._props.select(false), 'on double click: ' + this.Document.title); } else if (!Doc.IsSystem(this.Document) && defaultDblclick !== 'ignore') { UndoManager.RunInBatch(() => LightboxView.Instance.AddDocTab(this.Document, OpenWhere.lightbox), 'double tap'); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); Doc.UnBrushDoc(this.Document); } else { this._singleClickFunc?.(); @@ -377,7 +373,7 @@ export class DocumentViewInternal extends DocComponent DocumentView.LongPress && this._props.select(false), 1000); - if (!GestureOverlay.DownDocView) GestureOverlay.DownDocView = this._docView; + if (!DocumentView.DownDocView) DocumentView.DownDocView = this._docView; this._downX = e.clientX; this._downY = e.clientY; @@ -463,7 +459,7 @@ export class DocumentViewInternal extends DocComponent { - if (this._props.dontRegisterView || this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return false; + if (this._props.dontRegisterView) return false; if (this.Document === Doc.ActiveDashboard) { e.stopPropagation(); e.preventDefault(); @@ -484,7 +480,7 @@ export class DocumentViewInternal extends DocComponent SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); - zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views.forEach(dv => dv._props.bringToFront?.(dv.Document, true)), icon: 'arrow-down' }); + zorderItems.push({ description: 'Bring to Front', event: () => DocumentView.Selected().forEach(dv => dv._props.bringToFront?.(dv.Document, false)), icon: 'arrow-up' }); + zorderItems.push({ description: 'Send to Back', event: () => DocumentView.Selected().forEach(dv => dv._props.bringToFront?.(dv.Document, true)), icon: 'arrow-down' }); zorderItems.push({ description: !this.layoutDoc._keepZDragged ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged', event: undoBatch( @@ -601,7 +597,7 @@ export class DocumentViewInternal extends DocComponent this.toggleFollowLink(false, false), icon: 'link' }); !Doc.noviceMode && onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' }); !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' }); - } else if (LinkManager.Links(this.Document).length) { + } else if (Doc.Links(this.Document).length) { onClicks.push({ description: 'Restore On Click default', event: () => this.noOnClick(), icon: 'link' }); onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(), icon: 'link' }); !existingOnClick && cm.addItem({ description: 'OnClick...', subitems: onClicks, icon: 'mouse-pointer' }); @@ -718,7 +714,7 @@ export class DocumentViewInternal extends DocComponent d.link_displayLine || Doc.UserDoc().showLinkLines); + const filtered = DocUtils.FilterDocs(this.directLinks, this._props.childFilters?.() ?? [], []); return filtered.some(link => link._link_displayArrow) ? 0 : undefined; } default: @@ -726,34 +722,6 @@ export class DocumentViewInternal extends DocComponent () => link.link_displayLine = false; // prettier-ignore - @computed get allLinkEndpoints() { - // the small blue dots that mark the endpoints of links - if (this._componentView instanceof KeyValueBox || this._props.hideLinkAnchors || this.layoutDoc.layout_hideLinkAnchors || this._props.dontRegisterView || this.layoutDoc.layout_unrendered) return null; - return this.filteredLinks.map(link => ( -
    - -
    - )); - } - @computed get viewBoxContents() { TraceMobx(); const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString; @@ -780,7 +748,6 @@ export class DocumentViewInternal extends DocComponent - {this.layoutDoc.layout_hideAllLinks ? null : this.allLinkEndpoints}
    ); } @@ -1008,50 +975,45 @@ export class DocumentViewInternal extends DocComponent void) => void, onEnd?: () => void) { - let gumStream: any; - let recorder: any; - navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => { - let audioTextAnnos = Cast(dataDoc[field + '_audioAnnotations_text'], listSpec('string'), null); - if (audioTextAnnos) audioTextAnnos.push(''); - else audioTextAnnos = dataDoc[field + '_audioAnnotations_text'] = new List(['']); - DictationManager.Controls.listen({ - interimHandler: value => { audioTextAnnos[audioTextAnnos.length - 1] = value; }, // prettier-ignore - continuous: { indefinite: false }, - }).then(results => { - if (results && [DictationManager.Controls.Infringed].includes(results)) { - DictationManager.Controls.stop(); - } - onEnd?.(); - }); - - gumStream = stream; - recorder = new MediaRecorder(stream); - recorder.ondataavailable = async (e: any) => { - const [{ result }] = await Networking.UploadFilesToServer({ file: e.data }); - if (!(result instanceof Error)) { - const audioField = new AudioField(result.accessPaths.agnostic.client); - const audioAnnos = Cast(dataDoc[field + '_audioAnnotations'], listSpec(AudioField), null); - if (audioAnnos) audioAnnos.push(audioField); - else dataDoc[field + '_audioAnnotations'] = new List([audioField]); - } - }; - recorder.start(); - const stopFunc = () => { - recorder.stop(); - DictationManager.Controls.stop(/* false */); - dataDoc.audioAnnoState = AudioAnnoState.stopped; - gumStream.getAudioTracks()[0].stop(); - }; - if (onRecording) onRecording(stopFunc); - else setTimeout(stopFunc, 5000); - }); - } } @observer export class DocumentView extends DocComponent() { public static ROOT_DIV = 'documentView-effectsWrapper'; + // LinkFollower + public static FollowLink: (linkDoc: Opt, sourceDoc: Doc, altKey: boolean) => boolean; + // selection funcs + public static DeselectAll: (except?: Doc) => void | undefined; + public static DeselectView: (dv: DocumentView | undefined) => void | undefined; + public static SelectView: (dv: DocumentView | undefined, extendSelection: boolean) => void | undefined; + public static Selected: () => DocumentView[]; + public static SelectedDocs: () => Doc[]; + public static SelectSchemaDoc: (doc: Doc, deselectAllFirst?: boolean) => void; + public static SelectedSchemaDoc: () => Opt; + // view mgr funcs + public static activateTabView: (tabDoc: Doc) => boolean; + public static allViews: () => DocumentView[]; + public static addView: (dv: DocumentView) => void | undefined; + public static removeView: (dv: DocumentView) => void | undefined; + public static addViewRenderedCb: (doc: Opt, func: (dv: DocumentView) => any) => boolean; + public static getFirstDocumentView: (toFind: Doc) => DocumentView | undefined; + public static getDocumentView: (target: Doc | undefined, preferredCollection?: DocumentView) => Opt; + public static getContextPath: (doc: Opt, includeExistingViews?: boolean) => Doc[]; + public static getLightboxDocumentView: (toFind: Doc) => Opt; + public static showDocumentView: (targetDocView: DocumentView, options: FocusViewOptions) => Promise; + public static showDocument: ( + targetDoc: Doc, // document to display + optionsIn: FocusViewOptions, // options for how to navigate to target + finished?: (changed: boolean) => void // func called after focusing on target with flag indicating whether anything needed to be done. + ) => Promise; + public static linkCommonAncestor: (link: Doc) => DocumentView | undefined; + // pin func + public static PinDoc: (docIn: Doc | Doc[], pinProps: PinProps) => void; + // gesture + public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event. used by GestureOverlay to determine the doc a gesture should apply to. + // media playing + @observable public static CurrentlyPlaying: DocumentView[] = []; + public get displayName() { return 'DocumentView(' + (this.Document?.title??"") + ')'; } // prettier-ignore public ContentRef = React.createRef(); private _htmlOverlayEffect: Opt; @@ -1138,14 +1100,14 @@ export class DocumentView extends DocComponent() { componentDidMount() { runInAction(() => this.Document[DocViews].add(this)); this._disposers.onViewMounted = reaction(() => ScriptCast(this.Document.onViewMounted)?.script?.run({ this: this.Document }).result, emptyFunction); - !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.AddView(this); + !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.addView(this); } componentWillUnmount() { this._viewTimer && clearTimeout(this._viewTimer); runInAction(() => this.Document[DocViews].delete(this)); Object.values(this._disposers).forEach(disposer => disposer?.()); - !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentManager.Instance.RemoveView(this); + !BoolCast(this.Document.dontRegisterView, this._props.dontRegisterView) && DocumentView.removeView(this); } public set IsSelected(val) { runInAction(() => { this._selected = val; }); } // prettier-ignore @@ -1176,10 +1138,6 @@ export class DocumentView extends DocComponent() { const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse(); const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)]; - if (this._props.LayoutTemplateString?.includes(LinkAnchorBox.name)) { - const docuBox = this.ContentDiv.getElementsByClassName('linkAnchorBox-cont'); - if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), transition: undefined }; - } // transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox const transition = this.docViewPath().find((parent: DocumentView) => parent.DataTransition?.() || parent.ComponentView?.viewTransition?.()); return { left, top, right, bottom, transition: transition?.DataTransition?.() || transition?.ComponentView?.viewTransition?.() }; @@ -1289,7 +1247,7 @@ export class DocumentView extends DocComponent() { if (checkResult) { return Doc.UserDoc().defaultTextLayout; } - const view = SelectionManager.Views[0]?._props.renderDepth > 0 ? SelectionManager.Views[0] : undefined; + const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined; undoable(() => { let tempDoc: Opt; if (view) { @@ -1359,11 +1317,11 @@ export class DocumentView extends DocComponent() { screenToLocalScale = () => this._props.ScreenToLocalTransform().Scale; isSelected = () => this.IsSelected; select = (extendSelection: boolean, focusSelection?: boolean) => { - if (this.IsSelected && SelectionManager.Views.length > 1) SelectionManager.DeselectView(this); + if (this.IsSelected && DocumentView.Selected().length > 1) DocumentView.DeselectView(this); else { - SelectionManager.SelectView(this, extendSelection); + DocumentView.SelectView(this, extendSelection); if (focusSelection) { - DocumentManager.Instance.showDocument(this.Document, { + DocumentView.showDocument(this.Document, { willZoomCentered: true, zoomScale: 0.9, zoomTime: 500, @@ -1506,6 +1464,37 @@ export function returnEmptyDocViewList() { return emptyPath; } +// eslint-disable-next-line default-param-last +export function DocFocusOrOpen(docIn: Doc, optionsIn: FocusViewOptions = { willZoomCentered: true, zoomScale: 0, openLocation: OpenWhere.toggleRight }, containingDoc?: Doc) { + let doc = docIn; + const options = optionsIn; + const func = () => { + const cv = DocumentView.getDocumentView(containingDoc); + const dv = DocumentView.getDocumentView(doc, cv); + if (dv && (!containingDoc || dv.containerViewPath?.().lastElement()?.Document === containingDoc)) { + DocumentView.showDocumentView(dv, options).then(() => dv && Doc.linkFollowHighlight(dv.Document)); + } else { + const container = DocCast(containingDoc ?? doc.embedContainer ?? Doc.BestEmbedding(doc)); + const showDoc = !Doc.IsSystem(container) && !cv ? container : doc; + options.toggleTarget = undefined; + DocumentView.showDocument(showDoc, options, () => DocumentView.showDocument(doc, { ...options, openLocation: undefined })).then(() => { + const cvFound = DocumentView.getDocumentView(containingDoc); + const dvFound = DocumentView.getDocumentView(doc, cvFound); + dvFound && Doc.linkFollowHighlight(dvFound.Document); + }); + } + }; + if (Doc.IsDataProto(doc) && Doc.GetEmbeddings(doc).some(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))) { + doc = Doc.GetEmbeddings(doc).find(embed => embed.hidden && [AclAdmin, AclEdit].includes(GetEffectiveAcl(embed)))!; + } + if (doc.hidden) { + doc.hidden = false; + options.toggleTarget = false; + setTimeout(func); + } else func(); +} +ScriptingGlobals.add(DocFocusOrOpen); + // eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) { documentView.iconify(); @@ -1527,9 +1516,9 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc, linkSour const collectedLinks = DocListCast(linkCollection[DocData].data); let wid = NumCast(linkSource._width); let embedding: Doc | undefined; - const links = LinkManager.Links(linkSource); + const links = Doc.Links(linkSource); links.forEach(link => { - const other = LinkManager.getOppositeAnchor(link, linkSource); + const other = Doc.getOppositeAnchor(link, linkSource); const otherdoc = DocCast(other?.annotationOn ?? other); if (otherdoc && !collectedLinks?.some(d => Doc.AreProtosEqual(d, otherdoc))) { embedding = Doc.MakeEmbedding(otherdoc); diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 8d1617e66..5e3bb9fec 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -17,7 +17,7 @@ import { ContextMenu } from '../../ContextMenu'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { SelectedDocView } from '../../selectedDoc'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { FieldView, FieldViewProps } from '../FieldView'; import { OpenWhere } from '../OpenWhere'; import './FontIconBox.scss'; @@ -116,7 +116,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { default: type = 'slider'; break; } // prettier-ignore - const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, self: this.Document, value, _readOnly_: value === undefined }); + const numScript = (value?: number) => ScriptCast(this.Document.script).script.run({ this: this.Document, value, _readOnly_: value === undefined }); const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); // Script for checking the outcome of the toggle const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); @@ -142,7 +142,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { setupMoveUpEvents( this, e, - () => ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, self: this.Document, value: { doc: value, e } }).result, + () => ScriptCast(this.Document.onDragScript)?.script.run({ this: this.Document, value: { doc: value, e } }).result, emptyFunction, emptyFunction ); // prettier-ignore @@ -162,7 +162,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { const isViewDropdown = script?.script.originalScript.startsWith('{ return setView'); if (isViewDropdown) { const selected = Array.from(script?.script.run({ _readOnly_: true }).result) as Doc[]; - // const selected = SelectionManager.Docs; + // const selected = DocumentView.SelectedDocs(); if (selected.lastElement()) { if (StrCast(selected.lastElement().type) === DocumentType.COL) { text = StrCast(selected.lastElement()._type_collection); @@ -190,7 +190,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; } else { - text = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; + text = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; // text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); getStyle = (val: string) => ({ fontFamily: val }); } @@ -208,7 +208,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { return ( script.script.run({ this: this.Document, self: this.Document, value }), `dropdown select ${this.label}`)} + setSelectedVal={undoable(value => script.script.run({ this: this.Document, value }), `dropdown select ${this.label}`)} color={SnappingManager.userColor} background={SnappingManager.userVariantColor} type={Type.TERT} @@ -232,17 +232,17 @@ export class FontIconBox extends ViewBoxBaseComponent() { */ @computed get colorButton() { const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); - const curColor = this.colorScript?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; + const curColor = this.colorScript?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? 'transparent'; const tooltip: string = StrCast(this.Document.toolTip); return ( { if (!this.colorBatch) this.colorBatch = UndoManager.StartBatch(`Set ${tooltip} color`); - this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); }} setFinalColor={value => { - this.colorScript?.script.run({ this: this.Document, self: this.Document, value: value, _readOnly_: false }); + this.colorScript?.script.run({ this: this.Document, value: value, _readOnly_: false }); this.colorBatch?.end(); this.colorBatch = undefined; }} @@ -262,7 +262,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { const tooltip: string = StrCast(this.Document.toolTip); // const script = ScriptCast(this.Document.onClick); - // const toggleStatus = script ? script.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result : false; + // const toggleStatus = script ? script.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result : false; // Colors const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); const items = DocListCast(this.dataDoc.data); @@ -278,10 +278,10 @@ export class FontIconBox extends ViewBoxBaseComponent() { tooltip: StrCast(item.toolTip), val: StrCast(item.toolType), }))} - selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)} + selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)} setSelectedVal={(val: string | number) => { const itemDoc = items.find(item => item.toolType === val); - itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: val, _readOnly_: false }); + itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, value: val, _readOnly_: false }); }} /> ); @@ -296,7 +296,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { const script = ScriptCast(this.Document.onClick); const double = ScriptCast(this.Document.onDoubleClick); - const toggleStatus = script?.script.run({ this: this.Document, self: this.Document, value: undefined, _readOnly_: true }).result ?? false; + const toggleStatus = script?.script.run({ this: this.Document, value: undefined, _readOnly_: true }).result ?? false; // Colors const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); // const backgroundColor = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor); @@ -319,8 +319,8 @@ export class FontIconBox extends ViewBoxBaseComponent() { returnTrue, emptyFunction, action((clickEv, doubleTap) => { - (!doubleTap || !double) && script?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); - doubleTap && double?.script.run({ this: this.Document, self: this.Document, value: !toggleStatus, _readOnly_: false }); + (!doubleTap || !double) && script?.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false }); + doubleTap && double?.script.run({ this: this.Document, value: !toggleStatus, _readOnly_: false }); this._hackToRecompute += 1; }) ) @@ -341,15 +341,15 @@ export class FontIconBox extends ViewBoxBaseComponent() { @computed get editableText() { const script = ScriptCast(this.Document.script); - const checkResult = script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result; + const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result; - const setValue = (value: string): boolean => script?.script.run({ this: this.Document, self: this.Document, value, _readOnly_: false }).result; + const setValue = (value: string): boolean => script?.script.run({ this: this.Document, value, _readOnly_: false }).result; return (
    - script?.script.run({ this: this.Document, self: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine contents={checkResult} /> + script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine contents={checkResult} />
    ); @@ -358,7 +358,7 @@ export class FontIconBox extends ViewBoxBaseComponent() { renderButton = () => { const color = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color); const tooltip = StrCast(this.Document.toolTip); - const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, self: this.Document, _readOnly_: false }); + const scriptFunc = () => ScriptCast(this.Document.onClick)?.script.run({ this: this.Document, _readOnly_: false }); const btnProps = { tooltip, icon: this.Icon(color)!, label: this.label }; // prettier-ignore switch (this.type) { diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index f32d39aaf..3d1bd7563 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -2,7 +2,7 @@ import functionPlot from 'function-plot'; import { computed, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { DocListCast } from '../../../fields/Doc'; +import { Doc, DocListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { Cast, StrCast } from '../../../fields/Types'; @@ -11,7 +11,6 @@ import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; -import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; @@ -49,8 +48,8 @@ export class FunctionPlotBox extends ViewBoxAnnotatableComponent return anchor; }; @computed get graphFuncs() { - const links = LinkManager.Instance.getAllRelatedLinks(this.Document) - .map(d => LinkManager.getOppositeAnchor(d, this.Document)) + const links = Doc.Links(this.Document) + .map(d => Doc.getOppositeAnchor(d, this.Document)) .filter(d => d) .map(d => d!); const funcs = links.concat(DocListCast(this.dataDoc[this.fieldKey])).map(doc => diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d317f46bb..3945da104 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -19,7 +19,6 @@ import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; import { Networking } from '../../Network'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -30,7 +29,8 @@ import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { OverlayView } from '../OverlayView'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; @@ -254,7 +254,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent() impl cropping.y = NumCast(this.Document.y); this._props.addDocTab(cropping, OpenWhere.inParent); } - DocumentManager.Instance.AddViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); + DocumentView.addViewRenderedCb(cropping, dv => setTimeout(() => (dv.ComponentView as ImageBox).setNativeSize(), 200)); this._props.bringToFront?.(cropping); return cropping; }; diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 46bb16e50..cd591fa42 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -59,6 +59,8 @@ export class KeyValueBox extends ObservableReactComponent { get fieldDocToLayout() { return DocCast(this._props.Document); } + isUnstyledView = returnTrue; // used by style provider via ViewBoxInterface + dontRegisterView = returnTrue; // used by ViewBoxInterface @action onEnterKey = (e: React.KeyboardEvent): void => { @@ -111,7 +113,7 @@ export class KeyValueBox extends ObservableReactComponent { if (setResult) setResult?.(value); else target[key] = field; }; - const res = script.run({ this: Doc.Layout(doc), self: doc, _setCacheResult_ }, console.log); + const res = script.run({ this: Doc.Layout(doc), _setCacheResult_ }, console.log); if (!res.success) { if (key) target[key] = script.originalScript; return false; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 4dec3506c..f80ff5f94 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -13,7 +13,7 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { FieldView, FieldViewProps } from './FieldView'; import BigText from './LabelBigText'; import './LabelBox.scss'; diff --git a/src/client/views/nodes/LinkAnchorBox.scss b/src/client/views/nodes/LinkAnchorBox.scss deleted file mode 100644 index caff369df..000000000 --- a/src/client/views/nodes/LinkAnchorBox.scss +++ /dev/null @@ -1,34 +0,0 @@ -.linkAnchorBox-cont, -.linkAnchorBox-cont-small { - cursor: default; - position: absolute; - width: 15; - height: 15; - border-radius: 20px; - user-select: none; - pointer-events: all; - - .linkAnchorBox-linkCloser { - position: absolute; - width: 18; - height: 18; - background: rgb(219, 21, 21); - top: -1px; - left: -1px; - border-radius: 5px; - display: flex; - justify-content: center; - align-items: center; - padding-left: 2px; - padding-top: 1px; - } - .linkAnchorBox-button { - position: relative; - display: inline-block; - } -} - -.linkAnchorBox-cont-small { - width: 5px; - height: 5px; -} \ No newline at end of file diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx deleted file mode 100644 index d43241de0..000000000 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { action, computed, makeObservable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { setupMoveUpEvents } from '../../../ClientUtils'; -import { Utils, emptyFunction } from '../../../Utils'; -import { Doc } from '../../../fields/Doc'; -import { NumCast, StrCast } from '../../../fields/Types'; -import { TraceMobx } from '../../../fields/util'; -import { DragManager } from '../../util/DragManager'; -import { dropActionType } from '../../util/DropActionTypes'; -import { LinkFollower } from '../../util/LinkFollower'; -import { SelectionManager } from '../../util/SelectionManager'; -import { ViewBoxBaseComponent } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { FieldView, FieldViewProps } from './FieldView'; -import './LinkAnchorBox.scss'; -import { LinkInfo } from './LinkDocPreview'; - -const { MEDIUM_GRAY } = require('../global/globalCssVariables.module.scss'); // prettier-ignore - -@observer -export class LinkAnchorBox extends ViewBoxBaseComponent() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(LinkAnchorBox, fieldKey); - } - _doubleTap = false; - _lastTap: number = 0; - _ref = React.createRef(); - _isOpen = false; - _timeout: NodeJS.Timeout | undefined; - - constructor(props: FieldViewProps) { - super(props); - makeObservable(this); - } - - componentDidMount() { - this._props.setContentViewBox?.(this); - } - - @computed get linkSource() { - return this.DocumentView?.().containerViewPath?.().lastElement().Document; // this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.LinkSource); - } - - onPointerDown = (e: React.PointerEvent) => { - const { linkSource } = this; - linkSource && - setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, (clickEv, doubleTap) => { - if (doubleTap) LinkFollower.FollowLink(this.Document, linkSource, false); - else this._props.select(false); - }); - }; - onPointerMove = action((e: PointerEvent) => { - const cdiv = this._ref?.current?.parentElement; - if (!this._isOpen && cdiv) { - const bounds = cdiv.getBoundingClientRect(); - const pt = Utils.getNearestPointInPerimeter(bounds.left, bounds.top, bounds.width, bounds.height, e.clientX, e.clientY); - const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY)); - if (separation > 100) { - const dragData = new DragManager.DocumentDragData([this.Document]); - dragData.dropAction = dropActionType.embed; - dragData.dropPropertiesToRemove = ['link_anchor_1_x', 'link_anchor_1_y', 'link_anchor_2_x', 'link_anchor_2_y', 'onClick']; - DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]); - return true; - } - this.layoutDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100; - this.layoutDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100; - this.layoutDoc.link_autoMoveAnchors = false; - } - return false; - }); - - specificContextMenu = (): void => {}; - - render() { - TraceMobx(); - const small = this._props.PanelWidth() <= 1; // this happens when rendered in a treeView - const x = NumCast(this.layoutDoc[this.fieldKey + '_x'], 100); - const y = NumCast(this.layoutDoc[this.fieldKey + '_y'], 100); - const background = this._props.styleProvider?.(this.dataDoc, this._props, StyleProp.BackgroundColor + ':anchor'); - const anchor = this.fieldKey === 'link_anchor_1' ? 'link_anchor_2' : 'link_anchor_1'; - const anchorScale = !this.dataDoc[this.fieldKey + '_useSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25; - const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title); - const selView = SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_1') - ? 'link_anchor_1' - : SelectionManager.Views.lastElement()?._props.LayoutTemplateString?.includes('link_anchor_2') - ? 'link_anchor_2' - : ''; - return ( -
    - LinkInfo.SetLinkInfo({ - DocumentView: this.DocumentView, - styleProvider: this._props.styleProvider, - linkSrc: this.linkSource, - linkDoc: this.Document, - showHeader: true, - location: [e.clientX, e.clientY + 20], - noPreview: false, - }) - } - onPointerDown={this.onPointerDown} - onContextMenu={this.specificContextMenu} - style={{ - border: selView && this.dataDoc[selView] === this.dataDoc[this.fieldKey] ? `solid ${MEDIUM_GRAY} 2px` : undefined, - background, - left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`, - top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`, - transform: `scale(${anchorScale})`, - cursor: 'grab', - }} - /> - ); - } -} diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx index 559b1fcae..6caa38a7f 100644 --- a/src/client/views/nodes/LinkBox.tsx +++ b/src/client/views/nodes/LinkBox.tsx @@ -13,12 +13,11 @@ import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; -import { DocumentManager } from '../../util/DocumentManager'; import { SnappingManager } from '../../util/SnappingManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { EditableView } from '../EditableView'; import { LightboxView } from '../LightboxView'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { ComparisonBox } from './ComparisonBox'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; @@ -43,7 +42,7 @@ export class LinkBox extends ViewBoxBaseComponent() { anchor = (which: number) => { const anch = DocCast(this.dataDoc['link_anchor_' + which]); const anchor = anch?.layout_unrendered ? DocCast(anch.annotationOn) : anch; - return DocumentManager.Instance.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement()); + return DocumentView.getDocumentView(anchor, this.DocumentView?.().containerViewPath?.().lastElement()); }; _hackToSeeIfDeleted: any; componentWillUnmount() { diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 23cb25962..ff95f8547 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -2,16 +2,17 @@ import { action, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { DocData } from '../../../fields/DocSymbols'; +import { StrCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import './LinkDescriptionPopup.scss'; import { TaskCompletionBox } from './TaskCompletedBox'; -import { StrCast } from '../../../fields/Types'; @observer export class LinkDescriptionPopup extends React.Component<{}> { // eslint-disable-next-line no-use-before-define public static Instance: LinkDescriptionPopup; @observable public display: boolean = false; + // eslint-disable-next-line react/no-unused-class-component-methods @observable public showDescriptions: string = 'ON'; @observable public popupX: number = 700; @observable public popupY: number = 350; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 0936acc15..508008569 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -11,12 +11,10 @@ import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/T import { DocServer } from '../../DocServer'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; -import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DocumentView } from './DocumentView'; @@ -134,17 +132,17 @@ export class LinkDocPreview extends ObservableReactComponent { - if (anchor instanceof Doc && LinkManager.Links(anchor).length) { - this._linkDoc = this._linkDoc ?? LinkManager.Links(anchor)[0]; + if (anchor instanceof Doc && Doc.Links(anchor).length) { + this._linkDoc = this._linkDoc ?? Doc.Links(anchor)[0]; const automaticLink = this._linkDoc.link_relationship === LinkManager.AutoKeywords; if (automaticLink) { // automatic links specify the target in the link info, not the source const linkTarget = anchor; - this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget); + this._linkSrc = Doc.getOppositeAnchor(this._linkDoc, linkTarget); this._markerTargetDoc = this._targetDoc = linkTarget; } else { this._linkSrc = anchor; - const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + const linkTarget = Doc.getOppositeAnchor(this._linkDoc, this._linkSrc); this._markerTargetDoc = linkTarget; this._targetDoc = /* linkTarget?.type === DocumentType.MARKER && */ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget; } @@ -169,8 +167,8 @@ export class LinkDocPreview extends ObservableReactComponent { LinkInfo.Clear(); if (this._linkDoc && this._linkSrc) { - LinkFollower.FollowLink(this._linkDoc, this._linkSrc, false); + DocumentView.FollowLink(this._linkDoc, this._linkSrc, false); } else if (this._props.hrefs?.length) { const webDoc = Array.from(SearchUtil.SearchCollection(Doc.MyFilesystem, this._props.hrefs[0], false).keys()) .filter(doc => doc.type === DocumentType.WEB) .lastElement() ?? Docs.Create.WebDocument(this._props.hrefs[0], { title: this._props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, data_useCors: true }); - DocumentManager.Instance.showDocument(webDoc, { + DocumentView.showDocument(webDoc, { openLocation: OpenWhere.lightbox, willPan: true, zoomTime: 500, @@ -229,7 +227,7 @@ export class LinkDocPreview extends ObservableReactComponent +
    Edit Link
    } placement="top">
    @@ -282,7 +280,7 @@ export class LinkDocPreview extends ObservableReactComponent { - const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); + const targetanchor = this._linkDoc && this._linkSrc && Doc.getOppositeAnchor(this._linkDoc, this._linkSrc); targetanchor && this._targetDoc !== targetanchor && r?._props.focus?.(targetanchor, {}); }} Document={this._targetDoc!} @@ -325,7 +323,7 @@ export class LinkDocPreview extends ObservableReactComponent + style={{ borderColor: SnappingManager.userColor, left: this._props.location[0], top: this._props.location[1], width: this.width() + borders, height: this.height() + borders + (this._props.showHeader ? 37 : 0) }}> {this.docPreview}
    ); diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index aa89398f3..5f343bdfe 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -6,12 +6,11 @@ import { Doc } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { StrCast } from '../../../fields/Types'; import { Networking } from '../../Network'; -import { DocumentManager } from '../../util/DocumentManager'; +import { DocumentType } from '../../documents/DocumentTypes'; +import { Docs } from '../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; -import { DocumentType } from '../../documents/DocumentTypes'; -import { Docs } from '../../documents/Documents'; /** * LoadingBox Class represents a placeholder doc for documents that are currently @@ -39,25 +38,11 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LoadingBox, fieldKey); } - // removes from currently loading display - public static removeCurrentlyLoading(doc: Doc) { - if (DocumentManager.Instance.CurrentlyLoading) { - const index = DocumentManager.Instance.CurrentlyLoading.indexOf(doc); - runInAction(() => index !== -1 && DocumentManager.Instance.CurrentlyLoading.splice(index, 1)); - } - } - - // adds doc to currently loading display - public static addCurrentlyLoading(doc: Doc) { - if (DocumentManager.Instance.CurrentlyLoading.indexOf(doc) === -1) { - runInAction(() => DocumentManager.Instance.CurrentlyLoading.push(doc)); - } - } _timer: any; @observable progress = ''; componentDidMount() { - if (!DocumentManager.Instance.CurrentlyLoading?.includes(this.Document)) { + if (!Doc.CurrentlyLoading?.includes(this.Document)) { this.Document.loadingError = 'Upload interrupted, please try again'; } else { const updateFunc = async () => { diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx index f176317d2..b8fd8ac6a 100644 --- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx @@ -8,9 +8,9 @@ import { returnFalse } from '../../../../ClientUtils'; import { unimplementedFunction } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { DocumentView } from '../DocumentView'; @observer export class DirectionsAnchorMenu extends AntimodeMenu { @@ -56,7 +56,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => DirectionsAnchorMenu.Instance.fadeOut(true) ); } diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 174511e1a..103a35434 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -13,9 +13,9 @@ import { unimplementedFunction } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; import { NumCast, StrCast } from '../../../../fields/Types'; import { CalendarManager } from '../../../util/CalendarManager'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; +import { DocumentView } from '../DocumentView'; import './MapAnchorMenu.scss'; import { MapboxApiUtility, TransportationType } from './MapboxApiUtility'; import { MarkerIcons } from './MarkerIcons'; @@ -126,7 +126,7 @@ export class MapAnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => MapAnchorMenu.Instance.fadeOut(true) ); } diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 50831f8ea..ac8010f11 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -21,9 +21,7 @@ import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { LinkManager } from '../../../util/LinkManager'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent'; import { PinDocView, PinProps } from '../../PinFuncs'; @@ -249,7 +247,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem if (existingPin) { setTimeout(() => { // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet - if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + if (!Doc.Links(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { const anchor = this.getAnchor(true, undefined, existingPin); anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); doc.latitude = existingPin?.latitude; @@ -438,7 +436,7 @@ export class MapBox extends ViewBoxAnnotatableComponent() implem options.didMove = true; } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx index 9825824bd..7697fd295 100644 --- a/src/client/views/nodes/MapBox/MapBox2.tsx +++ b/src/client/views/nodes/MapBox/MapBox2.tsx @@ -509,9 +509,9 @@ // // TODO: auto center on select a document in the sidebar // private handleMapCenter = (map: google.maps.Map) => { -// // console.log("print the selected views in selectionManager:") -// // if (SelectionManager.Views.lastElement()) { -// // console.log(SelectionManager.Views.lastElement()); +// // console.log("print the selected views in Document.Selected:") +// // if (DocumentView.Selected().lastElement()) { +// // console.log(DocumentView.Selected().lastElement()); // // } // }; diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index 3b4ffd4bd..bfd40692b 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -13,9 +13,7 @@ import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types'; import { DocUtils } from '../../../documents/DocUtils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { LinkManager } from '../../../util/LinkManager'; import { Transform } from '../../../util/Transform'; import { UndoManager, undoable } from '../../../util/UndoManager'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -130,7 +128,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent if (existingPin) { setTimeout(() => { // we use a timeout in case this is called from the sidebar which may have just added a link that hasn't made its way into th elink manager yet - if (!LinkManager.Instance.getAllRelatedLinks(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { + if (!Doc.Links(doc).some(link => DocCast(link.link_anchor_1)?.mapPin === existingPin || DocCast(link.link_anchor_2)?.mapPin === existingPin)) { const anchor = this.getAnchor(true, undefined, existingPin); anchor && DocUtils.MakeLink(anchor, doc, { link_relationship: 'link to map location' }); doc.latitude = existingPin?.latitude; @@ -390,7 +388,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent options.didMove = true; } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; /* diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 5d1874aca..9038ed27a 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -19,9 +19,7 @@ import { emptyFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { KeyCodes } from '../../util/KeyCodes'; -import { SelectionManager } from '../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm'; import { CollectionStackingView } from '../collections/CollectionStackingView'; @@ -251,7 +249,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent() implem this.toggleSidebar(false); } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @@ -561,7 +559,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent() implem removeDocument={this.removeDocument} /> ) : ( -
    setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}> +
    setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> () { @@ -102,7 +102,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { screengrabber.overlayY = 590; // was 0 screengrabber[DocData][Doc.LayoutFieldKey(screengrabber) + '_trackScreen'] = true; Doc.AddToMyOverlay(screengrabber); // just adds doc to overlay - DocumentManager.Instance.AddViewRenderedCb(screengrabber, docView => { + DocumentView.addViewRenderedCb(screengrabber, docView => { RecordingBox.screengrabber = docView.ComponentView as RecordingBox; RecordingBox.screengrabber.Record?.(); }); @@ -119,7 +119,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { value.overlayX = 70; value.overlayY = window.innerHeight - 180; Doc.AddToMyOverlay(value); - DocumentManager.Instance.AddViewRenderedCb(value, docView => { + DocumentView.addViewRenderedCb(value, docView => { Doc.UserDoc().currentRecording = docView.Document; docView.select(false); RecordingBox.resumeWorkspaceReplaying(value); @@ -132,7 +132,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { */ @undoBatch public static addRecToWorkspace(value: RecordingBox) { - const ffView = Array.from(DocumentManager.Instance.DocumentViews).find(view => view.ComponentView instanceof CollectionFreeFormView); + const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); (ffView?.ComponentView as CollectionFreeFormView)._props.addDocument?.(value.Document); Doc.RemoveDocFromList(Doc.UserDoc(), 'workspaceRecordings', value.Document); Doc.RemFromMyOverlay(value.Document); @@ -142,7 +142,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { } public static resumeWorkspaceReplaying(doc: Doc) { - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); if (docView?.ComponentView instanceof VideoBox) { docView.ComponentView.Play(); } @@ -150,7 +150,7 @@ export class RecordingBox extends ViewBoxBaseComponent() { } public static pauseWorkspaceReplaying(doc: Doc) { - const docView = DocumentManager.Instance.getDocumentView(doc); + const docView = DocumentView.getDocumentView(doc); const videoBox = docView?.ComponentView as VideoBox; if (videoBox) { videoBox.Pause(); diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 8eb55b2f8..3be50f5e6 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -24,6 +24,7 @@ import { SettingsManager } from '../../util/SettingsManager'; import { TrackMovements } from '../../util/TrackMovements'; import { ContextMenu } from '../ContextMenu'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { DocViewUtils } from '../DocViewUtils'; import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { mediaState } from './AudioBox'; @@ -161,8 +162,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent() // }); } componentWillUnmount() { - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); } specificContextMenu = (): void => { @@ -265,7 +266,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent() this._screenCapture = true; this.dataDoc.mediaState = 'recording'; }); - DocUtils.ActiveRecordings.push(this); + DocViewUtils.ActiveRecordings.push(this); } else { this._audioRec?.stop(); this._videoRec?.stop(); @@ -273,8 +274,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent() this._screenCapture = false; this.dataDoc.mediaState = 'paused'; }); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + const ind = DocViewUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocViewUtils.ActiveRecordings.splice(ind, 1); CaptureManager.Instance.open(this.Document); } diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index f32d00386..afd73cfe8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -16,9 +16,7 @@ import { emptyFunction, formatTime } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils, FollowLinkScript } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { LinkManager } from '../../util/LinkManager'; import { ReplayMovements } from '../../util/ReplayMovements'; import { undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; @@ -26,16 +24,16 @@ import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionS import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent'; +import { VideoThumbnails } from '../global/globalEnums'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import { RecordingBox } from './RecordingBox'; import './VideoBox.scss'; -import { VideoThumbnails } from '../global/globalEnums'; /** * VideoBox @@ -88,7 +86,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl @observable _scrubbing: boolean = false; @computed get links() { - return LinkManager.Links(this.dataDoc); + return Doc.Links(this.dataDoc); } @computed get heightPercent() { return NumCast(this.layoutDoc._layout_timelineHeightPercent, 100); @@ -347,7 +345,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl this._props.addDocument?.(imageSnapshot); DocUtils.MakeLink(imageSnapshot, this.getAnchor(true), { link_relationship: 'video snapshot' }); // link && (DocCast(link.link_anchor_2)[DocData].timecodeToHide = NumCast(DocCast(link.link_anchor_2).timecodeToShow) + 3); // do we need to set an end time? should default to +0.1 - setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); + setTimeout(() => downX !== undefined && downY !== undefined && DocumentView.getFirstDocumentView(imageSnapshot)?.startDragging(downX, downY, dropActionType.move, true)); }; getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => { @@ -392,7 +390,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl return this._stackedTimeline.getView(doc, options); } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; @@ -446,7 +444,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl { fireImmediately: true } ); - (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length !== VideoBox.numThumbnails) && this.getVideoThumbnails(); + (!this.dataDoc[this.fieldKey + '_thumbnails'] || StrListCast(this.dataDoc[this.fieldKey + '_thumbnails']).length !== VideoThumbnails.DENSE) && this.getVideoThumbnails(); } }; @@ -652,20 +650,20 @@ export class VideoBox extends ViewBoxAnnotatableComponent() impl @action removeCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (CollectionStackedTimeline.CurrentlyPlaying && docView) { - const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView); - index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1); + if (DocumentView.CurrentlyPlaying && docView) { + const index = DocumentView.CurrentlyPlaying.indexOf(docView); + index !== -1 && DocumentView.CurrentlyPlaying.splice(index, 1); } }; // adds doc to currently playing display @action addCurrentlyPlaying = () => { const docView = this.DocumentView?.(); - if (!CollectionStackedTimeline.CurrentlyPlaying) { - CollectionStackedTimeline.CurrentlyPlaying = []; + if (!DocumentView.CurrentlyPlaying) { + DocumentView.CurrentlyPlaying = []; } - if (docView && CollectionStackedTimeline.CurrentlyPlaying.indexOf(docView) === -1) { - CollectionStackedTimeline.CurrentlyPlaying.push(docView); + if (docView && DocumentView.CurrentlyPlaying.indexOf(docView) === -1) { + DocumentView.CurrentlyPlaying.push(docView); } }; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 54f246b20..1003bc473 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -21,7 +21,6 @@ import { emptyFunction, stringHash } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocUtils } from '../../documents/DocUtils'; -import { DocumentManager } from '../../util/DocumentManager'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../util/UndoManager'; @@ -38,7 +37,7 @@ import { Annotation } from '../pdf/Annotation'; import { GPTPopup } from '../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../PinFuncs'; import { SidebarAnnos } from '../SidebarAnnos'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; @@ -324,7 +323,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href); if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false); return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index 1c5ea2dd4..5d53a2a5f 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -293,7 +293,6 @@ export class DashFieldView { unclickable = () => !this.tbox._props.rootSelected?.() && this.node.marks.some((m: any) => m.type === this.tbox.EditorView?.state.schema.marks.linkAnchor && m.attrs.noPreview); constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) { makeObservable(this); - const self = this; this.node = node; this.tbox = tbox; this.getpos = getPos; @@ -305,14 +304,14 @@ export class DashFieldView { this.dom.onkeypress = function (e: KeyboardEvent) { e.stopPropagation(); }; - this.dom.onkeydown = function (e: KeyboardEvent) { + this.dom.onkeydown = (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Tab') { e.preventDefault(); const editor = tbox.EditorView; if (editor) { const { state } = editor; - for (let i = self.getpos() + 1; i < state.doc.content.size; i++) { + for (let i = this.getpos() + 1; i < state.doc.content.size; i++) { if (state.doc.nodeAt(i)?.type.name === state.schema.nodes.dashField.name) { editor.dispatch(state.tr.setSelection(new NodeSelection(state.doc.resolve(i)))); return; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ad6629fc9..8a4a7718a 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -32,16 +32,13 @@ import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocUtils } from '../../../documents/DocUtils'; import { DictationManager } from '../../../util/DictationManager'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { RTFMarkup } from '../../../util/RTFMarkup'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager'; -import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { CollectionStackingView } from '../../collections/CollectionStackingView'; import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; @@ -53,9 +50,10 @@ import { AnchorMenu } from '../../pdf/AnchorMenu'; import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; import { PinDocView, PinProps } from '../../PinFuncs'; import { SidebarAnnos } from '../../SidebarAnnos'; -import { styleFromLayoutString, StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; +import { styleFromLayoutString } from '../../StyleProvider'; import { mediaState } from '../AudioBox'; -import { DocumentView, DocumentViewInternal } from '../DocumentView'; +import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { FocusViewOptions } from '../FocusViewOptions'; import { LinkInfo } from '../LinkDocPreview'; @@ -260,7 +258,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { stopFunc = stop }); // prettier-ignore + DictationManager.recordAudioAnnotation(targetData, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore const reactionDisposer = reaction( () => target.mediaState, @@ -271,7 +269,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { let linkTime; let linkAnchor; - LinkManager.Links(this.dataDoc).forEach(l => { + Doc.Links(this.dataDoc).forEach(l => { const anchor = DocCast(l.link_anchor_1)?.annotationOn ? DocCast(l.link_anchor_1) : DocCast(l.link_anchor_2)?.annotationOn ? DocCast(l.link_anchor_2) : undefined; if (anchor && (anchor.annotationOn as Doc).mediaState === mediaState.Recording) { linkTime = NumCast(anchor._timecodeToShow /* audioStart */); @@ -423,7 +421,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const newAutoLinks = new Set(); - const oldAutoLinks = LinkManager.Links(this.Document).filter( + const oldAutoLinks = Doc.Links(this.Document).filter( link => ((!Doc.isTemplateForField(this.Document) && (!Doc.isTemplateForField(DocCast(link.link_anchor_1)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) && @@ -442,7 +440,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(LinkManager.Instance.deleteLink); + oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.link_anchor_2 !== this.Document).forEach(doc => Doc.DeleteLink?.(doc)); }; updateTitle = () => { @@ -496,7 +494,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { alink = alink ?? - (LinkManager.Links(this.Document).find( + (Doc.Links(this.Document).find( link => Doc.AreProtosEqual(Cast(link.link_anchor_1, Doc, null), this.Document) && // Doc.AreProtosEqual(Cast(link.link_anchor_2, Doc, null), target) @@ -787,7 +785,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { const batch = UndoManager.StartBatch('delete link'); - LinkManager.Instance.deleteLink(LinkManager.Links(anchor)[0]); + Doc.DeleteLink?.(Doc.Links(anchor)[0]); // const docAnnotations = DocListCast(this._props.dataDoc[this.fieldKey]); // this._props.dataDoc[this.fieldKey] = new List(docAnnotations.filter(a => a !== this.annoTextRegion)); // AnchorMenu.Instance.fadeOut(true); @@ -837,7 +835,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - anchor && SelectionManager.SelectSchemaViewDoc(anchor as Doc); + anchor && DocumentView.SelectSchemaDoc(anchor as Doc); AnchorMenu.Instance.Status = 'annotation'; AnchorMenu.Instance.Delete = !anchor && editor.state.selection.empty ? returnFalse : !anchor ? deleteMarkups : () => this.deleteAnnotation(anchor as Doc); AnchorMenu.Instance.Pinned = false; @@ -1124,7 +1122,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent this._sidebarRef?.current?.makeDocUnfiltered(doc)); } return new Promise>(res => { - DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)); + DocumentView.addViewRenderedCb(doc, dv => res(dv)); }); }; focus = (textAnchor: Doc, options: FocusViewOptions) => { @@ -1206,7 +1204,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent Doc.RecordingEvent, this.breakupDictation); this._disposers.layout_autoHeight = reaction( () => ({ autoHeight: this.layout_autoHeight, fontSize: this.fontSize, css: this.Document[DocCss] }), @@ -1244,7 +1242,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent LinkManager.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks + () => Doc.Links(this.dataDoc), // if a link is deleted, then remove all hyperlinks that reference it from the text's marks newLinks => { this._cachedLinks.forEach(l => !newLinks.includes(l) && this.RemoveLinkFromDoc(l)); this._cachedLinks = newLinks; @@ -1556,7 +1554,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const docView = DocumentManager.Instance.getDocumentView(audiodoc); + const docView = DocumentView.getDocumentView(audiodoc); if (!docView) { this._props.addDocTab(audiodoc, OpenWhere.addBottom); setTimeout(func); @@ -1769,7 +1767,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const ComponentTag: any = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; + const ComponentTag: any = tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; return ComponentTag === CollectionStackingView ? ( ) : ( -
    setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.DocumentView?.()!, false), true)}> +
    setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => DocumentView.SelectView(this.DocumentView?.()!, false), true)}> { if (e.clientX > this.ProseRef!.getBoundingClientRect().right) { - if (this.dataDoc[this.SidebarKey + '_type_collection'] === CollectionViewType.Freeform) { - // if the scrolled freeform is a child of the sidebar component, we need to let the event go through - // so react can let the freeform view handle it. We prevent default to stop any containing views from scrolling - e.preventDefault(); - } return; } @@ -2037,7 +2030,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); const paddingX = NumCast(this.layoutDoc._xMargin, this._props.xPadding || 0); diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index bc6454146..7a8b72be0 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -11,7 +11,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy import { GetEffectiveAcl } from '../../../../fields/util'; import { Docs } from '../../../documents/Documents'; import { RTFMarkup } from '../../../util/RTFMarkup'; -import { SelectionManager } from '../../../util/SelectionManager'; +import { DocumentView } from '../DocumentView'; import { OpenWhere } from '../OpenWhere'; const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false; @@ -172,7 +172,7 @@ export function buildKeymap>(schema: S, props: any): KeyMa bind('Escape', (state: EditorState, dispatch: (tx: Transaction) => void) => { dispatch(state.tr.setSelection(TextSelection.create(state.doc, state.selection.from, state.selection.from))); (document.activeElement as any).blur?.(); - SelectionManager.DeselectAll(); + DocumentView.DeselectAll(); }); bind('Alt-Enter', () => onKey() || true); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 6c12b9991..a612f3c65 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -11,11 +11,10 @@ import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; import { DocServer } from '../../../DocServer'; -import { LinkManager } from '../../../util/LinkManager'; -import { SelectionManager } from '../../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu'; import { ObservableReactComponent } from '../../ObservableReactComponent'; +import { DocumentView } from '../DocumentView'; import { EquationBox } from '../EquationBox'; import { FieldViewProps } from '../FieldView'; import { FormattedTextBox } from './FormattedTextBox'; @@ -116,7 +115,7 @@ export class RichTextMenu extends AntimodeMenu { _disposer: IReactionDisposer | undefined; componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => this.updateMenu(undefined, undefined, undefined, undefined) ); } @@ -144,8 +143,8 @@ export class RichTextMenu extends AntimodeMenu { const { activeSizes } = active; const { activeColors } = active; const { activeHighlights } = active; - const refDoc = SelectionManager.Views.lastElement()?.layoutDoc ?? Doc.UserDoc(); - const refField = (pfx => (pfx ? pfx + '_' : ''))(SelectionManager.Views.lastElement()?.LayoutFieldKey); + const refDoc = DocumentView.Selected().lastElement()?.layoutDoc ?? Doc.UserDoc(); + const refField = (pfx => (pfx ? pfx + '_' : ''))(DocumentView.Selected().lastElement()?.LayoutFieldKey); const refVal = (field: string, dflt: string) => StrCast(refDoc[refField + field], StrCast(Doc.UserDoc()[field], dflt)); this._activeListType = this.getActiveListStyle(); @@ -237,8 +236,8 @@ export class RichTextMenu extends AntimodeMenu { m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize); m.type === state.schema.marks.pFontHighlight && activeHighlights.add(String(m.attrs.fontHighlight)); }); - } else if (SelectionManager.Views.some(dv => dv.ComponentView instanceof EquationBox)) { - SelectionManager.Views.forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); + } else if (DocumentView.Selected().some(dv => dv.ComponentView instanceof EquationBox)) { + DocumentView.Selected().forEach(dv => StrCast(dv.Document._text_fontSize) && activeSizes.add(StrCast(dv.Document._text_fontSize))); } return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) }; } @@ -566,7 +565,7 @@ export class RichTextMenu extends AntimodeMenu { if (linkDoc instanceof Doc) { const linkAnchor1 = await Cast(linkDoc.link_anchor_1, Doc); const linkAnchor2 = await Cast(linkDoc.link_anchor_2, Doc); - const currentDoc = SelectionManager.Docs.lastElement(); + const currentDoc = DocumentView.Selected().lastElement().Document; if (currentDoc && linkAnchor1 && linkAnchor2) { if (Doc.AreProtosEqual(currentDoc, linkAnchor1)) { return StrCast(linkAnchor2.title); @@ -605,7 +604,7 @@ export class RichTextMenu extends AntimodeMenu { .filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0) .forEach((aref: any) => { const anchorId = aref.href.replace(Doc.localServerPath(), '').split('?')[0]; - anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); + anchorId && DocServer.GetRefField(anchorId).then(linkDoc => Doc.DeleteLink?.(linkDoc as Doc)); }); } } diff --git a/src/client/views/nodes/formattedText/SummaryView.tsx b/src/client/views/nodes/formattedText/SummaryView.tsx index 2c366b49b..238267f6e 100644 --- a/src/client/views/nodes/formattedText/SummaryView.tsx +++ b/src/client/views/nodes/formattedText/SummaryView.tsx @@ -21,11 +21,10 @@ export class SummaryView { root: any; constructor(node: any, view: any, getPos: any) { - const self = this; this.dom = document.createElement('span'); this.dom.className = this.className(node.attrs.visibility); - this.dom.onpointerdown = function (e: any) { - self.onPointerDown(e, node, view, getPos); + this.dom.onpointerdown = (e: any) => { + this.onPointerDown(e, node, view, getPos); }; this.dom.onkeypress = function (e: any) { e.stopPropagation(); diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index d5fad296f..91b0ebd5c 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -16,7 +16,6 @@ import { NumCast } from '../../../../fields/Types'; import { Networking } from '../../../Network'; import { DocUtils } from '../../../documents/DocUtils'; import { Docs } from '../../../documents/Documents'; -import { DocumentManager } from '../../../util/DocumentManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { ImageEditorData } from '../ImageBox'; @@ -28,6 +27,7 @@ import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; +import { DocumentView } from '../DocumentView'; // enum BrushStyle { // ADD, @@ -295,7 +295,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD _height: newCollectionSize, title: 'Image edit collection', }); - DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History', link_displayLine: false }); + DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' }); // opening new tab CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); @@ -400,7 +400,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD parentDoc.current.gen_fill_children = new List([newImg]); } - DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}`, link_displayLine: true }); + DocUtils.MakeLink(parentDoc.current, newImg, { link_relationship: `Image edit; Prompt: ${input}` }); adjustImgPositions(); if (isNewCollection && newCollectionRef.current) { @@ -430,7 +430,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD ImageEditorData.Open = false; ImageEditorData.Source = ''; if (newCollectionRef.current) { - DocumentManager.Instance.AddViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce()); + DocumentView.addViewRenderedCb(newCollectionRef.current, dv => (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce()); } setEdits([]); }; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 485ba7367..6b4f5e073 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -19,17 +19,14 @@ import { emptyFunction, emptyPath, stringHash } from '../../../../Utils'; import { DocServer } from '../../../DocServer'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { dropActionType } from '../../../util/DropActionTypes'; import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; -import { SelectionManager } from '../../../util/SelectionManager'; import { SerializationHelper } from '../../../util/SerializationHelper'; -import { SettingsManager } from '../../../util/SettingsManager'; import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; -import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline'; +import { CollectionFreeFormPannableContents } from '../../collections/collectionFreeForm/CollectionFreeFormPannableContents'; import { CollectionView } from '../../collections/CollectionView'; import { TreeView } from '../../collections/TreeView'; import { ViewBoxBaseComponent } from '../../DocComponent'; @@ -55,8 +52,9 @@ export class PresBox extends ViewBoxBaseComponent() { super(props); makeObservable(this); if (!PresBox.navigateToDocScript) { - PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, self)')!; + PresBox.navigateToDocScript = ScriptField.MakeFunction('navigateToDoc(this.presentation_targetDoc, this)')!; } + CollectionFreeFormPannableContents.SetOverlayPlugin((fform: Doc) => PresBox.Instance.pathLines(fform)); } private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -122,8 +120,8 @@ export class PresBox extends ViewBoxBaseComponent() { return false; } @computed get selectedDocumentView() { - if (SelectionManager.Views.length) return SelectionManager.Views[0]; - if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.Document); + if (DocumentView.Selected().length) return DocumentView.Selected()[0]; + if (this.selectedArray.size) return DocumentView.getDocumentView(this.Document); return undefined; } @computed get isPres() { @@ -175,7 +173,7 @@ export class PresBox extends ViewBoxBaseComponent() { this._unmounting = false; this.turnOffEdit(true); this._disposers.selection = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), views => (!PresBox.Instance || views.some(view => view.Document === this.Document)) && this.updateCurrentPresentation(), { fireImmediately: true } ); @@ -200,7 +198,7 @@ export class PresBox extends ViewBoxBaseComponent() { startTempMedia = (targetDoc: Doc, activeItem: Doc) => { const duration: number = NumCast(activeItem.config_clipEnd) - NumCast(activeItem.config_clipStart); if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { - const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); + const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.config_clipStart), NumCast(activeItem.config_clipStart) + duration); } }; @@ -208,7 +206,7 @@ export class PresBox extends ViewBoxBaseComponent() { stopTempMedia = (targetDocField: FieldResult) => { const targetDoc = DocCast(DocCast(targetDocField).annotationOn) ?? DocCast(targetDocField); if ([DocumentType.VID, DocumentType.AUDIO].includes(targetDoc.type as any)) { - const targMedia = DocumentManager.Instance.getDocumentView(targetDoc); + const targMedia = DocumentView.getDocumentView(targetDoc); targMedia?.ComponentView?.Pause?.(); } }; @@ -260,7 +258,7 @@ export class PresBox extends ViewBoxBaseComponent() { // go to documents chain runSubroutines = (childrenToRun: Opt, normallyNextSlide: Doc) => { if (childrenToRun && childrenToRun[0] !== normallyNextSlide) { - childrenToRun.forEach(child => DocumentManager.Instance.showDocument(child, {})); + childrenToRun.forEach(child => DocumentView.showDocument(child, {})); } }; @@ -280,7 +278,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (listItems && presIndexed < listItems.length) { if (!first) { const listItemDoc = listItems[presIndexed]; - const targetView = listItems && DocumentManager.Instance.getFirstDocumentView(listItemDoc); + const targetView = listItems && DocumentView.getFirstDocumentView(listItemDoc); Doc.linkFollowUnhighlight(); Doc.HighlightDoc(listItemDoc); listItemDoc.presentation_effect = this.activeItem.presBulletEffect; @@ -425,7 +423,7 @@ export class PresBox extends ViewBoxBaseComponent() { const acontext = activeItem.config_activeFrame !== undefined ? DocCast(DocCast(activeItem.presentation_targetDoc).embedContainer) : DocCast(activeItem.presentation_targetDoc); const context = DocCast(acontext)?.annotationOn ? DocCast(DocCast(acontext).annotationOn) : acontext; if (context) { - const ffview = CollectionFreeFormView.from(DocumentManager.Instance.getFirstDocumentView(context)); + const ffview = CollectionFreeFormView.from(DocumentView.getFirstDocumentView(context)); if (ffview?.childDocs) { PresBox.Instance._keyTimer = CollectionFreeFormView.gotoKeyframe(PresBox.Instance._keyTimer, ffview.childDocs, frameTime); ffview.layoutDoc._currentFrame = NumCast(activeFrame); @@ -605,7 +603,7 @@ export class PresBox extends ViewBoxBaseComponent() { const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] }; bestTarget._freeform_panX = viewport.panX; bestTarget._freeform_panY = viewport.panY; - const dv = DocumentManager.Instance.getDocumentView(bestTarget); + const dv = DocumentView.getDocumentView(bestTarget); if (dv) { changed = true; const computedScale = NumCast(activeItem.config_zoom, 1) * Math.min(dv._props.PanelWidth() / viewport.width, dv._props.PanelHeight() / viewport.height); @@ -644,8 +642,8 @@ export class PresBox extends ViewBoxBaseComponent() { const eleViewCache = Array.from(this._eleArray); const resetSelection = action(() => { if (!this._props.isSelected()) { - const presDocView = DocumentManager.Instance.getDocumentView(this.Document); - if (presDocView) SelectionManager.SelectView(presDocView, false); + const presDocView = DocumentView.getDocumentView(this.Document); + if (presDocView) DocumentView.SelectView(presDocView, false); this.clearSelectedArray(); selViewCache.forEach(doc => this.addToSelectedArray(doc)); this._dragArray.splice(0, this._dragArray.length, ...dragViewCache); @@ -660,7 +658,7 @@ export class PresBox extends ViewBoxBaseComponent() { static NavigateToTarget(targetDoc: Doc, activeItem: Doc, finished?: () => void) { if (activeItem.presentation_movement === PresMovement.None && targetDoc.type === DocumentType.SCRIPTING) { - (DocumentManager.Instance.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); + (DocumentView.getFirstDocumentView(targetDoc)?.ComponentView as ScriptingBox)?.onRun?.(); return; } const effect = activeItem.presentation_effect && activeItem.presentation_effect !== PresEffect.None ? activeItem.presentation_effect : undefined; @@ -680,19 +678,19 @@ export class PresBox extends ViewBoxBaseComponent() { }; if (activeItem.presentation_openInLightbox) { const context = DocCast(targetDoc.annotationOn) ?? targetDoc; - if (!DocumentManager.Instance.getLightboxDocumentView(context)) { + if (!DocumentView.getLightboxDocumentView(context)) { LightboxView.Instance.SetLightboxDoc(context); } } if (targetDoc) { if (activeItem.presentation_targetDoc instanceof Doc) activeItem.presentation_targetDoc[Animation] = undefined; - DocumentManager.Instance.AddViewRenderedCb(LightboxView.LightboxDoc, () => { + DocumentView.addViewRenderedCb(LightboxView.LightboxDoc, () => { // if target or the doc it annotates is not in the lightbox, then close the lightbox - if (!DocumentManager.Instance.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { + if (!DocumentView.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) { LightboxView.Instance.SetLightboxDoc(undefined); } - DocumentManager.Instance.showDocument(targetDoc, options, finished); + DocumentView.showDocument(targetDoc, options, finished); }); } else finished?.(); } @@ -1032,8 +1030,8 @@ export class PresBox extends ViewBoxBaseComponent() { @action selectPres = () => { - const presDocView = DocumentManager.Instance.getDocumentView(this.Document); - presDocView && SelectionManager.SelectView(presDocView, false); + const presDocView = DocumentView.getDocumentView(this.Document); + presDocView && DocumentView.SelectView(presDocView, false); }; focusElement = (doc: Doc) => { @@ -1044,7 +1042,7 @@ export class PresBox extends ViewBoxBaseComponent() { // Regular click @action selectElement = (doc: Doc, noNav = false) => { - CollectionStackedTimeline.CurrentlyPlaying?.map(clip => clip?.ComponentView?.Pause?.()); + DocumentView.CurrentlyPlaying?.map(clip => clip?.ComponentView?.Pause?.()); if (noNav) { const index = this.childDocs.indexOf(doc); if (index >= 0 && index < this.childDocs.length) { @@ -1200,7 +1198,7 @@ export class PresBox extends ViewBoxBaseComponent() { const order: JSX.Element[] = []; const docs = new Set(); const presCollection = collection; - const dv = DocumentManager.Instance.getDocumentView(presCollection); + const dv = DocumentView.getDocumentView(presCollection); this.childDocs.forEach((doc, index) => { const tagDoc = PresBox.targetRenderedDoc(doc); const srcContext = Cast(tagDoc.embedContainer, Doc, null); @@ -2206,7 +2204,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (doc) { const tabMap = CollectionDockingView.Instance?.tabMap; const docTab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc; - const presCollection = DocumentManager.GetContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? docTab; + const presCollection = DocumentView.getContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? docTab; const data = Cast(presCollection?.data, listSpec(Doc)); const configData = Cast(this.Document.data, listSpec(Doc)); if (data && configData) { @@ -2282,12 +2280,12 @@ export class PresBox extends ViewBoxBaseComponent() { @action toggleProperties = () => { - SettingsManager.Instance.propertiesWidth = SettingsManager.Instance.propertiesWidth > 0 ? 0 : 250; + SnappingManager.SetPropertiesWidth(SnappingManager.PropertiesWidth > 0 ? 0 : 250); }; @computed get toolbar() { - const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; - const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; + const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; + const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel'; const mode = StrCast(this.Document._type_collection) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; const activeColor = SnappingManager.userVariantColor; @@ -2316,7 +2314,7 @@ export class PresBox extends ViewBoxBaseComponent() { {propTitle}
    }>
    - 0 ? activeColor : inactiveColor }} /> + 0 ? activeColor : inactiveColor }} />
    diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index af0ab3b53..306b98190 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -13,9 +13,8 @@ import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Ty import { emptyFunction } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; -import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; -import { SettingsManager } from '../../../util/SettingsManager'; +import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; import { undoable, undoBatch } from '../../../util/UndoManager'; import { TreeView } from '../../collections/TreeView'; @@ -23,7 +22,7 @@ import { ViewBoxBaseComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { PinDocView } from '../../PinFuncs'; -import { StyleProp } from '../../StyleProvider'; +import { StyleProp } from '../../StyleProp'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PresBox } from './PresBox'; @@ -282,8 +281,8 @@ export class PresElementBox extends ViewBoxBaseComponent() { @action toggleProperties = () => { - if (SettingsManager.Instance.propertiesWidth < 5) { - SettingsManager.Instance.propertiesWidth = 250; + if (SnappingManager.PropertiesWidth < 5) { + SnappingManager.SetPropertiesWidth(250); } }; @@ -417,7 +416,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @computed get toolbarWidth(): number { - const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); + const presBoxDocView = DocumentView.getDocumentView(this.presBox); const width = NumCast(this.presBox?._width); return presBoxDocView ? presBoxDocView._props.PanelWidth() : width || 300; } diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index f02c56471..a837969aa 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -8,12 +8,12 @@ import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtil import { emptyFunction, unimplementedFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; import { DocumentType } from '../../documents/DocumentTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { SettingsManager } from '../../util/SettingsManager'; import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu'; import { LinkPopup } from '../linking/LinkPopup'; import './AnchorMenu.scss'; import { GPTPopup } from './GPTPopup/GPTPopup'; +import { DocumentView } from '../nodes/DocumentView'; @observer export class AnchorMenu extends AntimodeMenu { @@ -66,7 +66,7 @@ export class AnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction( - () => SelectionManager.Views.slice(), + () => DocumentView.Selected().slice(), () => AnchorMenu.Instance.fadeOut(true) ); } @@ -145,7 +145,7 @@ export class AnchorMenu extends AntimodeMenu { * all selected text available to summarize but its only supported for pdf and web ATM. * @returns Whether the GPT icon for summarization should appear */ - canSummarize = () => SelectionManager.Docs.some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any)); + canSummarize = () => DocumentView.SelectedDocs().some(doc => [DocumentType.PDF, DocumentType.WEB].includes(doc.type as any)); render() { const buttons = diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 8b2b179d3..7dd4047c1 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -5,10 +5,10 @@ import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { Highlight } from '../../../fields/DocSymbols'; import { List } from '../../../fields/List'; import { BoolCast, DocCast, NumCast, StrCast } from '../../../fields/Types'; -import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; import { undoable } from '../../util/UndoManager'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { OpenWhere } from '../nodes/OpenWhere'; import { AnchorMenu } from './AnchorMenu'; @@ -56,7 +56,7 @@ export class Annotation extends ObservableReactComponent { @computed get linkHighlighted() { const found = LinkManager.Instance.getAllDirectLinks(this._props.annoDoc).find(link => { - const a1 = LinkManager.getOppositeAnchor(link, this._props.annoDoc); + const a1 = Doc.getOppositeAnchor(link, this._props.annoDoc); return a1 && Doc.GetBrushStatus(DocCast(a1.annotationOn, a1)); }); return found; @@ -103,7 +103,7 @@ export class Annotation extends ObservableReactComponent { e.preventDefault(); } else if (e.button === 0) { e.stopPropagation(); - LinkFollower.FollowLink(undefined, this._props.annoDoc, false); + DocumentView.FollowLink(undefined, this._props.annoDoc, false); } }; brushed = () => this._props.annoDoc && Doc.GetBrushHighlightStatus(this._props.annoDoc); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 15ac73f78..45b1d727e 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -15,17 +15,17 @@ import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import { LinkInfo } from '../nodes/LinkDocPreview'; import { PDFBox } from '../nodes/PDFBox'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { StyleProp } from '../StyleProvider'; +import { StyleProp } from '../StyleProp'; import { AnchorMenu } from './AnchorMenu'; import { Annotation } from './Annotation'; import { GPTPopup } from './GPTPopup/GPTPopup'; @@ -122,7 +122,7 @@ export class PDFViewer extends ObservableReactComponent { this._disposers.selected = reaction( () => this._props.isSelected(), - () => SelectionManager.Views.length === 1 && this.setupPdfJsViewer(), + () => DocumentView.Selected().length === 1 && this.setupPdfJsViewer(), { fireImmediately: true } ); this._disposers.curPage = reaction( diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 5df934231..56552c952 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -12,16 +12,15 @@ import { DocCast, StrCast } from '../../../fields/Types'; import { DocUtils } from '../../documents/DocUtils'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; -import { DocumentManager } from '../../util/DocumentManager'; -import { LinkManager } from '../../util/LinkManager'; import { SearchUtil } from '../../util/SearchUtil'; -import { SettingsManager } from '../../util/SettingsManager'; +import { SnappingManager } from '../../util/SnappingManager'; import { undoBatch } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { CollectionDockingView } from '../collections/CollectionDockingView'; import { IRecommendation, Recommendation } from '../newlightbox/components'; import { fetchRecommendations } from '../newlightbox/utils'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import './SearchBox.scss'; @@ -54,7 +53,7 @@ export class SearchBoxItem extends ObservableReactComponent * or opening it in a new tab. */ selectElement = async (doc: Doc, finishFunc: () => void) => { - await DocumentManager.Instance.showDocument(doc, { willZoomCentered: true }, finishFunc); + await DocumentView.showDocument(doc, { willZoomCentered: true }, finishFunc); }; /** @@ -65,12 +64,12 @@ export class SearchBoxItem extends ObservableReactComponent */ onResultClick = action(async (doc: Doc) => { this._props.selectItem(doc); - this.selectElement(doc, () => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._props.searchString, undefined, false)); + this.selectElement(doc, () => DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.(this._props.searchString, undefined, false)); }); componentWillUnmount(): void { const doc = this._props.Document; - DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); + DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); } @undoBatch @@ -99,20 +98,18 @@ export class SearchBoxItem extends ObservableReactComponent } } style={{ - fontWeight: LinkManager.Links(this._props.linkFrom).find( - link => - Doc.AreProtosEqual(LinkManager.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || - Doc.AreProtosEqual(DocCast(LinkManager.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document) + fontWeight: Doc.Links(this._props.linkFrom).find( + link => Doc.AreProtosEqual(Doc.getOppositeAnchor(link, this._props.linkFrom!), this._props.Document) || Doc.AreProtosEqual(DocCast(Doc.getOppositeAnchor(link, this._props.linkFrom!)?.annotationOn), this._props.Document) ) ? 'bold' : '', }} className={this._props.className}>
    {title as string}
    -
    +
    {formattedType}
    -
    +
    {this._props.matchedKeys.join(', ')}
    @@ -373,7 +370,7 @@ export class SearchBox extends ViewBoxBaseComponent() { const query = StrCast(this._searchString); Doc.SetSearchQuery(query); - if (!this._props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)); + if (!this._props.linkSearch) Array.from(this._results.keys()).forEach(doc => DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)); this._results.clear(); if (query) { @@ -412,7 +409,7 @@ export class SearchBox extends ViewBoxBaseComponent() { this._timeout && clearTimeout(this._timeout); this._timeout = undefined; this._results.forEach((_, doc) => { - DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); + DocumentView.getFirstDocumentView(doc)?.ComponentView?.search?.('', undefined, true); Doc.UnBrushDoc(doc); Doc.UnHighlightDoc(doc); Doc.ClearSearchMatches(); @@ -476,7 +473,7 @@ export class SearchBox extends ViewBoxBaseComponent() { const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => ); return ( -
    +
    {isLinkSearch ? null : (
- <> - {this.dataDoc.mass2_accelerationX} m/s2 - + {this.dataDoc.mass2_accelerationX + ''} m/s2 - <> - {this.dataDoc.mass2_accelerationY} m/s2 - + {this.dataDoc.mass2_accelerationY + ''} m/s2