aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ClientUtils.ts61
-rw-r--r--src/client/documents/Documents.ts1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts224
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx537
-rw-r--r--src/client/views/nodes/LabelBox.tsx1
-rw-r--r--src/client/views/nodes/PDFBox.tsx5
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx1
-rw-r--r--src/client/views/nodes/formattedText/ParagraphNodeSpec.ts1
-rw-r--r--src/client/views/nodes/formattedText/RichTextRules.ts1
9 files changed, 419 insertions, 413 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index 8011a1928..dbbba896f 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -5,6 +5,7 @@ import * as rp from 'request-promise';
import { numberRange, decimalToHexString } from './Utils';
import { CollectionViewType, DocumentType } from './client/documents/DocumentTypes';
import { Colors } from './client/views/global/globalEnums';
+import { CreateImage } from './client/views/nodes/WebBoxRenderer';
export function DashColor(color: string) {
try {
@@ -658,3 +659,63 @@ export function dateRangeStrToDates(dateStr: string) {
return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)];
}
+
+function replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
+ if (oldDiv.childNodes && newDiv) {
+ for (let i = 0; i < oldDiv.childNodes.length; i++) {
+ replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
+ }
+ }
+ if (oldDiv instanceof HTMLCanvasElement) {
+ if (oldDiv.className === 'collectionFreeFormView-grid') {
+ const newCan = newDiv as HTMLCanvasElement;
+ const parEle = newCan.parentElement as HTMLElement;
+ parEle.removeChild(newCan);
+ parEle.appendChild(document.createElement('div'));
+ } else {
+ const canvas = oldDiv;
+ const img = document.createElement('img'); // create a Image Element
+ try {
+ img.src = canvas.toDataURL(); // image source
+ } catch (e) {
+ console.log(e);
+ }
+ img.style.width = canvas.style.width;
+ img.style.height = canvas.style.height;
+ const newCan = newDiv as HTMLCanvasElement;
+ if (newCan) {
+ const parEle = newCan.parentElement as HTMLElement;
+ parEle.removeChild(newCan);
+ parEle.appendChild(img);
+ }
+ }
+ }
+}
+
+export function UpdateIcon(
+ filename: string,
+ docViewContent: HTMLElement,
+ width: number,
+ height: number,
+ panelWidth: number,
+ panelHeight: number,
+ scrollTop: number,
+ realNativeHeight: number,
+ noSuffix: boolean,
+ replaceRootFilename: string | undefined,
+ cb: (iconFile: string, nativeWidth: number, nativeHeight: number) => any
+) {
+ const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
+ newDiv.style.width = width.toString();
+ newDiv.style.height = height.toString();
+ replaceCanvases(docViewContent, newDiv);
+ const htmlString = new XMLSerializer().serializeToString(newDiv);
+ const nativeWidth = width;
+ const nativeHeight = height;
+ return CreateImage(ClientUtils.prepend(''), document.styleSheets, htmlString, nativeWidth, (nativeWidth * panelHeight) / panelWidth, (scrollTop * panelHeight) / realNativeHeight)
+ .then(async (dataUrl: any) => {
+ const returnedFilename = await ClientUtils.convertDataUri(dataUrl, filename, noSuffix, replaceRootFilename);
+ cb(returnedFilename as string, nativeWidth, nativeHeight);
+ })
+ .catch((error: any) => console.error('oops, something went wrong!', error));
+}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index ddb6f65b6..0e7f911c7 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -7,7 +7,6 @@ import * as JSZip from 'jszip';
import { action, reaction, runInAction } from 'mobx';
import { basename } from 'path';
import { ClientUtils, OmitKeys } from '../../ClientUtils';
-// eslint-disable-next-line import/extensions
import * as JSZipUtils from '../../JSZipUtils';
import { decycle } from '../../decycler/decycler';
import { DateField } from '../../fields/DateField';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
new file mode 100644
index 000000000..6fc295e27
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -0,0 +1,224 @@
+import { action, observable } from 'mobx';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { intersectRect } from '../../../../Utils';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { StyleProp } from '../../StyleProvider';
+import './CollectionFreeFormView.scss';
+import { CollectionFreeFormView } from '.';
+
+export class CollectionFreeFormClusters {
+ private _view: CollectionFreeFormView;
+ private _clusterDistance: number = 75;
+ private _hitCluster: number = -1;
+ @observable _clusterSets: Doc[][] = [];
+
+ constructor(view: CollectionFreeFormView) {
+ this._view = view;
+ }
+ get Document() { return this._view.Document; } // prettier-ignore
+ get DocumentView() { return this._view.DocumentView?.(); } // prettier-ignore
+ get childDocs() { return this._view.childDocs; } // prettier-ignore
+ get childLayoutPairs() { return this._view.childLayoutPairs; } // prettier-ignore
+ get screenToContentsXf() { return this._view.screenToFreeformContentsXf; } // prettier-ignore
+ get viewStyleProvider() { return this._view._props.styleProvider; } // prettier-ignore
+ get viewMoveDocument() { return this._view._props.moveDocument; } // prettier-ignore
+ 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 x = NumCast(doc1.x) - clusterDistance;
+ const y = NumCast(doc1.y) - clusterDistance;
+ const w = NumCast(doc1Layout._width) + clusterDistance;
+ const h = NumCast(doc1Layout._height) + clusterDistance;
+ return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
+ }
+ pickCluster(probe: number[]) {
+ this._hitCluster = this.childLayoutPairs
+ .map(pair => pair.layout)
+ .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;
+ }
+ return cluster;
+ }, -1);
+ return this._hitCluster;
+ }
+
+ tryDragCluster(e: PointerEvent) {
+ const cluster = this._hitCluster;
+ if (cluster !== -1) {
+ const ptsParent = e;
+ if (ptsParent) {
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
+ const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView)!);
+ const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
+ const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined);
+ de.moveDocument = this.viewMoveDocument;
+ de.offset = this.screenToContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
+ DragManager.StartDocumentDrag(
+ clusterDocs.map(v => v.ContentDiv!),
+ de,
+ ptsParent.clientX,
+ ptsParent.clientY,
+ { hideSource: !de.dropAction }
+ );
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ initClusters() {
+ if (this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length) {
+ return this.updateClusters(true);
+ }
+ return false;
+ }
+ @action
+ updateClusters(useClusters: boolean) {
+ this.Document._freeform_useClusters = useClusters;
+ this._clusterSets.length = 0;
+ this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
+ }
+
+ @action
+ updateClusterDocs(docs: Doc[]) {
+ const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+ if (this.Document._freeform_useClusters) {
+ const docFirst = docs[0];
+ docs.forEach(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
+ const preferredInd = NumCast(docFirst.layout_cluster);
+ docs.forEach(doc => {
+ doc.layout_cluster = -1;
+ });
+ docs.map(doc =>
+ this._clusterSets.map((set, i) =>
+ set.forEach(member => {
+ if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormClusters.overlapping(doc, member, this._clusterDistance)) {
+ docFirst.layout_cluster = i;
+ }
+ })
+ )
+ );
+ if (
+ docFirst.layout_cluster === -1 &&
+ preferredInd !== -1 &&
+ this._clusterSets.length > preferredInd &&
+ (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
+ ) {
+ docFirst.layout_cluster = preferredInd;
+ }
+ this._clusterSets.forEach((set, i) => {
+ if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+ docFirst.layout_cluster = i;
+ }
+ });
+ if (docFirst.layout_cluster === -1) {
+ docs.forEach(doc => {
+ doc.layout_cluster = this._clusterSets.length;
+ this._clusterSets.push([doc]);
+ });
+ } else if (this._clusterSets.length) {
+ for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
+ docs.forEach(doc => {
+ this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc);
+ });
+ }
+ childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child));
+ }
+ }
+
+ @action
+ updateCluster = (doc: Doc) => {
+ const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+ if (this.Document._freeform_useClusters) {
+ this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
+ const preferredInd = NumCast(doc.layout_cluster);
+ doc.layout_cluster = -1;
+ this._clusterSets.forEach((set, i) =>
+ set.forEach(member => {
+ if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormClusters.overlapping(doc, member, this._clusterDistance)) {
+ doc.layout_cluster = i;
+ }
+ })
+ );
+ if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+ doc.layout_cluster = preferredInd;
+ }
+ this._clusterSets.forEach((set, i) => {
+ if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+ doc.layout_cluster = i;
+ }
+ });
+ if (doc.layout_cluster === -1) {
+ doc.layout_cluster = this._clusterSets.length;
+ this._clusterSets.push([doc]);
+ } else if (this._clusterSets.length) {
+ for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
+ this._clusterSets[doc.layout_cluster ?? 0].push(doc);
+ }
+ }
+ };
+
+ styleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
+ let styleProp = this.viewStyleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
+ if (doc && this.childDocs?.includes(doc))
+ switch (property.split(':')[0]) {
+ case StyleProp.BackgroundColor:
+ {
+ const cluster = NumCast(doc?.layout_cluster);
+ if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
+ if (this._clusterSets.length <= cluster) {
+ setTimeout(() => doc && this.updateCluster(doc));
+ } else {
+ // choose a cluster color from a palette
+ const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+ styleProp = colors[cluster % colors.length];
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+ // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
+ set?.forEach(s => {
+ styleProp = StrCast(s.backgroundColor);
+ });
+ }
+ }
+ }
+ break;
+ case StyleProp.FillColor:
+ if (doc && this.Document._currentFrame !== undefined) {
+ return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
+ }
+ break;
+ default:
+ }
+ return styleProp;
+ };
+
+ trySelectCluster = (addToSel: boolean) => {
+ if (addToSel && this._hitCluster !== -1) {
+ !addToSel && SelectionManager.DeselectAll();
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+ this.selectDocuments(eles);
+ return true;
+ }
+ return false;
+ };
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c1a889539..69cb52233 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -7,7 +7,7 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
-import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
+import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
@@ -48,19 +48,23 @@ import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { PresBox } from '../../nodes/trails/PresBox';
-// eslint-disable-next-line import/extensions
-import { CreateImage } from '../../nodes/WebBoxRenderer';
import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
+import { CollectionFreeFormClusters } from './CollectionFreeFormClusters';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
+class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
+ render() {
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
+ }
+}
export interface collectionFreeformViewProps {
NativeWidth?: () => number;
NativeHeight?: () => number;
@@ -79,18 +83,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
public get displayName() {
return 'CollectionFreeFormView(' + (this.Document.title?.toString() ?? '') + ')';
} // this makes mobx trace() statements more descriptive
+ public unprocessedDocs: Doc[] = [];
+ public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
- @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
- @computed get paintFunc() {
- const field = this.dataDoc[this.fieldKey];
- const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as FieldType)).trim();
- return !paintFunc
- ? ''
- : paintFunc.includes('dashDiv')
- ? `const dashDiv = document.querySelector('#${this._paintedId}');
- (async () => { ${paintFunc} })()`
- : paintFunc;
- }
+ _oldWheel: any;
+ _clusters = new CollectionFreeFormClusters(this);
constructor(props: any) {
super(props);
makeObservable(this);
@@ -102,41 +99,48 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _downX: number = 0;
private _downY: number = 0;
private _downTime = 0;
- private _clusterDistance: number = 75;
- private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
private _batch: UndoManager.Batch | undefined = undefined;
private _brushtimer: any;
private _brushtimer1: any;
+ private _eraserLock = 0;
+ 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.
- public get isAnnotationOverlay() {
- return this._props.isAnnotationOverlay;
- }
- public get scaleFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_scale';
- }
- private get panXFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_panX';
- }
- private get panYFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_panY';
- }
- private get autoResetFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_autoReset';
- }
+ private get isAnnotationOverlay() { return this._props.isAnnotationOverlay; } // prettier-ignore
+ private get scaleFieldKey() { return (this._props.viewField ?? '') + '_freeform_scale'; } // prettier-ignore
+ private get panXFieldKey() { return (this._props.viewField ?? '') + '_freeform_panX'; } // prettier-ignore
+ private get panYFieldKey() { return (this._props.viewField ?? '') + '_freeform_panY'; } // prettier-ignore
+ private get autoResetFieldKey() { return (this._props.viewField ?? '') + '_freeform_autoReset'; } // prettier-ignore
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement
@observable _showAnimTimeline = false;
- @observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@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 _lightboxDoc: Opt<Doc> = undefined;
+ @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
+ @observable _keyframeEditing = false;
+ @computed get layoutEngine() {
+ return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ }
+ @computed get childPointerEvents() {
+ const engine = this._props.layoutEngine?.() || StrCast(this.Document._layoutEngine);
+ return SnappingManager.IsResizing
+ ? 'none'
+ : this._props.childPointerEvents?.() ??
+ (this._props.viewDefDivClick || //
+ (engine === computePassLayout.name && !this._props.isSelected()) ||
+ this.isContentActive() === false
+ ? 'none'
+ : 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);
const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
@@ -158,14 +162,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get contentBounds() {
- const cb = Cast(this.dataDoc.contentBounds, listSpec('number'));
- return cb
- ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
- : aggregateBounds(
- this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10),
- NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10)
- );
+ return aggregateBounds(
+ this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10),
+ NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10)
+ );
}
@computed get nativeWidth() {
return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
@@ -196,6 +197,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.translate(-this.centeringShiftX, -this.centeringShiftY)
.transform(this.panZoomXf);
}
+ @computed get backgroundColor() {
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor);
+ }
+ @computed get nativeDimScaling() {
+ if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 1;
+ const nw = this.nativeWidth;
+ const nh = this.nativeHeight;
+ const hscale = nh ? this._props.PanelHeight() / nh : 1;
+ const wscale = nw ? this._props.PanelWidth() / nw : 1;
+ return wscale < hscale || (this._props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
+ }
+ @computed get paintFunc() {
+ const field = this.dataDoc[this.fieldKey];
+ const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as FieldType)).trim();
+ return !paintFunc
+ ? ''
+ : paintFunc.includes('dashDiv')
+ ? `const dashDiv = document.querySelector('#${this._paintedId}');
+ (async () => { ${paintFunc} })()`
+ : paintFunc;
+ }
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true);
@@ -219,8 +241,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
return newTimer;
}
-
- _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.
changeKeyFrame = (back = false) => {
const currentFrame = Cast(this.Document._currentFrame, 'number', null);
if (currentFrame === undefined) {
@@ -236,11 +256,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
};
- @observable _keyframeEditing = false;
@action setKeyFrameEditing = (set: boolean) => {
this._keyframeEditing = set;
};
getKeyFrameEditing = () => this._keyframeEditing;
+
onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
@@ -276,7 +296,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
retVal = this._props.addDocument?.(newBox) || false;
if (retVal) {
this.bringToFront(newBox);
- this.updateCluster(newBox);
+ this._clusters.updateCluster(newBox);
}
} else {
retVal = this._props.addDocument?.(newBox) || false;
@@ -309,6 +329,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
}
+ /**
+ * focuses on a specified point in the freeform coordinate space. (alternative to focusing on a Document)
+ * @param options
+ * @returns how long a transition it will be to focus on the point, or undefined the doc is a group or something else already moved
+ */
focusOnPoint = (options: FocusViewOptions) => {
const { pointFocus, zoomTime, didMove } = options;
if (!this.Document.isGroup && pointFocus && !didMove) {
@@ -331,7 +356,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @returns
*/
groupFocus = (anchor: Doc, options: FocusViewOptions) => {
- if (options.pointFocus) return this.focusOnPoint(options);
+ if (options.pointFocus) return undefined;
options.docTransform = new Transform(NumCast(anchor.x) + NumCast(anchor._width)/2 - NumCast(this.layoutDoc[this.panXFieldKey]),
NumCast(anchor.y) + NumCast(anchor._height)/2- NumCast(this.layoutDoc[this.panYFieldKey]), 1); // prettier-ignore
const res = this._props.focus(this.Document, options);
@@ -339,6 +364,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return res;
};
+ /**
+ * focuses the freeform view on the anchor subject to options.
+ * If a pointFocus is specified, then groupFocus is triggered instad
+ * Otherwise, this shifts the pan and zoom to the anchor target (as specified by options).
+ * NOTE: focusing on a group only has an effet if the options contextPath is empty.
+ * @param anchor
+ * @param options
+ * @returns
+ */
focus = (anchor: Doc, options: FocusViewOptions): any => {
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
@@ -349,7 +383,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (options.pointFocus) return this.focusOnPoint(options);
const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor);
const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
- if (anchor.type !== DocumentType.CONFIG && !anchorInCollection && !anchorInChildViews) {
+ if (!anchorInCollection && !anchorInChildViews) {
return undefined;
}
const xfToCollection = options?.docTransform ?? Transform.Identity();
@@ -420,7 +454,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!d._keepZWhenDragged && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
- (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
+ (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this._clusters.updateClusterDocs(docDragData.droppedDocuments);
return true;
}
@@ -469,189 +503,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.screenToFreeformContentsXf.transformPoint(e.pageX, e.pageY));
- 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 x = NumCast(doc1.x) - clusterDistance;
- const y = NumCast(doc1.y) - clusterDistance;
- const w = NumCast(doc1Layout._width) + clusterDistance;
- const h = NumCast(doc1Layout._height) + clusterDistance;
- return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
- }
- pickCluster(probe: number[]) {
- return this.childLayoutPairs
- .map(pair => pair.layout)
- .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;
- }
- return cluster;
- }, -1);
- }
-
- tryDragCluster(e: PointerEvent, cluster: number) {
- if (cluster !== -1) {
- const ptsParent = e;
- if (ptsParent) {
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
- const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView?.())!);
- const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
- const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined);
- de.moveDocument = this._props.moveDocument;
- de.offset = this.screenToFreeformContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
- DragManager.StartDocumentDrag(
- clusterDocs.map(v => v.ContentDiv!),
- de,
- ptsParent.clientX,
- ptsParent.clientY,
- { hideSource: !de.dropAction }
- );
- return true;
- }
- }
-
- return false;
- }
-
- @action
- updateClusters(useClusters: boolean) {
- this.Document._freeform_useClusters = useClusters;
- this._clusterSets.length = 0;
- this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
- }
-
- @action
- updateClusterDocs(docs: Doc[]) {
- const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.Document._freeform_useClusters) {
- const docFirst = docs[0];
- docs.forEach(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
- const preferredInd = NumCast(docFirst.layout_cluster);
- docs.forEach(doc => {
- doc.layout_cluster = -1;
- });
- docs.map(doc =>
- this._clusterSets.map((set, i) =>
- set.forEach(member => {
- if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
- docFirst.layout_cluster = i;
- }
- })
- )
- );
- if (
- docFirst.layout_cluster === -1 &&
- preferredInd !== -1 &&
- this._clusterSets.length > preferredInd &&
- (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
- ) {
- docFirst.layout_cluster = preferredInd;
- }
- this._clusterSets.forEach((set, i) => {
- if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
- docFirst.layout_cluster = i;
- }
- });
- if (docFirst.layout_cluster === -1) {
- docs.forEach(doc => {
- doc.layout_cluster = this._clusterSets.length;
- this._clusterSets.push([doc]);
- });
- } else if (this._clusterSets.length) {
- for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
- docs.forEach(doc => {
- this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc);
- });
- }
- childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child));
- }
- }
-
- @action
- updateCluster = (doc: Doc) => {
- const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.Document._freeform_useClusters) {
- this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
- const preferredInd = NumCast(doc.layout_cluster);
- doc.layout_cluster = -1;
- this._clusterSets.forEach((set, i) =>
- set.forEach(member => {
- if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
- doc.layout_cluster = i;
- }
- })
- );
- if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
- doc.layout_cluster = preferredInd;
- }
- this._clusterSets.forEach((set, i) => {
- if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
- doc.layout_cluster = i;
- }
- });
- if (doc.layout_cluster === -1) {
- doc.layout_cluster = this._clusterSets.length;
- this._clusterSets.push([doc]);
- } else if (this._clusterSets.length) {
- for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
- this._clusterSets[doc.layout_cluster ?? 0].push(doc);
- }
- }
- };
-
- clusterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
- let styleProp = this._props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
- if (doc && this.childDocList?.includes(doc))
- switch (property.split(':')[0]) {
- case StyleProp.BackgroundColor:
- {
- const cluster = NumCast(doc?.layout_cluster);
- if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
- if (this._clusterSets.length <= cluster) {
- setTimeout(() => doc && this.updateCluster(doc));
- } else {
- // choose a cluster color from a palette
- const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
- styleProp = colors[cluster % colors.length];
- const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
- // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
- set?.forEach(s => {
- styleProp = StrCast(s.backgroundColor);
- });
- }
- }
- }
- break;
- case StyleProp.FillColor:
- if (doc && this.Document._currentFrame !== undefined) {
- return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
- }
- break;
- default:
- }
- return styleProp;
- };
-
- trySelectCluster = (addToSel: boolean) => {
- if (addToSel && this._hitCluster !== -1) {
- !addToSel && SelectionManager.DeselectAll();
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
- this.selectDocuments(eles);
- return true;
- }
- return false;
- };
-
@action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
@@ -672,8 +523,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
break;
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- this._hitCluster = this.pickCluster(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, this._hitCluster !== -1, false);
+ const hit = this._clusters.pickCluster(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
}
break;
default:
@@ -682,25 +533,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- public unprocessedDocs: Doc[] = [];
- public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@undoBatch
onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
switch (ge.gesture) {
- // case Gestures.Rectangle:
- // {
- // const strokes = this.getActiveDocuments()
- // .filter(doc => doc.type === DocumentType.INK)
- // .map(i => {
- // const d = Cast(i.stroke, InkField);
- // const x = NumCast(i.x) - Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
- // const y = NumCast(i.y) - Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
- // return !d ? [] : d.inkData.map(pd => ({ X: x + pd.X, Y: y + pd.Y }));
- // });
-
- // CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {});
- // }
- // break;
case Gestures.Text:
if (ge.text) {
const B = this.screenToFreeformContentsXf.transformPoint(ge.points[0].X, ge.points[0].Y);
@@ -756,7 +591,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- @action
scrollPan = (e: WheelEvent | { deltaX: number; deltaY: number }): void => {
PresBox.Instance?.pauseAutoPres();
this.setPan(NumCast(this.Document[this.panXFieldKey]) - e.deltaX, NumCast(this.Document[this.panYFieldKey]) - e.deltaY, 0, true);
@@ -775,7 +609,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._lastY = e.clientY;
};
- _eraserLock = 0;
/**
* Erases strokes by intersecting them with an invisible "eraser stroke".
* By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
@@ -816,7 +649,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
onPointerMove = (e: PointerEvent) => {
- if (this.tryDragCluster(e, this._hitCluster)) {
+ if (this._clusters.tryDragCluster(e)) {
e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over
return true;
}
@@ -835,20 +668,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
-
- return this.childDocs
+ // prettier-ignore
+ return this.childDocs
.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.()))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
.map(inkView => inkView!)
.map(inkView => ({ inkViewBounds: inkView.getBounds, inkStroke: inkView.ComponentView as InkingStroke, inkView }))
- .filter(
- ({ inkViewBounds }) =>
+ .filter(({ inkViewBounds }) =>
inkViewBounds && // bounding box of eraser segment and ink stroke overlap
eraserMin.X <= inkViewBounds.right &&
eraserMin.Y <= inkViewBounds.bottom &&
eraserMax.X >= inkViewBounds.left &&
- eraserMax.Y >= inkViewBounds.top
- )
+ eraserMax.Y >= inkViewBounds.top)
.reduce(
(intersections, { inkStroke, inkView }) => {
const { inkData } = inkStroke.inkScaledData();
@@ -1231,19 +1062,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return undefined;
};
- @computed get childPointerEvents() {
- const engine = this._props.layoutEngine?.() || StrCast(this.Document._layoutEngine);
- return SnappingManager.IsResizing
- ? 'none'
- : this._props.childPointerEvents?.() ??
- (this._props.viewDefDivClick || //
- (engine === computePassLayout.name && !this._props.isSelected()) ||
- this.isContentActive() === false
- ? 'none'
- : this._props.pointerEvents?.());
- }
-
- @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
childPointerEventsFunc = () => this._childPointerEvents;
childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
@@ -1256,7 +1074,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
key={childLayout[Id] + (entry.replica || '')}
Document={childLayout}
containerViewPath={this.DocumentView?.().docViewPath}
- styleProvider={this.clusterStyleProvider}
+ styleProvider={this._clusters.styleProvider}
TemplateDataDocument={childData}
dragStarting={this.dragStarting}
dragEnding={this.dragEnding}
@@ -1333,7 +1151,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return this._props.addDocTab(docs, where);
});
- @observable _lightboxDoc: Opt<Doc> = undefined;
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);
@@ -1351,16 +1168,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const rotation = Cast(_rotation,'number',
!this.layoutDoc._rotation_jitter ? null
: NumCast(this.layoutDoc._rotation_jitter) * random(-1, 1, NumCast(x), NumCast(y)) );
- const childProps = { ...this._props, fieldKey: '', styleProvider: this.clusterStyleProvider };
+ const childProps = { ...this._props, fieldKey: '', styleProvider: this._clusters.styleProvider };
return {
x: isNaN(NumCast(x)) ? 0 : NumCast(x),
y: isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
autoDim,
rotation,
- color: Cast(color, 'string') ? StrCast(color) : this.clusterStyleProvider(childDoc, childProps, StyleProp.Color),
- backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, childProps, StyleProp.BackgroundColor),
- opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.clusterStyleProvider?.(childDoc, childProps, StyleProp.Opacity),
+ color: Cast(color, 'string') ? StrCast(color) : this._clusters.styleProvider(childDoc, childProps, StyleProp.Color),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this._clusters.styleProvider(childDoc, childProps, StyleProp.BackgroundColor),
+ opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this._clusters.styleProvider?.(childDoc, childProps, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
@@ -1436,9 +1253,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return [] as ViewDefResult[];
}
- @computed get layoutEngine() {
- return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
- }
@computed get doInternalLayoutComputation() {
TraceMobx();
const newPool = new Map<string, PoolData>();
@@ -1451,7 +1265,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
} // prettier-ignore
}
- @action
doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
const elements = computedElementData.slice();
Array.from(newPool.entries())
@@ -1464,7 +1277,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
})
);
- this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
+ this._clusters.initClusters();
return elements;
};
@@ -1489,7 +1302,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
childDocsFunc = () => this.childDocs;
- @action closeInfo = () => { Doc.IsInfoUIDisabled = true }; // prettier-ignore
+ closeInfo = action(() => { Doc.IsInfoUIDisabled = true }); // prettier-ignore
infoUI = () => (Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth ? null : <CollectionFreeFormInfoUI Document={this.Document} LayoutDoc={this.layoutDoc} childDocs={this.childDocsFunc} close={this.closeInfo} />);
componentDidMount() {
@@ -1566,40 +1379,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
- static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
- if (oldDiv.childNodes && newDiv) {
- for (let i = 0; i < oldDiv.childNodes.length; i++) {
- this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
- }
- }
- if (oldDiv instanceof HTMLCanvasElement) {
- if (oldDiv.className === 'collectionFreeFormView-grid') {
- const newCan = newDiv as HTMLCanvasElement;
- const parEle = newCan.parentElement as HTMLElement;
- parEle.removeChild(newCan);
- parEle.appendChild(document.createElement('div'));
- } else {
- const canvas = oldDiv;
- const img = document.createElement('img'); // create a Image Element
- try {
- img.src = canvas.toDataURL(); // image source
- } catch (e) {
- console.log(e);
- }
- img.style.width = canvas.style.width;
- img.style.height = canvas.style.height;
- const newCan = newDiv as HTMLCanvasElement;
- if (newCan) {
- const parEle = newCan.parentElement as HTMLElement;
- parEle.removeChild(newCan);
- parEle.appendChild(img);
- }
- }
- }
+ componentWillUnmount() {
+ this.dataDoc[this.autoResetFieldKey] && this.resetView();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
updateIcon = () =>
- CollectionFreeFormView.UpdateIcon(
+ UpdateIcon(
this.layoutDoc[Id] + '-icon' + new Date().getTime(),
this.DocumentView?.().ContentDiv!,
NumCast(this.layoutDoc._width),
@@ -1617,40 +1403,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
);
- public static UpdateIcon(
- filename: string,
- docViewContent: HTMLElement,
- width: number,
- height: number,
- panelWidth: number,
- panelHeight: number,
- scrollTop: number,
- realNativeHeight: number,
- noSuffix: boolean,
- replaceRootFilename: string | undefined,
- cb: (iconFile: string, nativeWidth: number, nativeHeight: number) => any
- ) {
- const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
- newDiv.style.width = width.toString();
- newDiv.style.height = height.toString();
- this.replaceCanvases(docViewContent, newDiv);
- const htmlString = new XMLSerializer().serializeToString(newDiv);
- const nativeWidth = width;
- const nativeHeight = height;
- return CreateImage(ClientUtils.prepend(''), document.styleSheets, htmlString, nativeWidth, (nativeWidth * panelHeight) / panelWidth, (scrollTop * panelHeight) / realNativeHeight)
- .then(async (dataUrl: any) => {
- const returnedFilename = await ClientUtils.convertDataUri(dataUrl, filename, noSuffix, replaceRootFilename);
- cb(returnedFilename as string, nativeWidth, nativeHeight);
- })
- .catch((error: any) => console.error('oops, something went wrong!', error));
- }
-
- componentWillUnmount() {
- this.dataDoc[this.autoResetFieldKey] && this.resetView();
- Object.values(this._disposers).forEach(disposer => disposer?.());
- }
-
- @action
onCursorMove = () => {
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@@ -1734,7 +1486,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
!Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
- !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
+ !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this._clusters.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
const options = ContextMenu.Instance.findByDescription('Options...');
@@ -1827,21 +1579,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
- @computed get placeholder() {
- return (
- <div className="collectionfreeformview-placeholder" style={{ background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.Document.annotationOn ? '' : this.Document.title?.toString()}</span>
- </div>
- );
- }
-
showPresPaths = () => SnappingManager.ShowPresPaths;
brushedView = () => this._brushedView;
- gridColor = () =>
- DashColor(lightOrDark(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor)))
- .fade(0.5)
- .toString();
- @computed get backgroundGrid() {
+ gridColor = () => DashColor(lightOrDark(this.backgroundColor)).fade(0.5).toString(); // prettier-ignore
+ nativeDim = () => this.nativeDimScaling;
+
+ brushView = action((viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => {
+ this._brushtimer1 && clearTimeout(this._brushtimer1);
+ this._brushtimer && clearTimeout(this._brushtimer);
+ this._brushedView = undefined;
+ this._brushtimer1 = setTimeout(
+ action(() => {
+ this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
+ this._brushtimer = setTimeout(action(() => { this._brushedView = undefined; }), holdTime); // prettier-ignore
+ }),
+ transTime + 1
+ );
+ });
+ lightboxPanelWidth = () => Math.max(0, this._props.PanelWidth() - 30);
+ 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 scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
+ this._props.isSelected() && !scrollable && e.preventDefault();
+ };
+ get backgroundGrid() {
return (
<div>
<CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render when taking snapshot of a dashboard and the background grid is on!!?
@@ -1886,7 +1649,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
- trySelectCluster={this.trySelectCluster}
+ trySelectCluster={this._clusters.trySelectCluster}
activeDocuments={this.getActiveDocuments}
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
@@ -1902,39 +1665,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</MarqueeView>
);
}
-
- @computed get nativeDimScaling() {
- if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 1;
- const nw = this.nativeWidth;
- const nh = this.nativeHeight;
- const hscale = nh ? this._props.PanelHeight() / nh : 1;
- const wscale = nw ? this._props.PanelWidth() / nw : 1;
- return wscale < hscale || (this._props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
- }
- nativeDim = () => this.nativeDimScaling;
-
- @action
- brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => {
- this._brushtimer1 && clearTimeout(this._brushtimer1);
- this._brushtimer && clearTimeout(this._brushtimer);
- this._brushedView = undefined;
- this._brushtimer1 = setTimeout(
- action(() => {
- this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
- this._brushtimer = setTimeout(action(() => { this._brushedView = undefined; }), holdTime); // prettier-ignore
- }),
- transTime + 1
+ get placeholder() {
+ return (
+ <div className="collectionfreeformview-placeholder" style={{ background: this.backgroundColor }}>
+ <span className="collectionfreeformview-placeholderSpan">{this.Document.annotationOn ? '' : this.Document.title?.toString()}</span>
+ </div>
);
- };
- lightboxPanelWidth = () => Math.max(0, this._props.PanelWidth() - 30);
- 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 scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
- this._props.isSelected() && !scrollable && e.preventDefault();
- };
- _oldWheel: any;
+ }
render() {
TraceMobx();
return (
@@ -1992,10 +1729,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
) : (
<>
{this._firstRender ? this.placeholder : this.marqueeView}
- {
- // eslint-disable-next-line no-use-before-define
- this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />
- }
+ {this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
{!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />}
</>
)}
@@ -2003,13 +1737,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
- render() {
- return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
- }
-}
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
!readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index 80ece7cc8..9df928581 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -13,7 +13,6 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { PinProps, ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProvider';
import { FieldView, FieldViewProps } from './FieldView';
-// eslint-disable-next-line import/extensions
import BigText from './LabelBigText';
import './LabelBox.scss';
import { PresBox } from './trails';
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 780ac3acb..4d5013437 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -6,7 +6,7 @@ import { observer } from 'mobx-react';
import * as Pdfjs from 'pdfjs-dist';
import 'pdfjs-dist/web/pdf_viewer.css';
import * as React from 'react';
-import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
+import { ClientUtils, returnFalse, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -28,7 +28,6 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { PinProps, ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
import { Colors } from '../global/globalEnums';
-// eslint-disable-next-line import/extensions
import { CreateImage } from './WebBoxRenderer';
import { PDFViewer } from '../pdf/PDFViewer';
import { SidebarAnnos } from '../SidebarAnnos';
@@ -184,7 +183,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() implem
const docViewContent = this.DocumentView?.().ContentDiv!;
const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
this._pdfViewer?._mainCont.current &&
- CollectionFreeFormView.UpdateIcon(
+ UpdateIcon(
filename,
docViewContent,
NumCast(this.layoutDoc._width),
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 52d9c3b31..d659d5e3c 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -65,7 +65,6 @@ import { FootnoteView } from './FootnoteView';
import './FormattedTextBox.scss';
import { findLinkMark, FormattedTextBoxComment } from './FormattedTextBoxComment';
import { buildKeymap, updateBullets } from './ProsemirrorExampleTransfer';
-// eslint-disable-next-line import/extensions
import { removeMarkWithAttrs } from './prosemirrorPatches';
import { RichTextMenu, RichTextMenuPlugin } from './RichTextMenu';
import { RichTextRules } from './RichTextRules';
diff --git a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
index 6c88a0d29..8799964b3 100644
--- a/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
+++ b/src/client/views/nodes/formattedText/ParagraphNodeSpec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable import/extensions */
import { Node, DOMOutputSpec } from 'prosemirror-model';
import clamp from '../../../util/clamp';
import convertToCSSPTValue from '../../../util/convertToCSSPTValue';
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index c107a6724..72a2b706d 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -14,7 +14,6 @@ import { CollectionView } from '../../collections/CollectionView';
import { ContextMenu } from '../../ContextMenu';
import { KeyValueBox } from '../KeyValueBox';
import { FormattedTextBox } from './FormattedTextBox';
-// eslint-disable-next-line import/extensions
import { wrappingInputRule } from './prosemirrorPatches';
import { RichTextMenu } from './RichTextMenu';
import { schema } from './schema_rts';