aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-08-20 19:11:00 -0400
committerbobzel <zzzman@gmail.com>2024-08-20 19:11:00 -0400
commit5196009ec6bcb673fd2a4519c54442df218841f7 (patch)
tree79f4b1d559c20a6bfd9b4759a5cbe9d8f8c00fe1 /src/client/views/collections/collectionFreeForm
parent0e975569e5686138e52bdc554b3f0391f42aeead (diff)
parente57584a1be9d428fb40fc789494a7ac0ac14fb84 (diff)
fixed up a bunch of things in face recognition
Diffstat (limited to 'src/client/views/collections/collectionFreeForm')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx177
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx39
5 files changed, 133 insertions, 119 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index fc39cafaa..c17371151 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -12,7 +12,7 @@ import './CollectionFreeFormView.scss';
* returns a truthy value
*/
// eslint-disable-next-line no-use-before-define
-export type infoArc = [() => any, (res?: any) => infoState];
+export type infoArc = [() => unknown, (res?: unknown) => infoState];
export const StateMessage = Symbol('StateMessage');
export const StateMessageGIF = Symbol('StateMessageGIF');
@@ -20,9 +20,9 @@ export const StateEntryFunc = Symbol('StateEntryFunc');
export class infoState {
[StateMessage]: string = '';
[StateMessageGIF]?: string = '';
- [StateEntryFunc]?: () => any;
+ [StateEntryFunc]?: () => unknown;
[key: string]: infoArc;
- constructor(message: string, arcs: { [key: string]: infoArc }, messageGif?: string, entryFunc?: () => any) {
+ constructor(message: string, arcs: { [key: string]: infoArc }, messageGif?: string, entryFunc?: () => unknown) {
this[StateMessage] = message;
Object.assign(this, arcs);
this[StateMessageGIF] = messageGif;
@@ -44,7 +44,7 @@ export function InfoState(
msg: string, //
arcs: { [key: string]: infoArc },
gif?: string,
- entryFunc?: () => any
+ entryFunc?: () => unknown
) {
// eslint-disable-next-line new-cap
return new infoState(msg, arcs, gif, entryFunc);
@@ -52,7 +52,7 @@ export function InfoState(
export interface CollectionFreeFormInfoStateProps {
infoState: infoState;
- next: (state: infoState) => any;
+ next: (state: infoState) => unknown;
close: () => void;
}
@@ -61,7 +61,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent<Collec
_disposers: IReactionDisposer[] = [];
@observable _expanded = false;
- constructor(props: any) {
+ constructor(props: CollectionFreeFormInfoStateProps) {
super(props);
makeObservable(this);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index de51cc73c..79aad0ef2 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -9,7 +9,7 @@ import { aggregateBounds } from '../../../../Utils';
export interface ViewDefBounds {
type: string;
- payload: any;
+ payload: unknown;
x: number;
y: number;
z?: number;
@@ -72,11 +72,15 @@ function toLabel(target: FieldResult<FieldType>) {
*/
function getTextWidth(text: string, font: string): number {
// re-use canvas object for better performance
- const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas'));
+ const selfStoreHack = getTextWidth as unknown as { canvas: Element };
+ const canvas = (selfStoreHack.canvas = (selfStoreHack.canvas as unknown as HTMLCanvasElement) ?? document.createElement('canvas'));
const context = canvas.getContext('2d');
- context.font = font;
- const metrics = context.measureText(text);
- return metrics.width;
+ if (context) {
+ context.font = font;
+ const metrics = context.measureText(text);
+ return metrics.width;
+ }
+ return 0;
}
interface PivotColumn {
@@ -131,13 +135,13 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
return normalizeResults(burstDiam, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
}
-export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
+export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: unknown) {
const docMap = new Map<string, PoolData>();
const fieldKey = 'data';
const pivotColumnGroups = new Map<FieldResult<FieldType>, PivotColumn>();
let nonNumbers = 0;
- const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
+ const pivotFieldKey = toLabel((engineProps as { pivotField?: string })?.pivotField ?? pivotDoc._pivotField) || 'author';
childPairs.forEach(pair => {
const listValue = Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
@@ -265,7 +269,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
y: -maxColHeight + pivotAxisWidth,
width: pivotAxisWidth * numCols * expander,
height: maxColHeight,
- payload: pivotColumnGroups.get(key)!.filters,
+ payload: pivotColumnGroups.get(key)?.filters,
}));
groupNames.push(...dividers);
// eslint-disable-next-line no-use-before-define
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index e543b4008..bc9dd022c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -54,8 +54,8 @@ export class CollectionFreeFormPannableContents extends ObservableReactComponent
<div
className={'collectionfreeformview' + (this._props.viewDefDivClick ? '-viewDef' : '-none')}
onScroll={e => {
- const target = e.target as any;
- if (getComputedStyle(target)?.overflow === 'visible') {
+ const { target } = e;
+ if (target instanceof Element && getComputedStyle(target)?.overflow === 'visible') {
target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
}
}}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b6e1fca77..c4cf8dee7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,15 +1,14 @@
/* eslint-disable react/jsx-props-no-spreading */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
+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 { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
-import { ActiveEraserWidth, ActiveInkWidth, Doc, DocListCast, Field, FieldType, Opt, SetActiveInkColor, SetActiveInkWidth } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField';
@@ -32,20 +31,20 @@ import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { DocumentView } from '../../nodes/DocumentView';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveEraserWidth, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, 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, OpenWhereMod } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
import { StyleProp } from '../../StyleProp';
-import { CollectionSubView } from '../CollectionSubView';
+import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeViewType';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
import { CollectionFreeFormClusters } from './CollectionFreeFormClusters';
@@ -70,7 +69,7 @@ export interface collectionFreeformViewProps {
childPointerEvents?: () => string | undefined;
viewField?: string;
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
- engineProps?: any;
+ engineProps?: unknown;
getScrollHeight?: () => number | undefined;
}
@@ -82,13 +81,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
public unprocessedDocs: Doc[] = [];
public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
public static from(dv?: DocumentView): CollectionFreeFormView | undefined {
- const parent = CollectionFreeFormDocumentView.from(dv)?._props.parent;
+ const parent = CollectionFreeFormDocumentView.from(dv)?._props.reactParent;
return parent instanceof CollectionFreeFormView ? parent : undefined;
}
private _clusters = new CollectionFreeFormClusters(this);
- private _oldWheel: any;
- private _panZoomTransitionTimer: any;
+ private _oldWheel: HTMLDivElement | null = null;
+ private _panZoomTransitionTimer: NodeJS.Timeout | undefined = undefined;
+ private _brushtimer: NodeJS.Timeout | undefined = undefined;
+ private _brushtimer1: NodeJS.Timeout | undefined = undefined;
private _lastX: number = 0;
private _lastY: number = 0;
private _downX: number = 0;
@@ -97,8 +98,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
private _batch: UndoManager.Batch | undefined = undefined;
- private _brushtimer: any;
- private _brushtimer1: any;
private _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation.
private _presEaseFunc: string = 'ease';
@@ -122,14 +121,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined = undefined; // highlighted region of freeform canvas used by presentations to indicate a region
@observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
+ @observable _childPointerEvents: Property.PointerEvents | undefined = undefined;
@observable _lightboxDoc: Opt<Doc> = undefined;
@observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
@observable _keyframeEditing = false;
@observable _eraserX: number = 0;
@observable _eraserY: number = 0;
@observable _showEraserCircle: boolean = false; // to determine whether the radius eraser should show
- constructor(props: any) {
+ constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
@@ -139,12 +138,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@computed get childPointerEvents() {
return SnappingManager.IsResizing
? 'none'
- : this._props.childPointerEvents?.() ??
+ : (this._props.childPointerEvents?.() ??
(this._props.viewDefDivClick || //
(this.layoutEngine === computePassLayout.name && !this._props.isSelected()) ||
this.isContentActive() === false
? 'none'
- : this._props.pointerEvents?.());
+ : this._props.pointerEvents?.()));
}
@computed get contentViews() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
@@ -184,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.transform(this.panZoomXf);
}
@computed get backgroundColor() {
- return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor);
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string;
}
@computed get fitWidth() {
return this._props.fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth;
@@ -356,7 +355,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @param options
* @returns
*/
- focus = (anchor: Doc, options: FocusViewOptions): any => {
+ focus = (anchor: Doc, options: FocusViewOptions) => {
if (anchor.isGroup && !options.docTransform && options.contextPath?.length) {
// don't focus on group if there's a context path because we're about to focus on a group item
// which will override any group focus. (If we allowed the group to focus, it would mark didMove even if there were no net movement)
@@ -373,14 +372,14 @@ 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) && !DocumentView.LightboxDoc());
- const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined);
+ 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
const didMove = !cantTransform && !anchor.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
if (didMove) options.didMove = true;
// glr: freeform transform speed can be set by adjusting presentation_transition field - needs a way of knowing when presentation is not active...
if (didMove) {
- const focusTime = options?.instant ? 0 : options.zoomTime ?? 500;
+ const focusTime = options?.instant ? 0 : (options.zoomTime ?? 500);
(options.zoomScale ?? options.willZoomCentered) && scale && (this.Document[this.scaleFieldKey] = scale);
this.setPan(panX, panY, focusTime); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
return focusTime;
@@ -442,8 +441,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
- @undoBatch
- internalAnchorAnnoDrop(e: Event, de: DragManager.DropEvent, annoDragData: DragManager.AnchorAnnoDragData) {
+ internalAnchorAnnoDrop = undoable((e: Event, de: DragManager.DropEvent, annoDragData: DragManager.AnchorAnnoDragData) => {
const dropCreator = annoDragData.dropDocCreator;
const [xp, yp] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
@@ -456,10 +454,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return dropDoc || this.Document;
};
return true;
- }
+ }, 'anchor drop');
- @undoBatch
- internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData) {
+ internalLinkDrop = undoable((e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData) => {
if (this.DocumentView?.() && linkDragData.linkDragView.containerViewPath?.().includes(this.DocumentView())) {
const [x, y] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
// do nothing if link is dropped into any freeform view parent of dragged document
@@ -475,9 +472,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return added;
}
return false;
- }
+ }, 'link drop');
- onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => {
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);
@@ -523,8 +520,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- @undoBatch
- onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
+ onGesture = undoable((e: Event, ge: GestureUtils.GestureEvent) => {
switch (ge.gesture) {
case Gestures.Text:
if (ge.text) {
@@ -550,7 +546,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_width: B.width + inkWidth,
_height: B.height + inkWidth,
stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth
+ inkWidth,
+ ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ ActiveFillColor(),
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
);
if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
@@ -560,7 +563,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
}
}
- };
+ }, 'gesture');
@action
onEraserUp = (): void => {
this._deleteList.lastElement()?._props.removeDocument?.(this._deleteList.map(ink => ink.Document));
@@ -634,7 +637,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_width: B.width + inkWidth,
_height: B.height + inkWidth,
stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth
+ inkWidth,
+ ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ ActiveFillColor(),
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
);
});
newStrokes && this.addDocument?.(newStrokes);
@@ -684,8 +694,8 @@ 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, InkField.getBounds(points), text));
+ forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData) => {
+ this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points)));
};
onPointerMove = (e: PointerEvent) => {
@@ -1163,6 +1173,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection
// call in a test for linearity
bintersects = (curve: Bezier, otherCurve: Bezier) => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((curve as any)._linear) {
// bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line
const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] });
@@ -1172,6 +1183,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return intT ? [intT] : [];
}
}
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((otherCurve as any)._linear) {
return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] });
}
@@ -1463,17 +1475,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return ret;
};
childPointerEventsFunc = () => this._childPointerEvents;
- childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
+ childContentsActive = () => ((this._props.childContentsActive ?? this.isContentActive() === false) ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
return (
<CollectionFreeFormDocumentView
- // eslint-disable-next-line react/jsx-props-no-spreading
- {...OmitKeys(entry, ['replica', 'pair']).omit}
+ // eslint-disable-next-line react/jsx-props-no-spreading, @typescript-eslint/no-explicit-any
+ {...(OmitKeys(entry, ['replica', 'pair']).omit as any)}
key={childLayout[Id] + (entry.replica || '')}
Document={childLayout}
- parent={this}
+ reactParent={this}
containerViewPath={this.DocumentView?.().docViewPath}
styleProvider={this._clusters.styleProvider}
TemplateDataDocument={childData}
@@ -1588,7 +1600,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
}
- onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
+ onViewDefDivClick = (e: React.MouseEvent, payload: unknown) => {
(this._props.viewDefDivClick || ScriptCast(this.Document.onViewDefDivClick))?.script.run({ this: this.Document, payload });
e.stopPropagation();
};
@@ -1622,7 +1634,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
ele: (
<div
className="collectionFreeform-customDiv"
- title={viewDef.payload?.join(' ')}
+ title={StrListCast(viewDef.payload as string).join(' ')}
key={'div' + x + y + z + viewDef.payload}
onClick={e => this.onViewDefDivClick(e, viewDef)}
style={{ width, height, backgroundColor: color, transform }}
@@ -1639,11 +1651,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* since rendering a large collection of documents can be slow, at startup, docs are rendered in batches.
* each doc's render() method will call the cutoff provider which will let the doc know if it should render itself yet, or wait
*/
- renderCutoffProvider = computedFn((doc: Doc) => (this.Document.isTemplateDoc ? false : !this._renderCutoffData.get(doc[Id] + '')));
+ renderCutoffProvider = computedFn((doc: Doc) => (this.Document.isTemplateDoc || this.Document.isTemplateForField ? false : !this._renderCutoffData.get(doc[Id] + '')));
doEngineLayout(
poolData: Map<string, PoolData>,
- engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
+ engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: unknown) => ViewDefResult[]
) {
return engine(poolData, this.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps);
}
@@ -1673,7 +1685,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.forEach(entry =>
elements.push({
ele: this.getChildDocView(entry[1]),
- bounds: (entry[1].opacity === 0 ? { ...entry[1], width: 0, height: 0 } : { ...entry[1] }) as any,
+ 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,
})
);
@@ -1756,7 +1768,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.pointerevents = reaction(
() => this.childPointerEvents,
pointerevents => {
- this._childPointerEvents = pointerevents as any;
+ this._childPointerEvents = pointerevents as Property.PointerEvents | undefined;
},
{ fireImmediately: true }
);
@@ -1795,24 +1807,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- updateIcon = () =>
- UpdateIcon(
- this.layoutDoc[Id] + '-icon' + new Date().getTime(),
- this.DocumentView?.().ContentDiv!,
- NumCast(this.layoutDoc._width),
- NumCast(this.layoutDoc._height),
- this._props.PanelWidth(),
- this._props.PanelHeight(),
- 0,
- 1,
- false,
- '',
- (iconFile, nativeWidth, nativeHeight) => {
- this.dataDoc.icon = new ImageField(iconFile);
- this.dataDoc.icon_nativeWidth = nativeWidth;
- this.dataDoc.icon_nativeHeight = nativeHeight;
- }
- );
+ updateIcon = () => {
+ const contentDiv = this.DocumentView?.().ContentDiv;
+ contentDiv &&
+ UpdateIcon(
+ this.layoutDoc[Id] + '-icon' + new Date().getTime(),
+ contentDiv,
+ NumCast(this.layoutDoc._width),
+ NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(),
+ this._props.PanelHeight(),
+ 0,
+ 1,
+ false,
+ '',
+ (iconFile, nativeWidth, nativeHeight) => {
+ this.dataDoc.icon = new ImageField(iconFile);
+ this.dataDoc.icon_nativeWidth = nativeWidth;
+ this.dataDoc.icon_nativeHeight = nativeHeight;
+ }
+ );
+ };
@action
onCursorMove = (e: React.PointerEvent) => {
@@ -1831,8 +1846,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._showEraserCircle = true;
};
- @undoBatch
- promoteCollection = () => {
+ promoteCollection = undoable(() => {
const childDocs = this.childDocs.slice();
childDocs.forEach(docIn => {
const doc = docIn;
@@ -1841,10 +1855,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.y = scr?.[1];
});
this._props.addDocTab(childDocs, OpenWhere.inParentFromScreen);
- };
+ }, 'promote collection');
- @undoBatch
- layoutDocsInGrid = () => {
+ layoutDocsInGrid = undoable(() => {
const docs = this.childLayoutPairs.map(pair => pair.layout);
const width = Math.max(...docs.map(doc => NumCast(doc._width))) + 20;
const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
@@ -1854,40 +1867,37 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.x = NumCast(this.Document[this.panXFieldKey]) + (i % dim) * width - (width * dim) / 2;
doc.y = NumCast(this.Document[this.panYFieldKey]) + Math.floor(i / dim) * height - (height * dim) / 2;
});
- };
+ }, 'layout docs in grid');
- @undoBatch
- toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight);
+ toggleNativeDimensions = undoable(() => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight), 'toggle native dimensions');
///
/// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
/// the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
///
- @undoBatch
- resetView = () => {
+ resetView = undoable(() => {
this.layoutDoc[this.panXFieldKey] = NumCast(this.dataDoc[this.panXFieldKey + '_reset']);
this.layoutDoc[this.panYFieldKey] = NumCast(this.dataDoc[this.panYFieldKey + '_reset']);
this.layoutDoc[this.scaleFieldKey] = NumCast(this.dataDoc[this.scaleFieldKey + '_reset'], 1);
- };
+ }, 'reset view');
///
/// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
/// the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
///
- @undoBatch
- toggleResetView = () => {
+ toggleResetView = undoable(() => {
this.dataDoc[this.autoResetFieldKey] = !this.dataDoc[this.autoResetFieldKey];
if (this.dataDoc[this.autoResetFieldKey]) {
this.dataDoc[this.panXFieldKey + '_reset'] = this.layoutDoc[this.panXFieldKey];
this.dataDoc[this.panYFieldKey + '_reset'] = this.layoutDoc[this.panYFieldKey];
this.dataDoc[this.scaleFieldKey + '_reset'] = this.layoutDoc[this.scaleFieldKey];
}
- };
+ }, 'toggle reset view');
onContextMenu = () => {
if (this._props.isAnnotationOverlay || !ContextMenu.Instance) return;
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
- const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
+ const appearanceItems = appearance?.subitems ?? [];
!this.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
!this.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
if (this._props.setContentViewBox === emptyFunction) {
@@ -1914,7 +1924,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
const options = ContextMenu.Instance.findByDescription('Options...');
- const optionItems = options && 'subitems' in options ? options.subitems : [];
+ const optionItems = options?.subitems ?? [];
!this._props.isAnnotationOverlay &&
!Doc.noviceMode &&
optionItems.push({
@@ -1938,12 +1948,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
!options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
const mores = ContextMenu.Instance.findByDescription('More...');
- const moreItems = mores && 'subitems' in mores ? mores.subitems : [];
+ const moreItems = mores?.subitems ?? [];
!mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' });
};
- @undoBatch
- transcribeStrokes = () => {
+ transcribeStrokes = undoable(() => {
if (this.Document.isGroup && this.Document.transcription) {
const text = StrCast(this.Document.transcription);
const lines = text.split('\n');
@@ -1951,7 +1960,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
}
- };
+ }, 'transcribe strokes');
@action
dragEnding = () => {
@@ -1993,7 +2002,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
incrementalRender = action(() => {
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;
+ const loadIncrement = this.Document.isTemplateDoc || this.Document.isTemplateForField ? Number.MAX_VALUE : 5;
for (let i = 0; i < Math.min(layoutUnrendered.length, loadIncrement); i++) {
this._renderCutoffData.set(layoutUnrendered[i][Id] + '', true);
}
@@ -2117,7 +2126,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onDragOver={e => e.preventDefault()}
onContextMenu={this.onContextMenu}
style={{
- pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : (this._props.pointerEvents?.() as any),
+ pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : this._props.pointerEvents?.(),
textAlign: this.isAnnotationOverlay ? 'initial' : undefined,
transform: `scale(${this.nativeDimScaling})`,
width: `${100 / this.nativeDimScaling}%`,
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 1d3f88df1..50b91e8fe 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -1,26 +1,27 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton, Size } from 'browndash-components';
+import * as faceapi from 'face-api.js';
+import { FaceMatcher } from 'face-api.js';
+import 'ldrs/ring';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
-import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { ViewBoxBaseComponent } from '../../DocComponent';
-import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import 'ldrs/ring';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { action, computed, makeObservable, observable, reaction } from 'mobx';
-import { Doc, DocListCast, NumListCast } from '../../../../fields/Doc';
+import { Utils } from '../../../../Utils';
+import { Doc, DocListCast } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
import { ImageCast, StrCast } from '../../../../fields/Types';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { Docs } from '../../../documents/Documents';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { ViewBoxBaseComponent } from '../../DocComponent';
import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DocumentView } from '../../nodes/DocumentView';
+import { FieldView, FieldViewProps } from '../../nodes/FieldView';
import './FaceCollectionBox.scss';
-import { IconButton, Size } from 'browndash-components';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
-import { List } from '../../../../fields/List';
-import { DocumentView } from '../../nodes/DocumentView';
-import { Utils } from '../../../../Utils';
-import { DragManager } from '../../../util/DragManager';
-import * as faceapi from 'face-api.js';
-import { FaceMatcher } from 'face-api.js';
interface FaceDocumentProps {
faceDoc: Doc;
@@ -36,7 +37,7 @@ export class FaceDocumentItem extends ObservableReactComponent<FaceDocumentProps
private _dropDisposer?: DragManager.DragDropDisposer;
private _inputRef = React.createRef<HTMLInputElement>();
- constructor(props: any) {
+ constructor(props: FaceDocumentProps) {
super(props);
makeObservable(this);
this.ref = React.createRef();
@@ -194,7 +195,7 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
}
- constructor(props: any) {
+ constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
FaceCollectionBox.Instance = this;
@@ -204,7 +205,7 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<div className="searchBox-container" style={{ pointerEvents: 'all', color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}>
{this.currentDocs.map(doc => {
- return <FaceDocumentItem faceDoc={doc} />;
+ return <FaceDocumentItem key={doc[Id]} faceDoc={doc} />;
})}
</div>
);