aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-05-05 12:37:09 -0400
committerbobzel <zzzman@gmail.com>2025-05-05 12:37:09 -0400
commit3a733aa0fd24517e83649824dec0fc8bcc0bde43 (patch)
treeac01848cdab3b83582c0b7ab6f3d2b1c8187a24f /src/client/views/collections/collectionFreeForm
parente058d227ccbce47c86b0fa558adb01dfccaf4d60 (diff)
parentd4659e2bd3ddb947683948083232c26fb1227f39 (diff)
Merge branch 'master' into joanne-tutorialagent
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx142
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx34
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx33
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx33
10 files changed, 141 insertions, 154 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 3838852dd..903d92c90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -31,16 +31,14 @@ export class CollectionFreeFormClusters {
get selectDocuments() { return this._view.selectDocuments; } // prettier-ignore
static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
- const doc2Layout = Doc.Layout(doc2);
- const doc1Layout = Doc.Layout(doc1);
const x2 = NumCast(doc2.x) - clusterDistance;
const y2 = NumCast(doc2.y) - clusterDistance;
- const w2 = NumCast(doc2Layout._width) + clusterDistance;
- const h2 = NumCast(doc2Layout._height) + clusterDistance;
+ const w2 = NumCast(doc2._width) + clusterDistance;
+ const h2 = NumCast(doc2._height) + clusterDistance;
const x = NumCast(doc1.x) - clusterDistance;
const y = NumCast(doc1.y) - clusterDistance;
- const w = NumCast(doc1Layout._width) + clusterDistance;
- const h = NumCast(doc1Layout._height) + clusterDistance;
+ const w = NumCast(doc1._width) + clusterDistance;
+ const h = NumCast(doc1._height) + clusterDistance;
return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
}
handlePointerDown(probe: number[]) {
@@ -49,12 +47,11 @@ export class CollectionFreeFormClusters {
.reduce((cluster, cd) => {
const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
if (grouping !== -1) {
- const layoutDoc = Doc.Layout(cd);
const cx = NumCast(cd.x) - this._clusterDistance / 2;
const cy = NumCast(cd.y) - this._clusterDistance / 2;
- const cw = NumCast(layoutDoc._width) + this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ const cw = NumCast(cd._width) + this._clusterDistance;
+ const ch = NumCast(cd._height) + this._clusterDistance;
+ return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
}
return cluster;
}, -1);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 8b9a3e0ec..89d2bf2c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,20 +1,19 @@
import { makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc';
+import { Doc, DocListCast, FieldResult, FieldType } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { StrCast } from '../../../../fields/Types';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { TopBar } from '../../topbar/TopBar';
import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
-import './CollectionFreeFormView.scss';
-import { DocData } from '../../../../fields/DocSymbols';
import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
export interface CollectionFreeFormInfoUIProps {
- Document: Doc;
- LayoutDoc: Doc;
+ Doc: Doc;
+ layoutDoc: Doc;
childDocs: () => Doc[];
close: () => void;
}
@@ -24,7 +23,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
public static Init() {
CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => (
//
- <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} />
+ <CollectionFreeFormInfoUI Doc={doc} layoutDoc={layout} childDocs={childDocs} close={close} />
));
}
_firstDocPos = { x: 0, y: 0 };
@@ -41,7 +40,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
set currState(val) { runInAction(() => {this._currState = val;}); } // prettier-ignore
componentWillUnmount(): void {
- this._props.Document[DocData].backgroundColor = this._originalbackground;
+ this._props.Doc.$backgroundColor = this._originalbackground;
}
setCurrState = (state: infoState) => {
@@ -52,10 +51,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
};
setupStates = () => {
- this._originalbackground = StrCast(this._props.Document[DocData].backgroundColor);
+ this._originalbackground = StrCast(this._props.Doc.$backgroundColor);
// state entry functions
- // const setBackground = (colour: string) => () => {this._props.Document[DocData].backgroundColor = colour;} // prettier-ignore
- // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore
+ // const setBackground = (colour: string) => () => {this._props.Doc.$backgroundColor = colour;} // prettier-ignore
+ // const setOpacity = (opacity: number) => () => {this._props.layoutDoc.opacity = opacity;} // prettier-ignore
// arc transition trigger conditions
const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined);
const numDocs = () => this._props.childDocs().length;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 241a56a88..4d17dedfb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,6 @@
/* eslint-disable no-use-before-define */
import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc';
+import { DocLayout } from '../../../../fields/DocSymbols';
import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
@@ -107,7 +108,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
}
function toNumber(val: FieldResult<FieldType>) {
- return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getMilliseconds() : NumCast(val, Number(StrCast(val)));
+ return val === undefined ? undefined : DateCast(val) ? DateCast(val)!.date.getTime() : NumCast(val, Number(StrCast(val)));
}
export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
@@ -237,7 +238,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
payload: val,
});
val?.docs.forEach((doc, i) => {
- const layoutDoc = Doc.Layout(doc);
+ const layoutDoc = doc[DocLayout];
let wid = pivotAxisWidth;
let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
if (hgt > pivotAxisWidth) {
@@ -249,7 +250,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
y: -y + (pivotAxisWidth - hgt) / 2,
width: wid,
height: hgt,
- backgroundColor: StrCast(layoutDoc.backgroundColor, 'white'),
+ backgroundColor: StrCast(doc.backgroundColor, 'white'),
pair: { layout: doc },
replica: val.replicas[i],
});
@@ -362,7 +363,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
keyDocs.forEach(doc => {
const stack = findStack(x, stacking);
- const layoutDoc = Doc.Layout(doc);
+ const layoutDoc = doc[DocLayout];
let wid = pivotAxisWidth;
let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
if (hgt > pivotAxisWidth) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index bc9dd022c..2683d9439 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -7,7 +7,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
export interface CollectionFreeFormPannableContentsProps {
- Document: Doc;
+ Doc: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
transition: () => string;
@@ -33,7 +33,7 @@ export class CollectionFreeFormPannableContents extends ObservableReactComponent
makeObservable(this);
}
@computed get presPaths() {
- return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null;
+ return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Doc) : null;
}
// rectangle highlight used when following trail/link to a region of a collection that isn't a document
showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index f64c6715b..86310dca3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,7 +13,7 @@ import './CollectionFreeFormView.scss';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@computed protected get cursors(): CursorField[] {
- const { Document } = this.props;
+ const { Document: Document } = this.props;
const cursors = Cast(Document.cursors, listSpec(CursorField));
if (!cursors) {
return [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index cce0ff684..6c47a71b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -41,7 +41,7 @@
transition: background-color 1s ease 0s;
}
.collectionfreeformview-mask {
- mix-blend-mode: multiply;
+ mix-blend-mode: hard-light;
background-color: rgba(0, 0, 0, 0.8);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 89aa53c35..2364fd74a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,14 +1,17 @@
-import { Bezier } from 'bezier-js';
import { Button, Colors, Type } from '@dash/components';
+import { Slider } from '@mui/material';
+import { Bezier } from 'bezier-js';
import { Property } from 'csstype';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
+import { AiOutlineSend } from 'react-icons/ai';
+import ReactLoading from 'react-loading';
import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
-import { DocData, Height, Width } from '../../../../fields/DocSymbols';
+import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
@@ -28,6 +31,7 @@ import { DragManager } from '../../../util/DragManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SettingsManager } from '../../../util/SettingsManager';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -37,26 +41,26 @@ import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
import {
+ ActiveEraserWidth,
ActiveInkArrowEnd,
ActiveInkArrowStart,
- ActiveInkDash,
- ActiveEraserWidth,
- ActiveInkFillColor,
ActiveInkBezierApprox,
ActiveInkColor,
+ ActiveInkDash,
+ ActiveInkFillColor,
ActiveInkWidth,
ActiveIsInkMask,
DocumentView,
SetActiveInkColor,
SetActiveInkWidth,
} from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { OpenWhere } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
-import { StickerPalette } from '../../smartdraw/StickerPalette';
+import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
import { StyleProp } from '../../StyleProp';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeViewType';
@@ -67,11 +71,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import ReactLoading from 'react-loading';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { Slider } from '@mui/material';
-import { AiOutlineSend } from 'react-icons/ai';
-import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -180,10 +179,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get nativeWidth() {
- return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document);
}
@computed get nativeHeight() {
- return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document);
}
@computed get centeringShiftX(): number {
return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / this.nativeDimScaling; // shift so pan position is at center of window for non-overlay collections
@@ -257,8 +256,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
override contentBounds = () => {
const { x, y, r, b } = aggregateBounds(
this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 0),
- NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 0)
+ NumCast(this.layoutDoc._xPadding, NumCast(this.layoutDoc._xMargin, this._props.xPadding ?? 0)),
+ NumCast(this.layoutDoc._yPadding, NumCast(this.layoutDoc._yMargin, this._props.yPadding ?? 0))
);
const [width, height] = [r - x, b - y];
return {
@@ -276,7 +275,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
- panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
fitContentOnce = () => {
const { cx, cy, scale } = this.contentBounds(); // prettier-ignore
this.layoutDoc._freeform_panX = cx;
@@ -285,9 +284,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
// freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
// this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
- panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
- panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
- zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
+ panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
+ panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
+ zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1));
PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
@@ -389,7 +388,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (options.easeFunc) this.setPresEaseFunc(options.easeFunc);
if (this._lightboxDoc) return undefined;
if (options.pointFocus) return this.focusOnPoint(options);
- const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor);
+ const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutDataKey(this.Document)]).includes(anchor);
const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
if (!anchorInCollection && !anchorInChildViews) {
return undefined;
@@ -441,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
docDragData.droppedDocuments.forEach((d, i) => {
- const layoutDoc = Doc.Layout(d);
+ const layoutDoc = d[DocLayout];
const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
@@ -497,6 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}, 'link drop');
onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => {
+ if (this._props.rejectDrop?.(de, this._props.DocumentView?.())) return false;
if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de, de.complete.annoDragData);
if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData);
if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
@@ -561,7 +561,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = this.createInkDoc(points, B);
- if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent';
+ if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc.$backgroundColor = 'transparent';
if (Doc.ActiveInk === InkInkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -639,7 +639,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
const inkDoc = this.createInkDoc(points, B);
['color', 'fillColor', 'stroke_width', 'stroke_dash', 'stroke_bezier'].forEach(field => {
- inkDoc[DocData][field] = stroke.dataDoc[field];
+ inkDoc['$' + field] = stroke.dataDoc[field];
});
this.addDocument(inkDoc);
});
@@ -1260,15 +1260,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
- const docData = doc[DocData];
- docData.title = opts.text;
- docData._width = opts.size;
- docData.ai_drawing_input = opts.text;
- docData.ai_drawing_complexity = opts.complexity;
- docData.ai_drawing_colored = opts.autoColor;
- docData.ai_drawing_size = opts.size;
- docData.ai_drawing_data = gptRes;
- docData.ai = 'gpt';
+ doc.$title = opts.text;
+ doc.$width = opts.size;
+ doc.$ai_drawing_input = opts.text;
+ doc.$ai_drawing_complexity = opts.complexity;
+ doc.$ai_drawing_colored = opts.autoColor;
+ doc.$ai_drawing_size = opts.size;
+ doc.$ai_drawing_data = gptRes;
+ doc.$ai = 'gpt';
this._drawingContainer = doc;
if (x !== undefined && y !== undefined) {
[doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
@@ -1310,7 +1309,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.TriggerUserPanned();
if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
switch (
!e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
@@ -1442,9 +1441,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
- const layoutdoc = Doc.Layout(doc);
const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
- const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height));
+ const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height));
const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
if (scale !== undefined) {
@@ -1490,20 +1488,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @param below whether to place the new text Doc below or to the right of the one being typed into.
* @returns whether the new text doc was created and added successfully
*/
- createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
- const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
- const newDoc = Doc.MakeCopy(textDoc, true);
- newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
- newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
- newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
- DocumentView.SetSelectOnLoad(newDoc);
- return this.addDocument?.(newDoc);
+ createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
+ const textDoc = DocCast(textBox.Document);
+ if (textDoc) {
+ const newDoc = Doc.MakeCopy(textDoc, true);
+ newDoc['$' + Doc.LayoutDataKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
+ newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+ newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+ DocumentView.SetSelectOnLoad(newDoc);
+ return this.addDocument?.(newDoc);
+ }
+ return false;
}, 'copied text note');
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
+ if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
- return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
+ return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab');
}
return undefined;
};
@@ -1543,7 +1544,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
rootSelected={childData ? this.rootSelected : returnFalse}
waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
bringToFront={this.bringToFront}
ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
@@ -1562,6 +1563,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
pinToPres={this._props.pinToPres}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+ rejectDrop={this._props.childRejectDrop}
showTitle={this._props.childlayout_showTitle}
dontRegisterView={this._props.dontRegisterView}
pointerEvents={this.childPointerEventsFunc}
@@ -1590,7 +1592,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
case undefined:
case OpenWhere.lightbox:
- if (this.layoutDoc._isLightbox) {
+ if (this.dataDoc.$isLightbox) {
this._lightboxDoc = docs[0];
return true;
}
@@ -1602,14 +1604,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
const childDoc = pair.layout;
- const childDocLayout = Doc.Layout(childDoc);
const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
- const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
- const { z, zIndex } = childDoc;
+ const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const { z, zIndex, stroke_isInkMask } = childDoc;
const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
const { x, y, autoDim, _width, _height, opacity, _rotation } =
layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
- ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
+ ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
// prettier-ignore
const rotation = Cast(_rotation,'number',
@@ -1627,8 +1629,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
- transition: StrCast(childDocLayout.dataTransition),
- showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
+ transition: StrCast(childDoc.dataTransition),
+ showTags: BoolCast(childDoc.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
pair,
replica: '',
@@ -1721,7 +1723,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
elements.push({
ele: this.getChildDocView(entry[1]),
bounds: entry[1].opacity === 0 ? { payload: undefined, type: '', ...entry[1], width: 0, height: 0 } : { payload: undefined, type: '', ...entry[1] },
- inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
+ inkMask: BoolCast(entry[1].pair.layout?.$stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
})
);
@@ -1744,7 +1746,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (addAsAnnotation) {
if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), [])?.push(anchor);
} else {
this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
@@ -1841,15 +1843,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- updateIcon = (usePanelDimensions?: boolean) => {
+ updateIcon = (/*usePanelDimensions?: boolean*/) => {
const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
- usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
- usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
this._props.PanelWidth(),
this._props.PanelHeight(),
0,
@@ -1988,7 +1990,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({
description: 'Use Background Color as Default',
event: () => {
- Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor);
+ DocCast(Doc.UserDoc().emptyCollection) && (DocCast(Doc.UserDoc().emptyCollection)!.backgroundColor = StrCast(this.layoutDoc.backgroundColor));
},
icon: 'palette',
});
@@ -2088,7 +2090,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
onPassiveWheel = (e: WheelEvent) => {
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
this._props.isSelected() && !scrollable && e.preventDefault();
};
@@ -2111,12 +2113,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
);
}
- transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
get pannableContents() {
this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
return (
<CollectionFreeFormPannableContents
- Document={this.Document}
+ Doc={this.Document}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.PanZoomCenterXf}
@@ -2134,6 +2136,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
<MarqueeView
{...this._props}
ref={this._marqueeViewRef}
+ Doc={this.Document}
ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
@@ -2150,7 +2153,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
isAnnotationOverlay={this.isAnnotationOverlay}>
{this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
{this.pannableContents}
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} Doc={this._props.Document} /> : null}
</MarqueeView>
);
}
@@ -2265,10 +2268,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
id={this._paintedId}
ref={r => {
this.createDashEventsTarget(r);
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = r;
- // prevent wheel events from passivly propagating up through containers
- r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(r, this._props.isContentActive, this.onPassiveWheel);
r?.addEventListener('mouseleave', this.onMouseLeave);
r?.addEventListener('mouseenter', this.onMouseEnter);
}}
@@ -2315,7 +2315,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
NativeWidth={returnZero}
NativeHeight={returnZero}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
@@ -2387,7 +2387,7 @@ ScriptingGlobals.add(function datavizFromSchema() {
const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key !== 'text');
if (!keys) return;
- const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]);
+ const children = DocListCast(view.Document[Doc.LayoutDataKey(view.Document)]);
const csvRows = [];
csvRows.push(keys.join(','));
for (let i = 0; i < children.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index b9f8b13a7..142085e14 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -11,7 +11,7 @@ import { emptyFunction } from '../../../../Utils';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { List } from '../../../../fields/List';
-import { DocCast, ImageCast, NumCast, StrCast } from '../../../../fields/Types';
+import { DocCast, ImageCastToNameType, NumCast, StrCast } from '../../../../fields/Types';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
@@ -24,6 +24,7 @@ import { FaceRecognitionHandler } from '../../search/FaceRecognitionHandler';
import { CollectionStackingView } from '../CollectionStackingView';
import './FaceCollectionBox.scss';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import { returnEmptyDocViewList } from '../../StyleProvider';
/**
* This code is used to render the sidebar collection of unique recognized faces, where each
@@ -106,9 +107,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
// assign the face in the image that's closest to the face collection's face
if (faceAnno) {
- faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face));
+ DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face)!);
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
}
}
});
@@ -116,9 +117,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
?.filter(doc => DocCast(doc.face)?.type === DocumentType.UFACE)
.forEach(faceAnno => {
const imgDoc = faceAnno;
- faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face));
+ DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face)!);
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
});
e.stopPropagation();
return true;
@@ -149,11 +150,6 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, this.Document);
}, 'remove doc from face');
- /**
- * This stops scroll wheel events when they are used to scroll the face collection.
- */
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
-
render() {
return (
<div className="face-document-item" ref={ele => this.createDropTarget(ele!)}>
@@ -180,14 +176,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
style={{
pointerEvents: this._props.isContentActive() ? undefined : 'none',
}}
- ref={action((ele: HTMLDivElement | null) => {
- this._listRef?.removeEventListener('wheel', this.onPassiveWheel);
- this._listRef = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
- })}>
+ ref={r => this.fixWheelEvents(r, this._props.isContentActive)}>
{FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url.href.split('.') ?? ['-missing-', '.png'];
+ const [name, type] = ImageCastToNameType(doc?.[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
return (
<div
className="image-wrapper"
@@ -197,7 +188,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
this,
e,
() => {
- const dragDoc = DocListCast(doc.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
+ const dragDoc = DocListCast(doc?.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
DragManager.StartDocumentDrag([e.target as HTMLElement], new DragManager.DocumentDragData([dragDoc], dropActionType.embed), e.clientX, e.clientY);
return true;
},
@@ -205,7 +196,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
emptyFunction
)
}>
- <img onClick={() => DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
+ <img onClick={() => doc && DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
<div className="remove-item">
<IconButton tooltip={'Remove Doc From Face Collection'} onPointerDown={() => this.removeFaceImageFromUniqueFace(doc)} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
</div>
@@ -239,11 +230,12 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
addDocument = (doc: Doc | Doc[], annotationKey?: string) => {
const uniqueFaceDoc = doc instanceof Doc ? doc : doc[0];
const added = uniqueFaceDoc.type === DocumentType.UFACE;
if (added) {
- Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
+ Doc.MyFaceCollection && Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
}
return added;
@@ -267,6 +259,8 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
{...this._props} //
styleProvider={this.stackingStyleProvider}
Document={Doc.ActiveDashboard}
+ DocumentView={undefined}
+ docViewPath={returnEmptyDocViewList}
fieldKey="myUniqueFaces"
moveDocument={this.moveDocument}
addDocument={this.addDocument}
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index a3d9641da..ff9fb14e7 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -9,9 +9,8 @@ import React from 'react';
import { imageUrlToBase64 } from '../../../../ClientUtils';
import { Utils, numberRange } from '../../../../Utils';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
-import { DocData } from '../../../../fields/DocSymbols';
import { List } from '../../../../fields/List';
-import { ImageCast } from '../../../../fields/Types';
+import { ImageCastToNameType, ImageCastWithSuffix } from '../../../../fields/Types';
import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
@@ -165,9 +164,9 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Converts the images into a Base64 format, afterwhich the information is sent to GPT to label them.
const imageInfos = this._selectedImages.map(async doc => {
- if (!doc[DocData].tags_chat) {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
- return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 =>
+ if (!doc.$tags_chat) {
+ const url = ImageCastWithSuffix(doc[Doc.LayoutDataKey(doc)], '_o') ?? '';
+ return imageUrlToBase64(url).then(hrefBase64 =>
!hrefBase64 ? undefined :
gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels =>
({ doc, labels }))) ; // prettier-ignore
@@ -176,7 +175,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
(await Promise.all(imageInfos)).forEach(imageInfo => {
if (imageInfo) {
- imageInfo.doc[DocData].tags_chat = (imageInfo.doc[DocData].tags_chat as List<string>) ?? new List<string>();
+ imageInfo.doc.$tags_chat = (imageInfo.doc.$tags_chat as List<string>) ?? new List<string>();
const labels = imageInfo.labels.split('\n');
labels.forEach(label => {
@@ -186,7 +185,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
.replace(/^\d+\.\s*|-|f\*/, '')
.replace(/^#/, '')
.trim();
- (imageInfo.doc[DocData].tags_chat as List<string>).push(hashLabel);
+ (imageInfo.doc.$tags_chat as List<string>).push(hashLabel);
});
}
});
@@ -200,13 +199,11 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
groupImagesInBox = action(async () => {
this.startLoading();
- for (const doc of this._selectedImages) {
- for (let index = 0; index < (doc[DocData].tags_chat as List<string>).length; index++) {
- const label = (doc[DocData].tags_chat as List<string>)[index];
- const embedding = await gptGetEmbedding(label);
- doc[DocData][`tags_embedding_${index + 1}`] = new List<number>(embedding);
- }
- }
+ await Promise.all(
+ this._selectedImages
+ .map(doc => ({ doc, labels: doc.$tags_chat as List<string> }))
+ .map(({ doc, labels }) => labels.map((label, index) => gptGetEmbedding(label).then(embedding => (doc[`$tags_embedding_${index + 1}`] = new List<number>(embedding)))))
+ );
const labelToEmbedding = new Map<string, number[]>();
// Create embeddings for the labels.
@@ -215,13 +212,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// For each image, loop through the labels, and calculate similarity. Associate it with the
// most similar one.
this._selectedImages.forEach(doc => {
- const embedLists = numberRange((doc[DocData].tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`])));
+ const embedLists = numberRange((doc.$tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[`$tags_embedding_${n + 1}`])));
const bestEmbedScore = (embedding: Opt<number[]>) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0));
const {label: mostSimilarLabelCollect} =
this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) }))
.reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur,
{ label: '', similarityScore: 0, }); // prettier-ignore
- doc[DocData].data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
+ doc.$data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
});
this.endLoading();
@@ -313,7 +310,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
{this._displayImageInformation ? (
<div className="image-information-list">
{this._selectedImages.map(doc => {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
+ const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]);
return (
<div className="image-information" style={{ borderColor: SettingsManager.userColor }} key={Utils.GenerateGuid()}>
<img
@@ -322,7 +319,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
await DocumentView.showDocument(doc, { willZoomCentered: true });
}}></img>
<div className="image-information-labels" onClick={() => this._props.addDocTab(doc, OpenWhere.addRightKeyvalue)}>
- {(doc[DocData].tags_chat as List<string>).map(label => {
+ {(doc.$tags_chat as List<string>).map(label => {
return (
<div key={Utils.GenerateGuid()} className="image-label" style={{ backgroundColor: SettingsManager.userVariantColor, borderColor: SettingsManager.userColor }}>
{label}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 00d7ea451..3cc7c0f2d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -8,7 +8,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
import { DocUtils } from '../../../documents/DocUtils';
@@ -18,7 +18,6 @@ import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManag
import { Transform } from '../../../util/Transform';
import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { MainView } from '../../MainView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { MarqueeViewBounds } from '../../PinFuncs';
import { PreviewCursor } from '../../PreviewCursor';
@@ -32,6 +31,7 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
interface MarqueeViewProps {
+ Doc: Doc;
getContainerTransform: () => Transform;
getTransform: () => Transform;
activeDocuments: () => Doc[];
@@ -113,6 +113,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
// tslint:disable-next-line:prefer-const
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
+
if (e.key === '?') {
cm.setDefaultItem('?', (str: string) =>
this._props.addDocTab(Docs.Create.WebDocument(`https://wikipedia.org/wiki/${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: `wiki:${str}`, data_useCors: true }), OpenWhere.addRight)
@@ -164,7 +165,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
error && console.log(error);
data &&
ClientUtils.convertDataUri(data, this._props.Document[Id] + '_icon_' + new Date().getTime()).then(returnedfilename => {
- this._props.Document[DocData].icon = new ImageField(returnedfilename);
+ this._props.Document.$icon = new ImageField(returnedfilename);
});
})
);
@@ -370,13 +371,12 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
- const docData = doc[DocData];
- docData.data = new List<Doc>(selected);
- docData.isGroup = makeGroup;
- docData.title = makeGroup ? 'grouping' : 'nested freeform';
+ doc.$data = new List<Doc>(selected);
+ doc.$isGroup = makeGroup;
+ doc.$title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ })(DocCast(Doc.UserDoc().emptyCollection) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyCollection)!, true) : Docs.Create.FreeformDocument([], {}));
newCollection.isSystem = undefined;
newCollection._width = bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
newCollection._height = bounds.height || 1;
@@ -437,11 +437,11 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
*/
@undoBatch
classifyImages = async () => {
- const groupButton = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImageGrouper);
+ const groupButton = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImageGrouper);
if (groupButton) {
this._selectedDocs = this.marqueeSelect(false, DocumentType.IMG);
ImageLabelBoxData.Instance.setData(this._selectedDocs);
- MainView.Instance.expandFlyout(groupButton);
+ ScriptCast(groupButton.onClick)?.script.run({ this: groupButton });
}
};
@@ -461,9 +461,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newColDim = 900;
for (const label of labelGroups) {
const newCollection = MarqueeView.getCollection([], undefined, false, this.Bounds);
- newCollection[DocData].title = label + ' Collection';
- newCollection._x = this.Bounds.left + x_offset;
- newCollection._y = this.Bounds.top + y_offset;
+ newCollection.$title = label + ' Collection';
+ newCollection.x = this.Bounds.left + x_offset;
+ newCollection.y = this.Bounds.top + y_offset;
newCollection._width = newColDim;
newCollection._height = newColDim;
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
@@ -480,8 +480,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}
for (const doc of selectedImages) {
- if (doc[DocData].data_label) {
- Doc.AddDocToList(labelToCollection.get(doc[DocData].data_label as string)!, undefined, doc);
+ if (doc.$data_label) {
+ Doc.AddDocToList(labelToCollection.get(doc.$data_label as string)!, undefined, doc);
this._props.removeDocument?.(doc);
}
}
@@ -590,8 +590,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
marqueeSelect = (selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) => {
const selection: Doc[] = [];
const selectFunc = (doc: Doc) => {
- const layoutDoc = Doc.Layout(doc);
- const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) };
+ const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) };
if (!this._lassoFreehand) {
intersectRect(bounds, this.Bounds) && selection.push(doc);
} else {