aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx13
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx9
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss6
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx25
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx39
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx11
-rw-r--r--src/client/views/collections/CollectionMenu.tsx24
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx34
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx20
-rw-r--r--src/client/views/collections/CollectionPileView.tsx1
-rw-r--r--src/client/views/collections/CollectionPivotView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx22
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx63
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx40
-rw-r--r--src/client/views/collections/CollectionSubView.tsx64
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx2
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx30
-rw-r--r--src/client/views/collections/CollectionView.tsx23
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.tsx60
-rw-r--r--src/client/views/collections/TabDocView.scss12
-rw-r--r--src/client/views/collections/TabDocView.tsx67
-rw-r--r--src/client/views/collections/TreeView.tsx43
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx142
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx34
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx33
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx33
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx9
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx5
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx113
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx104
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss12
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx35
-rw-r--r--src/client/views/collections/collectionSchema/SchemaCellField.tsx69
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx99
42 files changed, 697 insertions, 658 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 756b37f99..d5edc3e0b 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -23,7 +23,8 @@ import { undoable, UndoManager } from '../../util/UndoManager';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
-import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { DocumentViewProps } from '../nodes/DocumentContentsView';
+import { DocumentView } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
@@ -39,7 +40,6 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
export class CollectionCardView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [key: string]: IReactionDisposer } = {};
- private _oldWheel: HTMLElement | null = null;
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
private _draggerRef = React.createRef<HTMLDivElement>();
@@ -55,13 +55,7 @@ export class CollectionCardView extends CollectionSubView() {
}
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
- }
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(ele, this._props.isContentActive);
};
@computed get cardWidth() {
return NumCast(this.layoutDoc._cardWidth, 50);
@@ -486,7 +480,6 @@ export class CollectionCardView extends CollectionSubView() {
);
});
}
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
docViewProps = (): DocumentViewProps => ({
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 9c8ef5519..755e31925 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -25,7 +25,6 @@ const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = requi
@observer
export class CollectionCarousel3DView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
- private _oldWheel: HTMLElement | null = null;
constructor(props: SubCollectionViewProps) {
super(props);
@@ -44,10 +43,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
}
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(ele, this._props.isContentActive);
};
@computed get scrollSpeed() {
@@ -101,7 +97,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
const docs = DocListCast(this.Document[this.fieldKey]);
if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
- const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+ const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor)!);
options.didMove = newIndex !== this.layoutDoc._carousel_index;
options.didMove && (this.layoutDoc._carousel_index = newIndex);
}
@@ -221,7 +217,6 @@ export class CollectionCarousel3DView extends CollectionSubView() {
return this.panelWidth() * (1 - index);
}
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
curDoc = () => this.carouselItems[NumCast(this.layoutDoc._carousel_index)]?.layout;
answered = (correct: boolean) => (!correct || !this.curDoc() || NumCast(this.layoutDoc._carousel_index) === this.carouselItems.length - 1) && this.changeSlide(1);
docViewProps = () => ({
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 544b3e262..962b590c8 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,12 +1,16 @@
.collectionCarouselView-outer {
height: 100%;
- position: relative;
+ position: absolute;
overflow: hidden;
display: flex;
+ transform-origin: top left;
+
.collectionCarouselView-caption {
height: 50;
display: inline-block;
width: 100%;
+ bottom: 0;
+ position: absolute;
}
.collectionCarouselView-image {
height: calc(100% - 50px);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index a7d217076..ac1981012 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -21,7 +21,6 @@ import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
- _oldWheel: HTMLElement | null = null;
_fadeTimer: NodeJS.Timeout | undefined;
@observable _last_index = this.carouselIndex;
@observable _last_opacity = 1;
@@ -43,10 +42,7 @@ export class CollectionCarouselView extends CollectionSubView() {
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
}
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(ele, this._props.isContentActive);
};
@computed get captionMarginX(){ return NumCast(this.layoutDoc.caption_xMargin, 50); } // prettier-ignore
@@ -77,7 +73,8 @@ export class CollectionCarouselView extends CollectionSubView() {
focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
const docs = DocListCast(this.Document[this.fieldKey]);
if (anchor.type === DocumentType.CONFIG || docs.includes(anchor)) {
- const newIndex = anchor.config_carousel_index ?? docs.getIndex(DocCast(anchor.annotationOn, anchor));
+ const annoOn = DocCast(anchor.annotationOn, anchor);
+ const newIndex = NumCast(anchor.config_carousel_index, (annoOn && docs.getIndex(annoOn)) ?? 0);
options.didMove = newIndex !== this.layoutDoc._carousel_index;
options.didMove && (this.layoutDoc._carousel_index = newIndex);
}
@@ -114,13 +111,14 @@ export class CollectionCarouselView extends CollectionSubView() {
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
: undefined; // prettier-ignore
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
+
renderDoc = (doc: Doc, showCaptions: boolean, overlayFunc?: (r: DocumentView | null) => void) => {
return (
<DocumentView
{...this._props}
ref={overlayFunc}
Document={doc}
+ TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined}
NativeWidth={returnZero}
NativeHeight={returnZero}
fitWidth={this._props.childLayoutFitWidth}
@@ -136,7 +134,6 @@ export class CollectionCarouselView extends CollectionSubView() {
renderDepth={this._props.renderDepth + 1}
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
- TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
childFilters={this.childDocFilters}
focus={this.focus}
hideDecorations={BoolCast(this.layoutDoc.layout_hideDecorations)}
@@ -182,7 +179,15 @@ export class CollectionCarouselView extends CollectionSubView() {
}
@computed get content() {
- const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
+ const captionProps = {
+ ...this._props, //
+ NativeScaling: returnOne,
+ PanelWidth: this.captionWidth,
+ fieldKey: 'caption',
+ setHeight: undefined,
+ setContentView: undefined,
+ noSidebar: true,
+ };
const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
return !this.curDoc() ? null : (
<>
@@ -242,11 +247,9 @@ export class CollectionCarouselView extends CollectionSubView() {
color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
left: NumCast(this.layoutDoc._xMargin),
top: NumCast(this.layoutDoc._yMargin),
- transformOrigin: 'top left',
transform: `scale(${this.nativeScaling()})`,
width: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._xMargin)) / this.nativeScaling()}px)`,
height: `calc(${100 / this.nativeScaling()}% - ${(2 * NumCast(this.layoutDoc._yMargin)) / this.nativeScaling()}px)`,
- position: 'relative',
}}>
{this.content}
{this.navButtons}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index e51bc18ef..13c3eb72f 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -5,7 +5,7 @@ import * as ReactDOM from 'react-dom/client';
import ResizeObserver from 'resize-observer-polyfill';
import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, incrementTitleCopy, returnTrue, UpdateIcon } from '../../../ClientUtils';
import { Doc, DocListCast, Field, Opt } from '../../../fields/Doc';
-import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -60,7 +60,7 @@ export class CollectionDockingView extends CollectionSubView() {
public get HasFullScreen() {
return this._goldenLayout._maximisedItem !== null;
}
- static _highlightStyleSheet = addStyleSheet();
+ static _highlightStyleSheet = addStyleSheet().sheet;
constructor(props: SubCollectionViewProps) {
super(props);
@@ -443,7 +443,7 @@ export class CollectionDockingView extends CollectionSubView() {
window.addEventListener('mouseup', this.onPointerUp);
if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
- if (className.includes('lm_maximise')) {
+ if (className.includes('lm_maximise') || className.includes('lm_close_tab')) {
// this._flush = UndoManager.StartBatch('tab maximize');
} else {
const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
@@ -473,15 +473,15 @@ export class CollectionDockingView extends CollectionSubView() {
}
return undefined;
}
- public static async TakeSnapshot(doc: Doc | undefined, clone = false) {
+ public static TakeSnapshot(doc: Doc | undefined, clone = false) {
if (!doc) return undefined;
let json = StrCast(doc.dockingConfig);
if (clone) {
- const cloned = await Doc.MakeClone(doc);
+ const cloned = Doc.MakeClone(doc);
Array.from(cloned.map.entries()).forEach(entry => {
json = json.replace(entry[0], entry[1][Id]);
});
- cloned.clone[DocData].dockingConfig = json;
+ cloned.clone.$dockingConfig = json;
return DashboardView.openDashboard(cloned.clone);
}
const matches = json.match(/"documentId":"[a-z0-9-]+"/g);
@@ -495,7 +495,7 @@ export class CollectionDockingView extends CollectionSubView() {
const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
if (newtabdocs.length) {
- newtab[DocData].data = new List<Doc>(newtabdocs);
+ newtab.$data = new List<Doc>(newtabdocs);
newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
}
json = json.replace(origtab[Id], newtab[Id]);
@@ -503,9 +503,8 @@ export class CollectionDockingView extends CollectionSubView() {
});
const dashboardDoc = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
- dashboardDoc.pane_count = 1;
- dashboardDoc.myOverlayDocs = new List<Doc>();
- dashboardDoc.myPublishedDocs = new List<Doc>();
+ dashboardDoc.$myOverlayDocs = new List<Doc>();
+ dashboardDoc.$myPublishedDocs = new List<Doc>();
DashboardView.SetupDashboardTrails();
DashboardView.SetupDashboardCalendars(); // Zaul TODO: needed?
@@ -525,11 +524,11 @@ export class CollectionDockingView extends CollectionSubView() {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
const dashDoc = tab.DashDoc;
if (dashDoc && ![DocumentType.PRES].includes(dashDoc.type) && !tab.contentItem.config.props.keyValue) {
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', dashDoc, undefined, undefined, true);
// if you close a tab that is not embedded somewhere else (an embedded Doc can be opened simultaneously in a tab), then add the tab to recently closed
if (dashDoc.embedContainer === this.Document) dashDoc.embedContainer = undefined;
if (!dashDoc.embedContainer) {
- Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
+ Doc.MyRecentlyClosed && Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
Doc.RemoveEmbedding(dashDoc, dashDoc);
}
}
@@ -555,15 +554,15 @@ export class CollectionDockingView extends CollectionSubView() {
stack.header?.element.on('mousedown', (e: MouseEvent) => {
const dashboard = Doc.ActiveDashboard;
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
- dashboard.pane_count = NumCast(dashboard.pane_count) + 1;
+ dashboard.$myPaneCount = NumCast(dashboard.$myPaneCount) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
_width: this._props.PanelWidth(),
_height: this._props.PanelHeight(),
_freeform_backgroundGrid: true,
_layout_fitWidth: true,
- title: `Untitled Tab ${NumCast(dashboard.pane_count)}`,
+ title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.Document, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
@@ -572,15 +571,15 @@ export class CollectionDockingView extends CollectionSubView() {
const addNewDoc = undoable(() => {
const dashboard = Doc.ActiveDashboard;
if (dashboard) {
- dashboard.pane_count = NumCast(dashboard.pane_count) + 1;
+ dashboard.$myPaneCount = NumCast(dashboard.$myPaneCount) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
_width: this._props.PanelWidth(),
_height: this._props.PanelHeight(),
_layout_fitWidth: true,
_freeform_backgroundGrid: true,
- title: `Untitled Tab ${NumCast(dashboard.pane_count)}`,
+ title: `Untitled Tab ${NumCast(dashboard.$myPaneCount)}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ Doc.MyHeaderBar && Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
inheritParentAcls(this.dataDoc, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
@@ -669,7 +668,5 @@ ScriptingGlobals.add(
);
// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(async function snapshotDashboard() {
- const batch = UndoManager.StartBatch('snapshot');
- await CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
- batch.end();
+ undoable(() => CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard), 'snapshot dashboard');
}, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 996626575..89ccf5a0f 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -5,7 +5,6 @@ import * as React from 'react';
import { returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction, numberRange } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
import { Docs } from '../../documents/Documents';
@@ -23,7 +22,7 @@ import './CollectionStackingView.scss';
interface CMVFieldRowProps {
rows: () => number;
headings: () => object[];
- Document: Doc;
+ Doc: Doc;
chromeHidden?: boolean;
heading: string;
headingObject: SchemaHeaderField | undefined;
@@ -74,7 +73,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
createRowDropRef = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
- if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
+ if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Doc);
else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -164,7 +163,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const { pivotField } = this._props;
const newDoc = Docs.Create.TextDocument(value, { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
DocumentView.SetSelectOnLoad(newDoc);
- pivotField && (newDoc[DocData][pivotField] = this.getValue(this._props.heading));
+ pivotField && (newDoc['$' + pivotField] = this.getValue(this._props.heading));
const docs = this._props.parent.childDocList;
return docs ? !!docs.splice(0, 0, newDoc) : this._props.parent._props.addDocument?.(newDoc) || false; // should really extend addDocument to specify insertion point (at beginning of list)
};
@@ -190,7 +189,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
};
headerMove = (e: PointerEvent) => {
- const embedding = Doc.MakeEmbedding(this._props.Document);
+ const embedding = Doc.MakeEmbedding(this._props.Doc);
const key = this._props.pivotField;
let value = this.getValue(this.heading);
value = typeof value === 'string' ? `"${value}"` : value;
@@ -290,7 +289,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const key = this._props.pivotField;
const evContents = this.heading ? this.heading : this._props.type && this._props.type === 'number' ? '0' : `NO ${key.toUpperCase()} VALUE`;
const editableHeaderView = <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine />;
- return this._props.Document.miniHeaders ? (
+ return this._props.Doc.miniHeaders ? (
<div className="collectionStackingView-miniHeader">{editableHeaderView}</div>
) : !this._props.headingObject ? null : (
<div className="collectionStackingView-sectionHeader" ref={this._headerRef}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index de999c91a..7f835938b 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-unused-class-component-methods */
-/* eslint-disable react/sort-comp */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from '@dash/components';
@@ -9,7 +7,6 @@ import * as React from 'react';
import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { RichTextField } from '../../../fields/RichTextField';
@@ -26,6 +23,7 @@ import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider';
import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import './CollectionMenu.scss';
import { CollectionLinearView } from './collectionLinear';
+import { SettingsManager } from '../../util/SettingsManager';
interface CollectionMenuProps {
panelHeight: () => number;
@@ -48,7 +46,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
makeObservable(this);
CollectionMenu.Instance = this;
this._canFade = false; // don't let the inking menu fade away
- this.Pinned = Cast(Doc.UserDoc().menuCollections_pinned, 'boolean', true);
+ this.Pinned = BoolCast(Doc.UserDoc().menuCollections_pinned, true);
this.jumpTo(300, 300);
}
@@ -121,7 +119,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
<div className="hardCodedButtons">
<Toggle
toggleType={ToggleType.BUTTON}
- type={Type.PRIM}
+ type={Type.TERT}
color={SnappingManager.userColor}
onClick={this.props.toggleTopBar}
toggleStatus={this.props.topBarHeight() > 0}
@@ -130,7 +128,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
/>
<Toggle
toggleType={ToggleType.BUTTON}
- type={Type.PRIM}
+ type={Type.TERT}
color={SnappingManager.userColor}
onClick={this._props.togglePropertiesFlyout}
toggleStatus={SnappingManager.PropertiesWidth > 0}
@@ -141,26 +139,22 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
);
// dash col linear view buttons
- const contMenuButtons = (
+ return (
<div
className="collectionMenu-container"
style={{
- background: SnappingManager.userBackgroundColor,
+ background: SettingsManager.userBackgroundColor,
// borderColor: SettingsManager.userColor
}}>
{this.contMenuButtons}
{hardCodedButtons}
</div>
);
-
- return contMenuButtons;
}
}
interface CollectionViewMenuProps {
- // eslint-disable-next-line react/no-unused-prop-types
type: CollectionViewType;
- // eslint-disable-next-line react/no-unused-prop-types
fieldKey: string;
docView: DocumentView;
}
@@ -185,7 +179,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
try {
formatStr && JSON.parse(formatStr);
- } catch (e) {
+ } catch {
formatStr = '';
}
if (source.length === 1 && formatStr) {
@@ -213,7 +207,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
title: 'set content',
script: 'getProto(this.target).data = copyField(this.source);',
immediate: undoable((source: Doc[]) => {
- this.target[DocData].data = new List<Doc>(source);
+ this.target.$data = new List<Doc>(source);
}, ''),
initialize: emptyFunction,
};
@@ -275,7 +269,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
initialize: (button: Doc) => {
const activeDash = Doc.ActiveDashboard;
if (activeDash) {
- button.target_childFilters = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as ObjectField) : undefined;
+ button.target_childFilters = (Doc.MySearcher?._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher?._childFilters || activeDash._childFilters) as ObjectField) : undefined;
button.target_searchFilterDocs = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs) : undefined;
}
},
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index c499bd288..7f639a11e 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -32,6 +32,7 @@ import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn
import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { Property } from 'csstype';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
/**
* CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1)
@@ -257,11 +258,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const noteTakingDocTransform = () => this.getDocTransform(doc, dref);
return (
<DocumentView
- ref={r => {
- dref = r || undefined;
- }}
+ ref={r => (dref = r || undefined)}
Document={doc}
- TemplateDataDocument={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
+ TemplateDataDocument={doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined}
pointerEvents={this.blockPointerEventsWhenDragging}
renderDepth={this._props.renderDepth + 1}
PanelWidth={width}
@@ -270,7 +269,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
containerViewPath={this.childContainerViewPath}
fitWidth={this._props.childLayoutFitWidth}
isContentActive={emptyFunction}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
// TODO: change this from a prop to a parameter passed into a function
dontHideOnDrag
isDocumentActive={this.isContentActive}
@@ -322,11 +321,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
};
- // how to get the height of a document. Nothing special here.
getDocHeight(d?: Doc) {
if (!d || d.hidden) return 0;
- const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
- const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
@@ -437,11 +435,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
@undoBatch
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
+ if ((e.ctrlKey || textBox.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
e.stopPropagation?.();
- const newDoc = Doc.MakeCopy(fieldProps.Document, true);
- newDoc[DocData].text = undefined;
+ const newDoc = Doc.MakeCopy(textBox.Document, true);
+ newDoc.$text = undefined;
DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
@@ -543,8 +541,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.Document}
- TemplateDataDocument={this._props.TemplateDataDocument}
+ Doc={this.Document}
+ TemplateDataDoc={this._props.TemplateDataDocument}
resizeColumns={this.resizeColumns}
renderChildren={this.children}
numGroupColumns={this.numGroupColumns}
@@ -567,7 +565,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
@undoBatch
remColumn = (value: SchemaHeaderField) => {
- const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const colHdrData = Array.from(Cast(this.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
if (value) {
const index = colHdrData.indexOf(value);
index !== -1 && colHdrData.splice(index, 1);
@@ -586,7 +584,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
return undefined;
});
- const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
const newColWidth = 1 / (this.numGroupColumns + 1);
columnHeaders.push(new SchemaHeaderField(value, undefined, undefined, newColWidth));
value && this.resizeColumns(columnHeaders);
@@ -643,7 +641,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
const headers = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
- headers.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
+ headers?.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
};
// renderedSections returns a list of all of the JSX elements used (columns and dividers). If the view
@@ -701,7 +699,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
{this.renderedSections}
<div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={undoable(fieldKey => {
this.layoutDoc._pivotField = fieldKey;
this.removeEmptyColumns();
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 40b3f9ef2..461689a70 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -21,8 +21,8 @@ import './CollectionNoteTakingView.scss';
import { DocumentView } from '../nodes/DocumentView';
interface CSVFieldColumnProps {
- Document: Doc;
- TemplateDataDocument: Opt<Doc>;
+ Doc: Doc;
+ TemplateDataDoc: Opt<Doc>;
backgroundColor?: () => string | undefined;
docList: Doc[];
heading: string;
@@ -65,7 +65,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
// columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- if (this._props.Document._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
+ if (this._props.Doc._notetaking_columns_autoSize) return this._props.availableWidth / (this._props.colHeaderData?.length || 1);
if (!this._props.colHeaderData || !this._props.headingObject || this._props.colHeaderData.length === 1) return `${(this._props.availableWidth / this._props.PanelWidth()) * 100}%`;
const i = this._props.colHeaderData.findIndex(hd => hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
@@ -81,7 +81,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
- if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
+ if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc);
else if (this._ele) this.props.refList.slice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -155,9 +155,9 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
// all docs are added to the column directly to the left.
@undoBatch
deleteColumn = () => {
- const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ const colHdrData = Array.from(Cast(this._props.Doc[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), [])!);
if (this._props.headingObject) {
- // this._props.docList.forEach(d => (d[DocData][this._props.pivotField] = undefined));
+ // this._props.docList.forEach(d => (d['$'+this._props.pivotField] = undefined));
colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
this._props.resizeColumns(colHdrData);
}
@@ -184,11 +184,11 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
);
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this._props.Document)[name] = '';
+ Doc.GetProto(this._props.Doc)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
this._props.addDocument?.(created);
}
@@ -267,7 +267,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
style={{
width: this.columnWidth,
background: this._hover && SnappingManager.IsDragging ? '#b4b4b4' : 'inherit',
- marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Document.xMargin) : 0,
+ marginLeft: this._props.headings().findIndex(h => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Doc.xMargin) : 0,
}}>
<div className="collectionNoteTakingViewFieldColumn" key={this._heading} ref={this.createColumnDropRef}>
{this.innards}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index eea128803..5a85c8ee3 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -67,7 +67,6 @@ export class CollectionPileView extends CollectionSubView() {
return (
<div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props} //
layoutEngine={this.layoutEngine}
addDocument={this.addPileDoc}
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 2600c0f57..4736070c3 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -103,7 +103,7 @@ export class CollectionPivotView extends CollectionSubView() {
{this.contents}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={fieldKey => {
this.layoutDoc._pivotField = fieldKey;
}}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index c3047e5fb..4e7e19548 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -5,7 +5,6 @@ import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../ClientUtils';
import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -187,13 +186,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@computed get rangeClick() {
// prettier-ignore
return ScriptField.MakeFunction('stackedTimeline.clickAnchor(this, clientX)',
- { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: 'string' /* should be CollectionStackedTimeline */ }
- )!;
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as unknown as string })!; // NOTE: scripts can't serialize a run-time React component as captured variable BUT this script will not be serialized so we can "stuff" anything we want in the capture variable
}
@computed get rangePlay() {
// prettier-ignore
return ScriptField.MakeFunction('stackedTimeline.playOnClick(this, clientX)',
- { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: 'string' /* should be CollectionStackedTimeline */})!;
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as unknown as string })!; // NOTE: scripts can't serialize a run-time React component as captured variable BUT this script will not be serialized so we can "stuff" anything we want in the capture variable
}
rangeClickScript = () => this.rangeClick;
rangePlayScript = () => this.rangePlay;
@@ -268,13 +266,13 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setupMoveUpEvents(
this,
e,
- action(() => {
+ action(movEv => {
if (!wasSelecting) {
this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
wasSelecting = true;
this._timelineWrapper && (this._timelineWrapper.style.cursor = 'ew-resize');
}
- this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
+ this._markerEnd = this.toTimeline(movEv.clientX - rect.x, rect.width);
return false;
}),
action((upEvent, movement, isClick) => {
@@ -433,11 +431,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
_isTimelineLabel: true,
layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
});
- anchor[DocData][startTag] = anchorStartTime;
- anchor[DocData][endTag] = anchorEndTime;
+ anchor['$' + startTag] = anchorStartTime;
+ anchor['$' + endTag] = anchorEndTime;
if (addAsAnnotation) {
if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
- Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
+ Cast(dataDoc[fieldKey], listSpec(Doc), [])!.push(anchor);
} else {
dataDoc[fieldKey] = new List<Doc>([anchor]);
}
@@ -805,9 +803,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
};
resetTitle = () => {
- this._props.mark[DocData].title = ComputedField.MakeFunction(
- `["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`
- );
+ this._props.mark.$title = ComputedField.MakeFunction(`["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"]`);
};
// context menu
contextMenuItems = () => {
@@ -844,7 +840,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
styleProvider={this._props.styleProvider}
renderDepth={this._props.renderDepth + 1}
LayoutTemplate={undefined}
- LayoutTemplateString={LabelBox.LayoutString('data')}
+ LayoutTemplateString={LabelBox.LayoutString('title')}
isDocumentActive={this._props.isDocumentActive}
PanelWidth={width}
PanelHeight={height}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6bbd43b1b..be570564b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -5,7 +5,6 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -35,6 +34,7 @@ import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { computedFn } from 'mobx-utils';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
export type collectionStackingViewProps = {
sortFunc?: (a: Doc, b: Doc) => number;
@@ -55,7 +55,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
_docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
// Doesn't look like this field is being used anywhere. Obsolete?
_columnStart: number = 0;
- _oldWheel: HTMLElement | null = null;
@observable _refList: HTMLElement[] = [];
// map of node headers to their heights. Used in Masonry
@@ -112,7 +111,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@computed get columnWidth() {
const availableWidth = this._props.PanelWidth() - 2 * this.xMargin;
const cwid = availableWidth / (NumCast(this.Document._layout_columnCount) || this._props.PanelWidth() / NumCast(this.Document._layout_columnWidth, this._props.PanelWidth() / 4));
- return Math.min(availableWidth, this.isStackingView ? Number.MAX_VALUE : cwid - this.gridGap);
+ return Math.min(availableWidth, this.isStackingView ? availableWidth / (this.numGroupColumns || 1) : cwid - this.gridGap);
}
@computed get NodeWidth() {
@@ -310,17 +309,17 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return this._props.styleProvider?.(doc, props, property);
};
@undoBatch
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
if (['Enter'].includes(e.key) && e.ctrlKey) {
e.stopPropagation?.();
- const layoutFieldKey = StrCast(fieldProps.fieldKey);
- const newDoc = Doc.MakeCopy(fieldProps.Document, true);
- const dataField = fieldProps.Document[Doc.LayoutFieldKey(newDoc)];
- newDoc[DocData][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
- if (layoutFieldKey !== 'layout' && fieldProps.Document[layoutFieldKey] instanceof Doc) {
- newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey];
+ const layoutFieldKey = StrCast(textBox.fieldKey);
+ const newDoc = Doc.MakeCopy(textBox.Document, true);
+ const dataField = textBox.Document[Doc.LayoutDataKey(newDoc)];
+ newDoc['$' + Doc.LayoutDataKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+ if (layoutFieldKey !== 'layout' && textBox.Document[layoutFieldKey] instanceof Doc) {
+ newDoc[layoutFieldKey] = textBox.Document[layoutFieldKey];
}
- newDoc[DocData].text = undefined;
+ newDoc.$text = undefined;
DocumentView.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
@@ -340,10 +339,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
isChildButtonContentActive = () => (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined);
@observable docRefs = new ObservableMap<Doc, DocumentView>();
- childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
+ childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null) ?? null);
// this is what renders the document that you see on the screen
// called in Children: this actually adds a document to our children list
- getDisplayDoc(doc: Doc, trans: () => string, count: number) {
+ getDisplayDoc = (doc: Doc, trans: () => string, count: number) => {
const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined;
this._docXfs.push({ stackedDocTransform: this.getDocTransform(doc), width: this.getDocWidth(doc), height: this.getDocHeight(doc) });
return count > this._renderCount ? null : (
@@ -359,7 +358,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
containerViewPath={this.childContainerViewPath}
fitWidth={this.childFitWidth}
isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
DataTransition={trans}
isDocumentActive={this.isContentActive}
LayoutTemplate={this._props.childLayoutTemplate}
@@ -384,6 +383,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
searchFilterDocs={this.searchFilterDocs}
xPadding={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
yPadding={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
+ rejectDrop={this._props.childRejectDrop}
addDocument={this._props.addDocument}
moveDocument={this._props.moveDocument}
removeDocument={this._props.removeDocument}
@@ -393,7 +393,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
pinToPres={this._props.pinToPres}
/>
);
- }
+ };
getDocTransform = computedFn((doc: Doc) => () => {
// these must be referenced for document decorations to update when the text box container is scrolled
@@ -404,11 +404,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
return new Transform(-translateX + (dref?.centeringX || 0) * scale,
-translateY + (dref?.centeringY || 0) * scale, 1)
- .scale(1 / scale); // prettier-ignore
+ .scale(1 / (scale||1)); // prettier-ignore
});
getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
- const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
const maxWidth = this.columnWidth / this.numGroupColumns;
if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
return Math.min(NumCast(d._width), maxWidth);
@@ -418,8 +418,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
getDocHeight = computedFn((d?: Doc) => () => {
if (!d || d.hidden) return 0;
- const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
- const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+ const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = d.isTemplateDoc || d.isTemplateForField ? this._props.TemplateDataDocument : undefined;
const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
@@ -575,8 +575,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.Document}
- TemplateDataDocument={this._props.TemplateDataDocument}
+ Doc={this.Document}
+ TemplateDataDoc={this._props.TemplateDataDocument}
renderChildren={this.children}
columnWidth={this.columnWidth}
numGroupColumns={this.numGroupColumns}
@@ -611,7 +611,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
<div style={{ top: this.yMargin }}>
<CollectionMasonryViewFieldRow
showHandle={first}
- Document={this.Document}
+ Doc={this.Document}
chromeHidden={this.chromeHidden}
pivotField={this.pivotField}
refList={this._refList}
@@ -675,12 +675,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return35 = () => 35;
+ @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore
@computed get buttonMenu() {
- const menuDoc = DocCast(this.layoutDoc.layout_headerButton);
- return !menuDoc ? null : (
- <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+ return !this.menuBtnDoc ? null : (
+ <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}>
<DocumentView
- Document={menuDoc}
+ Document={this.menuBtnDoc}
isContentActive={this.isContentActive}
isDocumentActive={this.isContentActive}
addDocument={this._props.addDocument}
@@ -720,7 +720,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return this._props.isContentActive() === false ? 'none' : undefined;
}
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
render() {
TraceMobx();
const editableViewProps = {
@@ -728,13 +727,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
SetValue: this.addGroup,
contents: '+ ADD A GROUP',
};
- const buttonMenu = this.layoutDoc.layout_headerButton;
const noviceExplainer = this.layoutDoc.layout_explainer;
return (
<>
- {buttonMenu || noviceExplainer ? (
+ {this.menuBtnDoc || noviceExplainer ? (
<div className="documentButtonMenu">
- {buttonMenu ? this.buttonMenu : null}
+ {this.menuBtnDoc ? this.buttonMenu : null}
{Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null}
</div>
) : null}
@@ -744,10 +742,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
ref={ele => {
this._masonryGridRef = ele;
this.createDashEventsTarget(ele); // so the whole grid is the drop target?
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(ele, this._props.isContentActive);
}}
style={{
overflowY: this.isContentActive() ? 'auto' : 'hidden',
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 6f32dd2e0..994669734 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -7,7 +7,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { RichTextField } from '../../../fields/RichTextField';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, NumCast } from '../../../fields/Types';
+import { BoolCast, DocCast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction } from '../../../Utils';
@@ -26,11 +26,12 @@ import { EditableView } from '../EditableView';
import { DocumentView } from '../nodes/DocumentView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import './CollectionStackingView.scss';
+import { DocData } from '../../../fields/DocSymbols';
// So this is how we are storing a column
interface CSVFieldColumnProps {
- Document: Doc;
- TemplateDataDocument: Opt<Doc>;
+ Doc: Doc;
+ TemplateDataDoc: Opt<Doc>;
docList: Doc[];
heading: string;
pivotField: string;
@@ -90,7 +91,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
// is that the only way to have drop targets?
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
- if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document, this.onInternalPreDrop.bind(this));
+ if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
this._ele = ele;
};
@@ -183,7 +184,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
// TODO: I think this is where I'm supposed to edit stuff
startDrag = (e: PointerEvent) => {
// is MakeEmbedding a way to make a copy of a doc without rendering it?
- const embedding = Doc.MakeEmbedding(this._props.Document);
+ const embedding = Doc.MakeEmbedding(this._props.Doc);
embedding._width = this._props.columnWidth / (this._props.colHeaderData?.length || 1);
embedding._pivotField = undefined;
let value = this.getValue(this._heading);
@@ -230,7 +231,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
- const dataDoc = this._props.TemplateDataDocument || this._props.Document;
+ const dataDoc = this._props.TemplateDataDoc || this._props.Doc;
const width = this._ele ? DivWidth(this._ele) : 0;
const height = this._ele ? DivHeight(this._ele) : 0;
DocUtils.addDocumentCreatorMenuItems(
@@ -250,10 +251,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
+ const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Doc));
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
return this._props.addDocument?.(created);
}
@@ -270,10 +271,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
event: () => {
const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
if (created) {
- const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
+ const container = DocCast(this._props.Doc.rootDocument)?.[DocData] ? Doc.GetProto(this._props.Doc) : this._props.Doc;
if (container.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, container);
- return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
+ return Doc.AddDocToList(container, Doc.LayoutDataKey(container), created);
}
return this._props.addDocument?.(created) || false;
}
@@ -285,11 +286,11 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this._props.Document)[name] = '';
+ Doc.GetProto(this._props.Doc)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this._props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this._props.Document);
+ if (this._props.Doc.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Doc);
}
this._props.addDocument?.(created);
}
@@ -304,10 +305,8 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
@computed get innards() {
TraceMobx();
const key = this._props.pivotField;
- const headings = this._props.headings();
const heading = this._heading;
const columnYMargin = this._props.headingObject ? 0 : this._props.yMargin;
- const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
const noValueHeader = `NO ${key.toUpperCase()} VALUE`;
const evContents = heading || (this._props?.type === 'number' ? '0' : noValueHeader);
const headingView = this._props.headingObject ? (
@@ -317,7 +316,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ref={this._headerRef}
style={{
marginTop: this._props.yMargin,
- width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1),
+ width: this._props.columnWidth,
}}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
@@ -350,17 +349,17 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
</div>
) : null;
const templatecols = `${this._props.columnWidth / this._props.numGroupColumns}px `;
- const { type } = this._props.Document;
+ const { type } = this._props.Doc;
return (
<>
- {this._props.Document._columnsHideIfEmpty ? null : headingView}
+ {this._props.Doc._columnsHideIfEmpty ? null : headingView}
{this.collapsed ? null : (
<div
style={{
margin: 'auto',
marginTop: this._props.dontCenter.includes('y') ? undefined : 'auto',
marginBottom: this._props.dontCenter.includes('y') ? undefined : 'auto',
- width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1),
+ width: this._props.columnWidth,
}}>
<div
key={`${heading}-stack`}
@@ -368,7 +367,6 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
style={{
padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
margin: this._props.dontCenter.includes('x') ? undefined : 'auto',
- // width: 'max-content', // singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
height: 'max-content',
position: 'relative',
gridGap: this._props.gridGap,
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 655894e40..e79d0a76d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -3,7 +3,7 @@ import * as React from 'react';
import * as rp from 'request-promise';
import { ClientUtils, DashColor, returnFalse } from '../../../ClientUtils';
import CursorField from '../../../fields/CursorField';
-import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, expandedFieldName, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
import { AclPrivate, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -13,6 +13,7 @@ import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from '
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { Upload } from '../../../server/SharedMediaTypes';
import { DocServer } from '../../DocServer';
import { Networking } from '../../Network';
import { DocUtils } from '../../documents/DocUtils';
@@ -24,11 +25,11 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { SnappingManager } from '../../util/SnappingManager';
import { UndoManager } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
+import { DocumentViewProps } from '../nodes/DocumentContentsView';
+import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
-import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FlashcardPracticeUI } from './FlashcardPracticeUI';
import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
-import { Upload } from '../../../server/SharedMediaTypes';
+import { FlashcardPracticeUI } from './FlashcardPracticeUI';
export enum docSortings {
Time = 'time',
@@ -57,6 +58,7 @@ export interface CollectionViewProps extends React.PropsWithChildren<FieldViewPr
childOpacity?: () => number;
childContextMenuItems?: () => { script: ScriptField; label: string }[];
childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
+ childRejectDrop?: (de: DragManager.DropEvent, subView?: DocumentView) => boolean; // whether a child document can be dropped on this document
childHideDecorationTitle?: boolean;
childHideResizeHandles?: boolean;
childHideDecorations?: boolean;
@@ -108,7 +110,11 @@ export function CollectionSubView<X>() {
}
get dataDoc() {
- return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField ? Doc.GetProto(this._props.TemplateDataDocument) : this.Document.resolvedDataDoc ? this.Document : this.Document[DocData]; // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+ return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField //
+ ? Doc.GetProto(this._props.TemplateDataDocument)
+ : this.Document.rootDocument
+ ? this.Document
+ : this.Document[DocData]; // if the layout document has a rootDocument, then we don't want to get its parent which would be the unexpanded template
}
get childContainerViewPath() {
@@ -127,16 +133,23 @@ export function CollectionSubView<X>() {
hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
@computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
- const { Document, TemplateDataDocument } = this._props;
- const validPairs = this.childDocs
- .map(doc => Doc.GetLayoutDataDocPair(Document, !this._props.isAnnotationOverlay ? TemplateDataDocument : undefined, doc))
- .filter(
- pair =>
- // filter out any documents that have a proto that we don't have permissions to
- !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate))
- )
- .filter(pair => !this._filterFunc?.(pair.layout!));
- return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
+ const lastEle = this.DocumentView?.();
+ if (!lastEle?.IsInvalid(this.Document)) {
+ const rootTemplate = lastEle && Doc.LayoutDoc(lastEle.rootDoc).isTemplateDoc && Doc.LayoutDoc(lastEle.rootDoc);
+ const templateFieldKey = rootTemplate &&
+ [expandedFieldName(rootTemplate),
+ ...this._props.docViewPath()
+ .filter(dv => dv.Document.isTemplateForField)
+ .map(dv => dv.Document.title),
+ ].join('_'); // prettier-ignore
+ return this.childDocs
+ .map(doc => Doc.GetLayoutDataDocPair(this.Document, !this._props.isAnnotationOverlay ? this._props.TemplateDataDocument : undefined, doc, templateFieldKey || ""))
+ .filter(pair => // filter out any documents that have a proto that we don't have permissions to
+ !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate)))
+ .filter(pair => !this._filterFunc?.(pair.layout!))
+ .map(({ data, layout }) => ({ data: data!, layout: layout! })); // prettier-ignore
+ }
+ return [];
}
/**
* This is the raw, stored list of children on a collection. If you modify this list, the database will be updated
@@ -156,7 +169,7 @@ export function CollectionSubView<X>() {
};
collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
- collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
+ collectionRangeDocFilters = () => this._focusRangeFilters ?? StrListCast(this.Document._childFiltersByRanges);
// child filters apply to the descendants of the documents in this collection
childDocFilters = () => [...(this._props.childFilters?.().filter(f => ClientUtils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
// unrecursive filters apply to the documents in the collection, but no their children. See Utils.noRecursionHack
@@ -173,7 +186,7 @@ export function CollectionSubView<X>() {
rawdocs = [this.dataField];
} else if (Cast(this.dataField, listSpec(Doc), null)) {
// otherwise, if the collection data is a list, then use it.
- rawdocs = Cast(this.dataField, listSpec(Doc), null);
+ rawdocs = DocListCast(this.dataField);
} else if (this.dataField) {
// Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
// For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
@@ -201,7 +214,7 @@ export function CollectionSubView<X>() {
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
if (notFiltered) {
notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -214,7 +227,7 @@ export function CollectionSubView<X>() {
newarray = [];
// eslint-disable-next-line no-loop-func
subDocs.forEach(t => {
- const docFieldKey = Doc.LayoutFieldKey(t);
+ const docFieldKey = Doc.LayoutDataKey(t);
const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
notFiltered =
notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
@@ -295,7 +308,7 @@ export function CollectionSubView<X>() {
const dragData = de.complete.docDragData;
if (dragData) {
const sourceDragAction = dragData.dropAction;
- const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this._props.Document);
+ const sameCollection = !dragData.draggedDocuments.some(d => d.embedContainer !== this.Document);
dragData.dropAction = !sameCollection // if doc from another tree
? sourceDragAction || targetDropAction // then use the source's dragAction otherwise the target's
: sourceDragAction === dropActionType.inPlace // if source drag is inPlace
@@ -311,7 +324,7 @@ export function CollectionSubView<X>() {
protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
const { docDragData } = de.complete;
- if (docDragData && !docDragData.draggedDocuments.includes(this.Document)) {
+ if (docDragData && !docDragData.draggedDocuments.includes(this.Document) && !this._props.rejectDrop?.(de, this.DocumentView?.())) {
let added;
const dropAction = docDragData.dropAction || docDragData.userDropAction;
const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]);
@@ -585,7 +598,7 @@ export function CollectionSubView<X>() {
/**
* How much the content of the collection is being scaled based on its nesting and its fit-to-width settings
*/
- @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale; } // prettier-ignore
+ @computed get contentScaling() { return this.ScreenToLocalBoxXf().Scale * (!this._props.fitWidth?.(this.Document) ? this._props.NativeDimScaling?.()||1: 1); } // prettier-ignore
/**
* The maximum size a UI widget can be in collection coordinates based on not wanting the widget to visually obscure too much of the collection
* This takes the desired screen space size and converts into collection coordinates. It then returns the smaller of the converted
@@ -598,7 +611,12 @@ export function CollectionSubView<X>() {
*/
@computed get uiBtnScaling() { return this.maxWidgetSize / this._sideBtnWidth; } // prettier-ignore
- screenXPadding = () => (this.uiBtnScaling * this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) / this._props.ScreenToLocalTransform().Scale;
+ screenXPadding = (docView?: DocumentView) => {
+ if (!docView) return 0;
+ const diff = this._props.PanelWidth() - docView.PanelWidth();
+ const xpad1 = this.uiBtnScaling * (this._sideBtnWidth - NumCast(this.layoutDoc.xMargin)) - diff / 2; // this._sideBtnWidth;
+ return xpad1 / (docView.NativeDimScaling?.() || 1);
+ };
filteredChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
childDocsFunc = () => this.childDocs;
@action setFilterFunc = (func?: (doc: Doc) => boolean) => { this._filterFunc = func; }; // prettier-ignore
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 98bd06221..fd562e64f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -121,7 +121,7 @@ export class CollectionTimeView extends CollectionSubView() {
{this.contents}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
- Document={this.Document}
+ Doc={this.Document}
selectFunc={fieldKey => {
this.layoutDoc._pivotField = fieldKey;
}}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e93724dd4..bee5d016d 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -8,7 +8,7 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, Utils } from '../../../Utils';
import { Docs } from '../../documents/Documents';
@@ -137,7 +137,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
if (res && de.complete.docDragData) {
if (this.Document !== Doc.MyRecentlyClosed)
de.complete.docDragData.droppedDocuments.forEach(doc => {
- if (this.Document !== Doc.MyRecentlyClosed) Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
+ if (this.Document !== Doc.MyRecentlyClosed) Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
});
}
return res;
@@ -187,20 +187,20 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
@action
addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
const addDocRelativeTo = (adocs: Doc | Doc[]) => (adocs as Doc[]).reduce((flg, doc) => flg && Doc.AddDocToList(this.Document[DocData], this._props.fieldKey, doc, relativeTo, before), true);
- if (this.Document.resolvedDataDoc instanceof Promise) return false;
+ if (this.Document.rootDocument instanceof Promise) return false;
const doclist = toList(docs);
const res = relativeTo === undefined ? this._props.addDocument?.(doclist) || false : addDocRelativeTo(doclist);
res &&
doclist.forEach(doc => {
Doc.SetContainer(doc, this.Document);
- if (this.Document !== Doc.MyRecentlyClosed) Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
+ if (this.Document !== Doc.MyRecentlyClosed) Doc.MyRecentlyClosed && Doc.RemoveDocFromList(Doc.MyRecentlyClosed, undefined, doc);
});
return res;
};
onContextMenu = (): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
const layoutItems: ContextMenuProps[] = [];
- const menuDoc = ScriptCast(Cast(this.layoutDoc.layout_headerButton, Doc, null)?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc;
+ const menuDoc = ScriptCast(this.menuBtnDoc?.onClick)?.script.originalScript === CollectionTreeView.AddTreeFunc;
menuDoc && layoutItems.push({ description: 'Create new folder', event: () => CollectionTreeView.addTreeFolder(this.Document), icon: 'paint-brush' });
if (!Doc.noviceMode) {
layoutItems.push({
@@ -241,7 +241,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
);
}
- onKey = (e: React.KeyboardEvent /* , fieldProps: FieldViewProps */) => {
+ onKey = (e: KeyboardEvent /* , textBox: FormattedTextBox */) => {
if (this.outlineMode && e.key === 'Enter') {
e.stopPropagation();
this.makeTextCollection(this.treeChildren);
@@ -252,7 +252,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
get documentTitle() {
return (
<FormattedTextBox
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
fieldKey="text"
renderDepth={this._props.renderDepth + 1}
@@ -274,8 +273,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
);
}
childContextMenuItems = () => {
- const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
- const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+ const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), [])!;
+ const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), [])!;
const icons = StrListCast(this.Document.childContextMenuIcons);
return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
@@ -348,14 +347,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
return35 = () => 35;
+ @computed get menuBtnDoc() { return DocCast(this.layoutDoc.layout_headerButton); } // prettier-ignore
@computed get buttonMenu() {
- const menuDoc = Cast(this.layoutDoc.layout_headerButton, Doc, null);
// To create a multibutton menu add a CollectionLinearView
- return !menuDoc ? null : (
- <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+ return !this.menuBtnDoc ? null : (
+ <div className="buttonMenu-docBtn" style={{ width: NumCast(this.menuBtnDoc._width, 30), height: NumCast(this.menuBtnDoc._height, 30) }}>
<DocumentView
- Document={menuDoc}
- TemplateDataDocument={menuDoc}
+ Document={this.menuBtnDoc}
+ TemplateDataDocument={this.menuBtnDoc}
isContentActive={this._props.isContentActive}
isDocumentActive={returnTrue}
addDocument={this._props.addDocument}
@@ -468,10 +467,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
const scale = this._props.NativeDimScaling?.() || 1;
return (
- <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
+ <div className="collectionTreeView" style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
{!(this.Document instanceof Doc) || !this.treeChildren ? null : this.Document.treeView_HasOverlay ? (
<CollectionFreeFormView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
setContentViewBox={emptyFunction}
NativeWidth={returnZero}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7ba8989ce..eb9caf29d 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -130,7 +130,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
{ description: 'Calendar', event: () => func(CollectionViewType.Calendar), icon: 'columns' },
{ description: 'Pivot', event: () => func(CollectionViewType.Pivot), icon: 'columns' },
{ description: 'Time', event: () => func(CollectionViewType.Time), icon: 'columns' },
- { description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' },
{ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' },
];
@@ -162,7 +161,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
if (this.Document.childClickedOpenTemplateView instanceof Doc) {
optionItems.push({ description: 'View Child Detailed Layout', event: () => this._props.addDocTab(this.Document.childClickedOpenTemplateView as Doc, OpenWhere.addRight), icon: 'project-diagram' });
}
- !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox; }, icon: 'project-diagram' }); // prettier-ignore
+ !Doc.noviceMode && optionItems.push({ description: `${this.dataDoc.$isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.dataDoc.$isLightbox = !this.dataDoc.$isLightbox; }, icon: 'project-diagram' }); // prettier-ignore
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
@@ -184,15 +183,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
},
})
);
- DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick =>
- onClicks.push({
- description: `Set child ${childClick.title}`,
- icon: 'edit',
- event: () => {
- this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data));
- },
- })
- );
+ DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data)
+ .filter(childClick => ScriptCast(childClick.data))
+ .forEach(childClick =>
+ onClicks.push({
+ description: `Set child ${childClick.title}`,
+ icon: 'edit',
+ event: () => {
+ this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)!);
+ },
+ })
+ );
!Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
}
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index c071c5fb8..2f46c00bd 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -7,13 +7,16 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { returnFalse, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
import './FlashcardPracticeUI.scss';
+import { StyleProp } from '../StyleProp';
+import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentViewProps } from '../nodes/DocumentContentsView';
export enum practiceMode {
PRACTICE = 'practice',
@@ -57,11 +60,11 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
- @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
+ @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns?.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
@computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
- btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
- btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));
+ btnHeight = () => NumCast(this.filterDoc?.height);
+ btnWidth = () => (!this.filterDoc ? 1 : NumCast(this.filterDoc._width));
/**
* Sets the practice mode answer style for flashcards
@@ -131,7 +134,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
<div
className="FlashcardPracticeUI-practiceModes"
style={{
- transform: this._props.ScreenToLocalBoxXf().Scale >= 1 ? undefined : `translateY(${this.btnHeight() / this._props.ScreenToLocalBoxXf().Scale - this.btnHeight()}px)`,
+ background: SnappingManager.userVariantColor,
}}>
<MultiToggle
tooltip="Practice flashcards one at a time"
@@ -139,7 +142,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
color={SnappingManager.userColor}
background={SnappingManager.userVariantColor}
multiSelect={false}
- isToggle={false}
toggleStatus={!!this.practiceMode}
label="Practice"
items={[
@@ -159,7 +161,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
color={SnappingManager.userColor}
background={SnappingManager.userVariantColor}
multiSelect={false}
- isToggle={false}
toggleStatus={!!this.practiceMode}
label={StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)}
items={[
@@ -179,6 +180,12 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
</div>
);
}
+ childStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
+ if (doc instanceof Doc && property === StyleProp.BackgroundColor) {
+ return SnappingManager.userVariantColor;
+ }
+ return this._props.docViewProps().styleProvider?.(doc, props, property);
+ };
tryFilterOut = (doc: Doc) => (this.practiceMode && doc?._layout_flashcardType && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
render() {
return (
@@ -186,24 +193,27 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
{this.emptyMessage}
{this.practiceButtons}
{this._props.layoutDoc._chromeHidden ? null : (
- <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnHeight(), transform: `scale(${this._props.uiBtnScaling})` }}>
+ <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnWidth(), transform: `scale(${this._props.uiBtnScaling})` }}>
{!this.filterDoc ? null : (
- <DocumentView
- {...this._props.docViewProps()}
- Document={this.filterDoc}
- TemplateDataDocument={undefined}
- PanelWidth={this.btnWidth}
- PanelHeight={this.btnHeight}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- hideDecorations={BoolCast(this._props.layoutDoc.layout_hideDecorations)}
- hideCaptions={true}
- hideFilterStatus={true}
- renderDepth={this._props.renderDepth + 1}
- fitWidth={undefined}
- showTags={false}
- setContentViewBox={undefined}
- />
+ <div style={{ background: SnappingManager.userVariantColor }}>
+ <DocumentView
+ {...this._props.docViewProps()}
+ Document={this.filterDoc}
+ TemplateDataDocument={undefined}
+ PanelWidth={this.btnWidth}
+ PanelHeight={this.btnHeight}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ hideDecorations={BoolCast(this._props.layoutDoc.layout_hideDecorations)}
+ hideCaptions={true}
+ hideFilterStatus={true}
+ renderDepth={this._props.renderDepth + 1}
+ styleProvider={this.childStyleProvider}
+ fitWidth={undefined}
+ showTags={false}
+ setContentViewBox={undefined}
+ />
+ </div>
)}
{this.practiceModesMenu}
</div>
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 397e35ca9..931cdac2b 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -4,7 +4,6 @@
height: 100%;
width: 100%;
}
-
input.lm_title:focus,
input.lm_title {
max-width: unset !important;
@@ -22,7 +21,6 @@ input.lm_title {
.lm_iconWrap {
display: flex;
color: black;
- width: 15px;
height: 15px;
align-items: center;
align-self: center;
@@ -30,6 +28,16 @@ input.lm_title {
margin: 3px;
border-radius: 20%;
+ width: auto;
+ svg:nth-of-type(2) {
+ display: none;
+ }
+ &:hover {
+ svg:nth-of-type(2) {
+ display: block;
+ }
+ }
+
.moreInfoDot {
background-color: white;
border-radius: 100%;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cc56a8ff9..4348bc7dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -10,12 +10,12 @@ import ResizeObserver from 'resize-observer-polyfill';
import { ClientUtils, DashColor, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocData, DocLayout } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { FieldId } from '../../../fields/RefField';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast, toList } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast, toList } from '../../../fields/Types';
import { DocServer } from '../../DocServer';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
@@ -41,9 +41,10 @@ import { CollectionDockingView } from './CollectionDockingView';
import { CollectionView } from './CollectionView';
import './TabDocView.scss';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { Tooltip } from '@mui/material';
interface TabMinimapViewProps {
- document: Doc;
+ doc: Doc;
tabView: () => DocumentView | undefined;
addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
PanelWidth: () => number;
@@ -100,15 +101,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
return bounds === undefined ? bounds : { l: bounds.x + width / 2 - dim / 2, t: bounds.y + height / 2 - dim / 2, cx, cy, dim };
}
@computed get xPadding() {
- return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
+ return !this.renderBounds ? 0 : Math.max(0, this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cx - this.renderBounds.l));
}
@computed get yPadding() {
- return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
+ return !this.renderBounds ? 0 : Math.max(0, this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) - 2 * (this.renderBounds.cy - this.renderBounds.l));
}
- childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
- returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
+ childLayoutTemplate = () => Cast(this._props.doc.childLayoutTemplate, Doc, null);
+ returnMiniSize = () => NumCast(this._props.doc._miniMapSize, 150);
miniDown = (e: React.PointerEvent) => {
- const doc = this._props.document;
+ const doc = this._props.doc;
const miniSize = this.returnMiniSize();
doc &&
setupMoveUpEvents(
@@ -127,15 +128,15 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
popup = () => {
const { renderBounds } = this;
if (!renderBounds) return <div />;
- const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
- const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.document._freeform_scale, 1) / renderBounds.dim) * 100;
- const miniLeft = () => 50 + ((NumCast(this._props.document._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
- const miniTop = () => 50 + ((NumCast(this._props.document._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
+ const miniWidth = () => (this._props.PanelWidth() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniHeight = () => (this._props.PanelHeight() / NumCast(this._props.doc._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniLeft = () => 50 + ((NumCast(this._props.doc._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
+ const miniTop = () => 50 + ((NumCast(this._props.doc._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
const miniSize = this.returnMiniSize();
return (
<div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
<CollectionFreeFormView
- Document={this._props.document}
+ Document={this._props.doc}
docViewPath={returnEmptyDocViewList}
childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
noOverlay // don't render overlay Docs since they won't scale
@@ -144,7 +145,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
select={emptyFunction}
isSelected={returnFalse}
dontRegisterView
- fieldKey={Doc.LayoutFieldKey(this._props.document)}
+ fieldKey={Doc.LayoutDataKey(this._props.doc)}
addDocument={returnFalse}
moveDocument={returnFalse}
removeDocument={returnFalse}
@@ -172,7 +173,7 @@ export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps
);
};
render() {
- return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._type_collection !== CollectionViewType.Freeform ? null : (
+ return this._props.doc.layout !== CollectionView.LayoutString(Doc.LayoutDataKey(this._props.doc)) || this._props.doc?._type_collection !== CollectionViewType.Freeform ? null : (
<div className="miniMap-hidden">
<Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SnappingManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
</div>
@@ -206,10 +207,10 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
const docs = toList(docIn);
const batch = UndoManager.StartBatch('Pin doc to pres trail');
- const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
+ const curPres = Doc.ActivePresentation ?? (DocCast(Doc.UserDoc().emptyTrail) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyTrail)!, true) : Docs.Create.PresDocument({}));
if (!Doc.ActivePresentation) {
- Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
+ Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
Doc.ActivePresentation = curPres;
}
@@ -235,7 +236,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
pinDoc.treeView_FieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
pinDoc.treeView_ExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
pinDoc.treeView_HideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
- const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}_duration`], null);
+ const duration = NumCast(doc[`${Doc.LayoutDataKey(pinDoc)}_duration`], null);
if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc);
if (!pinProps?.audioRange && duration !== undefined) {
@@ -311,7 +312,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
@observable _view: DocumentView | undefined = undefined;
@observable _forceInvalidateScreenToLocal = 0; // screentolocal is computed outside of react using a dom resize ovbserver. this hack allows the resize observer to trigger a react update
- @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } // prettier-ignore
+ @computed get layoutDoc() { return this._document?.[DocLayout]; } // prettier-ignore
@computed get isUserActivated() { return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive; } // prettier-ignore
@computed get isContentActive() { return this.isUserActivated || this._hovering; } // prettier-ignore
@@ -347,13 +348,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
undoable(() => {
const target = e.currentTarget as unknown as { value: string };
titleEle.size = target?.value.length + 3;
- doc[DocData].title = target?.value ?? '';
+ doc.$title = target?.value ?? '';
}, 'edit tab title')();
};
if (tab.element[0].children[1].children.length === 1) {
iconWrap.className = 'lm_iconWrap lm_moreInfo';
- iconWrap.title = 'click for menu, drag to embed in document';
const dragBtnDown = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
@@ -377,7 +377,26 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
);
};
- const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
+ const docIcon = (
+ <>
+ <Tooltip title="click for menu, drag to embed in document">
+ <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />
+ </Tooltip>
+ <Tooltip title="click to open in lightbox">
+ <FontAwesomeIcon
+ onPointerDown={dragBtnDown}
+ icon="external-link-alt"
+ onClick={() => {
+ if (doc.layout_fieldKey === 'layout_icon') {
+ const odoc = Doc.GetEmbeddings(doc).find(embedding => !embedding.embedContainer) ?? Doc.MakeEmbedding(doc);
+ Doc.deiconifyView(odoc);
+ }
+ this.addDocTab(doc, OpenWhere.lightboxAlways);
+ }}
+ />
+ </Tooltip>
+ </>
+ );
const closeIcon = <FontAwesomeIcon icon="eye" />;
ReactDOM.createRoot(iconWrap).render(docIcon);
ReactDOM.createRoot(closeWrap).render(closeIcon);
@@ -547,7 +566,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
return false;
};
- getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
+ getCurrentFrame = () => NumCast(DocCast(PresBox.Instance.activeItem?.presentation_targetDoc)?._currentFrame);
focusFunc = () => {
if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) {
this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost)
@@ -631,7 +650,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
}}>
{!this._activated || !this._document ? null : this.renderDocView(this._document)}
{this.disableMinimap() || !this._document ? null : (
- <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
+ <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} doc={this._document} tabView={this.tabView} />
)}
</div>
);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6208905fe..fb2d0955f 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -7,7 +7,7 @@ import * as React from 'react';
import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocData, DocLayout } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
@@ -70,7 +70,7 @@ export interface TreeViewProps {
treeViewHideHeaderFields: () => boolean;
renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
onCheckedClick?: () => ScriptField;
- onChildClick?: () => ScriptField;
+ onChildClick?: () => ScriptField | undefined;
skipFields?: string[];
firstLevel: boolean;
// TODO: [AL] add these
@@ -156,10 +156,10 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return this.Document[DocData];
}
@computed get layoutDoc() {
- return Doc.Layout(this.Document);
+ return this.Document[DocLayout];
}
@computed get fieldKey() {
- return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document));
+ return StrCast(this.Document._treeView_FieldKey, Doc.LayoutDataKey(this.Document));
}
@computed get childDocs() {
return this.childDocList(this.fieldKey);
@@ -186,7 +186,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
moving: boolean = false;
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
if (this.Document !== target && addDoc !== returnFalse) {
- const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
+ const canAdd1 = (this._props.parentTreeView as TreeView).dropping || !(ComputedField.DisableCompute(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
// bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
if (canAdd1 && this._props.removeDoc?.(doc) === true) {
@@ -372,9 +372,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
_width: 1000,
_height: 10,
});
- const bulletData = bullet[DocData];
- bulletData.title = ComputedField.MakeFunction('this.text?.Text');
- bulletData.data = new List<Doc>([]);
+ bullet.$title = ComputedField.MakeFunction('this.text?.Text');
+ bullet.$data = new List<Doc>([]);
DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
return bullet;
@@ -432,9 +431,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
localAdd = (docs: Doc | Doc[]): boolean => {
const innerAdd = (doc: Doc): boolean => {
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[this.fieldKey])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, this.fieldKey, doc);
- dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
+ dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(doc, DocCast(this.Document.embedContainer)!);
return added;
};
return toList(docs).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
@@ -512,9 +511,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const moveDoc = (docs: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(docs, target, addDoc);
const addDoc = (docs: Doc | Doc[], addBefore?: Doc, before?: boolean) => {
const innerAdd = (iDoc: Doc) => {
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, iDoc, addBefore, before, false, true);
- dataIsComputed && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer));
+ dataIsComputed && DocCast(this.Document.embedContainer) && Doc.SetContainer(iDoc, DocCast(this.Document.embedContainer)!);
return added;
};
return toList(docs).reduce((flg, iDoc) => flg && innerAdd(iDoc), true as boolean);
@@ -595,7 +594,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const key = match[1];
const assign = match[2];
const val = match[3];
- Doc.SetField(doc, key, assign + val, false);
+ Doc.SetField(doc, key, assign + val);
return true;
}
return false;
@@ -634,7 +633,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
d.zIndex = i;
});
}
- const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
+ const dataIsComputed = ComputedField.DisableCompute(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
!dataIsComputed && added && Doc.SetContainer(doc, this.Document);
@@ -835,7 +834,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
@observable headerEleWidth = 0;
@computed get titleButtons() {
- const customHeaderButtons = this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.Decorations);
+ const customHeaderButtons = this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.Decorations) as JSX.Element;
const color = SettingsManager.userColor;
return this._props.treeViewHideHeaderFields() || this.Document.treeView_HideHeaderFields ? null : (
<>
@@ -884,8 +883,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
];
};
childContextMenuItems = () => {
- const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
- const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+ const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), [])!;
+ const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), [])!;
const icons = StrListCast(this.Document.childContextMenuIcons);
return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
@@ -940,7 +939,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
if (property.startsWith(StyleProp.Decorations)) return null;
return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
};
- onKeyDown = (e: React.KeyboardEvent) => {
+ onKey = (e: KeyboardEvent) => {
if (this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode) {
switch (e.key) {
case 'Tab':
@@ -1139,7 +1138,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
ScreenToLocalTransform={this.docTransform}
renderDepth={this._props.renderDepth + 1}
onClickScript={this.onChildClick}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
containerViewPath={this.treeView.childContainerViewPath}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
@@ -1269,7 +1268,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
treeViewHideHeaderFields: () => boolean,
renderedIds: string[],
onCheckedClick: undefined | (() => ScriptField),
- onChildClick: undefined | (() => ScriptField),
+ onChildClick: undefined | (() => ScriptField | undefined),
skipFields: string[] | undefined,
firstLevel: boolean,
whenChildContentsActiveChanged: (isActive: boolean) => void,
@@ -1297,7 +1296,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
if (parent instanceof TreeView && parent._props.treeView.fileSysMode && !newParent.isFolder) return;
- const fieldKey = Doc.LayoutFieldKey(newParent);
+ const fieldKey = Doc.LayoutDataKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
DocumentView.SetSelectOnLoad(child);
@@ -1310,7 +1309,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView._props.parentTreeView : undefined);
const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
- const childLayout = Doc.Layout(pair.layout);
+ const childLayout = pair.layout[DocLayout];
const rowHeight = () => {
const aspect = Doc.NativeAspect(childLayout);
return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
index 3838852dd..903d92c90 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -31,16 +31,14 @@ export class CollectionFreeFormClusters {
get selectDocuments() { return this._view.selectDocuments; } // prettier-ignore
static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
- const doc2Layout = Doc.Layout(doc2);
- const doc1Layout = Doc.Layout(doc1);
const x2 = NumCast(doc2.x) - clusterDistance;
const y2 = NumCast(doc2.y) - clusterDistance;
- const w2 = NumCast(doc2Layout._width) + clusterDistance;
- const h2 = NumCast(doc2Layout._height) + clusterDistance;
+ const w2 = NumCast(doc2._width) + clusterDistance;
+ const h2 = NumCast(doc2._height) + clusterDistance;
const x = NumCast(doc1.x) - clusterDistance;
const y = NumCast(doc1.y) - clusterDistance;
- const w = NumCast(doc1Layout._width) + clusterDistance;
- const h = NumCast(doc1Layout._height) + clusterDistance;
+ const w = NumCast(doc1._width) + clusterDistance;
+ const h = NumCast(doc1._height) + clusterDistance;
return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
}
handlePointerDown(probe: number[]) {
@@ -49,12 +47,11 @@ export class CollectionFreeFormClusters {
.reduce((cluster, cd) => {
const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
if (grouping !== -1) {
- const layoutDoc = Doc.Layout(cd);
const cx = NumCast(cd.x) - this._clusterDistance / 2;
const cy = NumCast(cd.y) - this._clusterDistance / 2;
- const cw = NumCast(layoutDoc._width) + this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ const cw = NumCast(cd._width) + this._clusterDistance;
+ const ch = NumCast(cd._height) + this._clusterDistance;
+ return !cd.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
}
return cluster;
}, -1);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index 8b9a3e0ec..89d2bf2c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,20 +1,19 @@
import { makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc';
+import { Doc, DocListCast, FieldResult, FieldType } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { StrCast } from '../../../../fields/Types';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { TopBar } from '../../topbar/TopBar';
import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
-import './CollectionFreeFormView.scss';
-import { DocData } from '../../../../fields/DocSymbols';
import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
export interface CollectionFreeFormInfoUIProps {
- Document: Doc;
- LayoutDoc: Doc;
+ Doc: Doc;
+ layoutDoc: Doc;
childDocs: () => Doc[];
close: () => void;
}
@@ -24,7 +23,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
public static Init() {
CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => (
//
- <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} />
+ <CollectionFreeFormInfoUI Doc={doc} layoutDoc={layout} childDocs={childDocs} close={close} />
));
}
_firstDocPos = { x: 0, y: 0 };
@@ -41,7 +40,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
set currState(val) { runInAction(() => {this._currState = val;}); } // prettier-ignore
componentWillUnmount(): void {
- this._props.Document[DocData].backgroundColor = this._originalbackground;
+ this._props.Doc.$backgroundColor = this._originalbackground;
}
setCurrState = (state: infoState) => {
@@ -52,10 +51,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
};
setupStates = () => {
- this._originalbackground = StrCast(this._props.Document[DocData].backgroundColor);
+ this._originalbackground = StrCast(this._props.Doc.$backgroundColor);
// state entry functions
- // const setBackground = (colour: string) => () => {this._props.Document[DocData].backgroundColor = colour;} // prettier-ignore
- // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore
+ // const setBackground = (colour: string) => () => {this._props.Doc.$backgroundColor = colour;} // prettier-ignore
+ // const setOpacity = (opacity: number) => () => {this._props.layoutDoc.opacity = opacity;} // prettier-ignore
// arc transition trigger conditions
const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined);
const numDocs = () => this._props.childDocs().length;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 241a56a88..4d17dedfb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,6 @@
/* eslint-disable no-use-before-define */
import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc';
+import { DocLayout } from '../../../../fields/DocSymbols';
import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
@@ -107,7 +108,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
}
function toNumber(val: FieldResult<FieldType>) {
- return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getMilliseconds() : NumCast(val, Number(StrCast(val)));
+ return val === undefined ? undefined : DateCast(val) ? DateCast(val)!.date.getTime() : NumCast(val, Number(StrCast(val)));
}
export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
@@ -237,7 +238,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
payload: val,
});
val?.docs.forEach((doc, i) => {
- const layoutDoc = Doc.Layout(doc);
+ const layoutDoc = doc[DocLayout];
let wid = pivotAxisWidth;
let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
if (hgt > pivotAxisWidth) {
@@ -249,7 +250,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
y: -y + (pivotAxisWidth - hgt) / 2,
width: wid,
height: hgt,
- backgroundColor: StrCast(layoutDoc.backgroundColor, 'white'),
+ backgroundColor: StrCast(doc.backgroundColor, 'white'),
pair: { layout: doc },
replica: val.replicas[i],
});
@@ -362,7 +363,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
keyDocs.forEach(doc => {
const stack = findStack(x, stacking);
- const layoutDoc = Doc.Layout(doc);
+ const layoutDoc = doc[DocLayout];
let wid = pivotAxisWidth;
let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
if (hgt > pivotAxisWidth) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index bc9dd022c..2683d9439 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -7,7 +7,7 @@ import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
export interface CollectionFreeFormPannableContentsProps {
- Document: Doc;
+ Doc: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
transition: () => string;
@@ -33,7 +33,7 @@ export class CollectionFreeFormPannableContents extends ObservableReactComponent
makeObservable(this);
}
@computed get presPaths() {
- return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : null;
+ return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Doc) : null;
}
// rectangle highlight used when following trail/link to a region of a collection that isn't a document
showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index f64c6715b..86310dca3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -13,7 +13,7 @@ import './CollectionFreeFormView.scss';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@computed protected get cursors(): CursorField[] {
- const { Document } = this.props;
+ const { Document: Document } = this.props;
const cursors = Cast(Document.cursors, listSpec(CursorField));
if (!cursors) {
return [];
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index cce0ff684..6c47a71b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -41,7 +41,7 @@
transition: background-color 1s ease 0s;
}
.collectionfreeformview-mask {
- mix-blend-mode: multiply;
+ mix-blend-mode: hard-light;
background-color: rgba(0, 0, 0, 0.8);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 89aa53c35..2364fd74a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,14 +1,17 @@
-import { Bezier } from 'bezier-js';
import { Button, Colors, Type } from '@dash/components';
+import { Slider } from '@mui/material';
+import { Bezier } from 'bezier-js';
import { Property } from 'csstype';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
+import { AiOutlineSend } from 'react-icons/ai';
+import ReactLoading from 'react-loading';
import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
-import { DocData, Height, Width } from '../../../../fields/DocSymbols';
+import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkEraserTool, InkField, InkInkTool, InkTool, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
@@ -28,6 +31,7 @@ import { DragManager } from '../../../util/DragManager';
import { dropActionType } from '../../../util/DropActionTypes';
import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SettingsManager } from '../../../util/SettingsManager';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, UndoManager } from '../../../util/UndoManager';
@@ -37,26 +41,26 @@ import { InkingStroke } from '../../InkingStroke';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
import {
+ ActiveEraserWidth,
ActiveInkArrowEnd,
ActiveInkArrowStart,
- ActiveInkDash,
- ActiveEraserWidth,
- ActiveInkFillColor,
ActiveInkBezierApprox,
ActiveInkColor,
+ ActiveInkDash,
+ ActiveInkFillColor,
ActiveInkWidth,
ActiveIsInkMask,
DocumentView,
SetActiveInkColor,
SetActiveInkWidth,
} from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { OpenWhere } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
-import { StickerPalette } from '../../smartdraw/StickerPalette';
+import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
+import { StickerPalette } from '../../smartdraw/StickerPalette';
import { StyleProp } from '../../StyleProp';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeViewType';
@@ -67,11 +71,6 @@ import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannable
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
-import ReactLoading from 'react-loading';
-import { SettingsManager } from '../../../util/SettingsManager';
-import { Slider } from '@mui/material';
-import { AiOutlineSend } from 'react-icons/ai';
-import { DrawingFillHandler } from '../../smartdraw/DrawingFillHandler';
@observer
class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
@@ -180,10 +179,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get nativeWidth() {
- return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document);
}
@computed get nativeHeight() {
- return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document);
}
@computed get centeringShiftX(): number {
return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / this.nativeDimScaling; // shift so pan position is at center of window for non-overlay collections
@@ -257,8 +256,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
override contentBounds = () => {
const { x, y, r, b } = aggregateBounds(
this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 0),
- NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 0)
+ NumCast(this.layoutDoc._xPadding, NumCast(this.layoutDoc._xMargin, this._props.xPadding ?? 0)),
+ NumCast(this.layoutDoc._yPadding, NumCast(this.layoutDoc._yMargin, this._props.yPadding ?? 0))
);
const [width, height] = [r - x, b - y];
return {
@@ -276,7 +275,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
- panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
fitContentOnce = () => {
const { cx, cy, scale } = this.contentBounds(); // prettier-ignore
this.layoutDoc._freeform_panX = cx;
@@ -285,9 +284,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
// freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
// this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
- panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
- panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
- zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
+ panX = () => this.fitContentBounds?.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(this.Document.freeform_panX, 1));
+ panY = () => this.fitContentBounds?.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(this.Document.freeform_panY, 1));
+ zoomScaling = () => this.fitContentBounds?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.rootDocument)?.[this.scaleFieldKey], 1));
PanZoomCenterXf = () => (this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.centeringShiftX}px, ${this.centeringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`);
ScreenToContentsXf = () => this.screenToFreeformContentsXf.copy();
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
@@ -389,7 +388,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (options.easeFunc) this.setPresEaseFunc(options.easeFunc);
if (this._lightboxDoc) return undefined;
if (options.pointFocus) return this.focusOnPoint(options);
- const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor);
+ const anchorInCollection = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutDataKey(this.Document)]).includes(anchor);
const anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
if (!anchorInCollection && !anchorInChildViews) {
return undefined;
@@ -441,7 +440,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
docDragData.droppedDocuments.forEach((d, i) => {
- const layoutDoc = Doc.Layout(d);
+ const layoutDoc = d[DocLayout];
const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
@@ -497,6 +496,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}, 'link drop');
onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => {
+ if (this._props.rejectDrop?.(de, this._props.DocumentView?.())) return false;
if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de, de.complete.annoDragData);
if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData);
if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
@@ -561,7 +561,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = this.createInkDoc(points, B);
- if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc[DocData].backgroundColor = 'transparent';
+ if (Doc.ActiveInk === InkInkTool.Highlight) inkDoc.$backgroundColor = 'transparent';
if (Doc.ActiveInk === InkInkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -639,7 +639,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
const inkDoc = this.createInkDoc(points, B);
['color', 'fillColor', 'stroke_width', 'stroke_dash', 'stroke_bezier'].forEach(field => {
- inkDoc[DocData][field] = stroke.dataDoc[field];
+ inkDoc['$' + field] = stroke.dataDoc[field];
});
this.addDocument(inkDoc);
});
@@ -1260,15 +1260,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
- const docData = doc[DocData];
- docData.title = opts.text;
- docData._width = opts.size;
- docData.ai_drawing_input = opts.text;
- docData.ai_drawing_complexity = opts.complexity;
- docData.ai_drawing_colored = opts.autoColor;
- docData.ai_drawing_size = opts.size;
- docData.ai_drawing_data = gptRes;
- docData.ai = 'gpt';
+ doc.$title = opts.text;
+ doc.$width = opts.size;
+ doc.$ai_drawing_input = opts.text;
+ doc.$ai_drawing_complexity = opts.complexity;
+ doc.$ai_drawing_colored = opts.autoColor;
+ doc.$ai_drawing_size = opts.size;
+ doc.$ai_drawing_data = gptRes;
+ doc.$ai = 'gpt';
this._drawingContainer = doc;
if (x !== undefined && y !== undefined) {
[doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
@@ -1310,7 +1309,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.TriggerUserPanned();
if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
switch (
!e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
@@ -1442,9 +1441,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
- const layoutdoc = Doc.Layout(doc);
const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
- const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height));
+ const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(doc._width), NumCast(doc.y) + NumCast(doc._height));
const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
if (scale !== undefined) {
@@ -1490,20 +1488,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @param below whether to place the new text Doc below or to the right of the one being typed into.
* @returns whether the new text doc was created and added successfully
*/
- createTextDocCopy = undoable((fieldProps: FieldViewProps, below: boolean) => {
- const textDoc = DocCast(fieldProps.Document.rootDocument, fieldProps.Document);
- const newDoc = Doc.MakeCopy(textDoc, true);
- newDoc[DocData][Doc.LayoutFieldKey(newDoc, fieldProps.LayoutTemplateString)] = undefined; // the copy should not copy the text contents of it source, just the render style
- newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
- newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
- DocumentView.SetSelectOnLoad(newDoc);
- return this.addDocument?.(newDoc);
+ createTextDocCopy = undoable((textBox: FormattedTextBox, below: boolean) => {
+ const textDoc = DocCast(textBox.Document);
+ if (textDoc) {
+ const newDoc = Doc.MakeCopy(textDoc, true);
+ newDoc['$' + Doc.LayoutDataKey(newDoc)] = undefined; // the copy should not copy the text contents of it source, just the render style
+ newDoc.x = NumCast(textDoc.x) + (below ? 0 : NumCast(textDoc._width) + 10);
+ newDoc.y = NumCast(textDoc.y) + (below ? NumCast(textDoc._height) + 10 : 0);
+ DocumentView.SetSelectOnLoad(newDoc);
+ return this.addDocument?.(newDoc);
+ }
+ return false;
}, 'copied text note');
- onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+ onKey = (e: KeyboardEvent, textBox: FormattedTextBox) => {
+ if ((e.metaKey || e.ctrlKey || e.altKey || textBox.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
- return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
+ return this.createTextDocCopy(textBox, !e.altKey && e.key !== 'Tab');
}
return undefined;
};
@@ -1543,7 +1544,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
rootSelected={childData ? this.rootSelected : returnFalse}
waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
bringToFront={this.bringToFront}
ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
@@ -1562,6 +1563,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
pinToPres={this._props.pinToPres}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+ rejectDrop={this._props.childRejectDrop}
showTitle={this._props.childlayout_showTitle}
dontRegisterView={this._props.dontRegisterView}
pointerEvents={this.childPointerEventsFunc}
@@ -1590,7 +1592,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
case undefined:
case OpenWhere.lightbox:
- if (this.layoutDoc._isLightbox) {
+ if (this.dataDoc.$isLightbox) {
this._lightboxDoc = docs[0];
return true;
}
@@ -1602,14 +1604,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
const childDoc = pair.layout;
- const childDocLayout = Doc.Layout(childDoc);
const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
- const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
- const { z, zIndex } = childDoc;
+ const contentFrameNumber = Cast(childDoc._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const { z, zIndex, stroke_isInkMask } = childDoc;
const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
const { x, y, autoDim, _width, _height, opacity, _rotation } =
layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
- ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
+ ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDoc._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
// prettier-ignore
const rotation = Cast(_rotation,'number',
@@ -1627,8 +1629,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
- transition: StrCast(childDocLayout.dataTransition),
- showTags: BoolCast(childDocLayout.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
+ transition: StrCast(childDoc.dataTransition),
+ showTags: BoolCast(childDoc.showTags) || BoolCast(this.Document.showChildTags) || BoolCast(this.Document._layout_showTags),
pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
pair,
replica: '',
@@ -1721,7 +1723,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
elements.push({
ele: this.getChildDocView(entry[1]),
bounds: entry[1].opacity === 0 ? { payload: undefined, type: '', ...entry[1], width: 0, height: 0 } : { payload: undefined, type: '', ...entry[1] },
- inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
+ inkMask: BoolCast(entry[1].pair.layout?.$stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
})
);
@@ -1744,7 +1746,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (addAsAnnotation) {
if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), [])?.push(anchor);
} else {
this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
@@ -1841,15 +1843,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- updateIcon = (usePanelDimensions?: boolean) => {
+ updateIcon = (/*usePanelDimensions?: boolean*/) => {
const contentDiv = this._mainCont;
return !contentDiv
? new Promise<void>(res => res())
: UpdateIcon(
this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
- usePanelDimensions || true ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
- usePanelDimensions || true ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(), // usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ this._props.PanelHeight(), // usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
this._props.PanelWidth(),
this._props.PanelHeight(),
0,
@@ -1988,7 +1990,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({
description: 'Use Background Color as Default',
event: () => {
- Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor);
+ DocCast(Doc.UserDoc().emptyCollection) && (DocCast(Doc.UserDoc().emptyCollection)!.backgroundColor = StrCast(this.layoutDoc.backgroundColor));
},
icon: 'palette',
});
@@ -2088,7 +2090,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
onPassiveWheel = (e: WheelEvent) => {
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const docHeight = NumCast(this.Document[Doc.LayoutDataKey(this.Document) + '_nativeHeight'], this.nativeHeight);
const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
this._props.isSelected() && !scrollable && e.preventDefault();
};
@@ -2111,12 +2113,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
);
}
- transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
+ transitionFunc = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms ${this._presEaseFunc}` : (Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null) ?? null) ?? ''));
get pannableContents() {
this.incrementalRender(); // needs to happen synchronously or freshly typed text documents will flash and miss their first characters
return (
<CollectionFreeFormPannableContents
- Document={this.Document}
+ Doc={this.Document}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.PanZoomCenterXf}
@@ -2134,6 +2136,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
<MarqueeView
{...this._props}
ref={this._marqueeViewRef}
+ Doc={this.Document}
ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
@@ -2150,7 +2153,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
isAnnotationOverlay={this.isAnnotationOverlay}>
{this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
{this.pannableContents}
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} Doc={this._props.Document} /> : null}
</MarqueeView>
);
}
@@ -2265,10 +2268,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
id={this._paintedId}
ref={r => {
this.createDashEventsTarget(r);
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- this._oldWheel = r;
- // prevent wheel events from passivly propagating up through containers
- r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+ this.fixWheelEvents(r, this._props.isContentActive, this.onPassiveWheel);
r?.addEventListener('mouseleave', this.onMouseLeave);
r?.addEventListener('mouseenter', this.onMouseEnter);
}}
@@ -2315,7 +2315,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
NativeWidth={returnZero}
NativeHeight={returnZero}
onClickScript={this.onChildClickHandler}
- onKey={this.onKeyDown}
+ onKey={this.onKey}
onDoubleClickScript={this.onChildDoubleClickHandler}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
@@ -2387,7 +2387,7 @@ ScriptingGlobals.add(function datavizFromSchema() {
const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key !== 'text');
if (!keys) return;
- const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]);
+ const children = DocListCast(view.Document[Doc.LayoutDataKey(view.Document)]);
const csvRows = [];
csvRows.push(keys.join(','));
for (let i = 0; i < children.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index b9f8b13a7..142085e14 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -11,7 +11,7 @@ import { emptyFunction } from '../../../../Utils';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { List } from '../../../../fields/List';
-import { DocCast, ImageCast, NumCast, StrCast } from '../../../../fields/Types';
+import { DocCast, ImageCastToNameType, NumCast, StrCast } from '../../../../fields/Types';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
@@ -24,6 +24,7 @@ import { FaceRecognitionHandler } from '../../search/FaceRecognitionHandler';
import { CollectionStackingView } from '../CollectionStackingView';
import './FaceCollectionBox.scss';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import { returnEmptyDocViewList } from '../../StyleProvider';
/**
* This code is used to render the sidebar collection of unique recognized faces, where each
@@ -106,9 +107,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
// assign the face in the image that's closest to the face collection's face
if (faceAnno) {
- faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face));
+ DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(faceAnno, DocCast(faceAnno.face)!);
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
}
}
});
@@ -116,9 +117,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
?.filter(doc => DocCast(doc.face)?.type === DocumentType.UFACE)
.forEach(faceAnno => {
const imgDoc = faceAnno;
- faceAnno.face && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face));
+ DocCast(faceAnno.face) && FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, DocCast(faceAnno.face)!);
FaceRecognitionHandler.UniqueFaceAddFaceImage(faceAnno, this.Document);
- faceAnno[DocData].face = this.Document[DocData];
+ faceAnno.$face = this.Document[DocData];
});
e.stopPropagation();
return true;
@@ -149,11 +150,6 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, this.Document);
}, 'remove doc from face');
- /**
- * This stops scroll wheel events when they are used to scroll the face collection.
- */
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
-
render() {
return (
<div className="face-document-item" ref={ele => this.createDropTarget(ele!)}>
@@ -180,14 +176,9 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
style={{
pointerEvents: this._props.isContentActive() ? undefined : 'none',
}}
- ref={action((ele: HTMLDivElement | null) => {
- this._listRef?.removeEventListener('wheel', this.onPassiveWheel);
- this._listRef = ele;
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
- })}>
+ ref={r => this.fixWheelEvents(r, this._props.isContentActive)}>
{FaceRecognitionHandler.UniqueFaceImages(this.Document).map((doc, i) => {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)])?.url.href.split('.') ?? ['-missing-', '.png'];
+ const [name, type] = ImageCastToNameType(doc?.[Doc.LayoutDataKey(doc)]) ?? ['-missing-', '.png'];
return (
<div
className="image-wrapper"
@@ -197,7 +188,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
this,
e,
() => {
- const dragDoc = DocListCast(doc.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
+ const dragDoc = DocListCast(doc?.data_annotations).find(a => a.face === this.Document[DocData]) ?? this.Document;
DragManager.StartDocumentDrag([e.target as HTMLElement], new DragManager.DocumentDragData([dragDoc], dropActionType.embed), e.clientX, e.clientY);
return true;
},
@@ -205,7 +196,7 @@ export class UniqueFaceBox extends ViewBoxBaseComponent<FieldViewProps>() {
emptyFunction
)
}>
- <img onClick={() => DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
+ <img onClick={() => doc && DocumentView.showDocument(doc, { willZoomCentered: true })} style={{ maxWidth: '60px', margin: '10px' }} src={`${name}_o.${type}`} />
<div className="remove-item">
<IconButton tooltip={'Remove Doc From Face Collection'} onPointerDown={() => this.removeFaceImageFromUniqueFace(doc)} icon={'x'} style={{ width: '4px' }} size={Size.XSMALL} />
</div>
@@ -239,11 +230,12 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
}
moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
addDocument = (doc: Doc | Doc[], annotationKey?: string) => {
const uniqueFaceDoc = doc instanceof Doc ? doc : doc[0];
const added = uniqueFaceDoc.type === DocumentType.UFACE;
if (added) {
- Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
+ Doc.MyFaceCollection && Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection);
Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc);
}
return added;
@@ -267,6 +259,8 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
{...this._props} //
styleProvider={this.stackingStyleProvider}
Document={Doc.ActiveDashboard}
+ DocumentView={undefined}
+ docViewPath={returnEmptyDocViewList}
fieldKey="myUniqueFaces"
moveDocument={this.moveDocument}
addDocument={this.addDocument}
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index a3d9641da..ff9fb14e7 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -9,9 +9,8 @@ import React from 'react';
import { imageUrlToBase64 } from '../../../../ClientUtils';
import { Utils, numberRange } from '../../../../Utils';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
-import { DocData } from '../../../../fields/DocSymbols';
import { List } from '../../../../fields/List';
-import { ImageCast } from '../../../../fields/Types';
+import { ImageCastToNameType, ImageCastWithSuffix } from '../../../../fields/Types';
import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
@@ -165,9 +164,9 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// Converts the images into a Base64 format, afterwhich the information is sent to GPT to label them.
const imageInfos = this._selectedImages.map(async doc => {
- if (!doc[DocData].tags_chat) {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
- return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 =>
+ if (!doc.$tags_chat) {
+ const url = ImageCastWithSuffix(doc[Doc.LayoutDataKey(doc)], '_o') ?? '';
+ return imageUrlToBase64(url).then(hrefBase64 =>
!hrefBase64 ? undefined :
gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels =>
({ doc, labels }))) ; // prettier-ignore
@@ -176,7 +175,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
(await Promise.all(imageInfos)).forEach(imageInfo => {
if (imageInfo) {
- imageInfo.doc[DocData].tags_chat = (imageInfo.doc[DocData].tags_chat as List<string>) ?? new List<string>();
+ imageInfo.doc.$tags_chat = (imageInfo.doc.$tags_chat as List<string>) ?? new List<string>();
const labels = imageInfo.labels.split('\n');
labels.forEach(label => {
@@ -186,7 +185,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
.replace(/^\d+\.\s*|-|f\*/, '')
.replace(/^#/, '')
.trim();
- (imageInfo.doc[DocData].tags_chat as List<string>).push(hashLabel);
+ (imageInfo.doc.$tags_chat as List<string>).push(hashLabel);
});
}
});
@@ -200,13 +199,11 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
groupImagesInBox = action(async () => {
this.startLoading();
- for (const doc of this._selectedImages) {
- for (let index = 0; index < (doc[DocData].tags_chat as List<string>).length; index++) {
- const label = (doc[DocData].tags_chat as List<string>)[index];
- const embedding = await gptGetEmbedding(label);
- doc[DocData][`tags_embedding_${index + 1}`] = new List<number>(embedding);
- }
- }
+ await Promise.all(
+ this._selectedImages
+ .map(doc => ({ doc, labels: doc.$tags_chat as List<string> }))
+ .map(({ doc, labels }) => labels.map((label, index) => gptGetEmbedding(label).then(embedding => (doc[`$tags_embedding_${index + 1}`] = new List<number>(embedding)))))
+ );
const labelToEmbedding = new Map<string, number[]>();
// Create embeddings for the labels.
@@ -215,13 +212,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
// For each image, loop through the labels, and calculate similarity. Associate it with the
// most similar one.
this._selectedImages.forEach(doc => {
- const embedLists = numberRange((doc[DocData].tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`])));
+ const embedLists = numberRange((doc.$tags_chat as List<string>).length).map(n => Array.from(NumListCast(doc[`$tags_embedding_${n + 1}`])));
const bestEmbedScore = (embedding: Opt<number[]>) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0));
const {label: mostSimilarLabelCollect} =
this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) }))
.reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur,
{ label: '', similarityScore: 0, }); // prettier-ignore
- doc[DocData].data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
+ doc.$data_label = mostSimilarLabelCollect; // The label most similar to the image's contents.
});
this.endLoading();
@@ -313,7 +310,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
{this._displayImageInformation ? (
<div className="image-information-list">
{this._selectedImages.map(doc => {
- const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.');
+ const [name, type] = ImageCastToNameType(doc[Doc.LayoutDataKey(doc)]);
return (
<div className="image-information" style={{ borderColor: SettingsManager.userColor }} key={Utils.GenerateGuid()}>
<img
@@ -322,7 +319,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
await DocumentView.showDocument(doc, { willZoomCentered: true });
}}></img>
<div className="image-information-labels" onClick={() => this._props.addDocTab(doc, OpenWhere.addRightKeyvalue)}>
- {(doc[DocData].tags_chat as List<string>).map(label => {
+ {(doc.$tags_chat as List<string>).map(label => {
return (
<div key={Utils.GenerateGuid()} className="image-label" style={{ backgroundColor: SettingsManager.userVariantColor, borderColor: SettingsManager.userColor }}>
{label}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 00d7ea451..3cc7c0f2d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -8,7 +8,7 @@ import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSy
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
import { DocUtils } from '../../../documents/DocUtils';
@@ -18,7 +18,6 @@ import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManag
import { Transform } from '../../../util/Transform';
import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { MainView } from '../../MainView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { MarqueeViewBounds } from '../../PinFuncs';
import { PreviewCursor } from '../../PreviewCursor';
@@ -32,6 +31,7 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
interface MarqueeViewProps {
+ Doc: Doc;
getContainerTransform: () => Transform;
getTransform: () => Transform;
activeDocuments: () => Doc[];
@@ -113,6 +113,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
// tslint:disable-next-line:prefer-const
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
+
if (e.key === '?') {
cm.setDefaultItem('?', (str: string) =>
this._props.addDocTab(Docs.Create.WebDocument(`https://wikipedia.org/wiki/${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: `wiki:${str}`, data_useCors: true }), OpenWhere.addRight)
@@ -164,7 +165,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
error && console.log(error);
data &&
ClientUtils.convertDataUri(data, this._props.Document[Id] + '_icon_' + new Date().getTime()).then(returnedfilename => {
- this._props.Document[DocData].icon = new ImageField(returnedfilename);
+ this._props.Document.$icon = new ImageField(returnedfilename);
});
})
);
@@ -370,13 +371,12 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
- const docData = doc[DocData];
- docData.data = new List<Doc>(selected);
- docData.isGroup = makeGroup;
- docData.title = makeGroup ? 'grouping' : 'nested freeform';
+ doc.$data = new List<Doc>(selected);
+ doc.$isGroup = makeGroup;
+ doc.$title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ })(DocCast(Doc.UserDoc().emptyCollection) ? Doc.MakeCopy(DocCast(Doc.UserDoc().emptyCollection)!, true) : Docs.Create.FreeformDocument([], {}));
newCollection.isSystem = undefined;
newCollection._width = bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
newCollection._height = bounds.height || 1;
@@ -437,11 +437,11 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
*/
@undoBatch
classifyImages = async () => {
- const groupButton = DocListCast(Doc.MyLeftSidebarMenu.data).find(d => d.target === Doc.MyImageGrouper);
+ const groupButton = DocListCast(Doc.MyLeftSidebarMenu?.data).find(d => d.target === Doc.MyImageGrouper);
if (groupButton) {
this._selectedDocs = this.marqueeSelect(false, DocumentType.IMG);
ImageLabelBoxData.Instance.setData(this._selectedDocs);
- MainView.Instance.expandFlyout(groupButton);
+ ScriptCast(groupButton.onClick)?.script.run({ this: groupButton });
}
};
@@ -461,9 +461,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const newColDim = 900;
for (const label of labelGroups) {
const newCollection = MarqueeView.getCollection([], undefined, false, this.Bounds);
- newCollection[DocData].title = label + ' Collection';
- newCollection._x = this.Bounds.left + x_offset;
- newCollection._y = this.Bounds.top + y_offset;
+ newCollection.$title = label + ' Collection';
+ newCollection.x = this.Bounds.left + x_offset;
+ newCollection.y = this.Bounds.top + y_offset;
newCollection._width = newColDim;
newCollection._height = newColDim;
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
@@ -480,8 +480,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}
for (const doc of selectedImages) {
- if (doc[DocData].data_label) {
- Doc.AddDocToList(labelToCollection.get(doc[DocData].data_label as string)!, undefined, doc);
+ if (doc.$data_label) {
+ Doc.AddDocToList(labelToCollection.get(doc.$data_label as string)!, undefined, doc);
this._props.removeDocument?.(doc);
}
}
@@ -590,8 +590,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
marqueeSelect = (selectBackgrounds: boolean = false, docType: DocumentType | undefined = undefined) => {
const selection: Doc[] = [];
const selectFunc = (doc: Doc) => {
- const layoutDoc = Doc.Layout(doc);
- const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(layoutDoc._width), height: NumCast(layoutDoc._height) };
+ const bounds = { left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) };
if (!this._lassoFreehand) {
intersectRect(bounds, this.Bounds) && selection.push(doc);
} else {
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index e7f1de7f1..1d5e70be7 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -182,17 +182,17 @@ export class CollectionGridView extends CollectionSubView() {
isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
/**
*
- * @param layout
+ * @param childLayout
* @param dxf the x- and y-translations of the decorations box as a transform i.e. this.lookupIndividualTransform
* @param width
* @param height
* @returns the `ContentFittingDocumentView` of the node
*/
- getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
+ getDisplayDoc = (childLayout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
<DocumentView
{...this._props}
- Document={layout}
- TemplateDataDocument={layout.resolvedDataDoc as Doc}
+ Document={childLayout}
+ TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
NativeWidth={returnZero}
NativeHeight={returnZero}
fitWidth={this._props.childLayoutFitWidth}
@@ -201,6 +201,7 @@ export class CollectionGridView extends CollectionSubView() {
isContentActive={this.isChildContentActive}
PanelWidth={width}
PanelHeight={height}
+ rejectDrop={this._props.childRejectDrop}
ScreenToLocalTransform={dxf}
setContentViewBox={emptyFunction}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 80116dd2f..3c2a99b1e 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -102,7 +102,7 @@ export class CollectionLinearView extends CollectionSubView() {
getLinkUI = () =>
!DocumentLinksButton.StartLink ? null : (
- <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+ <span key="-link-ui-" className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
<span className="bottomPopup-text">
Creating link from:{' '}
<b>
@@ -126,7 +126,7 @@ export class CollectionLinearView extends CollectionSubView() {
);
getCurrentlyPlayingUI = () =>
!DocumentView.CurrentlyPlaying?.length ? null : (
- <span className="bottomPopup-background">
+ <span key="-currently-playing-" className="bottomPopup-background">
<span className="bottomPopup-text">
Currently playing:
{DocumentView.CurrentlyPlaying.map((clip, i) => (
@@ -192,6 +192,7 @@ export class CollectionLinearView extends CollectionSubView() {
renderDepth={this._props.renderDepth + 1}
dontRegisterView={BoolCast(this.Document.childDontRegisterViews)}
focus={emptyFunction}
+ rejectDrop={this._props.childRejectDrop}
styleProvider={this._props.styleProvider}
containerViewPath={this.childContainerViewPath}
whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 8aae24df0..caad1c7e0 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,14 +1,17 @@
+import { Button, IconButton } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
-import { Button, IconButton } from '@dash/components';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { FaChevronRight } from 'react-icons/fa';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable } from '../../../util/UndoManager';
import { DocumentView } from '../../nodes/DocumentView';
@@ -16,8 +19,6 @@ import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'
import './CollectionMulticolumnView.scss';
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
-import { dropActionType } from '../../../util/DropActionTypes';
-import { SnappingManager } from '../../../util/SnappingManager';
interface WidthSpecifier {
magnitude: number;
@@ -197,16 +198,14 @@ export class CollectionMulticolumnView extends CollectionSubView() {
* documents before the target.
*/
private lookupIndividualTransform = (layout: Doc) => {
- if (this.columnUnitLength === undefined) {
- return Transform.Identity(); // we're still waiting on promises to resolve
- }
- let offset = 0;
- // eslint-disable-next-line no-restricted-syntax
- for (const { layout: candidate } of this.childLayoutPairs) {
- if (candidate === layout) {
- return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
+ if (this.columnUnitLength !== undefined) {
+ let offset = 0;
+ for (const { layout: candidate } of this.childLayoutPairs) {
+ if (candidate === layout) {
+ return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
+ }
+ offset += this.lookupPixels(candidate) + resizerWidth;
}
- offset += this.lookupPixels(candidate) + resizerWidth;
}
return Transform.Identity();
};
@@ -262,50 +261,52 @@ export class CollectionMulticolumnView extends CollectionSubView() {
? true
: undefined;
};
- getDisplayDoc = (childLayout: Doc) => {
- const width = () => this.lookupPixels(childLayout);
- const height = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
- const dxf = () =>
- this.lookupIndividualTransform(childLayout)
+ childHeight = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+ childWidth = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc));
+ childXf = computedFn(
+ (childDoc: Doc) => () =>
+ this.lookupIndividualTransform(childDoc)
.translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
- .scale(this._props.NativeDimScaling?.() || 1);
- return (
- <DocumentView
- Document={childLayout}
- TemplateDataDocument={childLayout.resolvedDataDoc as Doc}
- styleProvider={this._props.styleProvider}
- containerViewPath={this.childContainerViewPath}
- LayoutTemplate={this._props.childLayoutTemplate}
- LayoutTemplateString={this._props.childLayoutString}
- renderDepth={this._props.renderDepth + 1}
- PanelWidth={width}
- PanelHeight={height}
- rootSelected={this.rootSelected}
- dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
- onClickScript={this.onChildClickHandler}
- onDoubleClickScript={this.onChildDoubleClickHandler}
- suppressSetHeight
- ScreenToLocalTransform={dxf}
- isContentActive={this.isChildContentActive}
- isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
- hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
- hideDecorationTitle={this._props.childHideDecorationTitle}
- fitContentsToBox={this._props.fitContentsToBox}
- focus={this._props.focus}
- childFilters={this.childDocFilters}
- childFiltersByRanges={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- dontRegisterView={this._props.dontRegisterView}
- addDocument={this._props.addDocument}
- moveDocument={this._props.moveDocument}
- removeDocument={this._props.removeDocument}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- addDocTab={this._props.addDocTab}
- pinToPres={this._props.pinToPres}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
- />
- );
- };
+ .scale(this._props.NativeDimScaling?.() || 1)
+ );
+ getDisplayDoc = (childLayout: Doc) => (
+ <DocumentView
+ Document={childLayout}
+ TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={this.childContainerViewPath}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ renderDepth={this._props.renderDepth + 1}
+ PanelWidth={this.childWidth(childLayout)}
+ PanelHeight={this.childHeight}
+ rootSelected={this.rootSelected}
+ rejectDrop={this._props.childRejectDrop}
+ dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+ onClickScript={this.onChildClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
+ suppressSetHeight
+ ScreenToLocalTransform={this.childXf(childLayout)}
+ isContentActive={this.isChildContentActive}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+ hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ fitContentsToBox={this._props.fitContentsToBox}
+ focus={this._props.focus}
+ childFilters={this.childDocFilters}
+ childFiltersByRanges={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ dontRegisterView={this._props.dontRegisterView}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+ />
+ );
+
/**
* @returns the resolved list of rendered child documents, displayed
* at their resolved pixel widths, each separated by a resizer.
@@ -315,7 +316,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
const collector: JSX.Element[] = [];
this.childLayouts.forEach((layout, i) => {
collector.push(
- // eslint-disable-next-line react/no-array-index-key
<Tooltip title={'Doc: ' + StrCast(layout.title)} key={'wrapper' + i}>
<div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
{this.getDisplayDoc(layout)}
@@ -327,7 +327,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
</Tooltip>,
<ResizeBar
width={resizerWidth}
- // eslint-disable-next-line react/no-array-index-key
key={'resizer' + i}
styleProvider={this._props.styleProvider}
isContentActive={this._props.isContentActive}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index bda8e91ac..593598479 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,5 +1,6 @@
import { action, computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
@@ -178,16 +179,14 @@ export class CollectionMultirowView extends CollectionSubView() {
* documents before the target.
*/
private lookupIndividualTransform = (layout: Doc) => {
- if (this.rowUnitLength === undefined) {
- return Transform.Identity(); // we're still waiting on promises to resolve
- }
- let offset = 0;
- // eslint-disable-next-line no-restricted-syntax
- for (const { layout: candidate } of this.childLayoutPairs) {
- if (candidate === layout) {
- return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
+ if (this.rowUnitLength !== undefined) {
+ let offset = 0;
+ for (const { layout: candidate } of this.childLayoutPairs) {
+ if (candidate === layout) {
+ return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
+ }
+ offset += this.lookupPixels(candidate) + resizerHeight;
}
- offset += this.lookupPixels(candidate) + resizerHeight;
}
return Transform.Identity(); // type coersion, this case should never be hit
};
@@ -243,49 +242,52 @@ export class CollectionMultirowView extends CollectionSubView() {
? true
: undefined;
};
- getDisplayDoc = (layout: Doc) => {
- const height = () => this.lookupPixels(layout);
- const width = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
- const dxf = () =>
- this.lookupIndividualTransform(layout)
+ childHeight = computedFn((childDoc: Doc) => () => this.lookupPixels(childDoc));
+ childWidth = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+ childXf = computedFn(
+ (childDoc: Doc) => () =>
+ this.lookupIndividualTransform(childDoc)
.translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
- .scale(this._props.NativeDimScaling?.() || 1);
- return (
- <DocumentView
- Document={layout}
- TemplateDataDocument={layout.resolvedDataDoc as Doc}
- styleProvider={this._props.styleProvider}
- containerViewPath={this.childContainerViewPath}
- LayoutTemplate={this._props.childLayoutTemplate}
- LayoutTemplateString={this._props.childLayoutString}
- renderDepth={this._props.renderDepth + 1}
- PanelWidth={width}
- PanelHeight={height}
- rootSelected={this.rootSelected}
- dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
- onClickScript={this.onChildClickHandler}
- onDoubleClickScript={this.onChildDoubleClickHandler}
- ScreenToLocalTransform={dxf}
- isContentActive={this.isChildContentActive}
- isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
- hideResizeHandles={!!(layout.layout_fitWidth || this._props.childHideResizeHandles)}
- hideDecorationTitle={this._props.childHideDecorationTitle}
- fitContentsToBox={this._props.fitContentsToBox}
- focus={this._props.focus}
- childFilters={this.childDocFilters}
- childFiltersByRanges={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- dontRegisterView={this._props.dontRegisterView}
- addDocument={this._props.addDocument}
- moveDocument={this._props.moveDocument}
- removeDocument={this._props.removeDocument}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- addDocTab={this._props.addDocTab}
- pinToPres={this._props.pinToPres}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'}
- />
- );
- };
+ .scale(this._props.NativeDimScaling?.() || 1)
+ );
+
+ getDisplayDoc = (childLayout: Doc) => (
+ <DocumentView
+ Document={childLayout}
+ TemplateDataDocument={childLayout.isTemplateDoc || childLayout.isTemplateForField ? this._props.TemplateDataDocument : undefined}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={this.childContainerViewPath}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ renderDepth={this._props.renderDepth + 1}
+ PanelWidth={this.childWidth}
+ PanelHeight={this.childHeight(childLayout)}
+ rootSelected={this.rootSelected}
+ rejectDrop={this._props.childRejectDrop}
+ dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+ onClickScript={this.onChildClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
+ ScreenToLocalTransform={this.childXf(childLayout)}
+ isContentActive={this.isChildContentActive}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+ hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ fitContentsToBox={this._props.fitContentsToBox}
+ focus={this._props.focus}
+ childFilters={this.childDocFilters}
+ childFiltersByRanges={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ dontRegisterView={this._props.dontRegisterView}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'}
+ />
+ );
+
/**
* @returns the resolved list of rendered child documents, displayed
* at their resolved pixel widths, each separated by a resizer.
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 0bf78f57c..53c0823ea 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -224,6 +224,7 @@
display: none;
}
+.schema-table-cell-selected,
.schema-table-cell,
.row-menu {
border: 1px solid global.$medium-gray;
@@ -232,6 +233,14 @@
display: inline-flex;
padding: 0;
align-items: center;
+ input[type='text'] {
+ border: unset;
+ }
+}
+.schema-table-cell-selected {
+ input[type='text'] {
+ background: lightgray;
+ }
}
.schemaRTFCell {
@@ -310,4 +319,7 @@
.schemaField-editing {
outline: none;
height: 100%;
+ cursor: text;
+ outline: none;
+ overflow: auto;
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 5a5cc3622..c06391f35 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -12,7 +12,7 @@ import { List } from '../../../../fields/List';
import { ColumnType } from '../../../../fields/SchemaHeaderField';
import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
import { DocUtils } from '../../../documents/DocUtils';
-import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents';
+import { Docs, DocumentOptions, FInfo, FInfoFieldType } from '../../../documents/Documents';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { dropActionType } from '../../../util/DropActionTypes';
@@ -49,14 +49,16 @@ import { SchemaRowBox } from './SchemaRowBox';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
-export const FInfotoColType: { [key: string]: ColumnType } = {
+export const FInfotoColType: { [key in FInfoFieldType]: ColumnType } = {
string: ColumnType.String,
number: ColumnType.Number,
boolean: ColumnType.Boolean,
date: ColumnType.Date,
- image: ColumnType.Image,
- rtf: ColumnType.RTF,
- enumeration: ColumnType.Enumeration,
+ richtext: ColumnType.RTF,
+ enum: ColumnType.Enumeration,
+ Doc: ColumnType.Any,
+ list: ColumnType.Any,
+ map: ColumnType.Any,
};
const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', 'text'];
@@ -198,7 +200,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._props.setContentViewBox?.(this);
document.addEventListener('keydown', this.onKeyDown);
- Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
+ Object.entries(this._documentOptions).forEach(pair => this.fieldInfos.set(pair[0], pair[1] as FInfo));
this._keysDisposer = observe(
this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
change => {
@@ -250,7 +252,8 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
onKeyDown = (e: KeyboardEvent) => {
if (this._selectedDocs.length > 0) {
- switch (e.key) {
+ switch (e.key + (e.shiftKey ? 'Shift' : '')) {
+ case 'Enter':
case 'ArrowDown':
{
const lastDoc = this._selectedDocs.lastElement();
@@ -270,6 +273,7 @@ export class CollectionSchemaView extends CollectionSubView() {
e.preventDefault();
}
break;
+ case 'EnterShift':
case 'ArrowUp':
{
const firstDoc = this._selectedDocs.lastElement();
@@ -289,6 +293,7 @@ export class CollectionSchemaView extends CollectionSubView() {
e.preventDefault();
}
break;
+ case 'Tab':
case 'ArrowRight':
if (this._selectedCells) {
this._selectedCol = Math.min(this._colEles.length - 1, this._selectedCol + 1);
@@ -296,6 +301,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.selectCell(this._selectedDocs[0], 0, false, false);
}
break;
+ case 'TabShift':
case 'ArrowLeft':
if (this._selectedCells) {
this._selectedCol = Math.max(0, this._selectedCol - 1);
@@ -1187,7 +1193,7 @@ export class CollectionSchemaView extends CollectionSubView() {
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.Document).length > 0;
if (notFiltered) {
notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, childFiltersByRanges, this.Document).length > 0;
- const fieldKey = Doc.LayoutFieldKey(d);
+ const fieldKey = Doc.LayoutDataKey(d);
const isAnnotatableDoc = d[fieldKey] instanceof List && !(d[fieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
const docChildDocs = d[isAnnotatableDoc ? fieldKey + '_annotations' : fieldKey];
const sidebarDocs = isAnnotatableDoc && d[fieldKey + '_sidebar'];
@@ -1200,7 +1206,7 @@ export class CollectionSchemaView extends CollectionSubView() {
newarray = [];
// eslint-disable-next-line no-loop-func
subDocs.forEach(t => {
- const docFieldKey = Doc.LayoutFieldKey(t);
+ const docFieldKey = Doc.LayoutDataKey(t);
const isSubDocAnnotatable = t[docFieldKey] instanceof List && !(t[docFieldKey] as List<Doc>)?.some(ele => !(ele instanceof Doc));
notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !childFiltersByRanges.length) || DocUtils.FilterDocs([t], childDocFilters, childFiltersByRanges, d).length));
DocListCast(t[isSubDocAnnotatable ? docFieldKey + '_annotations' : docFieldKey]).forEach(newdoc => newarray.push(newdoc));
@@ -1287,7 +1293,6 @@ export class CollectionSchemaView extends CollectionSubView() {
isContentActive = () => this._props.isSelected() || this._props.isContentActive();
screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0);
previewWidthFunc = () => this.previewWidth;
- onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
displayedDocsFunc = () => this.docsWithDrag.docs;
render() {
return (
@@ -1301,15 +1306,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.setColDrag(false);
}}>
<div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }} />
- <div
- className="schema-table"
- style={{ width: `calc(100% - ${this.previewWidth}px)` }}
- onWheel={e => this._props.isContentActive() && e.stopPropagation()}
- ref={ele => {
- // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
- this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
- (this._oldWheel = ele)?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
- }}>
+ <div className="schema-table" style={{ width: `calc(100% - ${this.previewWidth}px)` }} onWheel={e => this._props.isContentActive() && e.stopPropagation()} ref={ele => this.fixWheelEvents(ele, this._props.isContentActive)}>
<div className="schema-header-row" style={{ height: this.rowHeightFunc() }}>
<div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}>
<IconButton
diff --git a/src/client/views/collections/collectionSchema/SchemaCellField.tsx b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
index 5a64ecc62..9ad94cb31 100644
--- a/src/client/views/collections/collectionSchema/SchemaCellField.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaCellField.tsx
@@ -21,11 +21,11 @@ import DOMPurify from 'dompurify';
*/
export interface SchemaCellFieldProps {
- contents: FieldType;
+ Doc: Doc;
+ contents: FieldType | undefined;
fieldContents?: FieldViewProps;
editing?: boolean;
oneLine?: boolean;
- Document: Doc;
fieldKey: string;
// eslint-disable-next-line no-use-before-define
refSelectModeInfo: { enabled: boolean; currEditing: SchemaCellField | undefined };
@@ -55,7 +55,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
}); //must be moved to end of batch or else other docs aren't loaded, so render as d-1 in function
}
- get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+ get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
get selfRefPattern() {
return `d${this.docIndex}.${this._props.fieldKey}`;
@@ -85,16 +85,8 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
() => this._editing,
editing => {
if (editing) {
+ this.setContent((this._unrenderedContent = this._props.GetValue() ?? ''));
this.setupRefSelect(this.refSelectConditionMet);
- setTimeout(() => {
- if (this._inputref?.innerText.startsWith('=') || this._inputref?.innerText.startsWith(':=')) {
- this._overlayDisposer?.();
- this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
- this._props.highlightCells?.(this._unrenderedContent);
- this.setContent(this._unrenderedContent);
- setTimeout(() => this.setCursorPosition(this._unrenderedContent.length));
- }
- });
} else {
this._overlayDisposer?.();
this._overlayDisposer = undefined;
@@ -107,9 +99,8 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
this._disposers.fieldUpdate = reaction(
() => this._props.GetValue(),
fieldVal => {
- console.log('Update: ' + this._props.Document.title, this._props.fieldKey, fieldVal);
this._unrenderedContent = fieldVal ?? '';
- this.finalizeEdit(false, false, false);
+ this._editing && this.finalizeEdit(false, false, false);
}
);
}
@@ -127,7 +118,6 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
_unmounted = false;
componentWillUnmount(): void {
this._unmounted = true;
- console.log('Unmount: ' + this._props.Document.title, this._props.fieldKey);
this._overlayDisposer?.();
Object.values(this._disposers).forEach(disposer => disposer?.());
this.finalizeEdit(false, true, false);
@@ -194,6 +184,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
setIsFocused = (value: boolean) => {
const wasFocused = this._editing;
this._editing = value;
+ this._editing && setTimeout(() => this._inputref?.focus());
return wasFocused !== this._editing;
};
@@ -274,8 +265,6 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- if (e.nativeEvent.defaultPrevented) return; // hack .. DashFieldView grabs native events, but react ignores stoppedPropagation and preventDefault, so we need to check it here
-
switch (e.key) {
case 'Tab':
e.stopPropagation();
@@ -286,9 +275,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
break;
case 'Enter':
e.stopPropagation();
- if (!e.ctrlKey) {
- this.finalizeEdit(e.shiftKey, false, true);
- }
+ !e.ctrlKey && this.finalizeEdit(e.shiftKey, false, true);
break;
case 'Escape':
e.stopPropagation();
@@ -299,7 +286,7 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
case 'ArrowLeft':
case 'ArrowRight': // prettier-ignore
e.stopPropagation();
- setTimeout(() => this.setupRefSelect(this.refSelectConditionMet), 0);
+ setTimeout(() => this.setupRefSelect(this.refSelectConditionMet));
break;
case ' ':
{
@@ -308,18 +295,14 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
setTimeout(() => {
this.setContent(this._unrenderedContent);
setTimeout(() => this.setCursorPosition(cursorPos));
- }, 0);
+ });
}
break;
- case 'u': // for some reason 'u' otherwise exits the editor
- e.stopPropagation();
- break;
case 'Shift':
case 'Alt':
case 'Meta':
case 'Control':
- case ':': // prettier-ignore
- break;
+ case ':':
default:
break;
}
@@ -363,12 +346,9 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
<div
contentEditable
className="schemaField-editing"
- ref={r => {
- this._inputref = r;
- }}
- style={{ cursor: 'text', outline: 'none', overflow: 'auto', minHeight: `min(100%, ${(this._props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20 }}
+ ref={r => (this._inputref = r)}
+ style={{ minHeight: `min(100%, ${(this._props.GetValue()?.split('\n').length || 1) * 15})`, minWidth: 20 }}
onBlur={() => (this._props.refSelectModeInfo.enabled ? setTimeout(() => this.setIsFocused(true), 1000) : this.finalizeEdit(false, true, false))}
- autoFocus
onInput={this.onChange}
onKeyDown={this.onKeyDown}
onPointerDown={e => {
@@ -385,14 +365,37 @@ export class SchemaCellField extends ObservableReactComponent<SchemaCellFieldPro
);
};
+ onFocus = () => {
+ if (this._inputref?.innerText.startsWith('=') || this._inputref?.innerText.startsWith(':=')) {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ this._props.highlightCells?.(this._unrenderedContent);
+ this.setContent(this._unrenderedContent);
+ setTimeout(() => this.setCursorPosition(this._unrenderedContent.length));
+ }
+ };
+
+ onBlur = action(() => {
+ this._editing = false;
+ });
+
render() {
const gval = this._props.GetValue()?.replace(/\n/g, '\\r\\n');
if (this._editing && gval !== undefined) {
- return <div className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}>{this.renderEditor()}</div>;
+ return (
+ <div
+ className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`} //
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}>
+ {this.renderEditor()}
+ </div>
+ );
} else
return this._props.contents instanceof ObjectField ? null : (
<div
className={`editableView-container-editing${this._props.oneLine ? '-oneLine' : ''}`}
+ onFocus={this.onFocus}
+ onBlur={this.onBlur}
style={{
minHeight: '10px',
whiteSpace: this._props.oneLine ? 'nowrap' : 'pre-line',
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 81a2d8e64..16d33eb93 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -107,7 +107,7 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea
focus: emptyFunction,
addDocTab: SchemaTableCell.addFieldDoc,
pinToPres: returnZero,
- Document: DocCast(Document.rootDocument, Document),
+ Document: DocCast(Document.rootDocument, Document)!,
fieldKey: fieldKey,
PanelWidth: columnWidth,
PanelHeight: props.rowHeight,
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index da203abfa..c9853fab0 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -171,7 +171,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
isolatedSelection={this.isolatedSelection}
key={key}
rowSelected={this._props.isSelected}
- Document={this.Document}
+ Doc={this.Document}
col={index}
fieldKey={key}
allowCRs={false} // to enter text with new lines, must use \n
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index c847bc546..8b34b4139 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -32,6 +32,7 @@ import { FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaCellField } from './SchemaCellField';
+import { DocLayout } from '../../../../fields/DocSymbols';
/**
* SchemaTableCells make up the majority of the visual representation of the SchemaView.
@@ -40,7 +41,7 @@ import { SchemaCellField } from './SchemaCellField';
*/
export interface SchemaTableCellProps {
- Document: Doc;
+ Doc: Doc;
col: number;
deselectCell: () => void;
selectCell: (doc: Doc, col: number, shift: boolean, ctrl: boolean) => void;
@@ -71,7 +72,7 @@ export interface SchemaTableCellProps {
}
function selectedCell(props: SchemaTableCellProps) {
- return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Document)?.length;
+ return props.isRowActive() && props.selectedCol() === props.col && props.selectedCells()?.filter(d => d === props.Doc)?.length;
}
@observer
@@ -84,11 +85,11 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
makeObservable(this);
}
- get docIndex(){return DocumentView.getDocViewIndex(this._props.Document);} // prettier-ignore
+ get docIndex(){return DocumentView.getDocViewIndex(this._props.Doc);} // prettier-ignore
get isDefault(){return SchemaColumnHeader.isDefaultField(this._props.fieldKey);} // prettier-ignore
- get lockedInteraction(){return (this.isDefault || this._props.Document._lockedSchemaEditing);} // prettier-ignore
+ get lockedInteraction(){return (this.isDefault || this._props.Doc._lockedSchemaEditing);} // prettier-ignore
get backgroundColor() {
if (this.lockedInteraction) {
@@ -102,15 +103,16 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return true;
};
public static renderProps(props: SchemaTableCellProps) {
- const { Document, fieldKey, getFinfo, columnWidth, isRowActive } = props;
+ const { Doc: Document, fieldKey, /* getFinfo,*/ columnWidth, isRowActive } = props;
let protoCount = 0;
+ const layoutDoc = fieldKey.startsWith('_') ? Document[DocLayout] : Document;
let doc: Doc | undefined = Document;
while (doc) {
if (Object.keys(doc).includes(fieldKey.replace(/^_/, ''))) break;
protoCount++;
doc = DocCast(doc.proto);
}
- const color = protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells
+ const color = layoutDoc !== Document ? 'red' : protoCount === 0 || (fieldKey.startsWith('_') && Document[fieldKey] === undefined) ? 'black' : 'blue'; // color of text in cells
const textDecoration = '';
const fieldProps: FieldViewProps = {
childFilters: returnEmptyFilter,
@@ -130,13 +132,13 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
focus: emptyFunction,
addDocTab: SchemaTableCell.addFieldDoc,
pinToPres: returnZero,
- Document: DocCast(Document.rootDocument, Document),
+ Document: Document,
fieldKey: fieldKey,
PanelWidth: columnWidth,
PanelHeight: props.rowHeight,
rootSelected: props.rootSelected,
};
- const readOnly = getFinfo(fieldKey)?.readOnly ?? false;
+ const readOnly = false; // getFinfo(fieldKey)?.readOnly ?? false;
const cursor = !readOnly ? 'text' : 'default';
const pointerEvents: 'all' | 'none' = !readOnly && isRowActive() ? 'all' : 'none';
return { color, textDecoration, fieldProps, cursor, pointerEvents };
@@ -166,16 +168,19 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
while ((matches = idPattern.exec(field)) !== null) {
results.push([matches[0], matches[1].replace(/"/g, '')]);
}
- results.forEach(idFuncPair => {
- modField = modField.replace(idFuncPair[0], 'd' + DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1])).toString());
- });
+ results
+ .filter(idFuncPair => IdToDoc(idFuncPair[1]))
+ .forEach(idFuncPair => {
+ modField = modField.replace(idFuncPair[0], 'd' + DocumentView.getDocViewIndex(IdToDoc(idFuncPair[1])!).toString());
+ });
if (modField.endsWith(';')) modField = modField.substring(0, modField.length - 1);
const inQuotes = (strField: string) => {
return (strField.startsWith('`') && strField.endsWith('`')) || (strField.startsWith("'") && strField.endsWith("'")) || (strField.startsWith('"') && strField.endsWith('"'));
};
- if (!inQuotes(this._submittedValue) && inQuotes(modField)) modField = modField.substring(1, modField.length - 1);
+ const submittedValue = this._submittedValue.startsWith(eqSymbol) ? this._submittedValue.slice(eqSymbol.length) : this._submittedValue;
+ if (!inQuotes(submittedValue) && inQuotes(modField)) modField = modField.substring(1, modField.length - 1);
return eqSymbol + modField;
};
@@ -186,7 +191,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
return (
<div
className="schemacell-edit-wrapper"
- // onContextMenu={}
+ tabIndex={1}
style={{
color,
textDecoration,
@@ -196,7 +201,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
<SchemaCellField
fieldKey={this._props.fieldKey}
refSelectModeInfo={this._props.refSelectModeInfo}
- Document={this._props.Document}
+ Doc={this._props.Doc}
highlightCells={(text: string) => this._props.highlightCells(this.adjustSelfReference(text))}
getCells={(text: string) => this._props.eqHighlightFunc(this.adjustSelfReference(text))}
ref={r => selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)}
@@ -211,8 +216,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
this._props.finishEdit?.();
return true;
}
- const hasNoLayout = Doc.IsDataProto(fieldProps.Document) ? true : undefined; // the "delegate" is a a data document so never write to it's proto
- const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, hasNoLayout);
+ const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey, value);
this._submittedValue = value;
this._props.finishEdit?.();
return ret;
@@ -224,7 +228,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
get getCellType() {
const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
- const cellValue = this._props.Document[this._props.fieldKey];
+ const cellValue = this._props.Doc[this._props.fieldKey];
if (cellValue instanceof ImageField) return ColumnType.Image;
if (cellValue instanceof DateField) return ColumnType.Date;
@@ -232,9 +236,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
if (typeof cellValue === 'number') return ColumnType.Any;
if (typeof cellValue === 'string' && columnTypeStr !== FInfoFieldType.enumeration) return ColumnType.Any;
if (typeof cellValue === 'boolean') return ColumnType.Boolean;
- if (columnTypeStr && columnTypeStr in FInfotoColType) return FInfotoColType[columnTypeStr];
- return ColumnType.Any;
+ return columnTypeStr ? FInfotoColType[columnTypeStr] : ColumnType.Any;
}
get content() {
@@ -253,15 +256,15 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
const sides: Array<string | undefined> = [];
sides[0] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // left
sides[1] = selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // right
- sides[2] = !this._props.isolatedSelection(this._props.Document)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
- sides[3] = !this._props.isolatedSelection(this._props.Document)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
+ sides[2] = !this._props.isolatedSelection(this._props.Doc)[0] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // top
+ sides[3] = !this._props.isolatedSelection(this._props.Doc)[1] && selectedCell(this._props) ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined; // bottom
return sides;
}
render() {
return (
<div
- className="schema-table-cell"
+ className={`schema-table-cell${selectedCell(this._props) ? '-selected' : ''}`}
onContextMenu={e => StopEvent(e)}
onPointerDown={action(e => {
if (this.lockedInteraction) {
@@ -273,7 +276,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
if (this._props.refSelectModeInfo.enabled && !selectedCell(this._props)) {
e.stopPropagation();
e.preventDefault();
- this._props.selectReference(this._props.Document, this._props.col);
+ this._props.selectReference(this._props.Doc, this._props.col);
return;
}
@@ -281,9 +284,9 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
const ctrl: boolean = e.ctrlKey;
if (this._props.isRowActive?.()) {
if (selectedCell(this._props) && ctrl) {
- this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
e.stopPropagation();
- } else !selectedCell(this._props) && this._props.selectCell(this._props.Document, this._props.col, shift, ctrl);
+ } else !selectedCell(this._props) && this._props.selectCell(this._props.Doc, this._props.col, shift, ctrl);
}
})}
style={{
@@ -322,12 +325,12 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
}
get url() {
- const field = Cast(this._props.Document[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this._props.Document[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
+ const field = Cast(this._props.Doc[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(this._props.Doc[this._props.fieldKey + '_alternates']); // retrieve alternate documents that may be rendered as alternate images
const altpaths = alts
- .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+ .map(doc => Cast(doc[Doc.LayoutDataKey(doc)], ImageField, null)?.url)
.filter(url => url)
- .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+ .map(url => this.choosePath(url!)); // access the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
// If there is a path, follow it; otherwise, follow a link to a default image icon
const url = paths.length ? paths : [ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
@@ -360,7 +363,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
};
render() {
- const aspect = Doc.NativeAspect(this._props.Document); // aspect ratio
+ const aspect = Doc.NativeAspect(this._props.Doc); // aspect ratio
// let width = Math.max(75, this._props.columnWidth); // get a with that is no smaller than 75px
// const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined;
@@ -378,9 +381,9 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
}
@observable _pickingDate: boolean = false;
- @computed get date(): DateField {
+ @computed get date(): DateField | undefined {
// if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
- return DateCast(this._props.Document[this._props.fieldKey]);
+ return DateCast(this._props.Doc[this._props.fieldKey]);
}
handleChange = undoable((date: Date | null) => {
@@ -389,7 +392,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
// this.applyToDoc(this._document, this._props.row, this._props.col, script.run);
// } else {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
- date && (this._props.Document[this._props.fieldKey] = new DateField(date));
+ date && (this._props.Doc[this._props.fieldKey] = new DateField(date));
// }
}, 'date change');
@@ -397,10 +400,10 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
const { pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<>
- <div style={{ pointerEvents: 'none' }}>
- <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} />
+ <div style={{ pointerEvents: 'none' }} tabIndex={1}>
+ <DatePicker dateFormat="Pp" selected={this.date?.date ?? new Date()} onChange={emptyFunction} />
</div>
- {pointerEvents === 'none' ? null : (
+ {pointerEvents === 'none' || !selectedCell(this._props) ? null : (
<Popup
icon={<FontAwesomeIcon size="xs" icon="caret-down" />}
size={Size.XSMALL}
@@ -409,7 +412,7 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
background={SnappingManager.userBackgroundColor}
popup={
<div style={{ width: 'fit-content', height: '200px' }}>
- <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
+ <DatePicker open dateFormat="Pp" selected={this.date?.date ?? new Date()} onChange={this.handleChange} />
</div>
}
/>
@@ -449,13 +452,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
return (
<div className="schemaBoolCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
<input
+ onPointerDown={e => e.stopPropagation()}
style={{ marginRight: 4 }}
type="checkbox"
- checked={BoolCast(this._props.Document[this._props.fieldKey])}
+ checked={BoolCast(this._props.Doc[this._props.fieldKey])}
onChange={undoable((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
if ((value?.nativeEvent as MouseEvent | PointerEvent).shiftKey) {
this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
- } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
+ } else Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? ''));
}, 'set bool cell')}
/>
@@ -463,14 +467,14 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
contents=""
fieldContents={fieldProps}
editing={selectedCell(this._props) ? undefined : false}
- GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
+ GetValue={() => Field.toKeyValueString(this._props.Doc, this._props.fieldKey)}
SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
this._props.finishEdit?.();
return true;
}
- const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+ const set = Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Doc) ? true : undefined);
this._props.finishEdit?.();
return set;
}, 'set bool cell')}
@@ -494,14 +498,22 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
<div style={{ width: '100%' }}>
<Select
styles={{
+ dropdownIndicator: base => ({
+ ...base,
+ display: selectedCell(this._props) ? 'unset' : 'none',
+ }),
container: base => ({
...base,
height: 20,
+ border: 'unset !important',
+ pointerEvents: selectedCell(this._props) ? 'all' : 'none',
}),
control: base => ({
...base,
height: 20,
minHeight: 20,
+ border: 'unset !important',
+ background: selectedCell(this._props) ? 'lightgray' : undefined,
}),
placeholder: base => ({
...base,
@@ -517,6 +529,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
...base,
height: 20,
transform: 'scale(0.75)',
+ border: 'unset !important',
}),
menuPortal: base => ({
...base,
@@ -529,10 +542,10 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
}}
menuPortalTarget={this._props.menuTarget}
menuPosition="absolute"
- placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')}
+ placeholder={StrCast(this._props.Doc[this._props.fieldKey], 'select...')}
options={options}
isMulti={false}
- onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+ onChange={val => Doc.SetField(this._props.Doc, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
/>
</div>
</div>