aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
diff options
context:
space:
mode:
authorSophie Zhang <sophie_zhang@brown.edu>2024-02-22 10:22:58 -0500
committerSophie Zhang <sophie_zhang@brown.edu>2024-02-22 10:22:58 -0500
commit3f33a680af31a04b58c6163fda53a80a737837c6 (patch)
treeb84da40da1c13c8ab8ab8cd763b69ac5b39ce93c /src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
parent70cf5ad795055c1f628c918b08a13a96e4ab89a6 (diff)
parentcf85ee4ea73985529a16321d671d893ddb862439 (diff)
Merge branch 'master' into sophie-ai-images
Diffstat (limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx112
1 files changed, 49 insertions, 63 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index fb9d42bc7..1e0840495 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,11 +5,12 @@ import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -22,8 +23,8 @@ import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
-import { FollowLinkScript } from '../../../util/LinkFollower';
import { ReplayMovements } from '../../../util/ReplayMovements';
+import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
import { freeformScrollMode, SettingsManager } from '../../../util/SettingsManager';
@@ -39,7 +40,7 @@ import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { FocusViewOptions, FieldViewProps } from '../../nodes/FieldView';
+import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../../nodes/trails/PresBox';
import { CreateImage } from '../../nodes/WebBoxRenderer';
@@ -78,6 +79,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return 'CollectionFreeFormView(' + this.Document.title?.toString() + ')';
} // this makes mobx trace() statements more descriptive
+ @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
+ @computed get paintFunc() {
+ const field = this.layoutDoc[this.fieldKey];
+ const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as Field)).trim();
+ return !paintFunc
+ ? ''
+ : paintFunc.includes('dashDiv')
+ ? `const dashDiv = document.querySelector('#${this._paintedId}');
+ (async () => { ${paintFunc} })()`
+ : paintFunc;
+ }
constructor(props: any) {
super(props);
makeObservable(this);
@@ -230,6 +242,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
+ viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
fitContentOnce = () => {
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
@@ -301,10 +314,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focus = (anchor: Doc, options: FocusViewOptions) => {
if (this._lightboxDoc) return;
if (anchor === this.Document) {
- if (options.willZoomCentered && options.zoomScale) {
- this.fitContentOnce();
- options.didMove = true;
- }
+ // if (options.willZoomCentered && options.zoomScale) {
+ // this.fitContentOnce();
+ // options.didMove = true;
+ // }
}
if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return;
const xfToCollection = options?.docTransform ?? Transform.Identity();
@@ -368,28 +381,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!d._keepZWhenDragged && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
- if (this.layoutDoc._autoArrange || de.metaKey) {
- const sorted = this.childLayoutPairs.slice().sort((a, b) => NumCast(a.layout.y) - NumCast(b.layout.y));
- sorted.splice(
- sorted.findIndex(pair => pair.layout === refDoc),
- 1
- );
- if (sorted.length && refDoc && NumCast(sorted[0].layout.y) < NumCast(refDoc.y)) {
- const topIndexed = NumCast(refDoc.y) < NumCast(sorted[0].layout.y) + NumCast(sorted[0].layout._height) / 2;
- const deltay = sorted.length > 1 ? NumCast(refDoc.y) - (NumCast(sorted[0].layout.y) + (topIndexed ? 0 : NumCast(sorted[0].layout._height))) : 0;
- const deltax = sorted.length > 1 ? NumCast(refDoc.x) - NumCast(sorted[0].layout.x) : 0;
-
- let lastx = NumCast(refDoc.x);
- let lasty = NumCast(refDoc.y) + (topIndexed ? 0 : NumCast(refDoc._height));
- runInAction(() =>
- sorted.slice(1).forEach((pair, i) => {
- lastx = pair.layout.x = lastx + deltax;
- lasty = (pair.layout.y = lasty + deltay) + (topIndexed ? 0 : NumCast(pair.layout._height));
- })
- );
- }
- }
-
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
return true;
}
@@ -416,27 +407,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const [x, y] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
let added = false;
// do nothing if link is dropped into any freeform view parent of dragged document
- const source =
- !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.Document
- ? Docs.Create.TextDocument('', { _width: 200, _height: 75, x, y, title: 'dropped annotation' })
- : Docs.Create.FontIconDocument({
- title: 'anchor',
- icon_label: '',
- followLinkToggle: true,
- icon: 'map-pin',
- x,
- y,
- backgroundColor: '#ACCEF7',
- layout_hideAllLinks: true,
- layout_hideLinkButton: true,
- _width: 15,
- _height: 15,
- _xPadding: 0,
- onClick: FollowLinkScript(),
- });
+ const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x, y, title: 'dropped annotation' });
added = this._props.addDocument?.(source) ? true : false;
de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
-
+ if (de.complete.linkDocument) {
+ de.complete.linkDocument.layout_isSvg = true;
+ this.addDocument(de.complete.linkDocument);
+ }
e.stopPropagation();
!added && e.preventDefault();
return added;
@@ -1005,7 +982,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.isAnnotationOverlay && clamp) {
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
- const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
+ const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc && doc.type !== DocumentType.LINK);
const measuredDocs = docs
.map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc._width), height: NumCast(doc._height) } }))
.filter(({ pos, size }) => pos && size)
@@ -1232,6 +1209,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onKey={this.onKeyDown}
onDoubleClickScript={this.onChildDoubleClickHandler}
onBrowseClickScript={this.onBrowseClickHandler}
+ bringToFront={this.bringToFront}
ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
PanelWidth={childLayout[Width]}
PanelHeight={childLayout[Height]}
@@ -1487,6 +1465,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
})
);
+
+ this._disposers.paintFunc = reaction(
+ () => ({ code: this.paintFunc, first: this._firstRender, width: this.Document._width, height: this.Document._height }),
+ ({ code, first }) => {
+ if (!code.includes('dashDiv')) {
+ const script = CompileScript(code, { params: { docView: 'any' }, typecheck: false, editable: true });
+ if (script.compiled) script.run({ this: this.DocumentView?.() });
+ } else code && !first && eval(code);
+ },
+ { fireImmediately: true }
+ );
+
this._disposers.layoutElements = reaction(
// layoutElements can't be a computed value because doLayoutComputation() is an action that has side effect of updating clusters
() => this.doInternalLayoutComputation,
@@ -1630,9 +1620,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
toggleResetView = () => {
this.dataDoc[this.autoResetFieldKey] = !this.dataDoc[this.autoResetFieldKey];
if (this.dataDoc[this.autoResetFieldKey]) {
- this.dataDoc[this.panXFieldKey + '_reset'] = this.dataDoc[this.panXFieldKey];
- this.dataDoc[this.panYFieldKey + '_reset'] = this.dataDoc[this.panYFieldKey];
- this.dataDoc[this.scaleFieldKey + '_reset'] = this.dataDoc[this.scaleFieldKey];
+ 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];
}
};
@@ -1737,12 +1727,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const appearanceItems = appearance && 'subitems' in appearance ? 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' });
- !Doc.noviceMode &&
- appearanceItems.push({
- description: 'Toggle auto arrange',
- event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
- icon: 'compress-arrows-alt',
- });
if (this._props.setContentViewBox === emptyFunction) {
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
return;
@@ -1871,7 +1855,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
get pannableContents() {
- this.incrementalRender();
+ this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
return (
<CollectionFreeFormPannableContents
Document={this.Document}
@@ -1949,6 +1933,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (
<div
className="collectionfreeformview-container"
+ id={this._paintedId}
ref={r => {
this.createDashEventsTarget(r);
this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
@@ -1970,7 +1955,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
width: `${100 / (this.nativeDimScaling || 1)}%`,
height: this._props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
}}>
- {this._lightboxDoc ? (
+ {this.paintFunc ? null : this._lightboxDoc ? (
<div style={{ padding: 15, width: '100%', height: '100%' }}>
<DocumentView
{...this._props}
@@ -2065,6 +2050,7 @@ ScriptingGlobals.add(function sendToBack(doc: Doc) {
SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
});
ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
+ // creating a dataviz doc to represent the schema table
SelectionManager.Views.forEach(view => {
if (!view.layoutDoc.schema_columnKeys) {
view.layoutDoc.schema_columnKeys = new List<string>(['title', 'type', 'author', 'author_date']);
@@ -2085,15 +2071,15 @@ ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
csvRows.push(eachRow);
}
const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' });
- const options = { x: 0, y: -300, title: 'schemaTable', _width: 300, _height: 100, type: 'text/csv' };
+ const options = { x: 0, y: 0, title: 'schemaTable', _width: 300, _height: 100, type: 'text/csv' };
const file = new File([blob], 'schemaTable', options);
const loading = Docs.Create.LoadingDocument(file, options);
loading.presentation_openInLightbox = true;
DocUtils.uploadFileToDoc(file, {}, loading);
+ // holds the doc in a popup until it is dragged onto a canvas
if (view.ComponentView?.addDocument) {
- // loading.dataViz_fromSchema = true;
- loading.dataViz_asSchema = view.layoutDoc;
+ loading._dataViz_asSchema = view.layoutDoc;
SchemaCSVPopUp.Instance.setView(view);
SchemaCSVPopUp.Instance.setTarget(view.layoutDoc);
SchemaCSVPopUp.Instance.setDataVizDoc(loading);