aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/google_docs/GooglePhotosClientUtils.ts2
-rw-r--r--src/client/documents/DocFromField.ts59
-rw-r--r--src/client/util/CaptureManager.tsx5
-rw-r--r--src/client/util/DocumentManager.ts15
-rw-r--r--src/client/util/ReplayMovements.ts51
-rw-r--r--src/client/util/SnappingManager.ts3
-rw-r--r--src/client/util/UndoManager.ts10
-rw-r--r--src/client/views/DashboardView.tsx5
-rw-r--r--src/client/views/DocComponent.tsx62
-rw-r--r--src/client/views/DocumentDecorations.tsx5
-rw-r--r--src/client/views/GestureOverlay.tsx17
-rw-r--r--src/client/views/GlobalKeyHandler.ts7
-rw-r--r--src/client/views/InkingStroke.tsx3
-rw-r--r--src/client/views/LightboxView.tsx33
-rw-r--r--src/client/views/Main.tsx99
-rw-r--r--src/client/views/MainView.tsx29
-rw-r--r--src/client/views/ObservableReactComponent.tsx11
-rw-r--r--src/client/views/PropertiesDocContextSelector.tsx4
-rw-r--r--src/client/views/StyleProvider.tsx12
-rw-r--r--src/client/views/TemplateMenu.tsx4
-rw-r--r--src/client/views/ViewBoxInterface.ts60
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx13
-rw-r--r--src/client/views/collections/CollectionMenu.tsx4
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx3
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx54
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx5
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/collections/TabDocView.tsx16
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx25
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx70
-rw-r--r--src/client/views/linking/LinkPopup.tsx3
-rw-r--r--src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx22
-rw-r--r--src/client/views/newlightbox/ExploreView/ExploreView.tsx30
-rw-r--r--src/client/views/newlightbox/Header/LightboxHeader.tsx8
-rw-r--r--src/client/views/newlightbox/NewLightboxView.tsx62
-rw-r--r--src/client/views/newlightbox/RecommendationList/RecommendationList.tsx134
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx2
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx2
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx4
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx96
-rw-r--r--src/client/views/nodes/DocumentView.tsx120
-rw-r--r--src/client/views/nodes/EquationBox.tsx4
-rw-r--r--src/client/views/nodes/FieldView.tsx2
-rw-r--r--src/client/views/nodes/KeyValuePair.tsx3
-rw-r--r--src/client/views/nodes/LinkBox.tsx5
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx4
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx13
-rw-r--r--src/client/views/nodes/VideoBox.tsx31
-rw-r--r--src/client/views/nodes/WebBox.tsx6
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx35
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx21
-rw-r--r--src/client/views/search/SearchBox.tsx54
-rw-r--r--src/client/views/topbar/TopBar.tsx4
-rw-r--r--src/fields/Doc.ts2
-rw-r--r--src/fields/InkField.ts11
-rw-r--r--src/fields/Proxy.ts2
-rw-r--r--src/fields/RichTextUtils.ts2
-rw-r--r--src/fields/util.ts2
61 files changed, 664 insertions, 720 deletions
diff --git a/src/client/apis/google_docs/GooglePhotosClientUtils.ts b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
index fdc185a8e..b238f07e9 100644
--- a/src/client/apis/google_docs/GooglePhotosClientUtils.ts
+++ b/src/client/apis/google_docs/GooglePhotosClientUtils.ts
@@ -331,7 +331,7 @@ export namespace GooglePhotos {
if (typeof target === 'string') {
description = target;
} else if (target instanceof RichTextField) {
- description = RichTextUtils.ToPlainText(EditorState.fromJSON(FormattedTextBox.Instance.config, JSON.parse(target.Data)));
+ description = RichTextUtils.ToPlainText(EditorState.fromJSON(new FormattedTextBox({} as any).config, JSON.parse(target.Data)));
}
return description;
};
diff --git a/src/client/documents/DocFromField.ts b/src/client/documents/DocFromField.ts
index 1c0a9755b..b65bbbdf5 100644
--- a/src/client/documents/DocFromField.ts
+++ b/src/client/documents/DocFromField.ts
@@ -1,50 +1,31 @@
-/* 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 { StrCast } from '../../fields/Types';
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';
+/**
+ * Changes the field key in the doc's layout string to be the specified field
+ */
+export function ResetLayoutFieldKey(doc: Doc, fieldKey: string) {
+ doc.layout = StrCast(doc.layout).replace(/={'.*'}/, `={'${fieldKey}'}`);
+ return doc;
+}
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));
- }
+ const nonDocFieldToDoc = () => {
+ if (field instanceof ImageField) return Docs.Create.ImageDocument(field.url.href, resolved);
+ if (field instanceof VideoField) return Docs.Create.VideoDocument(field.url.href, resolved);
+ if (field instanceof PdfField) return Docs.Create.PdfDocument(field.url.href, resolved);
+ if (field instanceof AudioField) return Docs.Create.AudioDocument(field.url.href, resolved);
+ if (field instanceof InkField) return Docs.Create.InkDocument(field.inkData, resolved);
+ if (field instanceof List && field[0] instanceof Doc) return Docs.Create.StackingDocument(DocListCast(field), resolved);
+ return Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _layout_autoHeight: true }, ...resolved });
+ };
+ const created = field instanceof Doc ? field : ResetLayoutFieldKey(nonDocFieldToDoc(), fieldKey);
+ created.title = fieldKey;
+ proto && created.proto && (created.proto = Doc.GetProto(proto));
return created;
}
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index 4fd934774..253cdd8b5 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -7,10 +7,9 @@ import * as React from 'react';
import { addStyleSheet } from '../../ClientUtils';
import { Doc } from '../../fields/Doc';
import { DocCast, StrCast } from '../../fields/Types';
-import { LightboxView } from '../views/LightboxView';
import { MainViewModal } from '../views/MainViewModal';
-import './CaptureManager.scss';
import { DocumentView } from '../views/nodes/DocumentView';
+import './CaptureManager.scss';
@observer
export class CaptureManager extends React.Component<{}> {
@@ -79,7 +78,7 @@ export class CaptureManager extends React.Component<{}> {
<div
className="save"
onClick={() => {
- LightboxView.Instance.SetLightboxDoc(this._document);
+ DocumentView.SetLightboxDoc(this._document);
this.close();
}}>
Save
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 5bcac7330..97051207b 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -7,7 +7,6 @@ import { listSpec } from '../../fields/Schema';
import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
import { AudioField } from '../../fields/URLField';
import { CollectionViewType } from '../documents/DocumentTypes';
-import { LightboxView } from '../views/LightboxView';
import { DocumentView, DocumentViewInternal } from '../views/nodes/DocumentView';
import { FocusViewOptions } from '../views/nodes/FocusViewOptions';
import { OpenWhere } from '../views/nodes/OpenWhere';
@@ -25,7 +24,7 @@ export class DocumentManager {
// global holds all of the nodes (regardless of which collection they're in)
@observable private _documentViews = new Set<DocumentView>();
@computed public get DocumentViews() {
- return Array.from(this._documentViews).filter(view => (!view.ComponentView?.dontRegisterView?.() && !LightboxView.LightboxDoc) || LightboxView.Contains(view));
+ return Array.from(this._documentViews).filter(view => (!view.ComponentView?.dontRegisterView?.() && !DocumentView.LightboxDoc()) || DocumentView.LightboxContains(view));
}
public AddDocumentView(dv: DocumentView) {
this._documentViews.add(dv);
@@ -68,7 +67,7 @@ export class DocumentManager {
private _viewRenderedCbs: { doc: Doc; func: (dv: DocumentView) => any }[] = [];
public AddViewRenderedCb = (doc: Opt<Doc>, func: (dv: DocumentView) => any) => {
if (doc) {
- const dv = LightboxView.LightboxDoc ? this.getLightboxDocumentView(doc) : this.getDocumentView(doc);
+ const dv = DocumentView.LightboxDoc() ? this.getLightboxDocumentView(doc) : this.getDocumentView(doc);
this._viewRenderedCbs.push({ doc, func });
if (dv) {
this.callAddViewFuncs(dv);
@@ -141,18 +140,18 @@ export class DocumentManager {
public getLightboxDocumentView = (toFind: Doc): DocumentView | undefined => {
const views: DocumentView[] = [];
- DocumentManager.Instance.DocumentViews.forEach(view => LightboxView.Contains(view) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
+ DocumentManager.Instance.DocumentViews.forEach(view => DocumentView.LightboxContains(view) && Doc.AreProtosEqual(view.Document, toFind) && views.push(view));
return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /* && view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse */) || (views.length ? views[0] : undefined);
};
public getFirstDocumentView = (toFind: Doc): DocumentView | undefined => {
- if (LightboxView.LightboxDoc) return DocumentManager.Instance.getLightboxDocumentView(toFind);
+ if (DocumentView.LightboxDoc()) return DocumentManager.Instance.getLightboxDocumentView(toFind);
const views = this.getDocumentViews(toFind); // .filter(view => view.Document !== originatingDoc);
return views?.find(view => view.ContentDiv?.getBoundingClientRect().width /* && view._props.focus !== returnFalse) || views?.find(view => view._props.focus !== returnFalse */) || (views.length ? views[0] : undefined);
};
public getDocumentViews(toFind: Doc): DocumentView[] {
const toReturn: DocumentView[] = [];
- const docViews = DocumentManager.Instance.DocumentViews.filter(view => !LightboxView.Contains(view));
- const lightViews = DocumentManager.Instance.DocumentViews.filter(view => LightboxView.Contains(view));
+ const docViews = DocumentManager.Instance.DocumentViews.filter(view => !DocumentView.LightboxContains(view));
+ const lightViews = DocumentManager.Instance.DocumentViews.filter(view => DocumentView.LightboxContains(view));
// heuristic to return the "best" documents first:
// choose a document in the lightbox first
@@ -262,7 +261,7 @@ export class DocumentManager {
return;
}
options.didMove = true;
- (!LightboxView.LightboxDoc && docContextPath.some(doc => DocumentView.activateTabView(doc))) || DocumentViewInternal.addDocTabFunc(docContextPath[0], options.openLocation ?? OpenWhere.addRight);
+ (!DocumentView.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) {
diff --git a/src/client/util/ReplayMovements.ts b/src/client/util/ReplayMovements.ts
index 530fcf211..c5afe549c 100644
--- a/src/client/util/ReplayMovements.ts
+++ b/src/client/util/ReplayMovements.ts
@@ -1,16 +1,17 @@
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 { DocumentView } from '../views/nodes/DocumentView';
import { OpenWhereMod } from '../views/nodes/OpenWhere';
-import { VideoBox } from '../views/nodes/VideoBox';
+import { SnappingManager } from './SnappingManager';
import { Movement, Presentation } from './TrackMovements';
-import { DocumentView } from '../views/nodes/DocumentView';
+import { ViewBoxInterface } from '../views/ViewBoxInterface';
+import { StrCast } from '../../fields/Types';
export class ReplayMovements {
private timers: NodeJS.Timeout[] | null;
private videoBoxDisposeFunc: IReactionDisposer | null;
- private videoBox: VideoBox | null;
+ private videoBox: ViewBoxInterface<any> | null;
private isPlaying: boolean;
// create static instance and getter for global use
@@ -29,6 +30,22 @@ export class ReplayMovements {
this.videoBoxDisposeFunc = null;
this.videoBox = null;
this.isPlaying = false;
+
+ reaction(
+ () => SnappingManager.UserPanned,
+ () => {
+ if (Doc.UserDoc()?.presentationMode === 'watching') this.pauseFromInteraction();
+ }
+ );
+ reaction(
+ () => DocumentView.Selected().slice(),
+ selviews => {
+ const selVideo = selviews.find(dv => dv.ComponentView?.playFrom);
+ if (selVideo?.ComponentView?.Play) {
+ this.setVideoBox(selVideo.ComponentView);
+ } else this.removeVideoBox();
+ }
+ );
}
// pausing movements will dispose all timers that are planned to replay the movements
@@ -45,18 +62,16 @@ export class ReplayMovements {
this.timers?.map(timer => clearTimeout(timer));
};
- setVideoBox = async (videoBox: VideoBox) => {
- // console.info('setVideoBox', videoBox);
+ setVideoBox = async (videoBox: ViewBoxInterface<any>) => {
if (this.videoBox !== null) {
console.warn('setVideoBox on already videoBox');
}
- if (this.videoBoxDisposeFunc !== null) {
- console.warn('setVideoBox on already videoBox dispose func');
- this.videoBoxDisposeFunc();
- }
+ this.videoBoxDisposeFunc?.();
+
+ const data = StrCast(videoBox.dataDoc?.[videoBox.fieldKey + '_presentation']);
+ const presentation = data ? JSON.parse(data) : null;
- const { presentation } = videoBox;
- if (presentation == null) {
+ if (presentation === null) {
console.warn('setVideoBox on null videoBox presentation');
return;
}
@@ -64,18 +79,14 @@ export class ReplayMovements {
this.loadPresentation(presentation);
this.videoBoxDisposeFunc = reaction(
- () => ({ playing: videoBox._playing, timeViewed: videoBox.player?.currentTime || 0 }),
+ () => ({ playing: videoBox.IsPlaying?.(), timeViewed: videoBox.PlayerTime?.() || 0 }),
({ playing, timeViewed }) => (playing ? this.playMovements(presentation, timeViewed) : this.pauseMovements())
);
this.videoBox = videoBox;
};
removeVideoBox = () => {
- if (this.videoBoxDisposeFunc == null) {
- console.warn('removeVideoBox on null videoBox');
- return;
- }
- this.videoBoxDisposeFunc();
+ this.videoBoxDisposeFunc?.();
this.videoBox = null;
this.videoBoxDisposeFunc = null;
@@ -83,7 +94,7 @@ export class ReplayMovements {
// should be called from interacting with the screen
pauseFromInteraction = () => {
- this.videoBox?.Pause();
+ this.videoBox?.Pause?.();
this.pauseMovements();
};
@@ -117,7 +128,7 @@ export class ReplayMovements {
return undefined;
}
// console.log('openTab', docId, doc);
- CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
+ DocumentView.addSplit(doc, OpenWhereMod.right);
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/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 6789c2ab8..1337d271f 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -26,6 +26,7 @@ export class SnappingManager {
@observable _serverVersion: string = '';
@observable _lastBtnId: string = '';
@observable _propertyWid: number = 0;
+ @observable _printToConsole: boolean = false;
private constructor() {
SnappingManager._manager = this;
@@ -55,6 +56,7 @@ export class SnappingManager {
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 get PrintToConsole() { return this.Instance._printToConsole; } // 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
@@ -69,6 +71,7 @@ export class SnappingManager {
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 SetPrintToConsole = (state:boolean) =>runInAction(() => {this.Instance._printToConsole = state}); // prettier-ignore
public static userColor: string | undefined;
public static userVariantColor: string | undefined;
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 956c0e674..534ffd2c8 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -3,9 +3,7 @@
import { action, observable, runInAction } from 'mobx';
import { Without } from '../../Utils';
import { RichTextField } from '../../fields/RichTextField';
-
-// eslint-disable-next-line prefer-const
-let printToConsole = false; // Doc.MyDockedBtns.linearView_IsOpen
+import { SnappingManager } from './SnappingManager';
function getBatchName(target: any, key: string | symbol): string {
const keyName = key.toString();
@@ -108,7 +106,7 @@ export namespace UndoManager {
export function AddEvent(event: UndoEvent, value?: any): void {
if (currentBatch && batchCounter.get() && !undoing) {
- printToConsole &&
+ SnappingManager.PrintToConsole &&
console.log(
' '.slice(0, batchCounter.get()) +
'UndoEvent : ' +
@@ -183,7 +181,7 @@ export namespace UndoManager {
}
export function StartBatch(batchName: string): Batch {
- printToConsole && console.log(' '.slice(0, batchCounter.get()) + 'Start ' + batchCounter + ' ' + batchName);
+ SnappingManager.PrintToConsole && console.log(' '.slice(0, batchCounter.get()) + 'Start ' + batchCounter + ' ' + batchName);
runInAction(() => batchCounter.set(batchCounter.get() + 1));
if (currentBatch === undefined) {
currentBatch = [];
@@ -193,7 +191,7 @@ export namespace UndoManager {
const EndBatch = action((batchName: string, cancel: boolean = false) => {
runInAction(() => batchCounter.set(batchCounter.get() - 1));
- printToConsole && console.log(' '.slice(0, batchCounter.get()) + 'End ' + batchName + ' (' + (currentBatch?.length ?? 0) + ')');
+ SnappingManager.PrintToConsole && console.log(' '.slice(0, batchCounter.get()) + 'End ' + batchName + ' (' + (currentBatch?.length ?? 0) + ')');
if (batchCounter.get() === 0 && currentBatch?.length) {
if (!cancel) {
undoStack.push(currentBatch);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index fc6a330f7..b7383a37e 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -305,11 +305,6 @@ export class DashboardView extends ObservableReactComponent<{}> {
});
if (state.readonly === true || state.readonly === null) {
DocServer.Control.makeReadOnly();
- // } else if (state.safe) {
- // if (!state.nro) {
- // DocServer.Control.makeReadOnly();
- // }
- // CollectionView.SetSafeMode(true);
} else if (state.nro || state.nro === null || state.readonly === false) {
/* empty */
} else if (doc.readOnly) {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index f01666d62..94e84e647 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -2,67 +2,17 @@ import { action, computed, makeObservable, observable } from 'mobx';
import * as React from 'react';
import { returnFalse } from '../../ClientUtils';
import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, FieldType, Opt } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData, DocViews } from '../../fields/DocSymbols';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { RefField } from '../../fields/RefField';
import { toList } from '../../fields/Types';
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 } from './nodes/DocumentView';
-import { FocusViewOptions } from './nodes/FocusViewOptions';
+import { ViewBoxInterface } from './ViewBoxInterface';
import { FieldViewProps } from './nodes/FieldView';
-import { OpenWhere } from './nodes/OpenWhere';
-// import { DocUtils } from '../documents/Documents';
/**
- * 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
- * be as general as possible
- */
-export class ViewBoxInterface<P> extends ObservableReactComponent<React.PropsWithChildren<P>> {
- promoteCollection?: () => void; // moves contents of collection to parent
- updateIcon?: () => void; // updates the icon representation of the document
- getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
- restoreView?: (viewSpec: Doc) => boolean;
- scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
- brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
- getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
- addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
- addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
- removeDocument?: (doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean) => boolean; // add a document (used only by collections)
- select?: (ctrlKey: boolean, shiftKey: boolean) => void;
- focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt<number>;
- viewTransition?: () => Opt<string>; // duration of a view transition animation
- isAnyChildContentActive?: () => boolean; // is any child content of the document active
- 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
- TogglePause?: (keep?: boolean) => void; // toggle media document playing state
- setFocus?: () => void; // sets input focus to the componentView
- setData?: (data: FieldType | Promise<RefField | undefined>) => boolean;
- componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
- dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
- dragConfig?: (dragData: DragManager.DocumentDragData) => void; // function to setup dragData in custom way (see TreeViews which add a tree view flag)
- incrementalRendering?: () => void;
- infoUI?: () => JSX.Element | null;
- contentBounds?: () => undefined | { bounds: { x: number; y: number; r: number; b: number }; cx: number; cy: number; width: number; height: number }; // bounds of contents in collection coordinate space (used by TabDocViewThumb)
- screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; transition?: string }>;
- ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
- 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 unstyled -- not titles, no opacity, no animations
-}
-/**
* DocComponent returns a React base class used by Doc views with accessors for unpacking the Document,layoutDoc, and dataDoc's
* (note: this should not be used for the 'Box' views that render the contents of Doc views)
* Example derived views: CollectionFreeFormDocumentView, DocumentView, DocumentViewInternal)
@@ -238,11 +188,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
Doc.RemoveEmbedding(rdoc, rdoc);
}
});
- if (targetDataDoc.isGroup && DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]).length < 2) {
- Array.from(targetDataDoc[DocViews])[0]?.ComponentView?.promoteCollection?.();
- } else {
- this.isAnyChildContentActive() && this._props.select(false);
- }
+ this.isAnyChildContentActive() && this._props.select(false);
return true;
}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 4262c2d57..fd44909c0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -25,7 +25,6 @@ import { DocumentButtonBar } from './DocumentButtonBar';
import './DocumentDecorations.scss';
import { InkStrokeProperties } from './InkStrokeProperties';
import { InkingStroke } from './InkingStroke';
-import { LightboxView } from './LightboxView';
import { ObservableReactComponent } from './ObservableReactComponent';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
@@ -95,7 +94,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
@computed get ClippedBounds() {
const bounds = { ...this.Bounds };
const leftBounds = this._props.boundsLeft;
- const topBounds = LightboxView.LightboxDoc ? 0 : this._props.boundsTop;
+ const topBounds = DocumentView.LightboxDoc() ? 0 : this._props.boundsTop;
bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
const borderRadiusDraggerWidth = 15;
@@ -277,7 +276,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
openDoc = Doc.GetEmbeddings(openDoc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(openDoc);
Doc.deiconifyView(openDoc);
}
- LightboxView.Instance.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1));
+ DocumentView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1));
}
}
DocumentView.DeselectAll();
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 7246f0ba0..2f26bdaef 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -21,7 +21,7 @@ import {
SetActiveInkColor,
SetActiveInkWidth,
} from '../../fields/Doc';
-import { InkData, InkTool } from '../../fields/InkField';
+import { InkData, InkField, InkTool } from '../../fields/InkField';
import { NumCast } from '../../fields/Types';
// import MobileInkOverlay from '../../mobile/MobileInkOverlay';
import { Gestures } from '../../pen-gestures/GestureTypes';
@@ -362,7 +362,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
detail: {
points,
gesture,
- bounds: GestureOverlay.getBounds(points),
+ bounds: InkField.getBounds(points),
text,
},
})
@@ -370,19 +370,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
);
};
- public static getBounds = (stroke: InkData, pad?: boolean) => {
- const padding = pad ? [-20000, 20000] : [];
- const xs = [...padding, ...stroke.map(p => p.X)];
- const ys = [...padding, ...stroke.map(p => p.Y)];
- const right = Math.max(...xs);
- const left = Math.min(...xs);
- const bottom = Math.max(...ys);
- const top = Math.min(...ys);
- return { right, left, bottom, top, width: right - left, height: bottom - top };
- };
-
@computed get svgBounds() {
- return GestureOverlay.getBounds(this._points);
+ return InkField.getBounds(this._points);
}
get elements() {
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index c73da35bc..18ec0b6a9 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -15,7 +15,6 @@ import { UndoManager, undoable } from '../util/UndoManager';
import { ContextMenu } from './ContextMenu';
import { DocumentDecorations } from './DocumentDecorations';
import { InkStrokeProperties } from './InkStrokeProperties';
-import { LightboxView } from './LightboxView';
import { MainView } from './MainView';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
@@ -146,7 +145,7 @@ export class KeyManager {
}
if (doDeselect) {
DocumentView.DeselectAll();
- LightboxView.Instance.SetLightboxDoc(undefined);
+ DocumentView.SetLightboxDoc(undefined);
}
// DictationManager.Controls.stop();
GoogleAuthenticationManager.Instance.cancel();
@@ -163,8 +162,8 @@ export class KeyManager {
case 'delete':
case 'backspace':
if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
- if (LightboxView.LightboxDoc) {
- LightboxView.Instance.SetLightboxDoc(undefined);
+ if (DocumentView.LightboxDoc()) {
+ DocumentView.SetLightboxDoc(undefined);
DocumentView.DeselectAll();
} else if (!window.getSelection()?.toString()) DocumentDecorations.Instance.onCloseClick(true);
return { stopPropagation: true, preventDefault: true };
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f497ca447..55f28f415 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -35,7 +35,8 @@ import { InteractionUtils } from '../util/InteractionUtils';
import { SnappingManager } from '../util/SnappingManager';
import { UndoManager } from '../util/UndoManager';
import { ContextMenu } from './ContextMenu';
-import { ViewBoxAnnotatableComponent, ViewBoxInterface } from './DocComponent';
+import { ViewBoxInterface } from './ViewBoxInterface';
+import { ViewBoxAnnotatableComponent } from './DocComponent';
import { Colors } from './global/globalEnums';
import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles';
import './InkStroke.scss';
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 12d899388..269f4fa83 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -19,14 +19,16 @@ import { GestureOverlay } from './GestureOverlay';
import './LightboxView.scss';
import { ObservableReactComponent } from './ObservableReactComponent';
import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
-import { CollectionDockingView } from './collections/CollectionDockingView';
import { DocumentView } from './nodes/DocumentView';
import { OpenWhere, OpenWhereMod } from './nodes/OpenWhere';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { OverlayView } from './OverlayView';
interface LightboxViewProps {
PanelWidth: number;
PanelHeight: number;
maxBorder: number[];
+ addSplit: (document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string | undefined, keyValue?: boolean | undefined) => boolean;
}
const savedKeys = ['freeform_panX', 'freeform_panY', 'freeform_scale', 'layout_scrollTop', 'layout_fieldKey'];
@@ -39,7 +41,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
* @returns true if a DocumentView is descendant of the lightbox view
*/
public static Contains(view?:DocumentView) { return view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView); } // prettier-ignore
- public static get LightboxDoc() { return LightboxView.Instance?._doc; } // prettier-ignore
+ public static LightboxDoc = () => LightboxView.Instance?._doc;
// eslint-disable-next-line no-use-before-define
static Instance: LightboxView;
private _path: {
@@ -65,10 +67,20 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
super(props);
makeObservable(this);
LightboxView.Instance = this;
+ DocumentView._setLightboxDoc = this.SetLightboxDoc;
+ DocumentView._lightboxContains = LightboxView.Contains;
+ DocumentView._lightboxDoc = LightboxView.LightboxDoc;
}
-
+ /**
+ * Sets the root Doc to render in the lightbox view.
+ * @param doc
+ * @param target a Doc within 'doc' to focus on (useful for freeform collections)
+ * @param future a list of Docs to step through with the arrow buttons of the lightbox
+ * @param layoutTemplate a template to apply to 'doc' to render it.
+ * @returns success flag which is currently always true
+ */
@action
- public SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
+ public SetLightboxDoc = (doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) => {
const lightDoc = this._doc;
lightDoc &&
lightDoc !== doc &&
@@ -110,7 +122,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
this._docTarget = target ?? doc;
return true;
- }
+ };
public AddDocTab = (docs: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => {
const doc = toList(docs).lastElement();
@@ -183,7 +195,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
const lightDoc = this._docTarget ?? this._doc;
if (lightDoc) {
Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightDoc);
- CollectionDockingView.AddSplit(lightDoc, OpenWhereMod.none);
+ this._props.addSplit(lightDoc, OpenWhereMod.none);
this.SetLightboxDoc(undefined);
}
};
@@ -243,7 +255,9 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
/>
</div>
);
- return !this._doc ? null : (
+ return !this._doc ? (
+ <OverlayView />
+ ) : (
<div
className="lightboxView-frame"
style={{ background: SnappingManager.userBackgroundColor }}
@@ -324,3 +338,8 @@ export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
return this.props.navBtn('50%', 0, 0, 'chevron-down', this.props.lightboxDoc(), this.props.stepInto, '');
}
}
+
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
+ LightboxView.Instance.AddDocTab(documentView.Document, OpenWhere.lightbox, 'layout'); // , 0);
+});
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 259ffbbc5..8968acbbb 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -7,7 +7,6 @@ import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { AssignAllExtensions } from '../../extensions/Extensions';
-import { Doc } from '../../fields/Doc';
import { FieldLoader } from '../../fields/FieldLoader';
import { BranchingTrailManager } from '../util/BranchingTrailManager';
import { CurrentUserUtils } from '../util/CurrentUserUtils';
@@ -16,11 +15,49 @@ import { PingManager } from '../util/PingManager';
import { ReplayMovements } from '../util/ReplayMovements';
import { TrackMovements } from '../util/TrackMovements';
import { KeyManager } from './GlobalKeyHandler';
+import { InkingStroke } from './InkingStroke';
import { MainView } from './MainView';
+import { CollectionCalendarView } from './collections/CollectionCalendarView';
+import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionView } from './collections/CollectionView';
+import { TabDocView } from './collections/TabDocView';
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { CollectionFreeFormInfoUI } from './collections/collectionFreeForm/CollectionFreeFormInfoUI';
+import { CollectionSchemaView } from './collections/collectionSchema/CollectionSchemaView';
+import { SchemaRowBox } from './collections/collectionSchema/SchemaRowBox';
import './global/globalScripts';
+import { AudioBox } from './nodes/AudioBox';
+import { ComparisonBox } from './nodes/ComparisonBox';
+import { DataVizBox } from './nodes/DataVizBox/DataVizBox';
+import { DocumentContentsView, HTMLtag } from './nodes/DocumentContentsView';
+import { EquationBox } from './nodes/EquationBox';
+import { FieldView } from './nodes/FieldView';
+import { FontIconBox } from './nodes/FontIconBox/FontIconBox';
+import { FunctionPlotBox } from './nodes/FunctionPlotBox';
+import { ImageBox } from './nodes/ImageBox';
import { KeyValueBox } from './nodes/KeyValueBox';
+import { LabelBox } from './nodes/LabelBox';
+import { LinkBox } from './nodes/LinkBox';
+import { LoadingBox } from './nodes/LoadingBox';
+import { MapBox } from './nodes/MapBox/MapBox';
+import { MapPushpinBox } from './nodes/MapBox/MapPushpinBox';
+import { PDFBox } from './nodes/PDFBox';
+import { PhysicsSimulationBox } from './nodes/PhysicsBox/PhysicsSimulationBox';
+import { RecordingBox } from './nodes/RecordingBox';
+import { ScreenshotBox } from './nodes/ScreenshotBox';
+import { ScriptingBox } from './nodes/ScriptingBox';
+import { VideoBox } from './nodes/VideoBox';
+import { WebBox } from './nodes/WebBox';
+import { DashDocCommentView } from './nodes/formattedText/DashDocCommentView';
+import { DashDocView } from './nodes/formattedText/DashDocView';
+import { DashFieldView } from './nodes/formattedText/DashFieldView';
+import { EquationView } from './nodes/formattedText/EquationView';
+import { FootnoteView } from './nodes/formattedText/FootnoteView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { SummaryView } from './nodes/formattedText/SummaryView';
+import { ImportElementBox } from './nodes/importBox/ImportElementBox';
+import { PresBox, PresElementBox } from './nodes/trails';
+import { SearchBox } from './search/SearchBox';
dotenv.config();
@@ -36,22 +73,14 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
root.render(<FieldLoader />);
window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
const info = await CurrentUserUtils.loadCurrentUser();
- // if (info.email === 'guest') DocServer.Control.makeReadOnly();
if (!info.userDocumentId) {
alert('Fatal Error: user not found in database');
return;
}
await CurrentUserUtils.loadUserDocument(info);
setTimeout(() => {
- document.getElementById('root')!.addEventListener(
- 'wheel',
- event => {
- if (event.ctrlKey) {
- event.preventDefault();
- }
- },
- true
- );
+ // prevent zooming browser
+ document.getElementById('root')!.addEventListener('wheel', event => event.ctrlKey && event.preventDefault(), true);
const startload = (document as any).startLoad;
const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000);
console.log('Loading Time = ' + loading);
@@ -65,10 +94,56 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' };
new PingManager();
new KeyManager();
- // iniitialize plugin apis
+ // initialize plugins and classes that require plugins
+ CollectionDockingView.Init(TabDocView);
+ FormattedTextBox.Init((tbox: FormattedTextBox) => ({
+ dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); }, // prettier-ignore
+ dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, tbox); }, // prettier-ignore
+ dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, tbox); }, // prettier-ignore
+ equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, tbox); }, // prettier-ignore
+ summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); }, // prettier-ignore
+ footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); }, // prettier-ignore
+ }));
CollectionFreeFormInfoUI.Init();
LinkFollower.Init();
KeyValueBox.Init();
+ PresBox.Init(TabDocView.AllTabDocs);
+ DocumentContentsView.Init(KeyValueBox.LayoutString(), {
+ FormattedTextBox,
+ ImageBox,
+ FontIconBox,
+ LabelBox,
+ EquationBox,
+ FieldView,
+ CollectionFreeFormView,
+ CollectionDockingView,
+ CollectionSchemaView,
+ CollectionCalendarView,
+ CollectionView,
+ WebBox,
+ KeyValueBox,
+ PDFBox,
+ VideoBox,
+ AudioBox,
+ RecordingBox,
+ PresBox,
+ PresElementBox,
+ SearchBox,
+ FunctionPlotBox,
+ InkingStroke,
+ LinkBox,
+ ScriptingBox,
+ MapBox,
+ ScreenshotBox,
+ DataVizBox,
+ HTMLtag,
+ ComparisonBox,
+ LoadingBox,
+ PhysicsSimulationBox,
+ SchemaRowBox,
+ ImportElementBox,
+ MapPushpinBox,
+ });
root.render(<MainView />);
}, 0);
})();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 637824c31..e4a18dcea 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -12,7 +12,7 @@ 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, DocViews } from '../../fields/DocSymbols';
+import { DocData } from '../../fields/DocSymbols';
import { Id } from '../../fields/FieldSymbols';
import { DocCast, StrCast, toList } from '../../fields/Types';
import { DocServer } from '../DocServer';
@@ -45,10 +45,9 @@ import { GestureOverlay } from './GestureOverlay';
import { LightboxView } from './LightboxView';
import './MainView.scss';
import { ObservableReactComponent } from './ObservableReactComponent';
-import { OverlayView } from './OverlayView';
import { PreviewCursor } from './PreviewCursor';
import { PropertiesView } from './PropertiesView';
-import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
+import { DashboardStyleProvider, DefaultStyleProvider, returnEmptyDocViewList } from './StyleProvider';
import { TimelineMenu } from './animationtimeline/TimelineMenu';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionMenu } from './collections/CollectionMenu';
@@ -61,7 +60,7 @@ import { LinkMenu } from './linking/LinkMenu';
import { AudioBox } from './nodes/AudioBox';
import { SchemaCSVPopUp } from './nodes/DataVizBox/SchemaCSVPopUp';
import { DocButtonState } from './nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from './nodes/DocumentView';
+import { DocumentView, DocumentViewInternal } from './nodes/DocumentView';
import { ImageEditorData as ImageEditor } from './nodes/ImageBox';
import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
import { LinkDocPreview, LinkInfo } from './nodes/LinkDocPreview';
@@ -102,7 +101,7 @@ export class MainView extends ObservableReactComponent<{}> {
return this._hideUI ? 0 : 27;
} // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
@computed private get topOfDashUI() {
- return this._hideUI || LightboxView.LightboxDoc ? 0 : Number(TOPBAR_HEIGHT.replace('px', ''));
+ return this._hideUI || DocumentView.LightboxDoc() ? 0 : Number(TOPBAR_HEIGHT.replace('px', ''));
}
@computed private get topOfHeaderBarDoc() {
return this.topOfDashUI;
@@ -171,6 +170,10 @@ export class MainView extends ObservableReactComponent<{}> {
() => DocumentView.Selected().slice(),
views => views.length > 1 && (document.activeElement as any)?.blur !== undefined && (document.activeElement as any)!.blur()
);
+ reaction(
+ () => Doc.MyDockedBtns.linearView_IsOpen,
+ open => SnappingManager.SetPrintToConsole(!!open)
+ );
const scriptTag = document.createElement('script');
scriptTag.setAttribute('type', 'text/javascript');
scriptTag.setAttribute('src', 'https://www.bing.com/api/maps/mapcontrol?callback=makeMap');
@@ -252,7 +255,6 @@ export class MainView extends ObservableReactComponent<{}> {
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);
@@ -686,7 +688,7 @@ export class MainView extends ObservableReactComponent<{}> {
@computed get dockingContent() {
return (
- <GestureOverlay isActive={!LightboxView.LightboxDoc}>
+ <GestureOverlay isActive={!DocumentView.LightboxDoc()}>
<div
key="docking"
className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? '-flyout' : ''}`}
@@ -697,7 +699,7 @@ export class MainView extends ObservableReactComponent<{}> {
style={{
width: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
- transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined,
+ transform: DocumentView.LightboxDoc() ? 'scale(0.0001)' : undefined,
}}>
{!this.mainContainer ? null : this.mainDocView}
</div>
@@ -791,7 +793,7 @@ export class MainView extends ObservableReactComponent<{}> {
@computed get leftMenuPanel() {
return (
- <div key="menu" className="mainView-leftMenuPanel" style={{ background: SnappingManager.userBackgroundColor, display: LightboxView.LightboxDoc ? 'none' : undefined }}>
+ <div key="menu" className="mainView-leftMenuPanel" style={{ background: SnappingManager.userBackgroundColor, display: DocumentView.LightboxDoc() ? 'none' : undefined }}>
<DocumentView
Document={Doc.MyLeftSidebarMenu}
addDocument={undefined}
@@ -957,7 +959,7 @@ export class MainView extends ObservableReactComponent<{}> {
}
@computed get snapLines() {
const dragged = DragManager.docsBeingDragged.lastElement() ?? DocumentView.SelectedDocs().lastElement();
- const dragPar = dragged ? CollectionFreeFormView.from(Array.from(dragged[DocViews]).lastElement()) : undefined;
+ const dragPar = dragged ? CollectionFreeFormView.from(DocumentView.getViews(dragged).lastElement()) : undefined;
return !dragPar?.layoutDoc.freeform_snapLines ? null : (
<div className="mainView-snapLines">
<svg style={{ width: '100%', height: '100%' }}>
@@ -1066,7 +1068,7 @@ export class MainView extends ObservableReactComponent<{}> {
case 'home': return <DashboardView />;
case 'dashboard':
default: return (<>
- <div key="dashdiv" style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 2001 }}>
+ <div key="dashdiv" style={{ position: 'relative', display: this._hideUI || DocumentView.LightboxDoc() ? 'none' : undefined, zIndex: 2001 }}>
<CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} togglePropertiesFlyout={this.togglePropertiesFlyout} toggleTopBar={this.toggleTopBar} topBarHeight={this.headerBarHeightFunc}/>
</div>
{this.mainDashboardArea}
@@ -1083,14 +1085,11 @@ export class MainView extends ObservableReactComponent<{}> {
<MarqueeOptionsMenu />
<TimelineMenu />
<RichTextMenu />
- {/* <InkTranscription /> */}
{this.snapLines}
- <LightboxView key="lightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={this.lightboxMaxBorder} />
- {LightboxView.LightboxDoc ? null : <OverlayView />}
+ <LightboxView key="lightbox" PanelWidth={this._windowWidth} addSplit={CollectionDockingView.AddSplit} PanelHeight={this._windowHeight} maxBorder={this.lightboxMaxBorder} />
<GPTPopup key="gptpopup" />
<SchemaCSVPopUp key="schemacsvpopup" />
<GenerativeFill imageEditorOpen={ImageEditor.Open} imageEditorSource={ImageEditor.Source} imageRootDoc={ImageEditor.RootDoc} addDoc={ImageEditor.AddDoc} />
- {/* <NewLightboxView key="newLightbox" PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> */}
</div>
);
}
diff --git a/src/client/views/ObservableReactComponent.tsx b/src/client/views/ObservableReactComponent.tsx
index 266411770..34da82b6c 100644
--- a/src/client/views/ObservableReactComponent.tsx
+++ b/src/client/views/ObservableReactComponent.tsx
@@ -1,6 +1,8 @@
import { action, makeObservable, observable } from 'mobx';
import * as React from 'react';
import './AntimodeMenu.scss';
+import { observer } from 'mobx-react';
+import JsxParser from 'react-jsx-parser';
/**
* This is an abstract class that serves as the base for a PDF-style or Marquee-style
@@ -21,3 +23,12 @@ export abstract class ObservableReactComponent<T> extends React.Component<T, {}>
})); // prettier-ignore
}
}
+
+class ObserverJsxParser1 extends JsxParser {
+ constructor(props: any) {
+ super(props);
+ observer(this as any);
+ }
+}
+
+export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 7465c727a..1fea36d16 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -10,7 +10,7 @@ import { Cast, StrCast } from '../../fields/Types';
import { ObservableReactComponent } from './ObservableReactComponent';
import './PropertiesDocContextSelector.scss';
import { CollectionDockingView } from './collections/CollectionDockingView';
-import { DocFocusOrOpen, DocumentView } from './nodes/DocumentView';
+import { DocumentView } from './nodes/DocumentView';
import { OpenWhere } from './nodes/OpenWhere';
type PropertiesDocContextSelectorProps = {
@@ -58,7 +58,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
getOnClick = (clickCol: Doc) => {
if (!this._props.DocView) return;
const col = Doc.IsDataProto(clickCol) ? Doc.MakeDelegate(clickCol) : clickCol;
- DocFocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col);
+ DocumentView.FocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col);
};
render() {
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index e6e95e86c..0b8201903 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -12,7 +12,6 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
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';
@@ -23,10 +22,11 @@ import { SnappingManager } from '../util/SnappingManager';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { TreeSort } from './collections/TreeSort';
import { Colors } from './global/globalEnums';
-import { DocFocusOrOpen, DocumentView, DocumentViewProps } from './nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from './nodes/DocumentView';
import { FieldViewProps } from './nodes/FieldView';
import { StyleProp } from './StyleProp';
import './StyleProvider.scss';
+import { emptyPath } from '../../Utils';
function toggleLockedPosition(doc: Doc) {
UndoManager.RunInBatch(() => Doc.toggleLockedPosition(doc), 'toggleBackground');
@@ -134,7 +134,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
case StyleProp.Highlighting:
if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined;
if (doc && !doc.layout_disableBrushing && !disableBrushing) {
- const selected = Array.from(doc?.[DocViews]??[]).filter(dv => dv.IsSelected).length;
+ const selected = DocumentView.getViews(doc).filter(dv => dv.IsSelected).length;
const highlightIndex = Doc.GetBrushHighlightStatus(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0);
const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? "black" : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
@@ -403,7 +403,11 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps>
if (doc && property.split(':')[0] === StyleProp.Decorations) {
return doc._type_collection === CollectionViewType.Docking || Doc.IsSystem(doc)
? null
- : DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.embedContainer ?? doc?.annotationOn)));
+ : DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => DocumentView.FocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.embedContainer ?? doc?.annotationOn)));
}
return DefaultStyleProvider(doc, props, property);
}
+
+export function returnEmptyDocViewList() {
+ return emptyPath;
+}
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 5a7c2ef5b..cff32a557 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -13,8 +13,8 @@ import { DocUtils } from '../documents/DocUtils';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
import { Transform } from '../util/Transform';
import { CollectionTreeView } from './collections/CollectionTreeView';
-import { DocumentView, returnEmptyDocViewList } from './nodes/DocumentView';
-import { DefaultStyleProvider } from './StyleProvider';
+import { DocumentView } from './nodes/DocumentView';
+import { DefaultStyleProvider, returnEmptyDocViewList } from './StyleProvider';
import './TemplateMenu.scss';
@observer
diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts
new file mode 100644
index 000000000..c633f34fb
--- /dev/null
+++ b/src/client/views/ViewBoxInterface.ts
@@ -0,0 +1,60 @@
+import * as React from 'react';
+import { Doc, FieldType, Opt } from '../../fields/Doc';
+import { RefField } from '../../fields/RefField';
+import { DragManager } from '../util/DragManager';
+import { ObservableReactComponent } from './ObservableReactComponent';
+import { PinProps } from './PinFuncs';
+import { DocumentView } from './nodes/DocumentView';
+import { FocusViewOptions } from './nodes/FocusViewOptions';
+import { OpenWhere } from './nodes/OpenWhere';
+// import { DocUtils } from '../documents/Documents';
+
+/**
+ * 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
+ * be as general as possible
+ */
+export abstract class ViewBoxInterface<P> extends ObservableReactComponent<React.PropsWithChildren<P>> {
+ abstract get Document(): Doc;
+ abstract get dataDoc(): Doc;
+ abstract get fieldKey(): string;
+ promoteCollection?: () => void; // moves contents of collection to parent
+ updateIcon?: () => void; // updates the icon representation of the document
+ getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
+ restoreView?: (viewSpec: Doc) => boolean;
+ scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt<number>; // returns the duration of the focus
+ brushView?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number) => void; // highlight a region of a view (used by freeforms)
+ getView?: (doc: Doc, options: FocusViewOptions) => Promise<Opt<DocumentView>>; // returns a nested DocumentView for the specified doc or undefined
+ addDocTab?: (doc: Doc, where: OpenWhere) => boolean; // determines how to add a document - used in following links to open the target ina local lightbox
+ addDocument?: (doc: Doc | Doc[], annotationKey?: string) => boolean; // add a document (used only by collections)
+ removeDocument?: (doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean) => boolean; // add a document (used only by collections)
+ select?: (ctrlKey: boolean, shiftKey: boolean) => void;
+ focus?: (textAnchor: Doc, options: FocusViewOptions) => Opt<number>;
+ viewTransition?: () => Opt<string>; // duration of a view transition animation
+ isAnyChildContentActive?: () => boolean; // is any child content of the document active
+ 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, fullPlay?: boolean) => void; // play a range of a media document
+ Play?: () => void; // play a media documents
+ Pause?: () => void; // pause a media document (eg, audio/video)
+ IsPlaying?: () => boolean; // is a media document playing
+ PlayerTime?: () => number | undefined; // current timecode of player
+ TogglePause?: (keep?: boolean) => void; // toggle media document playing state
+ setFocus?: () => void; // sets input focus to the componentView
+ setData?: (data: FieldType | Promise<RefField | undefined>) => boolean;
+ componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element | null;
+ dragStarting?: (snapToDraggedDoc: boolean, showGroupDragTarget: boolean, visited: Set<Doc>) => void;
+ dragConfig?: (dragData: DragManager.DocumentDragData) => void; // function to setup dragData in custom way (see TreeViews which add a tree view flag)
+ incrementalRendering?: () => void;
+ infoUI?: () => JSX.Element | null;
+ contentBounds?: () => undefined | { bounds: { x: number; y: number; r: number; b: number }; cx: number; cy: number; width: number; height: number }; // bounds of contents in collection coordinate space (used by TabDocViewThumb)
+ screenBounds?: () => Opt<{ left: number; top: number; right: number; bottom: number; transition?: string }>;
+ ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+ 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 unstyled -- not titles, no opacity, no animations
+}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0ee3575f3..8fb2b30f1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -22,7 +22,6 @@ import { ScriptingGlobals } from '../../util/ScriptingGlobals';
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';
@@ -37,11 +36,13 @@ const _global = (window /* browser */ || global) /* node */ as any;
export class CollectionDockingView extends CollectionSubView() {
static tabClass: JSX.Element | null = null;
/**
- * Configure golden layout to render its documents using the specified React component
+ * Initialize by assigning the add split method to DocumentView and by
+ * configuring golden layout to render its documents using the specified React component
* @param ele - typically would be set to TabDocView
*/
- static setTabJSXComponent(ele: any) {
+ public static Init(ele: any) {
this.tabClass = ele;
+ DocumentView.addSplit = CollectionDockingView.AddSplit;
}
// eslint-disable-next-line no-use-before-define
@observable public static Instance: CollectionDockingView | undefined = undefined;
@@ -336,7 +337,7 @@ export class CollectionDockingView extends CollectionSubView() {
SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
- () => LightboxView.LightboxDoc,
+ () => DocumentView.LightboxDoc(),
doc => setTimeout(() => !doc && this.onResize())
);
new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
@@ -391,7 +392,7 @@ export class CollectionDockingView extends CollectionSubView() {
onResize = () => {
const cur = this._containerRef.current;
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
- !LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
+ !DocumentView.LightboxDoc() && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
};
endUndoBatch = () => {
@@ -633,7 +634,7 @@ export class CollectionDockingView extends CollectionSubView() {
ScriptingGlobals.add(
// eslint-disable-next-line prefer-arrow-callback
function openInLightbox(doc: any) {
- LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
+ CollectionDockingView.Instance?._props.addDocTab(doc, OpenWhere.lightbox);
},
'opens up document in a lightbox',
'(doc: any)'
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index e53071584..3eb3008c4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -26,8 +26,8 @@ import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { AntimodeMenu } from '../AntimodeMenu';
import { EditableView } from '../EditableView';
-import { DefaultStyleProvider } from '../StyleProvider';
-import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
+import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import './CollectionMenu.scss';
import { CollectionLinearView } from './collectionLinear';
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 53211be77..16c474996 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -22,7 +22,6 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { FieldsDropdown } from '../FieldsDropdown';
import { Colors } from '../global/globalEnums';
-import { LightboxView } from '../LightboxView';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
@@ -177,7 +176,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
);
this._disposers.refList = reaction(
- () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
+ () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
if (autoHeight) {
refList.forEach(r => this.observer.observe(r));
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index c098c033b..44ab1968d 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -4,14 +4,11 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { lightOrDark, returnEmptyString } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { RichTextField } from '../../../fields/RichTextField';
+import { Doc, Opt } from '../../../fields/Doc';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { Cast, NumCast } from '../../../fields/Types';
-import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { DocumentFromField } from '../../documents/DocFromField';
import { DocUtils } from '../../documents/DocUtils';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
@@ -19,11 +16,10 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, undoable } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import './CollectionNoteTakingView.scss';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import './CollectionNoteTakingView.scss';
interface CSVFieldColumnProps {
Document: Doc;
@@ -171,9 +167,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
menuCallback = (x: number, y: number) => {
ContextMenu.Instance.clearItems();
- const layoutItems: ContextMenuProps[] = [];
- const docItems: ContextMenuProps[] = [];
- const dataDoc = this._props.TemplateDataDocument || this._props.Document;
+ const { pivotField } = this._props;
const pivotValue = this.getValue(this._props.heading);
DocUtils.addDocumentCreatorMenuItems(
@@ -187,50 +181,10 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
x,
y,
true,
- this._props.pivotField,
+ pivotField, // when created, the new doc's pivotField will be set to pivotValue
pivotValue
);
- Array.from(Object.keys(Doc.GetProto(dataDoc)))
- .filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof dataDoc[fieldKey] === 'string')
- .map(fieldKey =>
- docItems.push({
- description: ':' + fieldKey,
- event: () => {
- const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
- if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
- }
- return this._props.addDocument?.(created);
- }
- return undefined;
- },
- icon: 'compress-arrows-alt',
- })
- );
- Array.from(Object.keys(Doc.GetProto(dataDoc)))
- .filter(fieldKey => DocListCast(dataDoc[fieldKey]).length)
- .map(fieldKey =>
- docItems.push({
- description: ':' + fieldKey,
- event: () => {
- const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
- if (created) {
- const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
- if (container.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, container);
- return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
- }
- return this._props.addDocument?.(created) || false;
- }
- return undefined;
- },
- icon: 'compress-arrows-alt',
- })
- );
- !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
- !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
Doc.GetProto(this._props.Document)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 1604920f6..fac885300 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -26,7 +26,6 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { VideoThumbnails } from '../global/globalEnums';
-import { LightboxView } from '../LightboxView';
import { AudioWaveform } from '../nodes/audio/AudioWaveform';
import { DocumentView } from '../nodes/DocumentView';
import { FocusFuncType, StyleProviderFuncType } from '../nodes/FieldView';
@@ -752,10 +751,10 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
// const dictationDoc = Cast(this._props.layoutDoc.data_dictation, Doc, null);
// const isDictation = dictationDoc && LinkManager.Links(this._props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc);
if (
- !LightboxView.LightboxDoc &&
+ !DocumentView.LightboxDoc() &&
// bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront.
// for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video.
- /* (isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc)) */
+ /* (isDictation || !Doc.AreProtosEqual(DocumentView.LightboxDoc(), this._props.layoutDoc)) */
!this._props.layoutDoc.dontAutoFollowLinks &&
Doc.Links(this._props.mark).length &&
time > NumCast(this._props.mark[this._props.startTag]) &&
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 07aa0f4f0..56d2a6c9c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -26,7 +26,6 @@ import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
-import { LightboxView } from '../LightboxView';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
@@ -229,7 +228,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
layoutAutoHeight => layoutAutoHeight && this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))
);
this._disposers.refList = reaction(
- () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
+ () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
this.observer.disconnect();
if (autoHeight) refList.forEach(r => this.observer.observe(r));
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index c1247f5b0..beb8c0666 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -209,7 +209,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
onContextMenu = (): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
const layoutItems: ContextMenuProps[] = [];
- const menuDoc = ScriptCast(Cast(this.layoutDoc.layout_headerButton, Doc, null)?.onClick).script.originalScript === CollectionTreeView.AddTreeFunc;
+ const menuDoc = ScriptCast(Cast(this.layoutDoc.layout_headerButton, Doc, null)?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc;
menuDoc && layoutItems.push({ description: 'Create new folder', event: () => CollectionTreeView.addTreeFolder(this.Document), icon: 'paint-brush' });
if (!Doc.noviceMode) {
layoutItems.push({
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7cadd072b..b52c7c54c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -13,7 +13,7 @@ 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 { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView } from '../nodes/FieldView';
import { OpenWhere } from '../nodes/OpenWhere';
import { CollectionCalendarView } from './CollectionCalendarView';
@@ -35,7 +35,7 @@ import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultir
import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
@observer
-export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() implements ViewBoxInterface {
+export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() {
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(CollectionView, fieldStr);
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 008ef6ab4..afd584154 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, DocViews } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { FieldId } from '../../../fields/RefField';
@@ -28,13 +28,14 @@ import { LightboxView } from '../LightboxView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
-import { DefaultStyleProvider } from '../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import { Colors } from '../global/globalEnums';
-import { DocumentView, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { KeyValueBox } from '../nodes/KeyValueBox';
import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
-import { PresBox, PresMovement } from '../nodes/trails';
+import { PresBox } from '../nodes/trails';
+import { PresMovement } from '../nodes/trails/PresEnums';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionView } from './CollectionView';
import './TabDocView.scss';
@@ -187,6 +188,11 @@ interface TabDocViewProps {
@observer
export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
static _allTabs = new ObservableSet<TabDocView>();
+ public static AllTabDocs() {
+ return Array.from(TabDocView._allTabs)
+ .filter(tv => tv._document)
+ .map(tv => tv._document!);
+ }
_mainCont: HTMLDivElement | null = null;
_tabReaction: IReactionDisposer | undefined;
@@ -294,7 +300,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
@observable _isActive: boolean = false;
@observable _isAnyChildContentActive = false;
public static IsSelected = (doc?: Doc) => {
- if (Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected)) {
+ if (DocumentView.getViews(doc).some(dv => dv?.IsSelected)) {
return true;
}
return false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index 65a2fe0aa..e543b4008 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -20,6 +20,11 @@ export interface CollectionFreeFormPannableContentsProps {
@observer
export class CollectionFreeFormPannableContents extends ObservableReactComponent<CollectionFreeFormPannableContentsProps> {
static _overlayPlugin: ((fform: Doc) => React.JSX.Element) | null = null;
+ /**
+ * Setup a plugin function that returns components to display on a layer above the collection
+ * See PresBox which renders presenstation paths over the collection
+ * @param plugin a function that receives the collection Doc and returns JSX Elements
+ */
public static SetOverlayPlugin(plugin: ((fform: Doc) => React.JSX.Element) | null) {
CollectionFreeFormPannableContents._overlayPlugin = plugin;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 74de6524b..dbd9fb11f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -28,7 +28,6 @@ import { CollectionViewType, DocumentType } from '../../../documents/DocumentTyp
import { DocUtils } from '../../../documents/DocUtils';
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 { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
@@ -36,9 +35,7 @@ import { Transform } from '../../../util/Transform';
import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
-import { GestureOverlay } from '../../GestureOverlay';
import { InkingStroke } from '../../InkingStroke';
-import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
import { DocumentView } from '../../nodes/DocumentView';
@@ -367,7 +364,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
- const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
+ const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc());
const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined);
// focus on the document in the collection
@@ -620,7 +617,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return false;
};
forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => {
- this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, GestureOverlay.getBounds(points), text));
+ this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text));
};
onPointerMove = (e: PointerEvent) => {
@@ -838,8 +835,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
setPan(panXIn: number, panYIn: number, panTime: number = 0, allowScroll = false) {
let [panX, panY] = [panXIn, panYIn];
- // this is the easiest way to do this -> will talk with Bob about using mobx to do this to remove this line of code.
- if (Doc.UserDoc()?.presentationMode === 'watching') ReplayMovements.Instance.pauseFromInteraction();
if (!this.isAnnotationOverlay && this.childDocs.length) {
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
@@ -849,7 +844,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
panX = clamp(panX, xrangeMin - widScaling / 2, xrangeMax + widScaling / 2);
panY = clamp(panY, yrangeMin - hgtScaling / 2, yrangeMax + hgtScaling / 2);
}
- if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc) {
+ if (!this.layoutDoc._lockedTransform || DocumentView.LightboxDoc()) {
this.setPanZoomTransition(panTime);
const minScale = NumCast(this.dataDoc._freeform_scale_min, 1);
const scale = 1 - minScale / this.zoomScaling();
@@ -882,6 +877,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
nudge = (x: number, y: number, nudgeTime: number = 500) => {
const collectionDoc = this.Document;
if (collectionDoc?._type_collection !== CollectionViewType.Freeform) {
+ SnappingManager.TriggerUserPanned();
this.setPan(
NumCast(this.layoutDoc[this.panXFieldKey]) + ((this._props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
NumCast(this.layoutDoc[this.panYFieldKey]) + ((this._props.PanelHeight() / 2) * -y) / this.zoomScaling(),
@@ -1008,6 +1004,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return undefined;
};
+
+ removeDocument = (docs: Doc | Doc[], annotationKey?: string | undefined) => {
+ const ret = !!this._props.removeDocument?.(docs, annotationKey);
+ // if this is a group and we have fewer than 2 Docs, then just promote what's left to our parent and get rid of the group.
+ if (ret && DocListCast(this.dataDoc[annotationKey ?? this.fieldKey]).length < 2 && this.Document.isGroup) {
+ this.promoteCollection();
+ }
+ return ret;
+ };
childPointerEventsFunc = () => this._childPointerEvents;
childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
@@ -1050,7 +1055,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus}
addDocTab={this.addDocTab}
addDocument={this._props.addDocument}
- removeDocument={this._props.removeDocument}
+ removeDocument={this.removeDocument}
moveDocument={this._props.moveDocument}
pinToPres={this._props.pinToPres}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
@@ -1525,7 +1530,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
incrementalRender = action(() => {
- if (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.())) {
+ if (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.())) {
const layoutUnrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = this.Document.isTemplateDoc ? Number.MAX_VALUE : 5;
for (let i = 0; i < Math.min(layoutUnrendered.length, loadIncrement); i++) {
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index b017eb62b..5874364e0 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -25,9 +25,9 @@ import { Transform } from '../../../util/Transform';
import { undoBatch, undoable } from '../../../util/UndoManager';
import { EditableView } from '../../EditableView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
-import { DocFocusOrOpen, returnEmptyDocViewList } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { FInfotoColType } from './CollectionSchemaView';
@@ -59,6 +59,14 @@ export interface SchemaTableCellProps {
rootSelected?: () => boolean;
}
+function selectedCell(props: SchemaTableCellProps) {
+ return (
+ props.isRowActive() &&
+ props.selectedCol() === props.col && //
+ props.selectedCells()?.filter(d => d === props.Document)?.length
+ );
+}
+
@observer
export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> {
constructor(props: SchemaTableCellProps) {
@@ -67,7 +75,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
}
static addFieldDoc = (docs: Doc | Doc[] /* , where: OpenWhere */) => {
- DocFocusOrOpen(toList(docs)[0]);
+ DocumentView.FocusOrOpen(toList(docs)[0]);
return true;
};
public static renderProps(props: SchemaTableCellProps) {
@@ -114,11 +122,6 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return { color, textDecoration, fieldProps, cursor, pointerEvents };
}
- @computed get selected() {
- const selectedDocs: Doc[] | undefined = this._props.selectedCells();
- return this._props.isRowActive() && selectedDocs?.filter(doc => doc === this._props.Document).length !== 0 && this._props.selectedCol() === this._props.col;
- }
-
@computed get defaultCellContent() {
const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props);
@@ -132,12 +135,12 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
pointerEvents,
}}>
<EditableView
- ref={r => this.selected && this._props.autoFocus && r?.setIsFocused(true)}
+ ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
oneLine={this._props.oneLine}
allowCRs={this._props.allowCRs}
contents={undefined}
fieldContents={fieldProps}
- editing={this.selected ? undefined : false}
+ editing={selectedCell(this._props) ? undefined : false}
GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)}
SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
@@ -157,30 +160,27 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
get getCellType() {
const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
const cellValue = this._props.Document[this._props.fieldKey];
+
if (cellValue instanceof ImageField) return ColumnType.Image;
if (cellValue instanceof DateField) return ColumnType.Date;
if (cellValue instanceof RichTextField) return ColumnType.RTF;
if (typeof cellValue === 'number') return ColumnType.Any;
if (typeof cellValue === 'string' && columnTypeStr !== FInfoFieldType.enumeration) return ColumnType.Any;
if (typeof cellValue === 'boolean') return ColumnType.Boolean;
-
- if (columnTypeStr && columnTypeStr in FInfotoColType) {
- return FInfotoColType[columnTypeStr];
- }
+ if (columnTypeStr && columnTypeStr in FInfotoColType) return FInfotoColType[columnTypeStr];
return ColumnType.Any;
}
get content() {
- const cellType: ColumnType = this.getCellType;
// prettier-ignore
- switch (cellType) {
- case ColumnType.Image: return <SchemaImageCell {...this._props} />;
- case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />;
- case ColumnType.RTF: return <SchemaRTFCell {...this._props} />;
+ switch (this.getCellType) {
+ case ColumnType.Image: return <SchemaImageCell {...this._props} />;
+ case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />;
+ case ColumnType.RTF: return <SchemaRTFCell {...this._props} />;
case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => Field.toString(val))} />;
- case ColumnType.Date: return <SchemaDateCell {...this._props} />;
- default: return this.defaultCellContent;
+ case ColumnType.Date: return <SchemaDateCell {...this._props} />;
+ default: return this.defaultCellContent;
}
}
@@ -193,13 +193,13 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
const shift: boolean = e.shiftKey;
const ctrl: boolean = e.ctrlKey;
if (this._props.isRowActive?.() !== false) {
- if (this.selected && ctrl) {
+ if (selectedCell(this._props) && ctrl) {
this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
e.stopPropagation();
- } else !this.selected && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
}
})}
- style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+ style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
{this.content}
</div>
);
@@ -329,20 +329,14 @@ export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps
makeObservable(this);
}
- @computed get selected() {
- const selected = this._props.selectedCells();
- return this._props.isRowActive() && selected && selected?.filter(doc => doc === this._props.Document).length !== 0 && this._props.selectedCol() === this._props.col;
- // return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
- }
-
// if the text box blurs and none of its contents are focused(), then the edit finishes
- selectedFunc = () => this.selected;
+ selectedFunc = () => !!selectedCell(this._props);
render() {
const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
fieldProps.isContentActive = this.selectedFunc;
return (
- <div className="schemaRTFCell" style={{ fontStyle: this.selected ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox {...fieldProps} autoFocus onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ <div className="schemaRTFCell" style={{ fontStyle: selectedCell(this._props) ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}>
+ {selectedCell(this._props) ? <FormattedTextBox {...fieldProps} autoFocus onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
@@ -354,10 +348,6 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
makeObservable(this);
}
- @computed get selected() {
- const selected = this._props.selectedCells();
- return this._props.isRowActive() && selected && selected?.filter(doc => doc === this._props.Document).length !== 0 && this._props.selectedCol() === this._props.col;
- }
render() {
const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
@@ -375,7 +365,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
<EditableView
contents={undefined}
fieldContents={fieldProps}
- editing={this.selected ? undefined : false}
+ editing={selectedCell(this._props) ? undefined : false}
GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
@@ -399,10 +389,6 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
makeObservable(this);
}
- @computed get selected() {
- const selected = this._props.selectedCells();
- return this._props.isRowActive() && selected && selected?.filter(doc => doc === this._props.Document).length !== 0 && this._props.selectedCol() === this._props.col;
- }
render() {
const { color, textDecoration, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
const options = this._props.options?.map(facet => ({ value: facet, label: facet }));
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 9fb1c0fdc..76a8396ff 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -5,8 +5,7 @@ import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '
import { emptyFunction } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { Transform } from '../../util/Transform';
-import { DefaultStyleProvider } from '../StyleProvider';
-import { returnEmptyDocViewList } from '../nodes/DocumentView';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import { SearchBox } from '../search/SearchBox';
import './LinkPopup.scss';
diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
index e48e993cf..3eb99f47a 100644
--- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
+++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx
@@ -1,24 +1,27 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { action } from 'mobx';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { SnappingManager } from '../../../util/SnappingManager';
import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { DocumentView } from '../../nodes/DocumentView';
import { OpenWhereMod } from '../../nodes/OpenWhere';
import { NewLightboxView } from '../NewLightboxView';
import './ButtonMenu.scss';
-import { IButtonMenu } from './utils';
-export const ButtonMenu = (props: IButtonMenu) => {
+export function ButtonMenu() {
return (
- <div className={`newLightboxButtonMenu-container`}>
+ <div className="newLightboxButtonMenu-container">
<div
className="newLightboxView-navBtn"
title="toggle fit width"
onClick={e => {
e.stopPropagation();
NewLightboxView.LightboxDoc!._fitWidth = !NewLightboxView.LightboxDoc!._fitWidth;
- }}></div>
+ }}
+ />
<div
className="newLightboxView-tabBtn"
title="open in tab"
@@ -27,7 +30,8 @@ export const ButtonMenu = (props: IButtonMenu) => {
CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none);
DocumentView.DeselectAll();
NewLightboxView.SetNewLightboxDoc(undefined);
- }}></div>
+ }}
+ />
<div
className="newLightboxView-penBtn"
title="toggle pen annotation"
@@ -35,7 +39,8 @@ export const ButtonMenu = (props: IButtonMenu) => {
onClick={e => {
e.stopPropagation();
Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
- }}></div>
+ }}
+ />
<div
className="newLightboxView-exploreBtn"
title="toggle explore mode to navigate among documents only"
@@ -43,7 +48,8 @@ export const ButtonMenu = (props: IButtonMenu) => {
onClick={action(e => {
e.stopPropagation();
SnappingManager.SetExploreMode(!SnappingManager.ExploreMode);
- })}></div>
+ })}
+ />
</div>
);
-};
+}
diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
index a1d6375c4..f8c07cc43 100644
--- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx
+++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx
@@ -1,32 +1,34 @@
-import './ExploreView.scss';
-import { IBounds, IExploreView, emptyBounds } from './utils';
-import { IRecommendation } from '../components';
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import * as React from 'react';
-import { NewLightboxView } from '../NewLightboxView';
import { StrCast } from '../../../../fields/Types';
+import { NewLightboxView } from '../NewLightboxView';
+import './ExploreView.scss';
+import { IExploreView, emptyBounds } from './utils';
-export const ExploreView = (props: IExploreView) => {
+export function ExploreView(props: IExploreView) {
const { recs, bounds = emptyBounds } = props;
return (
- <div className={`exploreView-container`}>
+ <div className="exploreView-container">
{recs &&
recs.map(rec => {
- const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x));
- const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y));
+ const xBound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x));
+ const yBound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y));
if (rec.embedding) {
- const x = (rec.embedding.x / x_bound) * 50;
- const y = (rec.embedding.y / y_bound) * 50;
+ const x = (rec.embedding.x / xBound) * 50;
+ const y = (rec.embedding.y / yBound) * 50;
return (
- <div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
+ <div className="exploreView-doc" onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}>
{rec.title}
</div>
);
- } else return null;
+ }
+ return null;
})}
- <div className={`exploreView-doc`} style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}>
+ <div className="exploreView-doc" style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}>
{StrCast(NewLightboxView.LightboxDoc?.title)}
</div>
</div>
);
-};
+}
diff --git a/src/client/views/newlightbox/Header/LightboxHeader.tsx b/src/client/views/newlightbox/Header/LightboxHeader.tsx
index 51bfaa4e5..882d28fba 100644
--- a/src/client/views/newlightbox/Header/LightboxHeader.tsx
+++ b/src/client/views/newlightbox/Header/LightboxHeader.tsx
@@ -4,8 +4,8 @@ import { BsBookmark, BsBookmarkFill } from 'react-icons/bs';
import { MdTravelExplore } from 'react-icons/md';
import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
-import { LightboxView } from '../../LightboxView';
import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
import { NewLightboxView } from '../NewLightboxView';
import { EditableText } from '../components/EditableText';
import { getType } from '../utils';
@@ -14,11 +14,11 @@ import { INewLightboxHeader } from './utils';
export function NewLightboxHeader(props: INewLightboxHeader) {
const { height = 100, width } = props;
- const [doc, setDoc] = React.useState<Doc | undefined>(LightboxView.LightboxDoc);
+ const [doc, setDoc] = React.useState<Doc | undefined>(DocumentView.LightboxDoc());
const [editing, setEditing] = React.useState<boolean>(false);
const [title, setTitle] = React.useState<JSX.Element | null>(null);
React.useEffect(() => {
- const lbDoc = LightboxView.LightboxDoc;
+ const lbDoc = DocumentView.LightboxDoc();
setDoc(lbDoc);
if (lbDoc) {
setTitle(
@@ -32,7 +32,7 @@ export function NewLightboxHeader(props: INewLightboxHeader) {
/>
);
}
- }, [LightboxView.LightboxDoc]);
+ }, [DocumentView.LightboxDoc()]);
const [saved, setSaved] = React.useState<boolean>(false);
diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx
index 558ce7e38..c86ddb745 100644
--- a/src/client/views/newlightbox/NewLightboxView.tsx
+++ b/src/client/views/newlightbox/NewLightboxView.tsx
@@ -1,6 +1,5 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -12,7 +11,6 @@ import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { GestureOverlay } from '../GestureOverlay';
-import { LightboxView } from '../LightboxView';
import { DefaultStyleProvider } from '../StyleProvider';
import { DocumentView } from '../nodes/DocumentView';
import { OpenWhere } from '../nodes/OpenWhere';
@@ -108,7 +106,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
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) {
+ if (doc !== DocumentView.LightboxDoc()) {
this._savedState = {
layout_fieldKey: StrCast(doc.layout_fieldKey),
panX: Cast(doc.freeform_panX, 'number', null),
@@ -151,11 +149,12 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
if (NewLightboxView._history?.lastElement().target !== target) NewLightboxView._history?.push({ doc, target });
} else if (!target && NewLightboxView.path.length) {
const saved = NewLightboxView._savedState;
- if (LightboxView.LightboxDoc && saved) {
- LightboxView.LightboxDoc._freeform_panX = saved.panX;
- LightboxView.LightboxDoc._freeform_panY = saved.panY;
- LightboxView.LightboxDoc._freeform_scale = saved.scale;
- LightboxView.LightboxDoc._layout_scrollTop = saved.scrollTop;
+ const lightboxDoc = DocumentView.LightboxDoc();
+ if (lightboxDoc && saved) {
+ lightboxDoc._freeform_panX = saved.panX;
+ lightboxDoc._freeform_panY = saved.panY;
+ lightboxDoc._freeform_scale = saved.scale;
+ lightboxDoc._layout_scrollTop = saved.scrollTop;
}
const pop = NewLightboxView.path.pop();
if (pop) {
@@ -176,7 +175,7 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
NewLightboxView.SetNewLightboxDoc(undefined);
return;
}
- const { doc, target } = NewLightboxView._history?.lastElement();
+ const { doc, target } = NewLightboxView._history?.lastElement() ?? { doc: undefined, target: undefined };
const docView = DocumentView.getLightboxDocumentView(target || doc);
if (docView) {
NewLightboxView._docTarget = target;
@@ -248,14 +247,15 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
@computed
get documentView() {
- if (!LightboxView.LightboxDoc) return null;
+ const lightboxDoc = DocumentView.LightboxDoc();
+ if (!lightboxDoc) return null;
return (
<GestureOverlay isActive>
<DocumentView
ref={action((r: DocumentView | null) => {
NewLightboxView._docView = r !== null ? r : undefined;
})}
- Document={LightboxView.LightboxDoc}
+ Document={lightboxDoc}
PanelWidth={this.newLightboxWidth}
PanelHeight={this.newLightboxHeight}
LayoutTemplate={NewLightboxView.LightboxDocTemplate}
@@ -281,50 +281,14 @@ export class NewLightboxView extends React.Component<LightboxViewProps> {
newLightboxWidth = () => this.props.PanelWidth - 420;
newLightboxHeight = () => this.props.PanelHeight - 140;
newLightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1);
- navBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => (
- <div
- className="newLightboxView-navBtn-frame"
- style={{
- display: display(),
- left,
- width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
- bottom,
- }}>
- <div className="newLightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
- <div style={{ height: 10 }}>{color}</div>
- <FontAwesomeIcon icon={icon as any} size="3x" />
- </div>
- </div>
- );
docFilters = () => NewLightboxView._docFilters || [];
- @action
- stepInto = () => {
- NewLightboxView.path.push({
- doc: LightboxView.LightboxDoc,
- target: NewLightboxView._docTarget,
- future: NewLightboxView._future,
- history: NewLightboxView._history,
- saved: NewLightboxView._savedState,
- });
- const coll = NewLightboxView._docTarget;
- if (coll) {
- const fieldKey = Doc.LayoutFieldKey(coll);
- const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '_annotations'])];
- 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);
- }
- };
- future = () => NewLightboxView._future;
render() {
const newLightboxHeaderHeight = 100;
let downx = 0;
let downy = 0;
- return !LightboxView.LightboxDoc ? null : (
+ return !DocumentView.LightboxDoc() ? null : (
<div
className="newLightboxView-frame"
onPointerDown={e => {
@@ -376,7 +340,7 @@ export class NewLightboxTourBtn extends React.Component<NewLightboxTourBtnProps>
0,
0,
'chevron-down',
- () => (LightboxView.LightboxDoc /* && this.props.future()?.length */ ? '' : 'none'),
+ () => (DocumentView.LightboxDoc() /* && this.props.future()?.length */ ? '' : 'none'),
e => {
e.stopPropagation();
this.props.stepInto();
diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
index 1d502b73f..dc3339cd3 100644
--- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
+++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx
@@ -1,3 +1,7 @@
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable guard-for-in */
import { IconButton, Size, Type } from 'browndash-components';
import * as React from 'react';
import { FaCaretDown, FaCaretUp } from 'react-icons/fa';
@@ -5,17 +9,15 @@ import { GrClose } from 'react-icons/gr';
import { DocListCast, StrListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { StrCast } from '../../../../fields/Types';
-import { LightboxView } from '../../LightboxView';
import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
import { IBounds } from '../ExploreView/utils';
import { NewLightboxView } from '../NewLightboxView';
import { IRecommendation, Recommendation } from '../components';
import { IDocRequest, fetchKeywords, fetchRecommendations } from '../utils';
import './RecommendationList.scss';
-import { IRecommendationList } from './utils';
-export const RecommendationList = (props: IRecommendationList) => {
- const { loading, keywords } = props;
+export function RecommendationList() {
const [loadingKeywords, setLoadingKeywords] = React.useState<boolean>(true);
const [showMore, setShowMore] = React.useState<boolean>(false);
const [keywordsLoc, setKeywordsLoc] = React.useState<string[]>([]);
@@ -25,21 +27,22 @@ export const RecommendationList = (props: IRecommendationList) => {
React.useEffect(() => {
const getKeywords = async () => {
- let text = StrCast(LightboxView.LightboxDoc?.text);
+ const text = StrCast(DocumentView.LightboxDoc()?.text);
console.log('[1] fetching keywords');
const response = await fetchKeywords(text, 5, true);
console.log('[2] response:', response);
const kw = response.keywords;
console.log(kw);
NewLightboxView.SetKeywords(kw);
- if (LightboxView.LightboxDoc) {
+ const lightboxDoc = DocumentView.LightboxDoc();
+ if (lightboxDoc) {
console.log('setting keywords on doc');
- LightboxView.LightboxDoc.keywords = new List<string>(kw);
+ lightboxDoc.keywords = new List<string>(kw);
setKeywordsLoc(NewLightboxView.Keywords);
}
setLoadingKeywords(false);
};
- let keywordsList = StrListCast(LightboxView.LightboxDoc!.keywords);
+ const keywordsList = StrListCast(DocumentView.LightboxDoc()!.keywords);
if (!keywordsList || keywordsList.length < 2) {
setLoadingKeywords(true);
getKeywords();
@@ -57,14 +60,14 @@ export const RecommendationList = (props: IRecommendationList) => {
console.log('fetching recommendations');
let query = 'undefined';
if (keywordsLoc) query = keywordsLoc.join(',');
- let src = StrCast(NewLightboxView.LightboxDoc?.text);
- let dashDocs: IDocRequest[] = [];
+ const src = StrCast(NewLightboxView.LightboxDoc?.text);
+ const dashDocs: IDocRequest[] = [];
// get linked docs
- let linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links);
+ const linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links);
console.log('linked docs', linkedDocs);
// get context docs (docs that are also in the collection)
- // let contextDocs: Doc[] = DocListCast(DocCast(LightboxView.LightboxDoc?.context).data)
- // let docId = LightboxView.LightboxDoc && LightboxView.LightboxDoc[Id]
+ // let contextDocs: Doc[] = DocListCast(DocCast(DocumentView.LightboxDoc()?.context).data)
+ // let docId = DocumentView.LightboxDoc() && DocumentView.LightboxDoc()[Id]
// console.log("context docs", contextDocs)
// contextDocs.forEach((doc: Doc) => {
// if (docId !== doc[Id]){
@@ -79,10 +82,8 @@ export const RecommendationList = (props: IRecommendationList) => {
console.log('dash docs', dashDocs);
if (query !== undefined) {
const response = await fetchRecommendations(src, query, [], true);
- const num_recs = response.num_recommendations;
- const recs = response.recommendations;
- const keywords = response.keywords;
- const response_bounds: IBounds = {
+ const theRecs = response.recommendations;
+ const responseBounds: IBounds = {
max_x: response.max_x,
max_y: response.max_y,
min_x: response.min_x,
@@ -93,22 +94,23 @@ export const RecommendationList = (props: IRecommendationList) => {
// setKeywordsLoc(NewLightboxView.Keywords);
// }
// console.log(response_bounds)
- NewLightboxView.SetBounds(response_bounds);
+ NewLightboxView.SetBounds(responseBounds);
const recommendations: IRecommendation[] = [];
- for (const key in recs) {
+ // eslint-disable-next-line no-restricted-syntax
+ for (const key in theRecs) {
console.log(key);
- const title = recs[key].title;
- const url = recs[key].url;
- const type = recs[key].type;
- const text = recs[key].text;
- const transcript = recs[key].transcript;
- const previewUrl = recs[key].previewUrl;
- const embedding = recs[key].embedding;
- const distance = recs[key].distance;
- const source = recs[key].source;
- const related_concepts = recs[key].related_concepts;
- const docId = recs[key].doc_id;
- related_concepts.length >= 1 &&
+ const { title } = theRecs[key];
+ const { url } = theRecs[key];
+ const { type } = theRecs[key];
+ const { text } = theRecs[key];
+ const { transcript } = theRecs[key];
+ const { previewUrl } = theRecs[key];
+ const { embedding } = theRecs[key];
+ const { distance } = theRecs[key];
+ const { source } = theRecs[key];
+ const { related_concepts: relatedConcepts } = theRecs[key];
+ const docId = theRecs[key].doc_id;
+ relatedConcepts.length >= 1 &&
recommendations.push({
title: title,
data: url,
@@ -119,14 +121,15 @@ export const RecommendationList = (props: IRecommendationList) => {
embedding: embedding,
distance: Math.round(distance * 100) / 100,
source: source,
- related_concepts: related_concepts,
+ related_concepts: relatedConcepts,
docId: docId,
});
}
recommendations.sort((a, b) => {
if (a.distance && b.distance) {
return a.distance - b.distance;
- } else return 0;
+ }
+ return 0;
});
console.log('[rec]: ', recommendations);
NewLightboxView.SetRecs(recommendations);
@@ -138,12 +141,12 @@ export const RecommendationList = (props: IRecommendationList) => {
return (
<div
- className={`recommendationlist-container`}
+ className="recommendationlist-container"
onPointerDown={e => {
e.stopPropagation();
}}>
- <div className={`header`}>
- <div className={`title`}>Recommendations</div>
+ <div className="header">
+ <div className="title">Recommendations</div>
{NewLightboxView.LightboxDoc && (
<div style={{ fontSize: 10 }}>
The recommendations are produced based on the text in the document{' '}
@@ -153,65 +156,58 @@ export const RecommendationList = (props: IRecommendationList) => {
. The following keywords are used to fetch the recommendations.
</div>
)}
- <div className={`lb-label`}>Keywords</div>
+ <div className="lb-label">Keywords</div>
{loadingKeywords ? (
- <div className={`keywords`}>
+ <div className="keywords">
<div className={`keyword ${loadingKeywords && 'loading'}`} />
<div className={`keyword ${loadingKeywords && 'loading'}`} />
<div className={`keyword ${loadingKeywords && 'loading'}`} />
<div className={`keyword ${loadingKeywords && 'loading'}`} />
</div>
) : (
- <div className={`keywords`}>
+ <div className="keywords">
{keywordsLoc &&
- keywordsLoc.map((word, ind) => {
- return (
- <div className={`keyword`}>
- {word}
- <IconButton
- type={Type.PRIM}
- size={Size.XSMALL}
- color={Colors.DARK_GRAY}
- icon={<GrClose />}
- onClick={() => {
- let kw = keywordsLoc;
- kw.splice(ind);
- NewLightboxView.SetKeywords(kw);
- }}
- />
- </div>
- );
- })}
+ keywordsLoc.map((word, ind) => (
+ <div className="keyword">
+ {word}
+ <IconButton
+ type={Type.PRIM}
+ size={Size.XSMALL}
+ color={Colors.DARK_GRAY}
+ icon={<GrClose />}
+ onClick={() => {
+ const kw = keywordsLoc;
+ kw.splice(ind);
+ NewLightboxView.SetKeywords(kw);
+ }}
+ />
+ </div>
+ ))}
</div>
)}
{!showMore ? (
<div
- className={`lb-caret`}
+ className="lb-caret"
onClick={() => {
setShowMore(true);
}}>
More <FaCaretDown />
</div>
) : (
- <div className={`more`}>
+ <div className="more">
<div
- className={`lb-caret`}
+ className="lb-caret"
onClick={() => {
setShowMore(false);
}}>
Less <FaCaretUp />
</div>
- <div className={`lb-label`}>Type</div>
- <div className={`lb-label`}>Sources</div>
+ <div className="lb-label">Type</div>
+ <div className="lb-label">Sources</div>
</div>
)}
</div>
- <div className={`recommendations`}>
- {recs &&
- recs.map((rec: IRecommendation) => {
- return <Recommendation {...rec} />;
- })}
- </div>
+ <div className="recommendations">{recs && recs.map((rec: IRecommendation) => <Recommendation {...rec} />)}</div>
</div>
);
-};
+}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 685a5aca4..62c4cc61a 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -296,9 +296,9 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
<div style={{ position: 'absolute', width: this.PanelWidth(), height: this.PanelHeight(), background: 'lightGreen' }} />
) : (
<DocumentView
- parent={this}
// eslint-disable-next-line react/jsx-props-no-spreading
{...OmitKeys(this._props,this.WrapperKeys.map(val => val.lower)).omit} // prettier-ignore
+ parent={this}
DataTransition={this.DataTransition}
LocalRotation={this.localRotation}
CollectionFreeFormDocumentView={this.returnThis}
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 474d54119..e1d16549c 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -13,7 +13,7 @@ import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { dropActionType } from '../../util/DropActionTypes';
import { undoBatch } from '../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import './ComparisonBox.scss';
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 15187b4e4..9ca63194c 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -18,7 +18,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { UndoManager, undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { PinProps } from '../../PinFuncs';
import { SidebarAnnos } from '../../SidebarAnnos';
@@ -41,7 +41,7 @@ export enum DataVizView {
}
@observer
-export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
+export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
private _marqueeref = React.createRef<MarqueeAnnotator>();
private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index ec9db8480..18529a429 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -2,7 +2,6 @@
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import JsxParser from 'react-jsx-parser';
import * as XRegExp from 'xregexp';
import { OmitKeys } from '../../../ClientUtils';
import { Without, emptyPath } from '../../../Utils';
@@ -11,56 +10,15 @@ import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { ScriptField } from '../../../fields/ScriptField';
import { Cast, DocCast, StrCast } from '../../../fields/Types';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { InkingStroke } from '../InkingStroke';
-import { ObservableReactComponent } from '../ObservableReactComponent';
-import { CollectionCalendarView } from '../collections/CollectionCalendarView';
-import { CollectionDockingView } from '../collections/CollectionDockingView';
-import { CollectionView } from '../collections/CollectionView';
-import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
-import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
-import { SchemaRowBox } from '../collections/collectionSchema/SchemaRowBox';
-import { SearchBox } from '../search/SearchBox';
-import { AudioBox } from './AudioBox';
-import { ComparisonBox } from './ComparisonBox';
-import { DataVizBox } from './DataVizBox/DataVizBox';
+import { ObservableReactComponent, ObserverJsxParser } from '../ObservableReactComponent';
import './DocumentView.scss';
-import { EquationBox } from './EquationBox';
-import { FieldView, FieldViewProps } from './FieldView';
-import { FontIconBox } from './FontIconBox/FontIconBox';
-import { FunctionPlotBox } from './FunctionPlotBox';
-import { ImageBox } from './ImageBox';
-import { KeyValueBox } from './KeyValueBox';
-import { LabelBox } from './LabelBox';
-import { LinkBox } from './LinkBox';
-import { LoadingBox } from './LoadingBox';
-import { MapBox } from './MapBox/MapBox';
-import { MapPushpinBox } from './MapBox/MapPushpinBox';
-import { PDFBox } from './PDFBox';
-import { PhysicsSimulationBox } from './PhysicsBox/PhysicsSimulationBox';
-import { RecordingBox } from './RecordingBox';
-import { ScreenshotBox } from './ScreenshotBox';
-import { ScriptingBox } from './ScriptingBox';
-import { VideoBox } from './VideoBox';
-import { WebBox } from './WebBox';
-import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { ImportElementBox } from './importBox/ImportElementBox';
-import { PresBox } from './trails/PresBox';
-import { PresElementBox } from './trails/PresElementBox';
+import { FieldViewProps } from './FieldView';
type BindingProps = Without<FieldViewProps, 'fieldKey'>;
export interface JsxBindings {
props: BindingProps;
}
-class ObserverJsxParser1 extends JsxParser {
- constructor(props: any) {
- super(props);
- observer(this as any);
- }
-}
-
-export const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
-
interface HTMLtagProps {
Document: Doc;
htmltag: string;
@@ -116,6 +74,15 @@ export interface DocumentContentsViewProps extends FieldViewProps {
}
@observer
export class DocumentContentsView extends ObservableReactComponent<DocumentContentsViewProps> {
+ private static DefaultLayoutString: string;
+ /**
+ * Set of all available rendering componets for Docs (e.g., ImageBox, CollectionFreeFormView, etc)
+ */
+ private static Components: { [key: string]: any };
+ public static Init(defaultLayoutString: string, components:{ [key: string]: any}) {
+ DocumentContentsView.DefaultLayoutString = defaultLayoutString;
+ DocumentContentsView.Components = components;
+ }
constructor(props: any) {
super(props);
makeObservable(this);
@@ -124,11 +91,11 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
TraceMobx();
if (this._props.LayoutTemplateString) return this._props.LayoutTemplateString;
if (!this.layoutDoc) return '<p>awaiting layout</p>';
- if (this._props.layoutFieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, KeyValueBox.LayoutString());
+ if (this._props.layoutFieldKey === 'layout_keyValue') return StrCast(this._props.Document.layout_keyValue, DocumentContentsView.DefaultLayoutString);
const tempLayout = DocCast(this.layoutDoc[this.layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(this.layoutDoc.layout_fieldKey, 'layout')]);
const layoutDoc = tempLayout ?? this.layoutDoc;
const layout = Cast(layoutDoc[layoutDoc === this._props.Document && this._props.layoutFieldKey ? this._props.layoutFieldKey : StrCast(layoutDoc.layout_fieldKey, 'layout')], 'string');
- if (layout === undefined) return this._props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString();
+ if (layout === undefined) return this._props.Document.data ? "<FieldView {...props} fieldKey='data' />" : DocumentContentsView.DefaultLayoutString;
if (typeof layout === 'string') return layout;
return '<p>Loading layout</p>';
}
@@ -223,42 +190,7 @@ export class DocumentContentsView extends ObservableReactComponent<DocumentConte
key={42}
blacklistedAttrs={emptyPath}
renderInWrapper={false}
- components={{
- FormattedTextBox,
- ImageBox,
- FontIconBox,
- LabelBox,
- EquationBox,
- FieldView,
- CollectionFreeFormView,
- CollectionDockingView,
- CollectionSchemaView,
- CollectionCalendarView,
- CollectionView,
- WebBox,
- KeyValueBox,
- PDFBox,
- VideoBox,
- AudioBox,
- RecordingBox,
- PresBox,
- PresElementBox,
- SearchBox,
- FunctionPlotBox,
- InkingStroke,
- LinkBox,
- ScriptingBox,
- MapBox,
- ScreenshotBox,
- DataVizBox,
- HTMLtag,
- ComparisonBox,
- LoadingBox,
- PhysicsSimulationBox,
- SchemaRowBox,
- ImportElementBox,
- MapPushpinBox,
- }}
+ components={DocumentContentsView.Components}
bindings={bindings}
jsx={layoutFrame}
showWarnings
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index dc597e5ff..8df28a770 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -8,7 +8,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Bounce, Fade, Flip, JackInTheBox, Roll, Rotate, Zoom } from 'react-awesome-reveal';
import { ClientUtils, DivWidth, isTargetChildOf as isParentOf, lightOrDark, returnFalse, returnVal, simulateMouseClick } from '../../../ClientUtils';
-import { Utils, emptyFunction, emptyPath } from '../../../Utils';
+import { Utils, emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../fields/Doc';
import { AclAdmin, AclEdit, AclPrivate, Animation, AudioPlay, DocData, DocViews } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -35,20 +35,21 @@ import { SnappingManager } from '../../util/SnappingManager';
import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent, ViewBoxInterface } from '../DocComponent';
+import { DocComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
import { FieldsDropdown } from '../FieldsDropdown';
-import { LightboxView } from '../LightboxView';
+import { ObserverJsxParser } from '../ObservableReactComponent';
import { PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
-import { DocumentContentsView, ObserverJsxParser } from './DocumentContentsView';
+import { ViewBoxInterface } from '../ViewBoxInterface';
+import { DocumentContentsView } from './DocumentContentsView';
import { DocumentLinksButton } from './DocumentLinksButton';
import './DocumentView.scss';
import { FieldViewProps, FieldViewSharedProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
-import { OpenWhere } from './OpenWhere';
+import { OpenWhere, OpenWhereMod } from './OpenWhere';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
-import { PresEffect, PresEffectDirection } from './trails';
+import { PresEffect, PresEffectDirection } from './trails/PresEnums';
export interface DocumentViewProps extends FieldViewSharedProps {
hideDecorations?: boolean; // whether to suppress all DocumentDecorations when doc is selected
@@ -77,7 +78,7 @@ export interface DocumentViewProps extends FieldViewSharedProps {
dragStarting?: () => void;
dragEnding?: () => void;
- parent?: any;
+ parent?: any; // parent React component view (see CollectionFreeFormDocumentView)
}
@observer
export class DocumentViewInternal extends DocComponent<FieldViewProps & DocumentViewProps>() {
@@ -320,7 +321,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
if (this.onDoubleClickHandler?.script) {
UndoManager.RunInBatch(() => 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');
+ UndoManager.RunInBatch(() => this._props.addDocTab(this.Document, OpenWhere.lightbox), 'double tap');
DocumentView.DeselectAll();
Doc.UnBrushDoc(this.Document);
} else {
@@ -561,7 +562,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
if (this._props.renderDepth === 0) {
- appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => LightboxView.Instance.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
+ appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => DocumentView.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
}
appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), icon: 'eye' });
!Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
@@ -980,6 +981,29 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
@observer
export class DocumentView extends DocComponent<DocumentViewProps>() {
public static ROOT_DIV = 'documentView-effectsWrapper';
+ public static addSplit: (Doc: Doc, where: OpenWhereMod) => void;
+ // Lightbox
+ public static _lightboxDoc: () => Doc | undefined;
+ public static _lightboxContains: (view?: DocumentView) => boolean | undefined;
+ public static _setLightboxDoc: (doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) => boolean;
+ /**
+ * @returns The Doc, if any, being displayed in the lightbox
+ */
+ public static readonly LightboxDoc = () => DocumentView._lightboxDoc?.();
+ /**
+ * @param view
+ * @returns whether 'view' is anywhere in the rendering hierarchy of the lightbox
+ */
+ public static readonly LightboxContains = (view?: DocumentView) => DocumentView._lightboxContains?.(view);
+ /**
+ * Sets the root Doc to render in the lightbox view.
+ * @param doc
+ * @param target a Doc within 'doc' to focus on (useful for freeform collections)
+ * @param future a list of Docs to step through with the arrow buttons of the lightbox
+ * @param layoutTemplate a template to apply to 'doc' to render it.
+ * @returns success flag which is currently always true
+ */
+ public static readonly SetLightboxDoc = (doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) => DocumentView._setLightboxDoc(doc, target, future, layoutTemplate);
// Sharing Manager
public static ShareOpen: (target?: DocumentView, targetDoc?: Doc) => void;
// LinkFollower
@@ -998,6 +1022,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public static addView: (dv: DocumentView) => void | undefined;
public static removeView: (dv: DocumentView) => void | undefined;
public static addViewRenderedCb: (doc: Opt<Doc>, func: (dv: DocumentView) => any) => boolean;
+ public static getViews = (doc?: Doc) => Array.from(doc?.[DocViews] ?? []) as DocumentView[];
public static getFirstDocumentView: (toFind: Doc) => DocumentView | undefined;
public static getDocumentView: (target: Doc | undefined, preferredCollection?: DocumentView) => Opt<DocumentView>;
public static getContextPath: (doc: Opt<Doc>, includeExistingViews?: boolean) => Doc[];
@@ -1032,8 +1057,8 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
public static UniquifyId(inLightbox: boolean | undefined, id: string) {
return (inLightbox ? 'lightbox-' : '') + id;
}
- public ViewGuid = DocumentView.UniquifyId(LightboxView.Contains(this), Utils.GenerateGuid()); // a unique id associated with the main <div>. used by LinkBox's Xanchor to find the arrowhead locations.
- public DocUniqueId = DocumentView.UniquifyId(LightboxView.Contains(this), this.Document[Id]);
+ public ViewGuid = DocumentView.UniquifyId(DocumentView.LightboxContains(this), Utils.GenerateGuid()); // a unique id associated with the main <div>. used by LinkBox's Xanchor to find the arrowhead locations.
+ public DocUniqueId = DocumentView.UniquifyId(DocumentView.LightboxContains(this), this.Document[Id]);
constructor(props: DocumentViewProps) {
super(props);
@@ -1401,6 +1426,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
}}>
<DocumentViewInternal
{...this._props}
+ parent={undefined}
fieldKey={this.LayoutFieldKey}
DataTransition={this.DataTransition}
DocumentView={this.selfView}
@@ -1451,58 +1477,52 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
const docId = ClientUtils.CurrentUserEmail() + Doc.GetProto(linkAnchor)[Id] + '-pivotish';
// prettier-ignore
DocServer.GetRefField(docId).then(docx =>
- LightboxView.Instance.SetLightboxDoc(
+ DocumentView.SetLightboxDoc(
(docx as Doc) ?? // reuse existing pivot view of documents, or else create a new collection
Docs.Create.StackingDocument([], { title: linkAnchor.title + '-pivot', _width: 500, _height: 500, target: linkAnchor, onViewMounted: ScriptField.MakeScript('updateLinkCollection(this, this.target)') }, docId)
)
);
}
-}
-
-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);
- });
+ // eslint-disable-next-line default-param-last
+ public static FocusOrOpen(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.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();
}
- 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();
- documentView.select(false);
+ScriptingGlobals.add(function DocFocusOrOpen(docIn: Doc, optionsIn?: FocusViewOptions, containingDoc?: Doc) {
+ return DocumentView.FocusOrOpen(docIn, optionsIn, containingDoc);
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function deiconifyViewToLightbox(documentView: DocumentView) {
- LightboxView.Instance.AddDocTab(documentView.Document, OpenWhere.lightbox, 'layout'); // , 0);
+ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
+ documentView.iconify();
+ documentView.select(false);
});
// eslint-disable-next-line prefer-arrow-callback
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 32d08fbe7..1f5c9b84b 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -11,7 +11,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { undoBatch } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
-import { LightboxView } from '../LightboxView';
+import { DocumentView } from './DocumentView';
import './EquationBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
import EquationEditor from './formattedText/EquationEditor';
@@ -30,7 +30,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- if (Doc.SelectOnLoad === this.Document && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) {
+ if (Doc.SelectOnLoad === this.Document && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) {
this._props.select(false);
this._ref.current!.mathField.focus();
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 66b134889..3f351a072 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -10,8 +10,8 @@ import { ScriptField } from '../../../fields/ScriptField';
import { WebField } from '../../../fields/URLField';
import { dropActionType } from '../../util/DropActionTypes';
import { Transform } from '../../util/Transform';
-import { ViewBoxInterface } from '../DocComponent';
import { PinProps } from '../PinFuncs';
+import { ViewBoxInterface } from '../ViewBoxInterface';
import { DocumentView } from './DocumentView';
import { FocusViewOptions } from './FocusViewOptions';
import { OpenWhere } from './OpenWhere';
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 397cc15ed..0956be3e9 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -13,8 +13,7 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DefaultStyleProvider } from '../StyleProvider';
-import { returnEmptyDocViewList } from './DocumentView';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import './KeyValueBox.scss';
import './KeyValuePair.scss';
import { OpenWhere } from './OpenWhere';
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 6caa38a7f..8d6ae9f73 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -16,7 +16,6 @@ import { DocumentType } from '../../documents/DocumentTypes';
import { SnappingManager } from '../../util/SnappingManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { EditableView } from '../EditableView';
-import { LightboxView } from '../LightboxView';
import { StyleProp } from '../StyleProp';
import { ComparisonBox } from './ComparisonBox';
import { DocumentView } from './DocumentView';
@@ -52,7 +51,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
this._disposers.deleting = reaction(
- () => !this.anchor1 && !this.anchor2 && this.DocumentView?.() && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView!())),
+ () => !this.anchor1 && !this.anchor2 && this.DocumentView?.() && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView!())),
empty => {
if (empty) {
this._hackToSeeIfDeleted = setTimeout(() => {
@@ -110,7 +109,7 @@ export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
const getAnchor = (field: FieldResult): Element[] => {
const docField = DocCast(field);
const doc = docField?.layout_unrendered ? DocCast(docField.annotationOn, docField) : docField;
- const ele = document.getElementById(DocumentView.UniquifyId(LightboxView.Contains(this.DocumentView?.()), doc[Id]));
+ const ele = document.getElementById(DocumentView.UniquifyId(DocumentView.LightboxContains(this.DocumentView?.()), doc[Id]));
if (ele?.className === 'linkBox-label') foundParent = true;
if (ele?.getBoundingClientRect().width) return [ele];
const eles = Array.from(document.getElementsByClassName(doc[Id])).filter(el => el?.getBoundingClientRect().width);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index ac8010f11..d7687e03e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -23,7 +23,7 @@ import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { UndoManager, undoable } from '../../../util/UndoManager';
-import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { PinDocView, PinProps } from '../../PinFuncs';
import { SidebarAnnos } from '../../SidebarAnnos';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
@@ -63,7 +63,7 @@ type PopupInfo = {
};
@observer
-export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implements ViewBoxInterface {
+export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(MapBox, fieldKey);
}
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index e46e40bfe..07381c7d0 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -50,9 +50,9 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
this.result = info;
this.dataDoc.type = DocumentType.VID;
- this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
+ this.dataDoc[this.fieldKey + '_recorded'] = this.dataDoc.layout; // save the recording layout to allow re-recording later
+ this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey); // then convert the recording box to a video
this.dataDoc[this._props.fieldKey] = new VideoField(this.result.accessPaths.client);
- this.dataDoc[this.fieldKey + '_recorded'] = true;
// stringify the presentation and store it
if (presentation?.movements) {
const presCopy = { ...presentation };
@@ -143,18 +143,13 @@ export class RecordingBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static resumeWorkspaceReplaying(doc: Doc) {
const docView = DocumentView.getDocumentView(doc);
- if (docView?.ComponentView instanceof VideoBox) {
- docView.ComponentView.Play();
- }
+ docView?.ComponentView?.Play?.();
Doc.UserDoc().workspaceReplayingState = mediaState.Playing;
}
public static pauseWorkspaceReplaying(doc: Doc) {
const docView = DocumentView.getDocumentView(doc);
- const videoBox = docView?.ComponentView as VideoBox;
- if (videoBox) {
- videoBox.Pause();
- }
+ docView?.ComponentView?.Pause?.();
Doc.UserDoc().workspaceReplayingState = mediaState.Paused;
}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3b1815f8a..fe7600fa3 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -17,7 +17,6 @@ import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DocUtils, FollowLinkScript } from '../../documents/DocUtils';
import { dropActionType } from '../../util/DropActionTypes';
-import { ReplayMovements } from '../../util/ReplayMovements';
import { undoBatch } from '../../util/UndoManager';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
@@ -32,7 +31,6 @@ import { StyleProp } from '../StyleProp';
import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
-import { RecordingBox } from './RecordingBox';
import './VideoBox.scss';
/**
@@ -101,18 +99,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return field?.url.href ?? vfield?.url.href ?? '';
}
- // returns the presentation data if it exists, null otherwise
- @computed get presentation() {
- const data = this.dataDoc[this.fieldKey + '_presentation'];
- return data ? JSON.parse(StrCast(data)) : null;
- }
-
@computed private get timeline() {
return this._stackedTimeline;
}
private get transition() {
return this._clicking ? 'left 0.5s, width 0.5s, height 0.5s' : '';
} // css transition for hiding/showing timeline
+
public get player(): HTMLVideoElement | null {
return this._videoRef;
}
@@ -122,10 +115,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._props.setContentViewBox?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
this.player && this.setPlayheadTime(this.timeline?.clipStart || 0);
document.addEventListener('keydown', this.keyEvents, true);
-
- if (this.presentation) {
- ReplayMovements.Instance.setVideoBox(this);
- }
}
componentWillUnmount() {
@@ -134,12 +123,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.Pause();
Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
document.removeEventListener('keydown', this.keyEvents, true);
-
- if (this.presentation) {
- ReplayMovements.Instance.removeVideoBox();
- }
}
+ override PlayerTime = () => this.player?.currentTime;
+ override Pause = (update: boolean = true) => {
+ this.pause(update);
+ !this._keepCurrentlyPlaying && this.removeCurrentlyPlaying();
+ };
+
// handles key events, when timeline scrubs fade controls
@action
keyEvents = (e: KeyboardEvent) => {
@@ -230,10 +221,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
this._playRegionTimer = undefined;
};
- @action Pause = (update: boolean = true) => {
- this.pause(update);
- !this._keepCurrentlyPlaying && this.removeCurrentlyPlaying();
- };
// toggles video full screen
@action public FullScreen = () => {
@@ -518,11 +505,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
icon: 'expand-arrows-alt',
});
// if the videobox was turned from a recording box
- if (this.dataDoc[this.fieldKey + '_recorded'] === true) {
+ if (this.dataDoc[this.fieldKey + '_recorded']) {
subitems.push({
description: 'Recreate recording',
event: () => {
- this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
+ this.dataDoc.layout = StrCast(this.dataDoc[this.fieldKey + '_recorded']); // restore the saed recording layout
// delete assoicated video data
this.dataDoc[this._props.fieldKey] = '';
this.dataDoc[this.fieldKey + '_duration'] = '';
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index b6ef36974..8835ea5e7 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -28,9 +28,8 @@ import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
+import { ViewBoxAnnotatableComponent } 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';
@@ -38,6 +37,7 @@ import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
import { PinDocView, PinProps } from '../PinFuncs';
import { SidebarAnnos } from '../SidebarAnnos';
import { StyleProp } from '../StyleProp';
+import { ViewBoxInterface } from '../ViewBoxInterface';
import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
@@ -623,7 +623,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
clearStyleSheetRules(WebBox.webStyleSheet);
this._scrollTimer = undefined;
const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
- if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()))) {
+ if (!LinkInfo.Instance?.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) {
this.layoutDoc.thumb = undefined;
this.layoutDoc.thumbScrollTop = undefined;
this.layoutDoc.thumbNativeWidth = undefined;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 321fdbb91..e354aedb7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -10,7 +11,7 @@ import { inputRules } from 'prosemirror-inputrules';
import { keymap } from 'prosemirror-keymap';
import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transaction } from 'prosemirror-state';
-import { EditorView } from 'prosemirror-view';
+import { EditorView, NodeViewConstructor } from 'prosemirror-view';
import * as React from 'react';
import { BsMarkdownFill } from 'react-icons/bs';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, StopEvent } from '../../../../ClientUtils';
@@ -45,7 +46,6 @@ import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { ViewBoxAnnotatableComponent } 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';
@@ -58,11 +58,6 @@ 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';
-import { EquationView } from './EquationView';
-import { FootnoteView } from './FootnoteView';
import './FormattedTextBox.scss';
import { findLinkMark, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { buildKeymap, updateBullets } from './ProsemirrorExampleTransfer';
@@ -70,7 +65,6 @@ import { removeMarkWithAttrs } from './prosemirrorPatches';
import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
import { schema } from './schema_rts';
-import { SummaryView } from './SummaryView';
// import * as applyDevTools from 'prosemirror-dev-tools';
export interface FormattedTextBoxProps extends FieldViewProps {
@@ -82,10 +76,15 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(FormattedTextBox, fieldStr);
}
- public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
- // eslint-disable-next-line no-use-before-define
- public static Instance: FormattedTextBox;
- public static LiveTextUndo: UndoManager.Batch | undefined;
+ private static nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor };
+ /**
+ * Initialize the class with all the plugin node view components
+ * @param nodeViews prosemirror plugins that render a custom UI for specific node types
+ */
+ public static Init(nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor }) {
+ FormattedTextBox.nodeViews = nodeViews;
+ }
+ public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch when typing a new text note into a collection
static _globalHighlightsCache: string = '';
static _globalHighlights = new ObservableSet<string>(['Audio Tags', 'Text from Others', 'Todo Items', 'Important Items', 'Disagree Items', 'Ignore Items']);
static _highlightStyleSheet: any = addStyleSheet();
@@ -189,7 +188,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
constructor(props: FormattedTextBoxProps) {
super(props);
makeObservable(this);
- FormattedTextBox.Instance = this;
this._recordingStart = Date.now();
}
@@ -1435,14 +1433,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return true;
},
dispatchTransaction: this.dispatchTransaction,
- nodeViews: {
- dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); }, // prettier-ignore
- dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, self); }, // prettier-ignore
- dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, self); }, // prettier-ignore
- equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, self); }, // prettier-ignore
- summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); }, // prettier-ignore
- footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); }, // prettier-ignore
- },
+ nodeViews: FormattedTextBox.nodeViews(this),
clipboardTextSerializer: this.clipboardTextSerializer,
handlePaste: this.handlePaste,
});
@@ -1463,7 +1454,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
(this._editorView as any).TextView = this;
}
- const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, Doc.SelectOnLoad) && (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.()));
+ const selectOnLoad = Doc.AreProtosEqual(this._props.TemplateDataDocument ?? this.Document, Doc.SelectOnLoad) && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()));
const selLoadChar = FormattedTextBox.SelectOnLoadChar;
if (selectOnLoad) {
Doc.SetSelectOnLoad(undefined);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6b4f5e073..75492d2f9 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -24,14 +24,12 @@ import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SerializationHelper } from '../../../util/SerializationHelper';
import { SnappingManager } from '../../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
-import { CollectionDockingView } from '../../collections/CollectionDockingView';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
import { CollectionFreeFormPannableContents } from '../../collections/collectionFreeForm/CollectionFreeFormPannableContents';
import { CollectionView } from '../../collections/CollectionView';
import { TreeView } from '../../collections/TreeView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { LightboxView } from '../../LightboxView';
import { pinDataTypes as dataTypes } from '../../PinFuncs';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
@@ -46,6 +44,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
public static LayoutString(fieldKey: string) {
return FieldView.LayoutString(PresBox, fieldKey);
}
+ private static _getTabDocs: () => Doc[];
+ public static Init(tabDocs: () => Doc[]) {
+ PresBox._getTabDocs = tabDocs;
+ }
static navigateToDocScript: ScriptField;
constructor(props: FieldViewProps) {
@@ -679,16 +681,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (activeItem.presentation_openInLightbox) {
const context = DocCast(targetDoc.annotationOn) ?? targetDoc;
if (!DocumentView.getLightboxDocumentView(context)) {
- LightboxView.Instance.SetLightboxDoc(context);
+ DocumentView.SetLightboxDoc(context);
}
}
if (targetDoc) {
if (activeItem.presentation_targetDoc instanceof Doc) activeItem.presentation_targetDoc[Animation] = undefined;
- DocumentView.addViewRenderedCb(LightboxView.LightboxDoc, () => {
+ DocumentView.addViewRenderedCb(DocumentView.LightboxDoc(), () => {
// if target or the doc it annotates is not in the lightbox, then close the lightbox
if (!DocumentView.getLightboxDocumentView(DocCast(targetDoc.annotationOn) ?? targetDoc)) {
- LightboxView.Instance.SetLightboxDoc(undefined);
+ DocumentView.SetLightboxDoc(undefined);
}
DocumentView.showDocument(targetDoc, options, finished);
});
@@ -789,7 +791,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
default:
}
});
- LightboxView.Instance.SetLightboxDoc(undefined);
+ DocumentView.SetLightboxDoc(undefined);
Doc.RemFromMyOverlay(this.Document);
return PresStatus.Edit;
};
@@ -892,7 +894,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
exitMinimize = () => {
if (Doc.IsInMyOverlay(this.layoutDoc)) {
Doc.RemFromMyOverlay(this.Document);
- CollectionDockingView.AddSplit(this.Document, OpenWhereMod.right);
+ DocumentView.addSplit(this.Document, OpenWhereMod.right);
}
return PresStatus.Edit;
};
@@ -2202,9 +2204,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (freeform && layout) doc = this.createTemplate(layout, title);
if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
if (doc) {
- const tabMap = CollectionDockingView.Instance?.tabMap;
- const docTab = tabMap && Array.from(tabMap).find(tab => tab.DashDoc.type === DocumentType.COL)?.DashDoc;
- const presCollection = DocumentView.getContextPath(this.activeItem).reverse().lastElement().presentation_targetDoc ?? docTab;
+ const docTab = PresBox._getTabDocs().find(tdoc => tdoc.type === DocumentType.COL);
+ const presCollection = DocCast(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) {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 56552c952..ae0838dd5 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -18,8 +18,8 @@ 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 { IRecommendation, Recommendation } from '../newlightbox/components';
+// import { fetchRecommendations } from '../newlightbox/utils';
import { DocumentView } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import './SearchBox.scss';
@@ -142,7 +142,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
@observable _searchString = '';
@observable _docTypeString = 'all';
@observable _results: Map<Doc, string[]> = new Map<Doc, string[]>();
- @observable _recommendations: IRecommendation[] = [];
+ // @observable _recommendations: IRecommendation[] = [];
@observable _pageRanks: Map<Doc, number> = new Map<Doc, number>();
@observable _linkedDocsOut: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@observable _linkedDocsIn: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@@ -375,29 +375,29 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
if (query) {
this.searchCollection(query);
- const response = await fetchRecommendations('', query, [], true);
- const recs = response.recommendations as any[];
- const recommendations: IRecommendation[] = [];
- recs.forEach(rec => {
- const { title, url, type, text, transcript, previewUrl, embedding, distance, source, related_concepts: relatedConcepts, doc_id: docId } = rec;
- recommendations.push({
- title,
- data: url,
- type,
- text,
- transcript,
- previewUrl,
- embedding,
- distance: Math.round(distance * 100) / 100,
- source: source,
- related_concepts: relatedConcepts,
- docId,
- });
- });
- const setRecommendations = action(() => {
- this._recommendations = recommendations;
- });
- setRecommendations();
+ // const response = await fetchRecommendations('', query, [], true);
+ // const recs = response.recommendations as any[];
+ // const recommendations: IRecommendation[] = [];
+ // recs.forEach(rec => {
+ // const { title, url, type, text, transcript, previewUrl, embedding, distance, source, related_concepts: relatedConcepts, doc_id: docId } = rec;
+ // recommendations.push({
+ // title,
+ // data: url,
+ // type,
+ // text,
+ // transcript,
+ // previewUrl,
+ // embedding,
+ // distance: Math.round(distance * 100) / 100,
+ // source: source,
+ // related_concepts: relatedConcepts,
+ // docId,
+ // });
+ // });
+ // const setRecommendations = action(() => {
+ // this._recommendations = recommendations;
+ // });
+ // setRecommendations();
}
};
@@ -470,7 +470,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
});
// eslint-disable-next-line react/jsx-props-no-spreading
- const recommendationsJSX: JSX.Element[] = this._recommendations.map(props => <Recommendation {...props} />);
+ const recommendationsJSX: JSX.Element[] = []; // this._recommendations.map(props => <Recommendation {...props} />);
return (
<div className="searchBox-container" style={{ pointerEvents: 'all', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}>
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 9f97efcce..e558e14e3 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -23,9 +23,9 @@ import { CollectionDockingView } from '../collections/CollectionDockingView';
import { CollectionLinearView } from '../collections/collectionLinear';
import { DashboardView } from '../DashboardView';
import { Colors } from '../global/globalEnums';
-import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DefaultStyleProvider } from '../StyleProvider';
+import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import './TopBar.scss';
/**
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 725221a66..fe044c035 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -102,7 +102,7 @@ export namespace Field {
export function Copy(field: any) {
return field instanceof ObjectField ? ObjectField.MakeCopy(field) : field;
}
- UndoManager.SetFieldPrinter(toJavascriptString);
+ UndoManager.SetFieldPrinter(toString);
}
export type FieldType = number | string | boolean | ObjectField | RefField;
export type Opt<T> = T | undefined;
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index c4f5f7a24..46704eb2b 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -89,6 +89,17 @@ export class InkField extends ObjectField {
[ToString]() {
return 'InkField';
}
+
+ public static getBounds(stroke: InkData, pad?: boolean) {
+ const padding = pad ? [-20000, 20000] : [];
+ const xs = [...padding, ...stroke.map(p => p.X)];
+ const ys = [...padding, ...stroke.map(p => p.Y)];
+ const right = Math.max(...xs);
+ const left = Math.min(...xs);
+ const bottom = Math.max(...ys);
+ const top = Math.min(...ys);
+ return { right, left, bottom, top, width: right - left, height: bottom - top };
+ }
}
ScriptingGlobals.add('InkField', InkField);
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index 4f8058ce4..83b5672b3 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -45,7 +45,7 @@ export class ProxyField<T extends RefField> extends ObjectField {
return Field.toScriptString(this[ToValue]()?.value); // not sure this is quite right since it doesn't recreate a proxy field, but better than 'invalid' ?
}
[ToString]() {
- return 'ProxyField';
+ return Field.toString(this[ToValue]()?.value);
}
@serializable(primitive())
diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts
index 5eb60a2f8..3763dcd2c 100644
--- a/src/fields/RichTextUtils.ts
+++ b/src/fields/RichTextUtils.ts
@@ -164,7 +164,7 @@ export namespace RichTextUtils {
const inlineObjectMap = await parseInlineObjects(document);
const title = document.title!;
const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
- let state = FormattedTextBox.blankState();
+ let state = EditorState.create(new FormattedTextBox({} as any).config);
const structured = parseLists(paragraphs);
let position = 3;
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 9361430cb..a6499c3e3 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -443,7 +443,7 @@ export function containedFieldChangedHandler(container: List<FieldType> | Doc, p
});
lastValue = ObjectField.MakeCopy((container as any)[prop as any]);
},
- prop: 'remove ' + (diff.items?.length ?? 0) + ' items from list',
+ prop: 'remove ' + (diff.items?.length ?? 0) + ' items from list(' + ((container as any)?.title ?? '') + ':' + prop + ')',
},
diff?.items
);