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/CollectionCalendarView.tsx34
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx104
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx16
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx210
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx101
-rw-r--r--src/client/views/collections/CollectionMenu.scss2
-rw-r--r--src/client/views/collections/CollectionMenu.tsx174
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx115
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx53
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewDivider.tsx5
-rw-r--r--src/client/views/collections/CollectionPileView.tsx40
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx180
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx95
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx92
-rw-r--r--src/client/views/collections/CollectionSubView.tsx215
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx44
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx126
-rw-r--r--src/client/views/collections/CollectionTreeViewType.ts5
-rw-r--r--src/client/views/collections/CollectionView.tsx102
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx32
-rw-r--r--src/client/views/collections/TabDocView.tsx604
-rw-r--r--src/client/views/collections/TreeView.tsx382
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts223
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx31
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx70
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx62
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx25
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx76
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx997
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx12
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx82
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx58
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx77
-rw-r--r--src/client/views/collections/collectionLinear/index.ts2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx57
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx24
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx26
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx2
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx3
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx28
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx249
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx21
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx15
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx60
45 files changed, 2667 insertions, 2265 deletions
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
index cbcc980a9..a08a7c7c1 100644
--- a/src/client/views/collections/CollectionCalendarView.tsx
+++ b/src/client/views/collections/CollectionCalendarView.tsx
@@ -1,7 +1,8 @@
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { dateRangeStrToDates, emptyFunction, returnTrue } from '../../../Utils';
+import { emptyFunction } from '../../../Utils';
+import { dateRangeStrToDates, returnTrue } from '../../../ClientUtils';
import { Doc, DocListCast } from '../../../fields/Doc';
import { StrCast } from '../../../fields/Types';
import { CollectionStackingView } from './CollectionStackingView';
@@ -24,16 +25,13 @@ export class CollectionCalendarView extends CollectionSubView() {
removeCalendar = () => {};
- addCalendar = (doc: Doc | Doc[], annotationKey?: string | undefined): boolean => {
+ addCalendar = (/* doc: Doc | Doc[], annotationKey?: string | undefined */): boolean =>
// bring up calendar modal with option to create a calendar
- return true;
- };
+ true;
_stackRef = React.createRef<CollectionStackingView>();
- panelHeight = () => {
- return this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title
- };
+ panelHeight = () => this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title
// most recent calendar should come first
sortByMostRecentDate = (calendarA: Doc, calendarB: Doc) => {
@@ -45,18 +43,18 @@ export class CollectionCalendarView extends CollectionSubView() {
if (aFromDate > bFromDate) {
return -1; // a comes first
- } else if (aFromDate < bFromDate) {
+ }
+ if (aFromDate < bFromDate) {
+ return 1; // b comes first
+ }
+ // start dates are the same
+ if (aToDate > bToDate) {
+ return -1; // a comes first
+ }
+ if (aToDate < bToDate) {
return 1; // b comes first
- } else {
- // start dates are the same
- if (aToDate > bToDate) {
- return -1; // a comes first
- } else if (aToDate < bToDate) {
- return 1; // b comes first
- } else {
- return 0; // same start and end dates
- }
}
+ return 0; // same start and end dates
};
screenToLocalTransform = () =>
@@ -73,12 +71,12 @@ export class CollectionCalendarView extends CollectionSubView() {
return (
<div className="collectionCalendarView">
<CollectionStackingView
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
setContentViewBox={emptyFunction}
ref={this._stackRef}
PanelHeight={this.panelHeight}
PanelWidth={this._props.PanelWidth}
- // childFilters={this.childFilters} DO I NEED THIS?
sortFunc={this.sortByMostRecentDate}
setHeight={undefined}
isAnnotationOverlay={false}
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 4e4bd43bf..7617f2a52 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,25 +1,28 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, returnFalse, returnZero } from '../../../Utils';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { returnZero } from '../../../ClientUtils';
+import { Utils } from '../../../Utils';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { StyleProp } from '../StyleProvider';
+import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
-import { FocusViewOptions } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
import './CollectionCarousel3DView.scss';
import { CollectionSubView } from './CollectionSubView';
+
const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@observer
export class CollectionCarousel3DView extends CollectionSubView() {
@computed get scrollSpeed() {
- return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
+ return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; // default scroll speed
}
constructor(props: any) {
super(props);
@@ -48,16 +51,16 @@ export class CollectionCarousel3DView extends CollectionSubView() {
panelHeight = () => this._props.PanelHeight() * Number(CAROUSEL3D_SIDE_SCALE);
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
- isChildContentActive = () => (this.isContentActive() ? true : false);
+ isChildContentActive = () => !!this.isContentActive();
childScreenToLocal = () =>
this._props // document's left is the panel shifted by the doc's index * panelWidth/#docs. But it scales by centerScale around its center, so it's left moves left by the distance of the left from the center (panelwidth/2) * the scale delta (centerScale-1)
.ScreenToLocalTransform() // the top behaves the same way ecept it's shifted by the 'top' amount specified for the panel in css and then by the scale factor.
.translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this._props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
.scale(1 / this.centerScale);
- focus = (anchor: Doc, options: FocusViewOptions) => {
+ focus = (anchor: Doc, options: FocusViewOptions): Opt<number> => {
const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
- if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+ if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return undefined;
options.didMove = true;
const target = DocCast(anchor.annotationOn) ?? anchor;
const index = docs.indexOf(target);
@@ -66,41 +69,38 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
- const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
- return (
- <DocumentView
- {...this._props}
- Document={childPair.layout}
- TemplateDataDocument={childPair.data}
- //suppressSetHeight={true}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- layout_fitWidth={undefined}
- onDoubleClickScript={this.onChildDoubleClick}
- renderDepth={this._props.renderDepth + 1}
- LayoutTemplate={this._props.childLayoutTemplate}
- LayoutTemplateString={this._props.childLayoutString}
- focus={this.focus}
- ScreenToLocalTransform={this.childScreenToLocal}
- isContentActive={this.isChildContentActive}
- isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
- PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight}
- />
- );
- };
-
- return this.carouselItems.map((childPair, index) => {
- return (
- <div key={childPair.layout[Id]} className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`} style={{ width: this.panelWidth() }}>
- {displayDoc(childPair)}
- </div>
- );
- });
+ const displayDoc = (childPair: { layout: Doc; data: Doc }) => (
+ <DocumentView
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ {...this._props}
+ Document={childPair.layout}
+ TemplateDataDocument={childPair.data}
+ // suppressSetHeight={true}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ layout_fitWidth={undefined}
+ onDoubleClickScript={this.onChildDoubleClick}
+ renderDepth={this._props.renderDepth + 1}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ focus={this.focus}
+ ScreenToLocalTransform={this.childScreenToLocal}
+ isContentActive={this.isChildContentActive}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+ PanelWidth={this.panelWidth}
+ PanelHeight={this.panelHeight}
+ />
+ );
+
+ return this.carouselItems.map((childPair, index) => (
+ <div key={childPair.layout[Id]} className={`collectionCarousel3DView-item${index === currentIndex ? '-active' : ''} ${index}`} style={{ width: this.panelWidth() }}>
+ {displayDoc(childPair)}
+ </div>
+ ));
}
changeSlide = (direction: number) => {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.carouselItems.length) % this.carouselItems.length;
};
@@ -124,21 +124,21 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
toggleAutoScroll = (direction: number) => {
- this.layoutDoc.autoScrollOn = this.layoutDoc.autoScrollOn ? false : true;
+ this.layoutDoc.autoScrollOn = !this.layoutDoc.autoScrollOn;
this.layoutDoc.autoScrollOn ? this.startAutoScroll(direction) : this.stopAutoScroll();
};
fadeScrollButton = () => {
window.setTimeout(() => {
- !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = 'none'); //fade away after 1.5s if it's not clicked.
+ !this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = 'none'); // fade away after 1.5s if it's not clicked.
}, 1500);
};
@computed get buttons() {
return (
<div className="arrow-buttons">
- <div title="click to go back" key="back" className="carousel3DView-back" onClick={e => this.onArrowClick(-1)} />
- <div title="click to advance" key="fwd" className="carousel3DView-fwd" onClick={e => this.onArrowClick(1)} />
+ <div title="click to go back" key="back" className="carousel3DView-back" onClick={() => this.onArrowClick(-1)} />
+ <div title="click to advance" key="fwd" className="carousel3DView-fwd" onClick={() => this.onArrowClick(1)} />
{/* {this.autoScrollButton} */}
</div>
);
@@ -149,17 +149,25 @@ export class CollectionCarousel3DView extends CollectionSubView() {
return (
<>
<div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(-1)}>
- {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-left'} size={'1x'} />}
+ {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon="pause" size="1x" /> : <FontAwesomeIcon icon="angle-double-left" size="1x" />}
</div>
<div className={`carousel3DView-fwd-scroll${whichButton === 'fwd' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(1)}>
- {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon={'pause'} size={'1x'} /> : <FontAwesomeIcon icon={'angle-double-right'} size={'1x'} />}
+ {this.layoutDoc.autoScrollOn ? <FontAwesomeIcon icon="pause" size="1x" /> : <FontAwesomeIcon icon="angle-double-right" size="1x" />}
</div>
</>
);
}
@computed get dots() {
- return this.carouselItems.map((_child, index) => <div key={Utils.GenerateGuid()} className={`dot${index === NumCast(this.layoutDoc._carousel_index) ? '-active' : ''}`} onClick={() => (this.layoutDoc._carousel_index = index)} />);
+ return this.carouselItems.map((_child, index) => (
+ <div
+ key={Utils.GenerateGuid()}
+ className={`dot${index === NumCast(this.layoutDoc._carousel_index) ? '-active' : ''}`}
+ onClick={() => {
+ this.layoutDoc._carousel_index = index;
+ }}
+ />
+ ));
}
@computed get translateX() {
const index = NumCast(this.layoutDoc._carousel_index);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 9c370bfbb..9d3657995 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,18 +1,22 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable react/jsx-props-no-spreading */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { StopEvent, emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
+import { emptyFunction } from '../../../Utils';
+import { StopEvent, returnFalse, returnOne, returnZero } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
-import { StyleProp } from '../StyleProvider';
+import { StyleProp } from '../StyleProp';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView } from './CollectionSubView';
-import { DocumentType } from '../../documents/DocumentTypes';
@observer
export class CollectionCarouselView extends CollectionSubView() {
@@ -48,7 +52,7 @@ export class CollectionCarouselView extends CollectionSubView() {
};
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => {
// first look for properties on the document in the carousel, then fallback to properties on the container
- const childValue = doc?.['caption-' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
+ const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
return childValue ?? this._props.styleProvider?.(this.layoutDoc, captionProps, property);
};
panelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
@@ -106,10 +110,10 @@ export class CollectionCarouselView extends CollectionSubView() {
return (
<>
<div key="back" className="carouselView-back" onClick={this.goback}>
- <FontAwesomeIcon icon={'chevron-left'} size={'2x'} />
+ <FontAwesomeIcon icon="chevron-left" size="2x" />
</div>
<div key="fwd" className="carouselView-fwd" onClick={this.advance}>
- <FontAwesomeIcon icon={'chevron-right'} size={'2x'} />
+ <FontAwesomeIcon icon="chevron-right" size="2x" />
</div>
</>
);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index b2897a9b7..0ee3575f3 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -2,60 +2,55 @@ import { action, IReactionDisposer, makeObservable, observable, reaction } from
import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
-import * as GoldenLayout from '../../../client/goldenLayout';
+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 { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
-import { FieldValue, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { GetEffectiveAcl, inheritParentAcls, SetPropSetterCb } from '../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, DivHeight, DivWidth, emptyFunction, incrementTitleCopy } from '../../../Utils';
+import { emptyFunction } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DocumentManager } from '../../util/DocumentManager';
+import * as GoldenLayout from '../../goldenLayout';
import { DragManager } from '../../util/DragManager';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from '../../util/SelectionManager';
-import { SettingsManager } from '../../util/SettingsManager';
+import { SnappingManager } from '../../util/SnappingManager';
import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
import { DashboardView } from '../DashboardView';
import { LightboxView } from '../LightboxView';
-import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
import { OverlayView } from '../OverlayView';
import { ScriptingRepl } from '../ScriptingRepl';
import { UndoStack } from '../UndoStack';
import './CollectionDockingView.scss';
-import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
-import { TabDocView } from './TabDocView';
-import { ComputedField } from '../../../fields/ScriptField';
+
const _global = (window /* browser */ || global) /* node */ as any;
@observer
export class CollectionDockingView extends CollectionSubView() {
- @observable public static Instance: CollectionDockingView | undefined = undefined;
- public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) {
- return {
- type: 'react-component',
- component: 'DocumentFrameRenderer',
- title: document.title,
- width: width,
- props: {
- documentId: document[Id],
- keyValue,
- panelName, // name of tab that can be used to close or replace its contents
- },
- };
+ static tabClass: JSX.Element | null = null;
+ /**
+ * Configure golden layout to render its documents using the specified React component
+ * @param ele - typically would be set to TabDocView
+ */
+ static setTabJSXComponent(ele: any) {
+ this.tabClass = ele;
}
+ // eslint-disable-next-line no-use-before-define
+ @observable public static Instance: CollectionDockingView | undefined = undefined;
private _reactionDisposer?: IReactionDisposer;
private _lightboxReactionDisposer?: IReactionDisposer;
private _containerRef = React.createRef<HTMLDivElement>();
- public _flush: UndoManager.Batch | undefined;
+ private _flush: UndoManager.Batch | undefined;
+ private _unmounting = false;
private _ignoreStateChange = '';
public tabMap: Set<any> = new Set();
public get HasFullScreen() {
@@ -68,7 +63,7 @@ export class CollectionDockingView extends CollectionSubView() {
super(props);
makeObservable(this);
if (this._props.renderDepth < 0) CollectionDockingView.Instance = this;
- //Why is this here?
+ // Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
DragManager.StartWindowDrag = this.StartOtherDrag;
@@ -84,7 +79,7 @@ export class CollectionDockingView extends CollectionSubView() {
*/
public StartOtherDrag = (e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => {
this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag');
- const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) };
+ const config = dragDocs.length === 1 ? DashboardView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => DashboardView.makeDocumentConfig(doc)) };
const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config);
this.tabDragStart(dragSource, finishDrag);
dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 });
@@ -118,7 +113,7 @@ export class CollectionDockingView extends CollectionSubView() {
@undoBatch
public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
if (CollectionDockingView.Instance) {
- const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document));
+ const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tabView => (panelName ? tabView.contentItem.config.props.panelName === panelName : tabView.DashDoc === document));
if (tab) {
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
if (j !== -1) {
@@ -137,7 +132,7 @@ export class CollectionDockingView extends CollectionSubView() {
public static ReplaceTab(document: Doc, mods: OpenWhereMod, stack: any, panelName: string, addToSplit?: boolean, keyValue?: boolean): boolean {
const instance = CollectionDockingView.Instance;
if (!instance) return false;
- const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
+ const newConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue);
if (!panelName && stack) {
const activeContentItemIndex = stack.contentItems.findIndex((item: any) => item.config === stack._activeContentItem.config);
const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout);
@@ -145,7 +140,7 @@ export class CollectionDockingView extends CollectionSubView() {
stack.contentItems[activeContentItemIndex].remove();
return instance.layoutChanged();
}
- const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName);
+ const tab = Array.from(instance.tabMap.keys()).find(tabView => tabView.contentItem.config.props.panelName === panelName);
if (tab) {
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
if (newConfig.props.documentId !== tab.header.parent.contentItems[j].config.props.documentId) {
@@ -170,7 +165,7 @@ export class CollectionDockingView extends CollectionSubView() {
public static AddSplit(document: Doc, pullSide: OpenWhereMod, stack?: any, panelName?: string, keyValue?: boolean) {
if (document?._type_collection === CollectionViewType.Docking && !keyValue) return DashboardView.openDashboard(document);
if (!CollectionDockingView.Instance) return false;
- const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document && !tab.contentItem.config.props.keyValue && !keyValue);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tabView => tabView.DashDoc === document && !tabView.contentItem.config.props.keyValue && !keyValue);
if (tab) {
tab.header.parent.setActiveContentItem(tab.contentItem);
return true;
@@ -178,7 +173,7 @@ export class CollectionDockingView extends CollectionSubView() {
const instance = CollectionDockingView.Instance;
const glayRoot = instance._goldenLayout.root;
if (!instance) return false;
- const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
+ const docContentConfig = DashboardView.makeDocumentConfig(document, panelName, undefined, keyValue);
CollectionDockingView.Instance._flush = CollectionDockingView.Instance._flush ?? UndoManager.StartBatch('Add Split');
setTimeout(CollectionDockingView.Instance.endUndoBatch, 100);
@@ -201,6 +196,7 @@ export class CollectionDockingView extends CollectionSubView() {
} else if (instance._goldenLayout.root.contentItems[0].isRow) {
// if row
switch (pullSide) {
+ // eslint-disable-next-line default-case-last
default:
case OpenWhereMod.none:
case OpenWhereMod.right:
@@ -210,7 +206,7 @@ export class CollectionDockingView extends CollectionSubView() {
glayRoot.contentItems[0].addChild(newContentItem(), 0);
break;
case OpenWhereMod.top:
- case OpenWhereMod.bottom:
+ case OpenWhereMod.bottom: {
// if not going in a row layout, must add already existing content into column
const rowlayout = glayRoot.contentItems[0];
const newColumn = rowlayout.layoutManager.createContentItem({ type: 'column' }, instance._goldenLayout);
@@ -229,6 +225,7 @@ export class CollectionDockingView extends CollectionSubView() {
rowlayout.config.height = 50;
newItem.config.height = 50;
+ }
}
} else {
// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
@@ -241,7 +238,7 @@ export class CollectionDockingView extends CollectionSubView() {
break;
case 'left':
case 'right':
- default:
+ default: {
// if not going in a row layout, must add already existing content into column
const collayout = glayRoot.contentItems[0];
const newRow = collayout.layoutManager.createContentItem({ type: 'row' }, instance._goldenLayout);
@@ -260,6 +257,7 @@ export class CollectionDockingView extends CollectionSubView() {
collayout.config.width = 50;
newItem.config.width = 50;
+ }
}
}
instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
@@ -278,10 +276,10 @@ export class CollectionDockingView extends CollectionSubView() {
}
setupGoldenLayout = async () => {
if (this._unmounting) return;
- //const config = StrCast(this.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.Document)));
+ // const config = StrCast(this.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.Document)));
const config = StrCast(this.Document.dockingConfig);
if (config) {
- const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const matches = config.match(/"documentId":"[a-z0-9-]+"/g);
const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
await Promise.all(docids.map(id => DocServer.GetRefField(id)));
@@ -289,12 +287,13 @@ export class CollectionDockingView extends CollectionSubView() {
if (this._goldenLayout) {
if (config === JSON.stringify(this._goldenLayout.toConfig())) {
return;
- } else {
- try {
- this._goldenLayout.unbind('tabCreated', this.tabCreated);
- this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- this._goldenLayout.unbind('stackCreated', this.stackCreated);
- } catch (e) {}
+ }
+ try {
+ this._goldenLayout.unbind('tabCreated', this.tabCreated);
+ this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
+ this._goldenLayout.unbind('stackCreated', this.stackCreated);
+ } catch (e) {
+ /* empty */
}
this.tabMap.clear();
this._goldenLayout.destroy();
@@ -303,7 +302,7 @@ export class CollectionDockingView extends CollectionSubView() {
glay.on('tabCreated', this.tabCreated);
glay.on('tabDestroyed', this.tabDestroyed);
glay.on('stackCreated', this.stackCreated);
- glay.registerComponent('DocumentFrameRenderer', TabDocView);
+ glay.registerComponent('DocumentFrameRenderer', CollectionDockingView.tabClass);
glay.container = this._containerRef.current;
glay.init();
glay.root.layoutManager.on('itemDropped', this.tabItemDropped);
@@ -323,7 +322,7 @@ export class CollectionDockingView extends CollectionSubView() {
*/
titleChanged = (target: any, value: any) => {
const title = Field.toString(value);
- if (title.startsWith('@') && !title.substring(1).match(/[\(\)\[\]@]/) && title.length > 1) {
+ if (title.startsWith('@') && !title.substring(1).match(/[()[\]@]/) && title.length > 1) {
const embedding = DocListCast(target.proto_embeddings).lastElement();
embedding && Doc.AddToMyPublished(embedding);
} else if (!title.startsWith('@')) {
@@ -332,12 +331,13 @@ export class CollectionDockingView extends CollectionSubView() {
};
componentDidMount: () => void = async () => {
+ this._props.setContentViewBox?.(this);
this._unmounting = false;
SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
- doc => setTimeout(() => !doc && this.onResize(undefined))
+ doc => setTimeout(() => !doc && this.onResize())
);
new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
this._reactionDisposer = reaction(
@@ -356,25 +356,26 @@ export class CollectionDockingView extends CollectionSubView() {
{ fireImmediately: true }
);
reaction(
- () => [SettingsManager.userBackgroundColor, SettingsManager.userBackgroundColor],
+ () => [SnappingManager.userBackgroundColor, SnappingManager.userBackgroundColor],
() => {
clearStyleSheetRules(CollectionDockingView._highlightStyleSheet);
- addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { background: `${SettingsManager.userBackgroundColor} !important` });
- addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { color: `${SettingsManager.userColor} !important` });
- addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${SettingsManager.userBackgroundColor} !important` });
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { background: `${SnappingManager.userBackgroundColor} !important` });
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { color: `${SnappingManager.userColor} !important` });
+ addStyleSheetRule(SnappingManager.SettingsStyle, 'lm_header', { background: `${SnappingManager.userBackgroundColor} !important` });
},
{ fireImmediately: true }
);
}
};
- _unmounting = false;
componentWillUnmount: () => void = () => {
this._unmounting = true;
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- } catch (e) {}
+ } catch (e) {
+ /* empty */
+ }
this._goldenLayout?.destroy();
window.removeEventListener('resize', this.onResize);
window.removeEventListener('mouseup', this.onPointerUp);
@@ -383,8 +384,11 @@ export class CollectionDockingView extends CollectionSubView() {
this._lightboxReactionDisposer?.();
};
+ // ViewBoxInterface overrides
+ override isUnstyledView = returnTrue;
+
@action
- onResize = (event: any) => {
+ onResize = () => {
const cur = this._containerRef.current;
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
!LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
@@ -392,7 +396,7 @@ export class CollectionDockingView extends CollectionSubView() {
endUndoBatch = () => {
const json = JSON.stringify(this._goldenLayout.toConfig());
- const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const matches = json.match(/"documentId":"[a-z0-9-]+"/g);
const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', ''));
const docs = !docids
? []
@@ -415,7 +419,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
@action
- onPointerUp = (e: MouseEvent): void => {
+ onPointerUp = (): void => {
window.removeEventListener('pointerup', this.onPointerUp);
DragManager.CompleteWindowDrag = undefined;
setTimeout(this.endUndoBatch, 100);
@@ -437,8 +441,8 @@ export class CollectionDockingView extends CollectionSubView() {
} else {
const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget);
- if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) {
- SelectionManager.SelectView(DocumentManager.Instance.getFirstDocumentView(map.DashDoc), false);
+ if (map?.DashDoc && DocumentView.getDocumentView(map.DashDoc, this.DocumentView?.())) {
+ DocumentView.SelectView(DocumentView.getDocumentView(map.DashDoc, this.DocumentView?.()), false);
}
}
}
@@ -453,24 +457,27 @@ export class CollectionDockingView extends CollectionSubView() {
if (content) {
const _width = DivWidth(content);
const _height = DivHeight(content);
- return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
+ return UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', iconFile => {
const proto = this.dataDoc; // Cast(img.proto, Doc, null)!;
- proto['thumb_nativeWidth'] = _width;
- proto['thumb_nativeHeight'] = _height;
+ proto.thumb_nativeWidth = _width;
+ proto.thumb_nativeHeight = _height;
proto.thumb = new ImageField(iconFile);
});
}
+ return undefined;
}
public static async TakeSnapshot(doc: Doc | undefined, clone = false) {
if (!doc) return undefined;
let json = StrCast(doc.dockingConfig);
if (clone) {
const cloned = await Doc.MakeClone(doc);
- Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
+ Array.from(cloned.map.entries()).forEach(entry => {
+ json = json.replace(entry[0], entry[1][Id]);
+ });
cloned.clone[DocData].dockingConfig = json;
return DashboardView.openDashboard(cloned.clone);
}
- const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
+ const matches = json.match(/"documentId":"[a-z0-9-]+"/g);
const origtabids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) || [];
const origtabs = origtabids
.map(id => DocServer.GetCachedRefField(id))
@@ -508,19 +515,20 @@ export class CollectionDockingView extends CollectionSubView() {
tabDestroyed = (tab: any) => {
this._flush = this._flush ?? UndoManager.StartBatch('tab movement');
- if (tab.DashDoc && ![DocumentType.PRES].includes(tab.DashDoc?.type) && !tab.contentItem.config.props.keyValue) {
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc, undefined, undefined, true);
+ 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);
// 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 (tab.DashDoc.embedContainer === this.Document) tab.DashDoc.embedContainer = undefined;
- if (!tab.DashDoc.embedContainer) {
- Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
- Doc.RemoveEmbedding(tab.DashDoc, tab.DashDoc);
+ if (dashDoc.embedContainer === this.Document) dashDoc.embedContainer = undefined;
+ if (!dashDoc.embedContainer) {
+ Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', dashDoc, undefined, true, true);
+ Doc.RemoveEmbedding(dashDoc, dashDoc);
}
}
if (CollectionDockingView.Instance) {
const dview = CollectionDockingView.Instance.Document;
- const fieldKey = CollectionDockingView.Instance.props.fieldKey;
- Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
+ const { fieldKey } = CollectionDockingView.Instance.props;
+ Doc.RemoveDocFromList(dview, fieldKey, dashDoc);
this.tabMap.delete(tab);
tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
this.stateChanged();
@@ -531,8 +539,8 @@ export class CollectionDockingView extends CollectionSubView() {
tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
};
- stackCreated = (stack: any) => {
- stack = stack.header ? stack : stack.origin;
+ stackCreated = (stackIn: any) => {
+ const stack = stackIn.header ? stackIn : stackIn.origin;
stack.header?.element.on('mousedown', (e: any) => {
const dashboard = Doc.ActiveDashboard;
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
@@ -550,32 +558,29 @@ export class CollectionDockingView extends CollectionSubView() {
}
});
- let addNewDoc = undoable(
- action(() => {
- const dashboard = Doc.ActiveDashboard;
- if (dashboard) {
- dashboard.pane_count = NumCast(dashboard.pane_count) + 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)}`,
- });
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
- inheritParentAcls(this.dataDoc, docToAdd, false);
- CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
- }
- }),
- 'add new tab'
- );
+ const addNewDoc = undoable(() => {
+ const dashboard = Doc.ActiveDashboard;
+ if (dashboard) {
+ dashboard.pane_count = NumCast(dashboard.pane_count) + 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)}`,
+ });
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ inheritParentAcls(this.dataDoc, docToAdd, false);
+ CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
+ }
+ }, 'add new tab');
stack.header?.controlsContainer
- .find('.lm_close') //get the close icon
- .off('click') //unbind the current click handler
+ .find('.lm_close') // get the close icon
+ .off('click') // unbind the current click handler
.click(
action(() => {
- //if (confirm('really close this?')) {
+ // if (confirm('really close this?')) {
if ((!stack.parent.isRoot && !stack.parent.parent.isRoot) || stack.parent.contentItems.length > 1) {
const batch = UndoManager.StartBatch('close stack');
stack.remove();
@@ -595,11 +600,11 @@ export class CollectionDockingView extends CollectionSubView() {
}
});
stack.header?.controlsContainer
- .find('.lm_maximise') //get the close icon
+ .find('.lm_maximise') // get the close icon
.click(() => setTimeout(this.stateChanged));
stack.header?.controlsContainer
- .find('.lm_popout') //get the popout icon
- .off('click') //unbind the current click handler
+ .find('.lm_popout') // get the popout icon
+ .off('click') // unbind the current click handler
.click(addNewDoc);
};
@@ -609,13 +614,14 @@ export class CollectionDockingView extends CollectionSubView() {
<div>
{href ? (
<img
+ alt="thumbnail of nested dashboard"
style={{ background: 'white', top: 0, position: 'absolute' }}
src={href} // + '?d=' + (new Date()).getTime()}
width={this._props.PanelWidth()}
height={this._props.PanelHeight()}
/>
) : (
- <p>nested dashboards has no thumbnail</p>
+ <p>nested dashboard has no thumbnail</p>
)}
</div>
) : (
@@ -625,6 +631,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function openInLightbox(doc: any) {
LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
},
@@ -632,26 +639,37 @@ ScriptingGlobals.add(
'(doc: any)'
);
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function openDoc(doc: any, where: OpenWhere) {
switch (where) {
case OpenWhere.addRight:
return CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
case OpenWhere.overlay:
+ default:
// prettier-ignore
switch (doc) {
case '<ScriptingRepl />': return OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: 'Scripting REPL' });
case "<UndoStack />": return OverlayView.Instance.addWindow(<UndoStack />, { x: 300, y: 100, width: 200, height: 200, title: 'Undo stack' });
+ default:
}
Doc.AddToMyOverlay(doc);
+ return true;
}
},
'opens up document in location specified',
'(doc: any)'
);
ScriptingGlobals.add(
+ // eslint-disable-next-line prefer-arrow-callback
function openRepl() {
return 'openRepl';
},
'opens up document in screen overlay layer',
'(doc: any)'
);
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(async function snapshotDashboard() {
+ const batch = UndoManager.StartBatch('snapshot');
+ await CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
+ batch.end();
+}, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 7dcfd32bd..9a6f1e2eb 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -1,9 +1,13 @@
+/* eslint-disable jsx-a11y/control-has-associated-label */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
-import { Doc, DocListCast } from '../../../fields/Doc';
+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';
@@ -51,13 +55,19 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
@observable private collapsed: boolean = false;
@observable private _paletteOn = false;
private set _heading(value: string) {
- runInAction(() => this._props.headingObject && (this._props.headingObject.heading = this.heading = value));
+ runInAction(() => {
+ this._props.headingObject && (this._props.headingObject.heading = this.heading = value);
+ });
}
private set _color(value: string) {
- runInAction(() => this._props.headingObject && (this._props.headingObject.color = this.color = value));
+ runInAction(() => {
+ this._props.headingObject && (this._props.headingObject.color = this.color = value);
+ });
}
private set _collapsed(value: boolean) {
- runInAction(() => this._props.headingObject && (this._props.headingObject.collapsed = this.collapsed = value));
+ runInAction(() => {
+ this._props.headingObject && (this._props.headingObject.collapsed = this.collapsed = value);
+ });
}
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -87,7 +97,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
if (this.collapsed) {
this._props.setDocHeight(this.heading, 20);
} else {
- const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
+ const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; // +15 accounts for the group header
const transformScale = this._props.screenToLocalTransform().Scale;
const trueHeight = rawHeight * transformScale;
this._props.setDocHeight(this.heading, trueHeight);
@@ -101,7 +111,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const key = this._props.pivotField;
const castedValue = this.getValue(this.heading);
if (this._props.parent.onInternalDrop(e, de)) {
- key && de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !this.onLayoutDoc(key)));
+ key && de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
}
return true;
}
@@ -117,7 +127,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
};
@action
- headingChanged = (value: string, shiftDown?: boolean) => {
+ headingChanged = (value: string /* , shiftDown?: boolean */) => {
this._createEmbeddingSelected = false;
const key = this._props.pivotField;
const castedValue = this.getValue(value);
@@ -140,7 +150,9 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
this._color = color;
};
- pointerEnteredRow = action(() => SnappingManager.IsDragging && (this._background = '#b4b4b4'));
+ pointerEnteredRow = action(() => {
+ SnappingManager.IsDragging && (this._background = '#b4b4b4');
+ });
@action
pointerLeaveRow = () => {
@@ -152,13 +164,13 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
addDocument = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
this._createEmbeddingSelected = false;
- const key = this._props.pivotField;
+ const { pivotField } = this._props;
const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = value;
- key && ((this.onLayoutDoc(key) ? newDoc : newDoc[DocData])[key] = this.getValue(this._props.heading));
+ pivotField && (newDoc[DocData][pivotField] = this.getValue(this._props.heading));
const docs = this._props.parent.childDocList;
- return docs ? (docs.splice(0, 0, newDoc) ? true : false) : this._props.parent._props.addDocument?.(newDoc) || false; // should really extend addDocument to specify insertion point (at beginning of list)
+ 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)
};
deleteRow = undoBatch(
@@ -197,21 +209,11 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
@action
headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
if (e.button === 0 && !e.ctrlKey) {
- setupMoveUpEvents(this, e, this.headerMove, emptyFunction, e => !this._props.chromeHidden && this.collapseSection(e));
+ setupMoveUpEvents(this, e, this.headerMove, emptyFunction, clickEv => !this._props.chromeHidden && this.collapseSection(clickEv));
this._createEmbeddingSelected = false;
}
};
- /**
- * Returns true if a key is on the layout doc of the documents in the collection.
- */
- onLayoutDoc = (key: string): boolean => {
- DocListCast(this._props.parent.Document.data).forEach(doc => {
- if (Doc.Get(doc, key, true)) return true;
- });
- return false;
- };
-
renderColorPicker = () => {
const selected = this.color;
@@ -228,27 +230,29 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
return (
<div className="collectionStackingView-colorPicker">
<div className="colorOptions">
- <div className={'colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.changeColumnColor(pink!)}></div>
- <div className={'colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
- <div className={'colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
- <div className={'colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
- <div className={'colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
- <div className={'colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
- <div className={'colorPicker' + (selected === green ? ' active' : '')} style={{ backgroundColor: green }} onClick={() => this.changeColumnColor(green!)}></div>
- <div className={'colorPicker' + (selected === cyan ? ' active' : '')} style={{ backgroundColor: cyan }} onClick={() => this.changeColumnColor(cyan!)}></div>
- <div className={'colorPicker' + (selected === orange ? ' active' : '')} style={{ backgroundColor: orange }} onClick={() => this.changeColumnColor(orange!)}></div>
+ <div className={'colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.changeColumnColor(pink!)} />
+ <div className={'colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)} />
+ <div className={'colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)} />
+ <div className={'colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)} />
+ <div className={'colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)} />
+ <div className={'colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)} />
+ <div className={'colorPicker' + (selected === green ? ' active' : '')} style={{ backgroundColor: green }} onClick={() => this.changeColumnColor(green!)} />
+ <div className={'colorPicker' + (selected === cyan ? ' active' : '')} style={{ backgroundColor: cyan }} onClick={() => this.changeColumnColor(cyan!)} />
+ <div className={'colorPicker' + (selected === orange ? ' active' : '')} style={{ backgroundColor: orange }} onClick={() => this.changeColumnColor(orange!)} />
</div>
</div>
);
};
- toggleEmbedding = action(() => (this._createEmbeddingSelected = true));
- toggleVisibility = () => (this._collapsed = !this.collapsed);
+ toggleEmbedding = action(() => {
+ this._createEmbeddingSelected = true;
+ });
+ toggleVisibility = () => {
+ this._collapsed = !this.collapsed;
+ };
@action
- textCallback = (char: string) => {
- return this.addDocument('', false);
- };
+ textCallback = (/* char: string */) => this.addDocument('', false);
@computed get contentLayout() {
const rows = Math.max(1, Math.min(this._props.docList.length, Math.floor((this._props.parent._props.PanelWidth() - 2 * this._props.parent.xMargin) / (this._props.parent.columnWidth + this._props.parent.gridGap))));
@@ -262,22 +266,22 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
className="collectionStackingView-addDocumentButton"
style={
{
- //width: style.columnWidth / style.numGroupColumns,
- //padding: `${NumCast(this._props.parent.layoutDoc._yPadding, this._props.parent.yMargin)}px 0px 0px 0px`,
+ // width: style.columnWidth / style.numGroupColumns,
+ // padding: `${NumCast(this._props.parent.layoutDoc._yPadding, this._props.parent.yMargin)}px 0px 0px 0px`,
}
}>
- <EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents={'+ NEW'} />
+ <EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents="+ NEW" />
</div>
) : null}
<div
- className={`collectionStackingView-masonryGrid`}
+ className="collectionStackingView-masonryGrid"
ref={this._contRef}
style={{
padding: stackPad,
minHeight: this._props.showHandle && this._props.parent._props.isContentActive() ? '10px' : undefined,
width: this._props.parent.NodeWidth,
gridGap: this._props.parent.gridGap,
- gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this._props.parent.columnWidth}px`, ''),
+ gridTemplateColumns: numberRange(rows).reduce(list => list + ` ${this._props.parent.columnWidth}px`, ''),
}}>
{this._props.parent.children(this._props.docList)}
</div>
@@ -289,7 +293,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const noChrome = this._props.chromeHidden;
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={true} />;
+ const editableHeaderView = <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine />;
return this._props.Document.miniHeaders ? (
<div className="collectionStackingView-miniHeader">{editableHeaderView}</div>
) : !this._props.headingObject ? null : (
@@ -303,6 +307,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
{noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : (
<div className="collectionStackingView-sectionColor">
<button
+ type="button"
className="collectionStackingView-sectionColorButton"
onPointerDown={e =>
setupMoveUpEvents(
@@ -310,7 +315,9 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
e,
returnFalse,
emptyFunction,
- action(e => (this._paletteOn = !this._paletteOn))
+ action(() => {
+ this._paletteOn = !this._paletteOn;
+ })
)
}>
<FontAwesomeIcon icon="palette" size="lg" />
@@ -319,13 +326,13 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
</div>
)}
{noChrome ? null : (
- <button className="collectionStackingView-sectionDelete" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, noChrome ? emptyFunction : this.collapseSection)}>
+ <button type="button" className="collectionStackingView-sectionDelete" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, noChrome ? emptyFunction : this.collapseSection)}>
<FontAwesomeIcon icon={this.collapsed ? 'chevron-down' : 'chevron-up'} size="lg" />
</button>
)}
{noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : (
<div className="collectionStackingView-sectionOptions" onPointerDown={e => e.stopPropagation()}>
- <button className="collectionStackingView-sectionOptionButton" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}>
+ <button type="button" className="collectionStackingView-sectionOptionButton" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>
</div>
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 5d46649b2..3ec875df4 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -10,7 +10,7 @@
border-bottom: $standard-border;
padding: 0 10px;
align-items: center;
- overflow-x: scroll;
+ overflow-x: auto;
&::-webkit-scrollbar {
display: none;
}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 4f25f69ef..e53071584 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,10 +1,17 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/control-has-associated-label */
+/* 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 'browndash-components';
import { Lambda, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
+import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { List } from '../../../fields/List';
@@ -12,14 +19,13 @@ import { ObjectField } from '../../../fields/ObjectField';
import { RichTextField } from '../../../fields/RichTextField';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DragManager, dropActionType } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { SettingsManager } from '../../util/SettingsManager';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
+import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { AntimodeMenu } from '../AntimodeMenu';
import { EditableView } from '../EditableView';
-import { MainView } from '../MainView';
import { DefaultStyleProvider } from '../StyleProvider';
import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
import './CollectionMenu.scss';
@@ -30,10 +36,12 @@ interface CollectionMenuProps {
panelWidth: () => number;
toggleTopBar: () => void;
topBarHeight: () => number;
+ togglePropertiesFlyout: () => void;
}
@observer
export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
+ // eslint-disable-next-line no-use-before-define
@observable static Instance: CollectionMenu;
@observable SelectedCollection: DocumentView | undefined = undefined;
@@ -44,13 +52,13 @@ 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 = Cast(Doc.UserDoc().menuCollections_pinned, 'boolean', true);
this.jumpTo(300, 300);
}
componentDidMount() {
reaction(
- () => SelectionManager.Views.lastElement(),
+ () => DocumentView.Selected().lastElement(),
view => view && this.SetSelection(view)
);
}
@@ -61,25 +69,16 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
}
@action
- toggleMenuPin = (e: React.MouseEvent) => {
- Doc.UserDoc()['menuCollections-pinned'] = this.Pinned = !this.Pinned;
+ toggleMenuPin = () => {
+ Doc.UserDoc().menuCollections_pinned = this.Pinned = !this.Pinned;
if (!this.Pinned && this._left < 0) {
this.jumpTo(300, 300);
}
};
- @action
- toggleProperties = () => {
- if (MainView.Instance.propertiesWidth() > 0) {
- SettingsManager.Instance.propertiesWidth = 0;
- } else {
- SettingsManager.Instance.propertiesWidth = 300;
- }
- };
-
buttonBarXf = () => {
if (!this._docBtnRef.current) return Transform.Identity();
- const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+ const { scale, translateX, translateY } = ClientUtils.GetScreenTransform(this._docBtnRef.current);
return new Transform(-translateX, -translateY, 1 / scale);
};
@@ -119,15 +118,15 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
render() {
const headerIcon = this.props.topBarHeight() > 0 ? 'angle-double-up' : 'angle-double-down';
const headerTitle = this.props.topBarHeight() > 0 ? 'Close Header Bar' : 'Open Header Bar';
- const propIcon = SettingsManager.Instance.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
- const propTitle = SettingsManager.Instance.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties';
+ const propIcon = SnappingManager.PropertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+ const propTitle = SnappingManager.PropertiesWidth > 0 ? 'Close Properties' : 'Open Properties';
const hardCodedButtons = (
- <div className={`hardCodedButtons`}>
+ <div className="hardCodedButtons">
<Toggle
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- color={SettingsManager.userColor}
+ color={SnappingManager.userColor}
onClick={this.props.toggleTopBar}
toggleStatus={this.props.topBarHeight() > 0}
icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
@@ -136,21 +135,21 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
<Toggle
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- color={SettingsManager.userColor}
- onClick={this.toggleProperties}
- toggleStatus={SettingsManager.Instance.propertiesWidth > 0}
+ color={SnappingManager.userColor}
+ onClick={this._props.togglePropertiesFlyout}
+ toggleStatus={SnappingManager.PropertiesWidth > 0}
icon={<FontAwesomeIcon icon={propIcon} size="lg" />}
tooltip={propTitle}
/>
</div>
);
- //dash col linear view buttons
+ // dash col linear view buttons
const contMenuButtons = (
<div
className="collectionMenu-container"
style={{
- background: SettingsManager.userBackgroundColor,
+ background: SnappingManager.userBackgroundColor,
// borderColor: SettingsManager.userColor
}}>
{this.contMenuButtons}
@@ -163,7 +162,9 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
}
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;
}
@@ -172,7 +173,7 @@ const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
@observer
export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
- //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
+ // (!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
get document() {
return this.props.docView?.Document;
@@ -206,14 +207,18 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
params: ['target', 'source'],
title: 'child click view',
script: 'this.target.childClickedOpenTemplateView = getDocTemplate(this.source?.[0])',
- immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
+ immediate: undoBatch((source: Doc[]) => {
+ source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]));
+ }),
initialize: emptyFunction,
};
_contentCommand = {
params: ['target', 'source'],
title: 'set content',
script: 'getProto(this.target).data = copyField(this.source);',
- immediate: undoBatch((source: Doc[]) => (this.target[DocData].data = new List<Doc>(source))),
+ immediate: undoBatch((source: Doc[]) => {
+ this.target[DocData].data = new List<Doc>(source);
+ }),
initialize: emptyFunction,
};
_onClickCommand = {
@@ -224,14 +229,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
getProto(this.proxy[0]).target = this.target.target;
getProto(this.proxy[0]).source = copyField(this.target.source);
}}`,
- immediate: undoBatch((source: Doc[]) => {}),
+ immediate: undoBatch(() => {}),
initialize: emptyFunction,
};
_viewCommand = {
params: ['target'],
title: 'bookmark view',
- script: "this.target._freeform_panX = self['target-freeform_panX']; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);",
- immediate: undoBatch((source: Doc[]) => {
+ script: "this.target._freeform_panX = this.target_freeform_panX; this.target._freeform_panY = this['target-freeform_panY']; this.target._freeform_scale = this['target_freeform_scale']; gotoFrame(this.target, this['target-currentFrame']);",
+ immediate: undoBatch(() => {
this.target._freeform_panX = 0;
this.target._freeform_panY = 0;
this.target._freeform_scale = 1;
@@ -248,30 +253,34 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
params: ['target'],
title: 'fit content',
script: 'this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;',
- immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)),
+ immediate: undoBatch(() => {
+ this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;
+ }),
initialize: emptyFunction,
};
_fitContentCommand = {
params: ['target'],
title: 'toggle clusters',
script: 'this.target._freeform_useClusters = !this.target._freeform_useClusters;',
- immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_useClusters)),
+ immediate: undoBatch(() => {
+ this.target._freeform_useClusters = !this.target._freeform_useClusters;
+ }),
initialize: emptyFunction,
};
_saveFilterCommand = {
params: ['target'],
title: 'save filter',
- script: `this.target._childFilters = compareLists(this['target-childFilters'],this.target._childFilters) ? undefined : copyField(this['target-childFilters']);
- this.target._searchFilterDocs = compareLists(this['target-searchFilterDocs'],this.target._searchFilterDocs) ? undefined: copyField(this['target-searchFilterDocs']);`,
- immediate: undoBatch((source: Doc[]) => {
+ script: `this.target._childFilters = compareLists(this.target_childFilters,this.target._childFilters) ? undefined : copyField(this.target_childFilters);
+ this.target._searchFilterDocs = compareLists(this.target_searchFilterDocs,this.target._searchFilterDocs) ? undefined: copyField(this.target_searchFilterDocs);`,
+ immediate: undoBatch(() => {
this.target._childFilters = undefined;
this.target._searchFilterDocs = undefined;
}),
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 any as ObjectField) : undefined;
- button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
+ button.target_childFilters = (Doc.MySearcher._childFilters || activeDash._childFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._childFilters || activeDash._childFilters) as any as ObjectField) : undefined;
+ button.target_searchFilterDocs = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
}
},
};
@@ -299,8 +308,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
}
private get _buttonizableCommands() {
switch (this.props.type) {
- default:
- return this._doc_commands;
case CollectionViewType.Freeform:
return this._freeform_commands;
case CollectionViewType.Tree:
@@ -319,6 +326,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
return this._freeform_commands;
case CollectionViewType.Carousel3D:
return this._freeform_commands;
+ default:
+ return this._doc_commands;
}
}
private _commandRef = React.createRef<HTMLInputElement>();
@@ -332,13 +341,13 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : DocCast(this.document.proto);
- //@ts-ignore
- target._type_collection = e.target.selectedOptions[0].value;
+ target._type_collection = (e.target as any).selectedOptions[0].value;
};
commandChanged = (e: React.ChangeEvent) => {
- //@ts-ignore
- runInAction(() => (this._currentKey = e.target.selectedOptions[0].value));
+ runInAction(() => {
+ this._currentKey = (e.target as any).selectedOptions[0].value;
+ });
};
@action closeViewSpecs = () => {
@@ -356,7 +365,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@undoBatch
@action
protected drop(e: Event, de: DragManager.DropEvent): boolean {
- const docDragData = de.complete.docDragData;
+ const { docDragData } = de.complete;
if (docDragData?.draggedDocuments.length) {
this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate(docDragData.draggedDocuments || []));
e.stopPropagation();
@@ -369,16 +378,18 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
setupMoveUpEvents(
this,
e,
- (e, down, delta) => {
+ moveEv => {
const vtype = this.props.type;
const c = {
params: ['target'],
title: vtype,
script: `this.target._type_collection = '${StrCast(this.props.type)}'`,
- immediate: (source: Doc[]) => (this.document._type_collection = Doc.getDocTemplate(source?.[0])),
+ immediate: (source: Doc[]) => {
+ this.document._type_collection = Doc.getDocTemplate(source?.[0]);
+ },
initialize: emptyFunction,
};
- DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
+ DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.document }, c.params, c.initialize, moveEv.clientX, moveEv.clientY);
return true;
},
emptyFunction,
@@ -389,8 +400,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
setupMoveUpEvents(
this,
e,
- (e, down, delta) => {
- this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
+ moveEv => {
+ this._buttonizableCommands
+ ?.filter(c => c.title === this._currentKey)
+ .map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.document }, c.params, c.initialize, moveEv.clientX, moveEv.clientY));
return true;
},
emptyFunction,
@@ -405,11 +418,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
<div className="collectionViewBaseChrome-template" ref={this.createDropTarget}>
<Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
<div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
- <button className={'antimodeMenu-button'}>
+ <button type="button" className="antimodeMenu-button">
<FontAwesomeIcon icon="bullseye" size="lg" />
</button>
<select className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={'empty'} value={''} />
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key="empty" value="" />
{this._buttonizableCommands?.map(cmd => (
<option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>
{cmd.title}
@@ -456,23 +469,19 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
if (docs instanceof Doc) {
const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- const noviceKeys = Array.from(keys).filter(
- key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
- );
- return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ const noviceKeys = Array.from(keys).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('author_date') >= 0 || key.indexOf('modificationDate') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_'));
+ return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
if (docs instanceof Doc) {
return Object.keys(docs).filter(key => key.toLowerCase().indexOf(val) > -1);
- } else {
- const keys = new Set<string>();
- docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
}
+ const keys = new Set<string>();
+ docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
+ return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
};
@action
@@ -482,9 +491,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
getSuggestionValue = (suggestion: string) => suggestion;
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- };
+ renderSuggestion = (suggestion: string) => <p>{suggestion}</p>;
onSuggestionFetch = async ({ value }: { value: string }) => {
const sugg = await this.getKeySuggestions(value);
@@ -572,12 +579,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
}
componentDidMount() {
- runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
+ runInAction(() => {
+ this.resize = this.props.docView.props.PanelWidth() < 700;
+ });
// listener to reduce text on chrome resize (panel resize)
this.resizeListenerDisposer = reaction(
() => this.panelWidth,
- newValue => (this.resize = newValue < 700)
+ newValue => {
+ this.resize = newValue < 700;
+ }
);
}
@@ -593,7 +604,10 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
* Sets the value of `numCols` on the grid's Document to the value entered.
*/
onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- if (e.currentTarget.valueAsNumber > 0) undoBatch(() => (this.document.gridNumCols = e.currentTarget.valueAsNumber))();
+ if (e.currentTarget.valueAsNumber > 0)
+ undoBatch(() => {
+ this.document.gridNumCols = e.currentTarget.valueAsNumber;
+ })();
};
/**
@@ -622,7 +636,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
onIncrementButtonClick = () => {
this.clicked = true;
this.entered && (this.document.gridNumCols as number)--;
- undoBatch(() => (this.document.gridNumCols = this.numCols + 1))();
+ undoBatch(() => {
+ this.document.gridNumCols = this.numCols + 1;
+ })();
this.entered = false;
};
@@ -633,7 +649,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
this.clicked = true;
if (this.numCols > 1 && !this.decrementLimitReached) {
this.entered && (this.document.gridNumCols as number)++;
- undoBatch(() => (this.document.gridNumCols = this.numCols - 1))();
+ undoBatch(() => {
+ this.document.gridNumCols = this.numCols - 1;
+ })();
if (this.numCols === 1) this.decrementLimitReached = true;
}
this.entered = false;
@@ -726,7 +744,13 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
<label className="flexLabel">{this.resize ? 'Flex' : 'Flexible'}</label>
</span>
- <button onClick={() => (this.document.gridResetLayout = true)}>{!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}</button>
+ <button
+ type="button"
+ onClick={() => {
+ this.document.gridResetLayout = true;
+ }}>
+ {!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}
+ </button>
</div>
);
}
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index d8a0aebb1..3f9eed1d6 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,7 +1,8 @@
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, Field, Opt } from '../../../fields/Doc';
+import { ClientUtils, DivHeight, lightOrDark, returnZero, smoothScroll } from '../../../ClientUtils';
+import { Doc, Field, FieldType, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -9,25 +10,29 @@ import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { DivHeight, emptyFunction, lightOrDark, returnZero, smoothScroll, Utils } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
-import { DragManager, dropActionType } from '../../util/DragManager';
+import { emptyFunction } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocUtils } from '../../documents/DocUtils';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable, undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { FieldsDropdown } from '../FieldsDropdown';
import { Colors } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { StyleProp } from '../StyleProvider';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
+import { StyleProp } from '../StyleProp';
import './CollectionNoteTakingView.scss';
import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn';
import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
import { CollectionSubView } from './CollectionSubView';
-import { FieldsDropdown } from '../FieldsDropdown';
+
const _global = (window /* browser */ || global) /* node */ as any;
/**
@@ -56,7 +61,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
@computed get chromeHidden() {
- return BoolCast(this.layoutDoc.chromeHidden) || this._props.onBrowseClickScript?.() ? true : false;
+ return BoolCast(this.layoutDoc.chromeHidden) || SnappingManager.ExploreMode;
}
// columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
@computed get colHeaderData() {
@@ -65,7 +70,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
if (needsUnsetCategory || colHeaderData === undefined || colHeaderData.length === 0) {
setTimeout(() => {
const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null) ?? []);
- const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
if (needsUnsetCategory || columnHeaders.length === 0) {
columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
this.resizeColumns(columnHeaders);
@@ -109,12 +113,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// to render the docs you see within an individual column.
children = (docs: Doc[]) => {
TraceMobx();
- return docs.map((d, i) => {
+ return docs.map(d => {
const height = () => this.getDocHeight(d);
const width = () => this.getDocWidth(d);
const style = { width: width(), marginTop: this.gridGap, height: height() };
return (
- <div className={`collectionNoteTakingView-columnDoc`} key={d[Id]} style={style}>
+ <div className="collectionNoteTakingView-columnDoc" key={d[Id]} style={style}>
{this.getDisplayDoc(d, width)}
</div>
);
@@ -133,7 +137,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
const rowCol = this.docsDraggedRowCol;
// this will sort the docs into the correct columns (minus the ones you're currently dragging)
- docs.map(d => {
+ docs.forEach(d => {
const sectionValue = (d[this.notetakingCategoryField] as object) ?? `unset`;
// look for if header exists already
const existingHeader = columnHeaders.find(sh => sh.heading === sectionValue.toString());
@@ -151,7 +155,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
removeDocDragHighlight = () => {
setTimeout(
- action(() => (this.docsDraggedRowCol.length = 0)),
+ action(() => {
+ this.docsDraggedRowCol.length = 0;
+ }),
100
);
};
@@ -189,13 +195,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
Object.keys(this._disposers).forEach(key => this._disposers[key]());
}
- moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string): boolean => {
- return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
- };
+ moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean) => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
createRef = (ele: HTMLDivElement | null) => {
this._masonryGridRef = ele;
- this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
+ this.createDashEventsTarget(ele!);
};
@computed get onChildClickHandler() {
@@ -215,14 +219,15 @@ export class CollectionNoteTakingView extends CollectionSubView() {
Doc.BrushDoc(doc);
const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
if (found) {
- const top = found.getBoundingClientRect().top;
+ const { top } = found.getBoundingClientRect();
const localTop = this.ScreenToLocalBoxXf().transformPoint(0, top);
if (Math.floor(localTop[1]) !== 0 && Math.ceil(this._props.PanelHeight()) < (this._mainCont?.scrollHeight || 0)) {
- let focusSpeed = options.zoomTime ?? 500;
+ const focusSpeed = options.zoomTime ?? 500;
smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
return focusSpeed;
}
}
+ return undefined;
};
styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
@@ -237,6 +242,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return this._props.childOpacity();
}
break;
+ default:
}
return this._props.styleProvider?.(doc, props, property);
};
@@ -252,7 +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)}
pointerEvents={this.blockPointerEventsWhenDragging}
@@ -264,8 +272,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
layout_fitWidth={this._props.childLayoutFitWidth}
isContentActive={emptyFunction}
onKey={this.onKeyDown}
- //TODO: change this from a prop to a parameter passed into a function
- dontHideOnDrag={true}
+ // TODO: change this from a prop to a parameter passed into a function
+ dontHideOnDrag
isDocumentActive={this.isContentActive}
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
@@ -277,7 +285,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
layout_showTitle={this._props.childlayout_showTitle}
dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
onClickScript={this.onChildClickHandler}
- onBrowseClickScript={this._props.onBrowseClickScript}
onDoubleClickScript={this.onChildDoubleClickHandler}
ScreenToLocalTransform={noteTakingDocTransform}
focus={this.focusDocument}
@@ -299,8 +306,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// getDocTransform is used to get the coordinates of a document when we go from a view like freeform to columns
getDocTransform(doc: Doc, dref?: DocumentView) {
- const y = this._scroll; // required for document decorations to update when the text box container is scrolled
- const { translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+ this._scroll; // required for document decorations to update when the text box container is scrolled
+ const { translateX, translateY } = ClientUtils.GetScreenTransform(dref?.ContentDiv || undefined);
// the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off
return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.ScreenToLocalBoxXf().Scale);
}
@@ -308,7 +315,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// how to get the width of a document. Currently returns the width of the column (minus margins)
// if a note doc. Otherwise, returns the normal width (for graphs, images, etc...)
getDocWidth(d: Doc) {
- const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
+ const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as FieldType);
const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
const existingWidth = this.layoutDoc._notetaking_columns_autoSize ? 1 / (this.colHeaderData.length ?? 1) : existingHeader?.width ? existingHeader.width : 0;
const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
@@ -344,7 +351,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// Adding example: column widths are [0.6, 0.4] --> user adds column at end --> column widths are [0.4, 0.267, 0.33]
@action
resizeColumns = (headers: SchemaHeaderField[]) => {
- const n = headers.length;
const curWidths = headers.reduce((sum, hdr) => sum + Math.abs(hdr.width), 0);
const scaleFactor = 1 / curWidths;
this.dataDoc[this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>(
@@ -382,7 +388,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// we alter the pivot fields of the docs in case they are moved to a new column.
const colIndex = this.getColumnFromXCoord(xCoord);
const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
- DragManager.docsBeingDragged.map(doc => doc[DocData]).forEach(d => (d[this.notetakingCategoryField] = colHeader));
+ DragManager.docsBeingDragged
+ .map(doc => doc[DocData])
+ .forEach(d => {
+ d[this.notetakingCategoryField] = colHeader;
+ });
// used to notify sections to re-render
this.docsDraggedRowCol.length = 0;
const columnFromCoord = this.getColumnFromXCoord(xCoord);
@@ -393,7 +403,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// getColumnFromXCoord returns the column index for a given x-coordinate (currently always the client's mouse coordinate).
// This function is used to know which document a column SHOULD be in while it is being dragged.
getColumnFromXCoord = (xCoord: number): number | undefined => {
- let colIndex: number | undefined = undefined;
+ let colIndex: number | undefined;
const numColumns = this.colHeaderData.length;
const coords = [];
let colStartXCoord = 0;
@@ -416,10 +426,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const docsMatchingHeader: Doc[] = [];
const colIndex = this.getColumnFromXCoord(xCoord);
const colHeader = colIndex === undefined ? 'unset' : StrCast(this.colHeaderData[colIndex].heading);
- this.childDocs?.map(d => {
+ this.childDocs?.forEach(d => {
if (d instanceof Promise) return;
const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
- if (sectionValue.toString() == colHeader) {
+ if (sectionValue.toString() === colHeader) {
docsMatchingHeader.push(d);
}
});
@@ -432,9 +442,10 @@ export class CollectionNoteTakingView extends CollectionSubView() {
e.stopPropagation?.();
const newDoc = Doc.MakeCopy(fieldProps.Document, true);
newDoc[DocData].text = undefined;
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
+ return undefined;
};
// onInternalDrop is used when dragging and dropping a document within the view, such as dragging
@@ -463,7 +474,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && CollectionFreeFormDocumentView.from(de.complete.linkDragData?.linkDragView)) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
if (!this._props.addDocument?.(source)) e.preventDefault();
de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
@@ -572,6 +583,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
alert('You cannot use an existing column name. Please try a new column name');
return value;
}
+ return undefined;
});
const columnHeaders = Array.from(Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
const newColWidth = 1 / (this.numGroupColumns + 1);
@@ -590,17 +602,33 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const subItems: ContextMenuProps[] = [];
subItems.push({
description: `${this.layoutDoc._notetaking_columns_autoCreate ? 'Manually' : 'Automatically'} Create columns`,
- event: () => (this.layoutDoc._notetaking_columns_autoCreate = !this.layoutDoc._notetaking_columns_autoCreate),
+ event: () => {
+ this.layoutDoc._notetaking_columns_autoCreate = !this.layoutDoc._notetaking_columns_autoCreate;
+ },
icon: 'computer',
});
subItems.push({ description: 'Remove Empty Columns', event: this.removeEmptyColumns, icon: 'computer' });
subItems.push({
description: `${this.layoutDoc._notetaking_columns_autoSize ? 'Variable Size' : 'Autosize'} Columns`,
- event: () => (this.layoutDoc._notetaking_columns_autoSize = !this.layoutDoc._notetaking_columns_autoSize),
+ event: () => {
+ this.layoutDoc._notetaking_columns_autoSize = !this.layoutDoc._notetaking_columns_autoSize;
+ },
icon: 'plus',
});
- subItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
- subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
+ subItems.push({
+ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`,
+ event: () => {
+ this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight;
+ },
+ icon: 'plus',
+ });
+ subItems.push({
+ description: 'Clear All',
+ event: () => {
+ this.dataDoc.data = new List([]);
+ },
+ icon: 'times',
+ });
ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
}
};
@@ -625,6 +653,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const sections = Array.from(this.Sections.entries());
return sections.reduce((list, sec, i) => {
list.push(this.sectionNoteTaking(sec[0], sec[1]));
+ // eslint-disable-next-line react/no-array-index-key
i !== sections.length - 1 && list.push(<CollectionNoteTakingViewDivider key={`divider${i}`} isContentActive={this.isContentActive} index={i} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />);
return list;
}, [] as JSX.Element[]);
@@ -658,14 +687,18 @@ export class CollectionNoteTakingView extends CollectionSubView() {
background: this.backgroundColor(),
pointerEvents: this.backgroundEvents,
}}
- onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
- onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))}
+ onScroll={action(e => {
+ this._scroll = e.currentTarget.scrollTop;
+ })}
+ onPointerLeave={action(() => {
+ this.docsDraggedRowCol.length = 0;
+ })}
onPointerMove={e => e.buttons && this.onPointerMove(false, e.clientX, e.clientY)}
onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
onWheel={e => this._props.isContentActive() && e.stopPropagation()}>
- <>{this.renderedSections}</>
+ {this.renderedSections}
<div className="collectionNotetaking-pivotField" style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
Document={this.Document}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 448b11b05..c098c033b 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -1,8 +1,9 @@
+/* eslint-disable jsx-a11y/control-has-associated-label */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { lightOrDark, returnEmptyString } from '../../../Utils';
+import { lightOrDark, returnEmptyString } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
@@ -10,17 +11,19 @@ import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { DocUtils, Docs } from '../../documents/Documents';
+import { DocumentFromField } from '../../documents/DocFromField';
+import { DocUtils } from '../../documents/DocUtils';
+import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, undoable } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionNoteTakingView.scss';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
interface CSVFieldColumnProps {
Document: Doc;
@@ -113,13 +116,15 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
};
@action
- headingChanged = (value: string, shiftDown?: boolean) => {
+ headingChanged = (value: string /* , shiftDown?: boolean */) => {
const castedValue = this.getValue(value);
if (castedValue) {
if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- this._props.docList.forEach(d => (d[this._props.pivotField] = castedValue));
+ this._props.docList.forEach(d => {
+ d[this._props.pivotField] = castedValue;
+ });
if (this._props.headingObject) {
this._props.headingObject.setHeading(castedValue.toString());
this._heading = this._props.headingObject.heading;
@@ -129,10 +134,14 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
return false;
};
- @action pointerEntered = () => (this._hover = true);
- @action pointerLeave = () => (this._hover = false);
- @undoBatch
- addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
+ @action pointerEntered = () => {
+ this._hover = true;
+ };
+ @action pointerLeave = () => {
+ this._hover = false;
+ };
+
+ addTextNote = undoable(() => this.addNewTextDoc('-typed text-', false, true), 'add text note');
// addNewTextDoc is called when a user starts typing in a column to create a new node
@action
@@ -142,7 +151,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
const colValue = this.getValue(this._props.heading);
newDoc[key] = colValue;
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
return this._props.addDocument?.(newDoc) || false;
};
@@ -171,7 +180,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
doc => {
const key = this._props.pivotField;
doc[key] = this.getValue(this._props.heading);
- FormattedTextBox.SetSelectOnLoad(doc);
+ Doc.SetSelectOnLoad(doc);
return this._props.addDocument?.(doc);
},
this._props.addDocument,
@@ -188,13 +197,14 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
+ const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
if (created) {
if (this._props.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
return this._props.addDocument?.(created);
}
+ return undefined;
},
icon: 'compress-arrows-alt',
})
@@ -214,6 +224,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
}
return this._props.addDocument?.(created) || false;
}
+ return undefined;
},
icon: 'compress-arrows-alt',
})
@@ -238,7 +249,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
const key = this._props.pivotField;
const heading = this._heading;
const columnYMargin = this._props.headingObject ? 0 : this._props.yMargin;
- const evContents = heading ? heading : '25';
+ const evContents = heading || '25';
const headingView = this._props.headingObject ? (
<div
key={heading}
@@ -252,17 +263,16 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
className="collectionNoteTakingView-sectionHeader-subCont"
title={evContents === `No Value` ? `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ''}
style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
- <EditableView GetValue={() => evContents} isEditingCallback={isEditing => isEditing && this._props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
+ <EditableView GetValue={() => evContents} isEditingCallback={isEditing => isEditing && this._props.select(false)} SetValue={this.headingChanged} contents={evContents} oneLine />
</div>
{(this._props.colHeaderData?.length ?? 0) > 1 && (
- <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
+ <button type="button" className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>
)}
</div>
) : null;
const templatecols = this.columnWidth;
- const type = this._props.Document.type;
return (
<>
{headingView}
@@ -270,7 +280,7 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
<div className="collectionNoteTakingView-columnStack">
<div
key={`${heading}-stack`}
- className={`collectionNoteTakingView-Nodes`}
+ className="collectionNoteTakingView-Nodes"
style={{
padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
gridGap: this._props.gridGap,
@@ -282,10 +292,13 @@ export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSV
{!this._props.chromeHidden ? (
<div className="collectionNoteTakingView-DocumentButtons" style={{ display: this._props.isContentActive() ? 'flex' : 'none', marginBottom: 10 }}>
<div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
- <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents={'+ Node'} menuCallback={this.menuCallback} />
+ <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents="+ Node" menuCallback={this.menuCallback} />
</div>
<div className="collectionNoteTakingView-addDocumentButton" style={{ color: lightOrDark(this._props.backgroundColor?.()) }}>
- <EditableView {...this._props.editableViewProps()} />
+ {
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ <EditableView {...this._props.editableViewProps()} />
+ }
</div>
</div>
) : null}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 50a97b978..ddd4b8137 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,7 +1,8 @@
import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction } from '../../../Utils';
+import { setupMoveUpEvents } from '../../../ClientUtils';
import { UndoManager } from '../../util/UndoManager';
import { ObservableReactComponent } from '../ObservableReactComponent';
@@ -27,7 +28,7 @@ export class CollectionNoteTakingViewDivider extends ObservableReactComponent<Di
setupMoveUpEvents(
this,
e,
- (e, down, delta) => {
+ (moveEv, down, delta) => {
if (!batch) batch = UndoManager.StartBatch('resizing');
this._props.setColumnStartXCoords(delta[0], this._props.index);
return false;
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 9d68c621b..5b3f625db 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,19 +1,22 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { Doc, DocListCast } from '../../../fields/Doc';
import { ScriptField } from '../../../fields/ScriptField';
-import { NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
-import { dropActionType } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
+import { NumCast, StrCast, toList } from '../../../fields/Types';
+import { emptyFunction } from '../../../Utils';
+import { DocUtils } from '../../documents/DocUtils';
+import { dropActionType } from '../../util/DropActionTypes';
import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { OpenWhere } from '../nodes/DocumentView';
+import { OpenWhere } from '../nodes/OpenWhere';
import { computePassLayout, computeStarburstLayout } from './collectionFreeForm';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import './CollectionPileView.scss';
import { CollectionSubView } from './CollectionSubView';
+import { DocumentView } from '../nodes/DocumentView';
@observer
export class CollectionPileView extends CollectionSubView() {
@@ -40,15 +43,15 @@ export class CollectionPileView extends CollectionSubView() {
layoutEngine = () => StrCast(this.Document._freeform_pileEngine);
@undoBatch
- addPileDoc = (doc: Doc | Doc[]) => {
- (doc instanceof Doc ? [doc] : doc).map(d => DocUtils.iconify(d));
- return this._props.addDocument?.(doc) || false;
+ addPileDoc = (docs: Doc | Doc[]) => {
+ toList(docs).map(doc => DocUtils.iconify(doc));
+ return this._props.addDocument?.(docs) || false;
};
@undoBatch
- removePileDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- (doc instanceof Doc ? [doc] : doc).forEach(d => Doc.deiconifyView(d));
- const ret = this._props.moveDocument?.(doc, targetCollection, addDoc) || false;
+ removePileDoc = (docs: Doc | Doc[], targetCollection: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
+ toList(docs).forEach(doc => Doc.deiconifyView(doc));
+ const ret = this._props.moveDocument?.(docs, targetCollection, addDoc) || false;
if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this.DocumentView?.()._props.removeDocument?.(this.Document);
return ret;
};
@@ -66,6 +69,7 @@ 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}
@@ -116,16 +120,16 @@ export class CollectionPileView extends CollectionSubView() {
setupMoveUpEvents(
this,
e,
- (e: PointerEvent, down: number[], delta: number[]) => {
- if (this.layoutEngine() === 'pass' && this.childDocs.length && e.shiftKey) {
+ (moveEv: PointerEvent, down: number[], delta: number[]) => {
+ if (this.layoutEngine() === 'pass' && this.childDocs.length && moveEv.shiftKey) {
dist += Math.sqrt(delta[0] * delta[0] + delta[1] * delta[1]);
if (dist > 100) {
if (!this._undoBatch) {
this._undoBatch = UndoManager.StartBatch('layout pile');
}
const doc = this.childDocs[0];
- doc.x = e.clientX;
- doc.y = e.clientY;
+ doc.x = moveEv.clientX;
+ doc.y = moveEv.clientY;
this._props.addDocTab(doc, OpenWhere.inParentFromScreen) && (this._props.removeDocument?.(doc) || false);
dist = 0;
}
@@ -146,7 +150,7 @@ export class CollectionPileView extends CollectionSubView() {
@undoBatch
onClick = (e: React.MouseEvent) => {
if (e.button === 0) {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
this.toggleStarburst();
e.stopPropagation();
}
@@ -154,7 +158,7 @@ export class CollectionPileView extends CollectionSubView() {
render() {
return (
- <div className={`collectionPileView`} onClick={this.onClick} onPointerDown={this.pointerDown} style={{ width: this._props.PanelWidth(), height: '100%' }}>
+ <div className="collectionPileView" onClick={this.onClick} onPointerDown={this.pointerDown} style={{ width: this._props.PanelWidth(), height: '100%' }}>
{this.contents}
</div>
);
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 656f850b3..f9b123bb6 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,7 +1,13 @@
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable no-use-before-define */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
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 { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -10,26 +16,26 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
-import { emptyFunction, formatTime, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { emptyFunction, formatTime } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
-import { DocumentManager } from '../../util/DocumentManager';
+import { FollowLinkScript, IsFollowLinkScript } from '../../documents/DocUtils';
import { DragManager } from '../../util/DragManager';
-import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower';
-import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { CollectionSubView } from '../collections/CollectionSubView';
+import { VideoThumbnails } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
import { AudioWaveform } from '../nodes/audio/AudioWaveform';
-import { DocumentView, OpenWhere } from '../nodes/DocumentView';
-import { FocusFuncType, FocusViewOptions, StyleProviderFuncType } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusFuncType, StyleProviderFuncType } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
import { LabelBox } from '../nodes/LabelBox';
-import { VideoBox } from '../nodes/VideoBox';
+import { OpenWhere } from '../nodes/OpenWhere';
import { ObservableReactComponent } from '../ObservableReactComponent';
import './CollectionStackedTimeline.scss';
+import { CollectionSubView } from './CollectionSubView';
export type CollectionStackedTimelineProps = {
Play: () => void;
@@ -57,9 +63,14 @@ export enum TrimScope {
@observer
export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
+ // eslint-disable-next-line no-use-before-define
public static SelectingRegions: Set<CollectionStackedTimeline> = new Set();
public static StopSelecting() {
- this.SelectingRegions.forEach(action(region => (region._selectingRegion = false)));
+ this.SelectingRegions.forEach(
+ action(region => {
+ region._selectingRegion = false;
+ })
+ );
this.SelectingRegions.clear();
}
constructor(props: any) {
@@ -73,7 +84,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
private _timeline: HTMLDivElement | null = null; // ref to actual timeline div
private _timelineWrapper: HTMLDivElement | null = null; // ref to timeline wrapper div for zooming and scrolling
private _markerStart: number = 0;
- @observable public static CurrentlyPlaying: DocumentView[] = [];
@observable _selectingRegion = false;
@observable _markerEnd: number | undefined = undefined;
@observable _trimming: number = TrimScope.None;
@@ -130,7 +140,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
componentWillUnmount() {
document.removeEventListener('keydown', this.keyEvents, true);
if (this._selectingRegion) {
- runInAction(() => (this._selectingRegion = false));
+ runInAction(() => {
+ this._selectingRegion = false;
+ });
CollectionStackedTimeline.SelectingRegions.delete(this);
}
}
@@ -166,7 +178,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(res => {
if (doc.hidden) options.didMove = !(doc.hidden = false);
- const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
+ const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv));
findDoc(dv => res(dv));
});
@@ -174,9 +186,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this._props.endTag], val) ?? null);
// converts screen pixel offset to time
- toTimeline = (screen_delta: number, width: number) => {
- return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
- };
+ // prettier-ignore
+ toTimeline = (screenDelta: number, width: number) => //
+ Math.max(this.clipStart, Math.min(this.clipEnd, (screenDelta / width) * this.clipDuration + this.clipStart));
@computed get rangeClick() {
// prettier-ignore
@@ -234,6 +246,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
e.stopPropagation();
break;
+ default:
}
}
};
@@ -253,17 +266,15 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@action
onPointerDownTimeline = (e: React.PointerEvent): void => {
const rect = this._timeline?.getBoundingClientRect();
- const clientX = e.clientX;
- const diff = rect ? clientX - rect?.x : null;
- const shiftKey = e.shiftKey;
+ const { clientX, shiftKey } = e;
if (rect && this._props.isContentActive()) {
const wasPlaying = this._props.playing();
if (wasPlaying) this._props.Pause();
- var wasSelecting = this._markerEnd !== undefined;
+ let wasSelecting = this._markerEnd !== undefined;
setupMoveUpEvents(
this,
e,
- action(e => {
+ action(() => {
if (!wasSelecting) {
this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
wasSelecting = true;
@@ -272,8 +283,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
return false;
}),
- action((e, movement, isClick) => {
- this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
+ action((upEvent, movement, isClick) => {
+ this._markerEnd = this.toTimeline(upEvent.clientX - rect.x, rect.width);
if (this._markerEnd < this._markerStart) {
const tmp = this._markerStart;
this._markerStart = this._markerEnd;
@@ -281,13 +292,13 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}
if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
- setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
+ setTimeout(() => DocumentView.getDocumentView(anchor)?.select(false));
}
(!isClick || !wasSelecting) && (this._markerEnd = undefined);
this._timelineWrapper && (this._timelineWrapper.style.cursor = '');
}),
- (e, doubleTap) => {
- if (e.button !== 2) {
+ (clickEv, doubleTap) => {
+ if (clickEv.button !== 2) {
this._props.select(false);
!wasPlaying && doubleTap && this._props.Play();
}
@@ -309,11 +320,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
onHover = (e: React.MouseEvent): void => {
e.stopPropagation();
const rect = this._timeline?.getBoundingClientRect();
- const clientX = e.clientX;
+ const { clientX } = e;
if (rect) {
this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
if (this.thumbnails) {
- const nearest = Math.floor((this._hoverTime / this._props.rawDuration) * VideoBox.numThumbnails);
+ const nearest = Math.floor((this._hoverTime / this._props.rawDuration) * VideoThumbnails.DENSE);
const imgField = this.thumbnails.length > 0 ? new ImageField(this.thumbnails[nearest]) : undefined;
this._thumbnail = imgField?.url?.href ? imgField.url.href.replace('.png', '_m.png') : undefined;
}
@@ -327,14 +338,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setupMoveUpEvents(
this,
e,
- action((e, [], []) => {
+ action(moveEv => {
if (rect && this._props.isContentActive()) {
- this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
+ this._trimStart = Math.min(Math.max(this.trimStart + (moveEv.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
}
return false;
}),
emptyFunction,
- action((e, doubleTap) => {
+ action((clickEv, doubleTap) => {
doubleTap && (this._trimStart = this.clipStart);
})
);
@@ -347,14 +358,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setupMoveUpEvents(
this,
e,
- action((e, [], []) => {
+ action(moveEv => {
if (rect && this._props.isContentActive()) {
- this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
+ this._trimEnd = Math.max(Math.min(this.trimEnd + (moveEv.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
}
return false;
}),
emptyFunction,
- action((e, doubleTap) => {
+ action((clickEv, doubleTap) => {
doubleTap && (this._trimEnd = this.clipEnd);
})
);
@@ -383,7 +394,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
// handles dragging and dropping markers in timeline
@action
- internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
+ internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) {
if (super.onInternalDrop(e, de)) {
// determine x coordinate of drop and assign it to the documents being dragged --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
const localPt = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y);
@@ -403,7 +414,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
- if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
+ if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
return false;
};
@@ -441,7 +452,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}
@action
- playOnClick = (anchorDoc: Doc, clientX: number) => {
+ playOnClick = (anchorDoc: Doc /* , clientX: number */) => {
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
const endTime = this.anchorEnd(anchorDoc);
if (this.layoutDoc.autoPlayAnchors) {
@@ -450,17 +461,15 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._props.playFrom(seekTimeInSeconds, endTime);
this.scrollToTime(seekTimeInSeconds);
}
- } else {
- if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) {
- if (!this.layoutDoc.autoPlayAnchors && this._props.playing()) {
- this._props.Pause();
- } else {
- this._props.Play();
- }
+ } else if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) && endTime > NumCast(this.layoutDoc._layout_currentTimecode)) {
+ if (!this.layoutDoc.autoPlayAnchors && this._props.playing()) {
+ this._props.Pause();
} else {
- this._props.playFrom(seekTimeInSeconds, endTime);
- this.scrollToTime(seekTimeInSeconds);
+ this._props.Play();
}
+ } else {
+ this._props.playFrom(seekTimeInSeconds, endTime);
+ this.scrollToTime(seekTimeInSeconds);
}
return { select: true };
};
@@ -468,7 +477,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@action
clickAnchor = (anchorDoc: Doc, clientX: number) => {
if (IsFollowLinkScript(anchorDoc.onClick)) {
- LinkFollower.FollowLink(undefined, anchorDoc, false);
+ DocumentView.FollowLink(undefined, anchorDoc, false);
}
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
const endTime = this.anchorEnd(anchorDoc);
@@ -479,19 +488,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const rect = this._timeline?.getBoundingClientRect();
rect && this._props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
+ } else if (this.layoutDoc.autoPlayAnchors) {
+ this._props.playFrom(seekTimeInSeconds, endTime);
} else {
- if (this.layoutDoc.autoPlayAnchors) {
- this._props.playFrom(seekTimeInSeconds, endTime);
- } else {
- this._props.setTime(seekTimeInSeconds);
- }
+ this._props.setTime(seekTimeInSeconds);
}
return { select: true };
};
// makes sure no anchors overlaps each other by setting the correct position and width
getLevel = (m: Doc, placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]) => {
- const timelineContentWidth = this.timelineContentWidth;
+ const { timelineContentWidth } = this;
const x1 = this.anchorStart(m);
const x2 = this.anchorEnd(m, x1 + (10 / timelineContentWidth) * this.clipDuration);
let max = 0;
@@ -503,6 +510,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
max = Math.max(max, p.level);
return p.level;
}
+ return undefined;
})
);
let level = max + 1;
@@ -564,10 +572,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
onWheel={e => this.isContentActive() && e.stopPropagation()}
onScroll={this.setScroll}
onMouseMove={e => this.isContentActive() && this.onHover(e)}
- ref={wrapper => (this._timelineWrapper = wrapper)}>
+ ref={wrapper => {
+ this._timelineWrapper = wrapper;
+ }}>
<div
className="collectionStackedTimeline"
- ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+ ref={(timeline: HTMLDivElement | null) => {
+ this._timeline = timeline;
+ }}
onClick={e => this.isContentActive() && StopEvent(e)}
onPointerDown={e => this.isContentActive() && this.onPointerDownTimeline(e)}
style={{ width: this.timelineContentWidth }}>
@@ -582,7 +594,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const height = this._props.PanelHeight() / maxLevel;
return this.Document.hideAnchors ? null : (
<div
- className={'collectionStackedTimeline-marker-timeline'}
+ className="collectionStackedTimeline-marker-timeline"
key={d.anchor[Id]}
style={{
left,
@@ -592,6 +604,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
pointerEvents: 'none',
}}>
<StackedTimelineAnchor
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
mark={d.anchor}
containerViewPath={this._props.containerViewPath}
@@ -646,7 +659,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
{this.IsTrimming !== TrimScope.None && (
<>
- <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}></div>
+ <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }} />
<div
className="collectionStackedTimeline-trim-controls"
@@ -654,8 +667,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
}}>
- <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft}></div>
- <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight}></div>
+ <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft} />
+ <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight} />
</div>
<div
@@ -663,7 +676,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
style={{
left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
- }}></div>
+ }}
+ />
</>
)}
</div>
@@ -685,7 +699,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
interface StackedTimelineAnchorProps {
mark: Doc;
whenChildContentsActiveChanged: (isActive: boolean) => void;
- addDocTab: (doc: Doc, where: OpenWhere) => boolean;
+ addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
rangeClickScript: () => ScriptField;
rangePlayScript: () => ScriptField;
left: number;
@@ -735,20 +749,20 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
this._disposer = reaction(
() => this._props.currentTimecode(),
time => {
- const dictationDoc = Cast(this._props.layoutDoc.data_dictation, Doc, null);
- const isDictation = dictationDoc && LinkManager.Links(this._props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc);
+ // const dictationDoc = Cast(this._props.layoutDoc.data_dictation, Doc, null);
+ // const isDictation = dictationDoc && LinkManager.Links(this._props.mark).some(link => Cast(link.link_anchor_1, Doc, null)?.annotationOn === dictationDoc);
if (
!LightboxView.LightboxDoc &&
// bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront.
// for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video.
- /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc))*/
+ /* (isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc)) */
!this._props.layoutDoc.dontAutoFollowLinks &&
- LinkManager.Links(this._props.mark).length &&
+ Doc.Links(this._props.mark).length &&
time > NumCast(this._props.mark[this._props.startTag]) &&
time < NumCast(this._props.mark[this._props.endTag]) &&
this._lastTimecode < NumCast(this._props.mark[this._props.startTag]) - 1e-5
) {
- LinkFollower.FollowLink(undefined, this._props.mark, false);
+ DocumentView.FollowLink(undefined, this._props.mark, false);
}
this._lastTimecode = time;
}
@@ -763,34 +777,33 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
// starting the drag event for anchor resizing
@action
onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
- //this._props._timeline?.setPointerCapture(e.pointerId);
- const newTime = (e: PointerEvent) => {
- const rect = (e.target as any).getBoundingClientRect();
- return this._props.toTimeline(e.clientX - rect.x, rect.width);
+ const newTime = (timeDownEv: PointerEvent) => {
+ const rect = (timeDownEv.target as any).getBoundingClientRect();
+ return this._props.toTimeline(timeDownEv.clientX - rect.x, rect.width);
};
- const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => {
+ const changeAnchor = (time: number | undefined) => {
const timelineOnly = Cast(anchor[this._props.startTag], 'number', null) !== undefined;
if (timelineOnly) {
- if (!left && time !== undefined && time <= NumCast(anchor[this._props.startTag])) time = undefined;
- Doc.SetInPlace(anchor, left ? this._props.startTag : this._props.endTag, time, true);
- if (!left) Doc.SetInPlace(anchor, 'layout_borderRounding', time !== undefined ? undefined : '100%', true);
+ const timeMod = !left && time !== undefined && time <= NumCast(anchor[this._props.startTag]) ? undefined : time;
+ Doc.SetInPlace(anchor, left ? this._props.startTag : this._props.endTag, timeMod, true);
+ if (!left) Doc.SetInPlace(anchor, 'layout_borderRounding', timeMod !== undefined ? undefined : '100%', true);
} else {
anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
}
return false;
};
this.noEvents = true;
- var undo: UndoManager.Batch | undefined;
+ let undo: UndoManager.Batch | undefined;
setupMoveUpEvents(
this,
e,
- e => {
+ moveEv => {
if (!undo) undo = UndoManager.StartBatch('drag anchor');
- this._props.setTime(newTime(e));
- return changeAnchor(anchor, left, newTime(e));
+ this._props.setTime(newTime(moveEv));
+ return changeAnchor(newTime(moveEv));
},
- action(e => {
- this._props.setTime(newTime(e));
+ action(upEv => {
+ this._props.setTime(newTime(upEv));
undo?.end();
this.noEvents = false;
}),
@@ -828,7 +841,9 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
{...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
- ref={action((r: DocumentView | null) => (anchor.view = r))}
+ ref={action((r: DocumentView | null) => {
+ anchor.view = r;
+ })}
Document={mark}
TemplateDataDocument={undefined}
containerViewPath={this._props.containerViewPath}
@@ -851,7 +866,7 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
onClickScript={script}
onDoubleClickScript={this._props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
ignoreAutoHeight={false}
- hideResizeHandles={true}
+ hideResizeHandles
contextMenuItems={this.contextMenuItems}
/>
),
@@ -877,12 +892,15 @@ class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnch
);
}
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function formatToTime(time: number): any {
return formatTime(time);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function min(num1: number, num2: number): number {
return Math.min(num1, num2);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function max(num1: number, num2: number): number {
return Math.max(num1, num2);
});
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index bf0393883..3f12a281e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,8 +1,11 @@
+/* eslint-disable react/jsx-props-no-spreading */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+// eslint-disable-next-line import/no-extraneous-dependencies
import * as CSS from 'csstype';
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { ClientUtils, DivHeight, returnEmptyDoclist, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -11,10 +14,12 @@ import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { DivHeight, emptyFunction, returnEmptyDoclist, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { emptyFunction } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
-import { DragManager, dropActionType } from '../../util/DragManager';
+import { DocUtils } from '../../documents/DocUtils';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -24,13 +29,14 @@ import { EditableView } from '../EditableView';
import { LightboxView } from '../LightboxView';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocumentView } from '../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { StyleProp } from '../StyleProvider';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FocusViewOptions } from '../nodes/FocusViewOptions';
+import { StyleProp } from '../StyleProp';
import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView } from './CollectionSubView';
+
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
@@ -125,11 +131,16 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// TODO: plj - these are the children
children = (docs: Doc[]) => {
- //TODO: can somebody explain me to what exactly TraceMobX is?
+ // TODO: can somebody explain me to what exactly TraceMobX is?
TraceMobx();
// appears that we are going to reset the _docXfs. TODO: what is Xfs?
this._docXfs.length = 0;
- this._renderCount < docs.length && setTimeout(action(() => (this._renderCount = Math.min(docs.length, this._renderCount + 5))));
+ this._renderCount < docs.length &&
+ setTimeout(
+ action(() => {
+ this._renderCount = Math.min(docs.length, this._renderCount + 5);
+ })
+ );
return docs.map((d, i) => {
const height = () => this.getDocHeight(d);
const width = () => this.getDocWidth(d);
@@ -152,19 +163,21 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
};
// is sections that all collections inherit? I think this is how we show the masonry/columns
- //TODO: this seems important
+ // TODO: this seems important
get Sections() {
// appears that pivot field IS actually for sorting
if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
if (this.colHeaderData === undefined) {
- setTimeout(() => (this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>()), 0);
+ setTimeout(() => {
+ this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
+ });
return new Map<SchemaHeaderField, Doc[]>();
}
const colHeaderData = Array.from(this.colHeaderData);
const fields = new Map<SchemaHeaderField, Doc[]>(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []]));
let changed = false;
- this.filteredChildren.map(d => {
+ this.filteredChildren.forEach(d => {
const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
// the next five lines ensures that floating point rounding errors don't create more than one section -syip
const parsed = parseInt(sectionValue.toString());
@@ -186,7 +199,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (this.layoutDoc._columnsHideIfEmpty) {
Array.from(fields.keys())
.filter(key => !fields.get(key)!.length)
- .map(header => {
+ .forEach(header => {
fields.delete(header);
colHeaderData.splice(colHeaderData.indexOf(header), 1);
changed = true;
@@ -207,11 +220,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// reset section headers when a new filter is inputted
this._disposers.pivotField = reaction(
() => this.pivotField,
- () => (this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
+ () => {
+ this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List();
+ }
);
this._disposers.autoHeight = reaction(
() => this.layoutDoc._layout_autoHeight,
- layout_autoHeight => layout_autoHeight && this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))
+ layoutAutoHeight => layoutAutoHeight && this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))
);
this._disposers.refList = reaction(
() => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !LightboxView.Contains(this.DocumentView?.()) }),
@@ -231,9 +246,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
isAnyChildContentActive = () => this._props.isAnyChildContentActive();
- moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
- return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
- };
+ moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc));
onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
@computed get onChildDoubleClickHandler() {
@@ -250,10 +263,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
if (found) {
- const top = found.getBoundingClientRect().top;
+ const { top } = found.getBoundingClientRect();
const localTop = this.ScreenToLocalBoxXf().transformPoint(0, top);
if (Math.floor(localTop[1]) !== 0) {
- let focusSpeed = options.zoomTime ?? 500;
+ const focusSpeed = options.zoomTime ?? 500;
smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
return focusSpeed;
}
@@ -276,18 +289,18 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
if (['Enter'].includes(e.key) && e.ctrlKey) {
e.stopPropagation?.();
- const below = !e.altKey && e.key !== 'Tab';
- const layout_fieldKey = StrCast(fieldProps.fieldKey);
+ 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 (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
- newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
+ if (layoutFieldKey !== 'layout' && fieldProps.Document[layoutFieldKey] instanceof Doc) {
+ newDoc[layoutFieldKey] = fieldProps.Document[layoutFieldKey];
}
newDoc[DocData].text = undefined;
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
+ return false;
};
isContentActive = () => (this._props.isContentActive() ? true : this._props.isSelected() === false || this._props.isContentActive() === false ? false : undefined);
@@ -327,7 +340,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
onKey={this.onKeyDown}
DataTransition={trans}
- onBrowseClickScript={this._props.onBrowseClickScript}
isDocumentActive={this.isContentActive}
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
@@ -363,7 +375,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocTransform(doc: Doc) {
const dref = this.docRefs.get(doc);
this._scroll; // must be referenced for document decorations to update when the text box container is scrolled
- const { translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv);
+ const { translateX, translateY } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
// the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off
return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.ScreenToLocalBoxXf().Scale);
}
@@ -399,7 +411,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// This following three functions must be from the view Mehek showed
columnDividerDown = (e: React.PointerEvent) => {
- runInAction(() => (this._cursor = 'grabbing'));
+ runInAction(() => {
+ this._cursor = 'grabbing';
+ });
const batch = UndoManager.StartBatch('stacking width');
setupMoveUpEvents(
this,
@@ -425,7 +439,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
onPointerDown={this.columnDividerDown}
ref={this._draggerRef}
style={{ cursor: this._cursor, color: SettingsManager.userColor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
- <FontAwesomeIcon icon={'arrows-alt-h'} />
+ <FontAwesomeIcon icon="arrows-alt-h" />
</div>
);
}
@@ -439,7 +453,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
let dropAfter = 0;
if (de.complete.docDragData) {
// going to re-add the docs to the _docXFs based on position of where we just dropped
- this._docXfs.map((cd, i) => {
+ this._docXfs.forEach((cd, i) => {
const pos = cd
.stackedDocTransform()
.inverse()
@@ -468,7 +482,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && CollectionFreeFormDocumentView.from(de.complete.linkDragData?.linkDragView)) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
if (!this._props.addDocument?.(source)) e.preventDefault();
de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
@@ -496,7 +510,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
const where = [e.clientX, e.clientY];
let targInd = -1;
- this._docXfs.map((cd, i) => {
+ this._docXfs.forEach((cd, i) => {
const pos = cd
.stackedDocTransform()
.inverse()
@@ -521,10 +535,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// what a section looks like if we're in stacking view
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
const key = this.pivotField;
- let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
+ let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
if (this.pivotField) {
const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ // eslint-disable-next-line prefer-destructuring
type = types[0];
}
}
@@ -557,9 +572,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// what a section looks like if we're in masonry. Shouldn't actually need to use this.
sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
const key = this.pivotField;
- let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
+ let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ // eslint-disable-next-line prefer-destructuring
type = types[0];
}
const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
@@ -609,9 +625,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const cm = ContextMenu.Instance;
const options = cm.findByDescription('Options...');
const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
- optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
- optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight), icon: 'plus' });
- optionItems.push({ description: 'Clear All', event: () => (this.dataDoc[this.fieldKey ?? 'data'] = new List([])), icon: 'times' });
+ optionItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => { this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill; }, icon: 'plus' }); // prettier-ignore
+ optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => { this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight; }, icon: 'plus' }); // prettier-ignore
+ optionItems.push({ description: 'Clear All', event: () => { this.dataDoc[this.fieldKey ?? 'data'] = new List([]); } , icon: 'times' }); // prettier-ignore
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
}
};
@@ -639,7 +655,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
addDocument={this._props.addDocument}
moveDocument={this._props.moveDocument}
addDocTab={this._props.addDocTab}
- onBrowseClickScript={this._props.onBrowseClickScript}
pinToPres={emptyFunction}
rootSelected={this.rootSelected}
removeDocument={this._props.removeDocument}
@@ -700,7 +715,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
className={this.isStackingView ? 'collectionStackingView' : 'collectionMasonryView'}
ref={ele => {
this._masonryGridRef = ele;
- this.createDashEventsTarget(ele); //so the whole grid is the drop target?
+ 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
@@ -711,7 +726,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor),
pointerEvents: (this._props.pointerEvents?.() as any) ?? this.backgroundEvents,
}}
- onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
+ onScroll={action(e => {
+ this._scroll = e.currentTarget.scrollTop;
+ })}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
onWheel={e => this.isContentActive() && e.stopPropagation()}>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index c5292f880..e2ad5b31d 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,7 +1,11 @@
+/* eslint-disable jsx-a11y/control-has-associated-label */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { DivHeight, DivWidth, returnEmptyString, setupMoveUpEvents } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { RichTextField } from '../../../fields/RichTextField';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -9,10 +13,13 @@ import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { DivHeight, DivWidth, emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { emptyFunction } from '../../../Utils';
+import { DocumentFromField } from '../../documents/DocFromField';
+import { Docs } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
-import { DragManager, dropActionType } from '../../util/DragManager';
+import { DocUtils } from '../../documents/DocUtils';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
@@ -95,7 +102,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
this._ele && this.props.refList.push(this._ele);
this._disposers.collapser = reaction(
() => this._props.headingObject?.collapsed,
- collapsed => (this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false),
+ collapsed => { this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false; }, // prettier-ignore
{ fireImmediately: true }
);
}
@@ -105,7 +112,6 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
this._ele = null;
}
- //TODO: what is scripting? I found it in SetInPlace def but don't know what that is
@undoBatch
columnDrop = action((e: Event, de: DragManager.DropEvent) => {
const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
@@ -121,13 +127,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
};
@action
- headingChanged = (value: string, shiftDown?: boolean) => {
+ headingChanged = (value: string /* , shiftDown?: boolean */) => {
const castedValue = this.getValue(value);
if (castedValue) {
if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- this._props.pivotField && this._props.docList.forEach(d => (d[this._props.pivotField] = castedValue));
+ this._props.pivotField && this._props.docList.forEach(d => { d[this._props.pivotField] = castedValue; }) // prettier-ignore
if (this._props.headingObject) {
this._props.headingObject.setHeading(castedValue.toString());
this._heading = this._props.headingObject.heading;
@@ -143,9 +149,9 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
this._color = color;
};
- @action pointerEntered = () => SnappingManager.IsDragging && (this._background = '#b4b4b4');
- @action pointerLeave = () => (this._background = 'inherit');
- @undoBatch typedNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
+ @action pointerEntered = () => { SnappingManager.IsDragging && (this._background = '#b4b4b4'); } // prettier-ignore
+ @action pointerLeave = () => { this._background = 'inherit'}; // prettier-ignore
+ @undoBatch typedNote = () => this.addNewTextDoc('-typed text-', false, true);
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
@@ -153,17 +159,20 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
const key = this._props.pivotField;
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
key && (newDoc[key] = this.getValue(this._props.heading));
- const maxHeading = this._props.docList.reduce((maxHeading, doc) => (NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading), 0);
+ const maxHeading = this._props.docList.reduce((prevHeading, doc) => (NumCast(doc.heading) > prevHeading ? NumCast(doc.heading) : prevHeading), 0);
const heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
newDoc.heading = heading;
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
return this._props.addDocument?.(newDoc) || false;
};
@action
deleteColumn = () => {
- this._props.pivotField && this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
+ this._props.pivotField &&
+ this._props.docList.forEach(d => {
+ d[this._props.pivotField] = undefined;
+ });
if (this._props.colHeaderData && this._props.headingObject) {
const index = this._props.colHeaderData.indexOf(this._props.headingObject);
this._props.colHeaderData.splice(index, 1);
@@ -178,8 +187,8 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
headerDown = (e: React.PointerEvent<HTMLDivElement>) => setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
- //TODO: I think this is where I'm supposed to edit stuff
- startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
+ // 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);
embedding._width = this._props.columnWidth / (this._props.colHeaderData?.length || 1);
@@ -210,23 +219,21 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
);
};
- renderMenu = () => {
- return (
- <div className="collectionStackingView-optionPicker">
- <div className="optionOptions">
- <div className={'optionPicker' + (true ? ' active' : '')} onClick={action(() => {})}>
- Add options here
- </div>
- </div>
+ renderMenu = () => (
+ <div className="collectionStackingView-optionPicker">
+ <div className="optionOptions">
+ <div className="optionPicker active">Add options here</div>
</div>
- );
- };
+ </div>
+ );
@observable private collapsed: boolean = false;
- private toggleVisibility = action(() => (this.collapsed = !this.collapsed));
+ private toggleVisibility = action(() => {
+ this.collapsed = !this.collapsed;
+ });
- menuCallback = (x: number, y: number) => {
+ menuCallback = () => {
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
@@ -235,7 +242,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
const height = this._ele ? DivHeight(this._ele) : 0;
DocUtils.addDocumentCreatorMenuItems(
doc => {
- FormattedTextBox.SetSelectOnLoad(doc);
+ Doc.SetSelectOnLoad(doc);
return this._props.addDocument?.(doc);
},
this._props.addDocument,
@@ -250,13 +257,14 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
+ const created = DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
if (created) {
if (this._props.Document.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
return this._props.addDocument?.(created);
}
+ return false;
},
icon: 'compress-arrows-alt',
})
@@ -276,6 +284,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
}
return this._props.addDocument?.(created) || false;
}
+ return false;
},
icon: 'compress-arrows-alt',
})
@@ -307,7 +316,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
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 ? heading : this._props?.type === 'number' ? '0' : noValueHeader;
+ const evContents = heading || (this._props?.type === 'number' ? '0' : noValueHeader);
const headingView = this._props.headingObject ? (
<div
key={heading}
@@ -324,14 +333,19 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
onPointerDown={this.headerDown}
title={evContents === noValueHeader ? `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ''}
style={{ background: evContents !== noValueHeader ? this._color : 'inherit' }}>
- <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
+ <EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine />
<div className="collectionStackingView-sectionColor" style={{ display: evContents === noValueHeader ? 'none' : undefined }}>
- <button className="collectionStackingView-sectionColorButton" onClick={action(e => (this._paletteOn = !this._paletteOn))}>
+ <button
+ type="button"
+ className="collectionStackingView-sectionColorButton"
+ onClick={action(() => {
+ this._paletteOn = !this._paletteOn;
+ })}>
<FontAwesomeIcon icon="palette" size="lg" />
</button>
{this._paletteOn ? this.renderColorPicker() : null}
</div>
- <button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
+ <button type="button" className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>
{/* {evContents === noValueHeader ? null : (
@@ -352,7 +366,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
</div>
) : null;
const templatecols = `${this._props.columnWidth / this._props.numGroupColumns}px `;
- const type = this._props.Document.type;
+ const { type } = this._props.Document;
return (
<>
{this._props.Document._columnsHideIfEmpty ? null : headingView}
@@ -364,7 +378,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
style={{
padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
margin: 'auto',
- width: 'max-content', //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
+ 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,
@@ -376,10 +390,10 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
{!this._props.chromeHidden && type !== DocumentType.PRES ? (
// TODO: this is the "new" button: see what you can work with here
// change cursor to pointer for this, and update dragging cursor
- //TODO: there is a bug that occurs when adding a freeform document and trying to move it around
- //TODO: would be great if there was additional space beyond the frame, so that you can actually see your
+ // TODO: there is a bug that occurs when adding a freeform document and trying to move it around
+ // TODO: would be great if there was additional space beyond the frame, so that you can actually see your
// bottom note
- //TODO: ok, so we are using a single column, and this is it!
+ // TODO: ok, so we are using a single column, and this is it!
<div
key={`${heading}-add-document`}
onKeyDown={e => e.stopPropagation()}
@@ -390,7 +404,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
SetValue={this.addNewTextDoc}
textCallback={this.typedNote}
placeholder={"Type ':' for commands"}
- contents={<FontAwesomeIcon icon={'plus'} />}
+ contents={<FontAwesomeIcon icon="plus" />}
menuCallback={this.menuCallback}
/>
</div>
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 32198e3a2..a4708bed5 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,37 +1,68 @@
import { action, computed, makeObservable, observable } from 'mobx';
import * as React from 'react';
import * as rp from 'request-promise';
-import { Utils, returnFalse } from '../../../Utils';
+import { ClientUtils, returnFalse } from '../../../ClientUtils';
import CursorField from '../../../fields/CursorField';
-import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate, DocData } from '../../../fields/DocSymbols';
+import { Doc, DocListCast, GetDocFromUrl, GetHrefFromHTML, Opt, RTFIsFragment, StrListCast } from '../../../fields/Doc';
+import { AclPrivate } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
import { GestureUtils } from '../../../pen-gestures/GestureUtils';
import { DocServer } from '../../DocServer';
import { Networking } from '../../Network';
+import { DocUtils } from '../../documents/DocUtils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { ViewBoxBaseComponent } from '../DocComponent';
-import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents';
-import { DragManager, dropActionType } from '../../util/DragManager';
+import { Docs, DocumentOptions } from '../../documents/Documents';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { UndoManager, undoBatch } from '../../util/UndoManager';
-import { LoadingBox } from '../nodes/LoadingBox';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { CollectionView, CollectionViewProps } from './CollectionView';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+
+export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
+ isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+ isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
+ layoutEngine?: () => string;
+ setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => void;
+ ignoreUnrendered?: boolean;
+
+ // property overrides for child documents
+ childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
+ childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
+ childContentsActive?: () => boolean | undefined;
+ childLayoutFitWidth?: (child: Doc) => boolean;
+ childlayout_showTitle?: () => string;
+ childOpacity?: () => number;
+ childContextMenuItems?: () => { script: ScriptField; label: string }[];
+ childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
+ childHideDecorationTitle?: boolean;
+ childHideResizeHandles?: boolean;
+ childDragAction?: dropActionType;
+ childXPadding?: number;
+ childYPadding?: number;
+ childLayoutString?: string;
+ childIgnoreNativeSize?: boolean;
+ childClickScript?: ScriptField;
+ childDoubleClickScript?: ScriptField;
+ AddToMap?: (treeViewDoc: Doc, index: number[]) => void;
+ RemFromMap?: (treeViewDoc: Doc, index: number[]) => void;
+ hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
+}
export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
}
-export function CollectionSubView<X>(moreProps?: X) {
- class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps>() {
+export function CollectionSubView<X>() {
+ class CollectionSubViewInternal extends ViewBoxBaseComponent<X & SubCollectionViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private gestureDisposer?: GestureUtils.GestureEventDisposer;
protected _mainCont?: HTMLDivElement;
@@ -53,7 +84,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
};
protected CreateDropTarget(ele: HTMLDivElement) {
- //used in schema view
+ // used in schema view
this.createDashEventsTarget(ele);
}
@@ -83,10 +114,11 @@ export function CollectionSubView<X>(moreProps?: X) {
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
- return !pair.layout?.hidden && pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
- });
+ .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))
+ );
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
}
@computed get childDocList() {
@@ -95,9 +127,9 @@ export function CollectionSubView<X>(moreProps?: X) {
collectionFilters = () => this._focusFilters ?? StrListCast(this.Document._childFilters);
collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.Document._childFiltersByRanges, listSpec('string'), []);
// child filters apply to the descendants of the documents in this collection
- childDocFilters = () => [...(this._props.childFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
+ 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
- unrecursiveDocFilters = () => [...(this._props.childFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
+ unrecursiveDocFilters = () => [...(this._props.childFilters?.().filter(f => !ClientUtils.IsRecursiveFilter(f)) || [])];
childDocRangeFilters = () => [...(this._props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
searchFilterDocs = () => this._props.searchFilterDocs?.() ?? DocListCast(this.Document._searchFilterDocs);
@computed.struct get childDocs() {
@@ -129,29 +161,30 @@ export function CollectionSubView<X>(moreProps?: X) {
const docsforFilter: Doc[] = [];
childDocs.forEach(d => {
// dragging facets
- const dragged = this._props.childFilters?.().some(f => f.includes(Utils.noDragDocsFilter));
- if (dragged && SnappingManager.CanEmbed && DragManager.docsBeingDragged.includes(d)) return false;
+ const dragged = this._props.childFilters?.().some(f => f.includes(ClientUtils.noDragDocsFilter));
+ if (dragged && SnappingManager.CanEmbed && DragManager.docsBeingDragged.includes(d)) return;
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 annos = !Field.toString(Doc.LayoutField(d) as Field).includes(CollectionView.name);
- const data = d[annos ? fieldKey + '_annotations' : fieldKey];
- const side = annos && d[fieldKey + '_sidebar'];
- if (data !== undefined || side !== undefined) {
- let subDocs = [...DocListCast(data), ...DocListCast(side)];
+ 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'];
+ if (docChildDocs !== undefined || sidebarDocs !== undefined) {
+ let subDocs = [...DocListCast(docChildDocs), ...DocListCast(sidebarDocs)];
if (subDocs.length > 0) {
let newarray: Doc[] = [];
notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, childFiltersByRanges, d).length);
while (subDocs.length > 0 && !notFiltered) {
newarray = [];
+ // eslint-disable-next-line no-loop-func
subDocs.forEach(t => {
- const fieldKey = Doc.LayoutFieldKey(t);
- const annos = !Field.toString(Doc.LayoutField(t) as Field).includes(CollectionView.name);
+ const docFieldKey = Doc.LayoutFieldKey(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[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
- annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
+ DocListCast(t[isSubDocAnnotatable ? docFieldKey + '_annotations' : docFieldKey]).forEach(newdoc => newarray.push(newdoc));
+ isSubDocAnnotatable && DocListCast(t[docFieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
});
subDocs = newarray;
}
@@ -168,7 +201,7 @@ export function CollectionSubView<X>(moreProps?: X) {
let ind;
const doc = this.Document;
const id = Doc.UserDoc()[Id];
- const email = Doc.CurrentUserEmail;
+ const email = ClientUtils.CurrentUserEmail();
const pos = { x: position[0], y: position[1] };
if (id && email) {
const proto = Doc.GetProto(doc);
@@ -184,6 +217,7 @@ export function CollectionSubView<X>(moreProps?: X) {
if (!cursors) {
proto.cursors = cursors = new List<CursorField>();
}
+ // eslint-disable-next-line no-cond-assign
if (cursors.length > 0 && (ind = cursors.findIndex(entry => entry.data.metadata.id === id)) > -1) {
cursors[ind].setPosition(pos);
} else {
@@ -194,6 +228,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
@undoBatch
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetDropAction: dropActionType) {
@@ -212,12 +247,12 @@ export function CollectionSubView<X>(moreProps?: X) {
addDocument = (doc: Doc | Doc[], annotationKey?: string) => this._props.addDocument?.(doc, annotationKey) || false;
removeDocument = (doc: Doc | Doc[], annotationKey?: string) => this._props.removeDocument?.(doc, annotationKey) || false;
- moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean, annotationKey?: string) => this._props.moveDocument?.(doc, targetCollection, addDocument) || false;
+ moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[], annotationKey?: string) => boolean) => this._props.moveDocument?.(doc, targetCollection, addDocument) || false;
protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
- const docDragData = de.complete.docDragData;
+ const { docDragData } = de.complete;
if (docDragData) {
- let added = undefined;
+ let added;
const dropAction = docDragData.dropAction || docDragData.userDropAction;
const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]);
const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
@@ -245,8 +280,9 @@ export function CollectionSubView<X>(moreProps?: X) {
}
added === false && !this._props.isAnnotationOverlay && e.preventDefault();
added === true && e.stopPropagation();
- return added ? true : false;
- } else if (de.complete.annoDragData) {
+ return !!added;
+ }
+ if (de.complete.annoDragData) {
const dropCreator = de.complete.annoDragData.dropDocCreator;
de.complete.annoDragData.dropDocCreator = () => {
const dropped = dropCreator(this._props.isAnnotationOverlay ? this.Document : undefined);
@@ -280,10 +316,10 @@ export function CollectionSubView<X>(moreProps?: X) {
const addDocument = (doc: Doc | Doc[]) => this.addDocument(doc);
if (html) {
- if (FormattedTextBox.IsFragment(html)) {
- const href = FormattedTextBox.GetHref(html);
+ if (RTFIsFragment(html)) {
+ const href = GetHrefFromHTML(html);
if (href) {
- const docId = FormattedTextBox.GetDocFromUrl(href);
+ const docId = GetDocFromUrl(href);
if (docId) {
// prosemirror text containing link to dash document
DocServer.GetRefField(docId).then(f => {
@@ -316,7 +352,7 @@ export function CollectionSubView<X>(moreProps?: X) {
const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: [imgSrc] })).lastElement();
const newImgSrc =
result.accessPaths.agnostic.client.indexOf('dashblobstore') === -1 //
- ? Utils.prepend(result.accessPaths.agnostic.client)
+ ? ClientUtils.prepend(result.accessPaths.agnostic.client)
: result.accessPaths.agnostic.client;
addDocument(ImageUtils.AssignImgInfo(Docs.Create.ImageDocument(newImgSrc, imgOpts), result));
@@ -325,39 +361,39 @@ export function CollectionSubView<X>(moreProps?: X) {
addDocument(ImageUtils.AssignImgInfo(doc, await ImageUtils.ExtractImgInfo(doc)));
}
return;
+ }
+ const path = window.location.origin + '/doc/';
+ if (text.startsWith(path)) {
+ const docId = text.replace(Doc.globalServerPath(), '').split('?')[0];
+ DocServer.GetRefField(docId).then(f => {
+ const fDoc = f;
+ if (fDoc instanceof Doc) {
+ if (options.x || options.y) {
+ fDoc.x = options.x as number;
+ fDoc.y = options.y as number;
+ } // should be in CollectionFreeFormView
+ addDocument(fDoc);
+ }
+ });
} else {
- const path = window.location.origin + '/doc/';
- if (text.startsWith(path)) {
- const docId = text.replace(Doc.globalServerPath(), '').split('?')[0];
- DocServer.GetRefField(docId).then(f => {
- if (f instanceof Doc) {
- if (options.x || options.y) {
- f.x = options.x as number;
- f.y = options.y as number;
- } // should be in CollectionFreeFormView
- f instanceof Doc && addDocument(f);
- }
- });
- } else {
- const srcWeb = SelectionManager.Views.lastElement();
- const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0];
- const reg = new RegExp(Utils.prepend(''), 'g');
- const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
- const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, '$1')).filter(t => t)?.[0];
- const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? 'from:' + srcUrl : '-web clip-', _width: 300, _height: 300, backgroundColor });
- Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
- addDocument(htmlDoc);
- if (srcWeb) {
- const iframe = SelectionManager.Views[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
- const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
- if (focusNode) {
- const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
- anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
- }
+ const srcWeb = DocumentView.Selected().lastElement();
+ const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0];
+ const reg = new RegExp(ClientUtils.prepend(''), 'g');
+ const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
+ const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, '$1')).filter(t => t)?.[0];
+ const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? 'from:' + srcUrl : '-web clip-', _width: 300, _height: 300, backgroundColor });
+ Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
+ addDocument(htmlDoc);
+ if (srcWeb) {
+ const iframe = DocumentView.Selected()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+ const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
+ if (focusNode) {
+ const anchor = srcWeb?.ComponentView?.getAnchor?.(true);
+ anchor && DocUtils.MakeLink(htmlDoc, anchor, {});
}
}
- return;
}
+ return;
}
}
@@ -414,10 +450,15 @@ export function CollectionSubView<X>(moreProps?: X) {
for (let i = 0; i < length; i++) {
const item = e.dataTransfer.items[i];
if (item.kind === 'string' && item.type.includes('uri')) {
- const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
- const type = (await rp.head(Utils.CorsProxy(stringContents)))['content-type'];
+ // eslint-disable-next-line no-await-in-loop
+ const stringContents = await new Promise<string>(resolve => {
+ item.getAsString(resolve);
+ });
+ // eslint-disable-next-line no-await-in-loop
+ const type = (await rp.head(ClientUtils.CorsProxy(stringContents)))['content-type'];
if (type) {
- const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
+ // eslint-disable-next-line no-await-in-loop
+ const doc = await DocUtils.DocumentFromType(type, ClientUtils.CorsProxy(stringContents), options);
doc && generatedDocuments.push(doc);
}
}
@@ -426,7 +467,7 @@ export function CollectionSubView<X>(moreProps?: X) {
file?.type && files.push(file);
file?.type === 'application/json' &&
- Utils.readUploadedFileAsText(file).then(result => {
+ ClientUtils.readUploadedFileAsText(file).then(result => {
const json = JSON.parse(result as string);
addDocument(
Docs.Create.TreeDocument(
@@ -450,17 +491,17 @@ export function CollectionSubView<X>(moreProps?: X) {
// create placeholder docs
// inside placeholder docs have some func that
- let pileUpDoc = undefined;
+ let pileUpDoc;
if (typeof files === 'string') {
const loading = Docs.Create.LoadingDocument(files, options);
generatedDocuments.push(loading);
- LoadingBox.addCurrentlyLoading(loading);
+ Doc.addCurrentlyLoading(loading);
DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
generatedDocuments.push(
...files.map(file => {
const loading = Docs.Create.LoadingDocument(file, options);
- LoadingBox.addCurrentlyLoading(loading);
+ Doc.addCurrentlyLoading(loading);
DocUtils.uploadFileToDoc(file, {}, loading);
return loading;
})
@@ -478,23 +519,19 @@ export function CollectionSubView<X>(moreProps?: X) {
})
: [];
if (completed) completed(set);
- else {
- if (isFreeformView && generatedDocuments.length > 1) {
- pileUpDoc = DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!;
- addDocument(pileUpDoc);
- } else {
- generatedDocuments.forEach(addDocument);
- }
- }
- } else {
- if (text && !text.includes('https://')) {
- addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
+ else if (isFreeformView && generatedDocuments.length > 1) {
+ pileUpDoc = DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!;
+ addDocument(pileUpDoc);
} else {
- alert('Document upload failed - possibly an unsupported file type.');
+ generatedDocuments.forEach(addDocument);
}
+ } else if (text && !text.includes('https://')) {
+ addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
+ } else {
+ alert('Document upload failed - possibly an unsupported file type.');
}
};
}
- return CollectionSubView;
+ return CollectionSubViewInternal;
}
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 37452ddfb..0369e4a2a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,7 +1,10 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
+import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
import { Doc, Opt, StrListCast } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
@@ -9,14 +12,12 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { Docs } from '../../documents/Documents';
-import { DocumentManager } from '../../util/DocumentManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { FieldsDropdown } from '../FieldsDropdown';
+import { PinDocView } from '../PinFuncs';
import { DocumentView } from '../nodes/DocumentView';
-import { FocusViewOptions } from '../nodes/FieldView';
-import { PresBox } from '../nodes/trails';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTimeView.scss';
import { ViewDefBounds, computePivotLayout, computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
@@ -53,7 +54,7 @@ export class CollectionTimeView extends CollectionSubView() {
title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
annotationOn: this.Document,
});
- PresBox.pinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
+ PinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.Document);
if (addAsAnnotation) {
// when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
@@ -67,7 +68,7 @@ export class CollectionTimeView extends CollectionSubView() {
};
@action
- scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: FocusViewOptions) => {
+ scrollPreview = (docView: DocumentView, anchor: Doc /* , focusSpeed: number, options: FocusViewOptions */) => {
// if in preview, then override document's fields with view spec
this._focusFilters = StrListCast(anchor.config_docFilters);
this._focusRangeFilters = StrListCast(anchor.config_docRangeFilters);
@@ -76,13 +77,15 @@ export class CollectionTimeView extends CollectionSubView() {
};
layoutEngine = () => this._layoutEngine;
- toggleVisibility = action(() => (this._collapsed = !this._collapsed));
+ toggleVisibility = action(() => {
+ this._collapsed = !this._collapsed;
+ });
onMinDown = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
e,
- action((e: PointerEvent, down: number[], delta: number[]) => {
+ action((moveEv: PointerEvent, down: number[], delta: number[]) => {
const minReq = NumCast(this.Document[this._props.fieldKey + '-timelineMinReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMin'], 0));
const maxReq = NumCast(this.Document[this._props.fieldKey + '-timelineMaxReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMax'], 10));
this.Document[this._props.fieldKey + '-timelineMinReq'] = minReq + ((maxReq - minReq) * delta[0]) / this._props.PanelWidth();
@@ -98,7 +101,7 @@ export class CollectionTimeView extends CollectionSubView() {
setupMoveUpEvents(
this,
e,
- action((e: PointerEvent, down: number[], delta: number[]) => {
+ action((moveEv, down: number[], delta: number[]) => {
const minReq = NumCast(this.Document[this._props.fieldKey + '-timelineMinReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMin'], 0));
const maxReq = NumCast(this.Document[this._props.fieldKey + '-timelineMaxReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMax'], 10));
this.Document[this._props.fieldKey + '-timelineMaxReq'] = maxReq + ((maxReq - minReq) * delta[0]) / this._props.PanelWidth();
@@ -113,7 +116,7 @@ export class CollectionTimeView extends CollectionSubView() {
setupMoveUpEvents(
this,
e,
- action((e: PointerEvent, down: number[], delta: number[]) => {
+ action((moveEv: PointerEvent, down: number[], delta: number[]) => {
const minReq = NumCast(this.Document[this._props.fieldKey + '-timelineMinReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMin'], 0));
const maxReq = NumCast(this.Document[this._props.fieldKey + '-timelineMaxReq'], NumCast(this.Document[this._props.fieldKey + '-timelineMax'], 10));
this.Document[this._props.fieldKey + '-timelineMinReq'] = minReq - ((maxReq - minReq) * delta[0]) / this._props.PanelWidth();
@@ -133,7 +136,7 @@ export class CollectionTimeView extends CollectionSubView() {
};
@action
- contentsDown = (e: React.MouseEvent) => {
+ contentsDown = () => {
const prevFilterIndex = NumCast(this.layoutDoc._prevFilterIndex);
if (prevFilterIndex > 0) {
this.goTo(prevFilterIndex - 1);
@@ -146,6 +149,7 @@ export class CollectionTimeView extends CollectionSubView() {
return (
<div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
<CollectionFreeFormView
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
fitContentsToBox={returnTrue}
@@ -161,7 +165,7 @@ export class CollectionTimeView extends CollectionSubView() {
const fieldKey = Doc.LayoutFieldKey(doc);
doc[fieldKey + '-timelineCur'] = ComputedField.MakeFunction("(activePresentationItem()[this._pivotField || 'year'] || 0)");
}
- specificMenu = (e: React.MouseEvent) => {
+ specificMenu = () => {
const layoutItems: ContextMenuProps[] = [];
const doc = this.layoutDoc;
@@ -193,9 +197,9 @@ export class CollectionTimeView extends CollectionSubView() {
render() {
let nonNumbers = 0;
- this.childDocs.map(doc => {
+ this.childDocs.forEach(doc => {
const num = NumCast(doc[this.pivotField], Number(StrCast(doc[this.pivotField])));
- if (Number.isNaN(num)) {
+ if (isNaN(num)) {
nonNumbers++;
}
});
@@ -225,13 +229,20 @@ export class CollectionTimeView extends CollectionSubView() {
</>
)}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
- <FieldsDropdown Document={this.Document} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
+ <FieldsDropdown
+ Document={this.Document}
+ selectFunc={fieldKey => {
+ this.layoutDoc._pivotField = fieldKey;
+ }}
+ placeholder={StrCast(this.layoutDoc._pivotField)}
+ />
</div>
</div>
);
}
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
const pivotField = StrCast(pivotDoc._pivotField, 'author');
let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
@@ -245,9 +256,10 @@ ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBou
action(() => {
const filterVals = bounds.payload as string[];
filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, 'check'));
- const pivotView = DocumentManager.Instance.getDocumentView(pivotDoc);
+ const pivotView = DocumentView.getDocumentView(pivotDoc);
if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
+ // eslint-disable-next-line prefer-destructuring
pivotDoc._pivotField = filterVals[0];
}
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5741fc29b..c1247f5b0 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,19 +1,22 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { DivHeight, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils';
import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
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 } from '../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { DivHeight, emptyFunction, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero, Utils } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../util/DragManager';
+import { emptyFunction, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocUtils } from '../../documents/DocUtils';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -21,13 +24,14 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { DocumentView } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { StyleProp } from '../StyleProvider';
+import { StyleProp } from '../StyleProp';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTreeView.scss';
+import { TreeViewType } from './CollectionTreeViewType';
import { TreeView } from './TreeView';
+
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionTreeViewProps = {
@@ -44,12 +48,6 @@ export type collectionTreeViewProps = {
hierarchyIndex?: number[];
};
-export enum TreeViewType {
- outline = 'outline',
- fileSystem = 'fileSystem',
- default = 'default',
-}
-
@observer
export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() {
public static AddTreeFunc = 'addTreeFolder(this.embedContainer)';
@@ -92,8 +90,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
// these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
@observable _isAnyChildContentActive = false;
- whenChildContentsActiveChanged = action((isActive: boolean) => this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
- isContentActive = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this._props.isContentActive() ? true : false);
+ whenChildContentsActiveChanged = action((isActive: boolean) => {
+ this._props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive));
+ });
+ isContentActive = () => (this._isAnyChildContentActive ? true : !!this._props.isContentActive());
componentWillUnmount() {
this._isDisposing = true;
@@ -103,7 +103,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
componentDidMount() {
- //this._props.setContentView?.(this);
+ // this._props.setContentView?.(this);
this._disposers.autoheight = reaction(
() => this.layoutDoc.layout_autoHeight,
auto => auto && this.computeHeight(),
@@ -126,20 +126,19 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
observeHeight = (ref: any) => {
if (ref) {
this.refList.add(ref);
- this.observer = new _global.ResizeObserver(
- action((entries: any) => {
- if (this.layoutDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.IsDragging) {
- this.computeHeight();
- }
- })
- );
+ this.observer = new _global.ResizeObserver(() => {
+ if (this.layoutDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.IsDragging) {
+ this.computeHeight();
+ }
+ });
this.layoutDoc.layout_autoHeight && this.computeHeight();
this.observer.observe(ref);
}
};
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer?.();
- if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.Document, this.onInternalPreDrop.bind(this));
+ this._mainEle = ele;
+ if (ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.Document, this.onInternalPreDrop.bind(this));
};
protected onInternalDrop(e: Event, de: DragManager.DropEvent) {
@@ -167,39 +166,39 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
};
- dragConfig = (dragData: DragManager.DocumentDragData) => (dragData.treeViewDoc = this.Document);
+ dragConfig = (dragData: DragManager.DocumentDragData) => { dragData.treeViewDoc = this.Document; }; // prettier-ignore
screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, -this._headerHeight);
@action
- remove = (doc: Doc | Doc[]): boolean => {
- const docs = doc instanceof Doc ? [doc] : doc;
+ remove = (docIn: Doc | Doc[]): boolean => {
+ const docs = toList(docIn);
const targetDataDoc = this.Document[DocData];
const value = DocListCast(targetDataDoc[this._props.fieldKey]);
const result = value.filter(v => !docs.includes(v));
- if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll();
+ if (docs.some(doc => DocumentView.Selected().some(dv => Doc.AreProtosEqual(dv.Document, doc)))) DocumentView.DeselectAll();
if (result.length !== value.length) {
- if (doc instanceof Doc) {
- const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(doc);
+ if (docIn instanceof Doc) {
+ const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(docIn);
const prev = ind && DocListCast(targetDataDoc[this._props.fieldKey])[ind - 1];
- this._props.removeDocument?.(doc);
+ this._props.removeDocument?.(docIn);
if (ind > 0 && prev) {
- FormattedTextBox.SetSelectOnLoad(prev);
- DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false);
+ Doc.SetSelectOnLoad(prev);
+ DocumentView.getDocumentView(prev, this.DocumentView?.())?.select(false);
}
return true;
}
- return this._props.removeDocument?.(doc) ?? false;
+ return this._props.removeDocument?.(docIn) ?? false;
}
return false;
};
@action
addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
- const doclist = docs instanceof Doc ? [docs] : docs;
- const addDocRelativeTo = (doc: Doc | Doc[]) => doclist.reduce((flg, doc) => flg && Doc.AddDocToList(this.Document[DocData], this._props.fieldKey, doc, relativeTo, before), true);
+ 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;
- const res = relativeTo === undefined ? this._props.addDocument?.(docs) || false : addDocRelativeTo(docs);
+ const doclist = toList(docs);
+ const res = relativeTo === undefined ? this._props.addDocument?.(doclist) || false : addDocRelativeTo(doclist);
res &&
doclist.forEach(doc => {
Doc.SetContainer(doc, this.Document);
@@ -207,7 +206,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
});
return res;
};
- onContextMenu = (e: React.MouseEvent): void => {
+ 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;
@@ -215,11 +214,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
if (!Doc.noviceMode) {
layoutItems.push({
description: 'Make tree state ' + (this.Document.treeView_OpenIsTransient ? 'persistent' : 'transient'),
- event: () => (this.Document.treeView_OpenIsTransient = !this.Document.treeView_OpenIsTransient),
+ event: () => { this.Document.treeView_OpenIsTransient = !this.Document.treeView_OpenIsTransient; }, // prettier-ignore
icon: 'paint-brush',
});
- layoutItems.push({ description: (this.Document.treeView_HideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.Document.treeView_HideHeaderFields = !this.Document.treeView_HideHeaderFields), icon: 'paint-brush' });
- layoutItems.push({ description: (this.Document.treeView_HideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.Document.treeView_HideTitle = !this.Document.treeView_HideTitle), icon: 'paint-brush' });
+ layoutItems.push({ description: (this.Document.treeView_HideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => { this.Document.treeView_HideHeaderFields = !this.Document.treeView_HideHeaderFields; }, icon: 'paint-brush' }); // prettier-ignore
+ layoutItems.push({ description: (this.Document.treeView_HideTitle ? 'Show' : 'Hide') + ' Title', event: () => { this.Document.treeView_HideTitle = !this.Document.treeView_HideTitle; }, icon: 'paint-brush' }); // prettier-ignore
}
ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
if (!Doc.noviceMode) {
@@ -238,9 +237,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
return (
<EditableView
contents={this.dataDoc.title}
- display={'block'}
+ display="block"
maxHeight={72}
- height={'auto'}
+ height="auto"
GetValue={() => StrCast(this.dataDoc.title)}
SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
if (enter && this.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
@@ -251,22 +250,24 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
);
}
- onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+ onKey = (e: React.KeyboardEvent /* , fieldProps: FieldViewProps */) => {
if (this.outlineMode && e.key === 'Enter') {
e.stopPropagation();
this.makeTextCollection(this.treeChildren);
return true;
}
+ return undefined;
};
get documentTitle() {
return (
<FormattedTextBox
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
fieldKey="text"
renderDepth={this._props.renderDepth + 1}
isContentActive={this.isContentActive}
isDocumentActive={this.isContentActive}
- forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not layout_autoHeight
+ forceAutoHeight // needed to make the title resize even if the rest of the tree view is not layout_autoHeight
PanelWidth={this.documentTitleWidth}
PanelHeight={this.documentTitleHeight}
NativeDimScaling={returnOne}
@@ -292,9 +293,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
@computed get treeViewElements() {
TraceMobx();
const dragAction = StrCast(this.Document.childDragAction) as any as dropActionType;
- const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
+ const treeAddDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before);
const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this._props.moveDocument?.(d, target, addDoc) || false;
- if (this._renderCount < this.treeChildren.length) setTimeout(action(() => (this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20))));
+ if (this._renderCount < this.treeChildren.length)
+ setTimeout(
+ action(() => {
+ this._renderCount = Math.min(this.treeChildren.length, this._renderCount + 20);
+ })
+ );
return TreeView.GetChildElements(
this.treeChildren,
this,
@@ -303,7 +309,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
this._props.TemplateDataDocument,
undefined,
undefined,
- addDoc,
+ treeAddDoc,
this.remove,
moveDoc,
dragAction,
@@ -324,7 +330,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
this.observeHeight,
this.unobserveHeight,
this.childContextMenuItems(),
- //TODO: [AL] add these
this._props.AddToMap,
this._props.RemFromMap,
this._props.hierarchyIndex,
@@ -335,7 +340,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
return this.dataDoc === null ? null : (
<div
className="collectionTreeView-titleBar"
- ref={action((r: any) => (this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.ScreenToLocalBoxXf().Scale))}
+ ref={action((r: any) => {
+ (this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.ScreenToLocalBoxXf().Scale);
+ })}
key={this.Document[Id]}
style={!this.outlineMode ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}}>
{this.outlineMode ? this.documentTitle : this.editableTitle}
@@ -406,8 +413,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this._props.fieldKey}_annotations`) || false;
remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this._props.fieldKey}_annotations`) || false;
- moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
- this.moveDocument(doc, targetCollection, addDocument, `${this._props.fieldKey}_annotations`) || false;
+ moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) => this.moveDocument(doc, targetCollection, addDocument) || false;
@observable _headerHeight = 0;
@computed get content() {
@@ -418,7 +424,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
{!this.buttonMenu && !this.noviceExplainer ? null : (
- <div className="documentButtonMenu" ref={action((r: HTMLDivElement | null) => r && (this._headerHeight = DivHeight(r)))}>
+ <div
+ className="documentButtonMenu"
+ ref={action((r: HTMLDivElement | null) => {
+ r && (this._headerHeight = DivHeight(r));
+ })}>
{this.buttonMenu}
{this.noviceExplainer}
</div>
@@ -451,7 +461,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
minHeight: '100%',
}}
onWheel={e => e.stopPropagation()}
- onClick={e => (!this.layoutDoc.forceActive ? this._props.select(false) : SelectionManager.DeselectAll())}
+ onClick={() => (!this.layoutDoc.forceActive ? this._props.select(false) : DocumentView.DeselectAll())}
onDrop={this.onTreeDrop}>
<ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
</div>
@@ -468,13 +478,14 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
<div 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}
NativeHeight={returnZero}
pointerEvents={this._props.isContentActive() && SnappingManager.IsDragging ? returnAll : returnNone}
- isAnnotationOverlay={true}
- isAnnotationOverlayScrollable={true}
+ isAnnotationOverlay
+ isAnnotationOverlayScrollable
childDocumentsActive={this._props.isContentActive}
fieldKey={this._props.fieldKey + '_annotations'}
dropAction={dropActionType.move}
@@ -498,6 +509,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
}
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function addTreeFolder(doc: Doc) {
CollectionTreeView.addTreeFolder(doc);
});
diff --git a/src/client/views/collections/CollectionTreeViewType.ts b/src/client/views/collections/CollectionTreeViewType.ts
new file mode 100644
index 000000000..53b88160e
--- /dev/null
+++ b/src/client/views/collections/CollectionTreeViewType.ts
@@ -0,0 +1,5 @@
+export enum TreeViewType {
+ outline = 'outline',
+ fileSystem = 'fileSystem',
+ default = 'default',
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 18eb4dd1f..7cadd072b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,21 +1,21 @@
+/* eslint-disable react/jsx-props-no-spreading */
import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { returnEmptyString } from '../../../Utils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { returnEmptyString } from '../../../ClientUtils';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { ObjectField } from '../../../fields/ObjectField';
-import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { CollectionViewType } from '../../documents/DocumentTypes';
-import { DocUtils } from '../../documents/Documents';
-import { dropActionType } from '../../util/DragManager';
+import { DocUtils } from '../../documents/DocUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { Docs } from '../../documents/Documents';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
-import { OpenWhere } from '../nodes/DocumentView';
-import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import { FieldView } from '../nodes/FieldView';
+import { OpenWhere } from '../nodes/OpenWhere';
import { CollectionCalendarView } from './CollectionCalendarView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
@@ -23,7 +23,7 @@ import { CollectionDockingView } from './CollectionDockingView';
import { CollectionNoteTakingView } from './CollectionNoteTakingView';
import { CollectionPileView } from './CollectionPileView';
import { CollectionStackingView } from './CollectionStackingView';
-import { SubCollectionViewProps } from './CollectionSubView';
+import { CollectionViewProps, SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
@@ -33,36 +33,7 @@ import { CollectionLinearView } from './collectionLinear';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
-export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
- isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
- isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
- layoutEngine?: () => string;
- setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => void;
- ignoreUnrendered?: boolean;
- // property overrides for child documents
- childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
- childDocumentsActive?: () => boolean | undefined; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
- childContentsActive?: () => boolean | undefined;
- childLayoutFitWidth?: (child: Doc) => boolean;
- childlayout_showTitle?: () => string;
- childOpacity?: () => number;
- childContextMenuItems?: () => { script: ScriptField; label: string }[];
- childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
- childHideDecorationTitle?: boolean;
- childHideResizeHandles?: boolean;
- childDragAction?: dropActionType;
- childXPadding?: number;
- childYPadding?: number;
- childLayoutString?: string;
- childIgnoreNativeSize?: boolean;
- childClickScript?: ScriptField;
- childDoubleClickScript?: ScriptField;
- //TODO: [AL] add these fields
- AddToMap?: (treeViewDoc: Doc, index: number[]) => void;
- RemFromMap?: (treeViewDoc: Doc, index: number[]) => void;
- hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
-}
@observer
export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() implements ViewBoxInterface {
public static LayoutString(fieldStr: string) {
@@ -89,7 +60,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
// this a reaction, downstream invalidations only occur when the reaction value actually changes.
this.reactionDisposer = reaction(
() => (this.isAnyChildContentActive() ? true : this._props.isContentActive()),
- active => (this._isContentActive = active),
+ active => {
+ this._isContentActive = active;
+ },
{ fireImmediately: true }
);
}
@@ -98,17 +71,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
}
get collectionViewType(): CollectionViewType | undefined {
- const viewField = StrCast(this.layoutDoc._type_collection);
+ const viewField = StrCast(this.layoutDoc._type_collection) as any as CollectionViewType;
if (CollectionView._safeMode) {
- switch (viewField) {
+ switch (viewField) {
case CollectionViewType.Freeform:
- case CollectionViewType.Schema:
- return CollectionViewType.Tree;
- case CollectionViewType.Invalid:
- return CollectionViewType.Freeform;
- }
+ case CollectionViewType.Schema: return CollectionViewType.Tree;
+ case CollectionViewType.Invalid: return CollectionViewType.Freeform;
+ default:
+ } // prettier-ignore
}
- return viewField as any as CollectionViewType;
+ return viewField;
}
screenToLocalTransform = () => (this._props.renderDepth ? this.ScreenToLocalBoxXf() : this.ScreenToLocalBoxXf().scale(this._props.PanelWidth() / this.bodyPanelWidth()));
@@ -117,8 +89,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
TraceMobx();
if (type === undefined) return null;
switch (type) {
- default:
- case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
case CollectionViewType.Calendar: return <CollectionCalendarView key="collview" {...props} />;
case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
@@ -134,6 +104,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
+ case CollectionViewType.Freeform:
+ default: return <CollectionFreeFormView key="collview" {...props} />;
}
};
@@ -144,9 +116,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
{ description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' },
{ description: 'Schema', event: () => func(CollectionViewType.Schema), icon: 'th-list' },
{ description: 'Tree', event: () => func(CollectionViewType.Tree), icon: 'tree' },
- { description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._layout_autoHeight = true), icon: 'ellipsis-v' },
+ { description: 'Stacking', event: () => {func(CollectionViewType.Stacking)._layout_autoHeight = true}, icon: 'ellipsis-v' },
{ description: 'Calendar', event: () => func(CollectionViewType.Calendar), icon: 'columns'},
- { description: 'Notetaking', event: () => (func(CollectionViewType.NoteTaking)._layout_autoHeight = true), icon: 'ellipsis-v' },
+ { description: 'Notetaking', event: () => {func(CollectionViewType.NoteTaking)._layout_autoHeight = true}, icon: 'ellipsis-v' },
{ description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' },
{ description: 'Multirow', event: () => func(CollectionViewType.Multirow), icon: 'columns' },
{ description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' },
@@ -178,14 +150,14 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
- !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.Document.forceActive = !this.Document.forceActive), icon: 'project-diagram' }) : null;
+ !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.Document.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => {this.Document.forceActive = !this.Document.forceActive}, icon: 'project-diagram' }) : null; // prettier-ignore
if (this.Document.childLayout instanceof Doc) {
optionItems.push({ description: 'View Child Layout', event: () => this._props.addDocTab(this.Document.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' });
}
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' });
+ !Doc.noviceMode && optionItems.push({ description: `${this.layoutDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => { this.layoutDoc._isLightbox = !this.layoutDoc._isLightbox; }, icon: 'project-diagram' }); // prettier-ignore
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
@@ -200,7 +172,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
onClicks.push({
description: `Edit ${func.name} script`,
icon: 'edit',
- event: (obj: any) => {
+ event: () => {
const embedding = Doc.MakeEmbedding(this.Document);
DocUtils.makeCustomViewClicked(embedding, undefined, func.key);
this._props.addDocTab(embedding, OpenWhere.addRight);
@@ -211,7 +183,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
onClicks.push({
description: `Set child ${childClick.title}`,
icon: 'edit',
- event: () => (this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
+ 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' });
@@ -229,7 +203,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
bodyPanelWidth = () => this._props.PanelWidth();
childLayoutTemplate = () => this._props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null);
- isContentActive = (outsideReaction?: boolean) => this._isContentActive;
+ isContentActive = () => this._isContentActive;
pointerEvents = () =>
this.layoutDoc._lockedPosition && //
@@ -261,3 +235,19 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
);
}
}
+
+Docs.Prototypes.TemplateMap.set(DocumentType.COL, {
+ layout: { view: CollectionView, dataField: 'data' },
+ options: {
+ acl: '',
+ _layout_fitWidth: true,
+ freeform: '',
+ _freeform_panX: 0,
+ _freeform_panY: 0,
+ _freeform_scale: 1,
+ _layout_nativeDimEditable: true,
+ _layout_reflowHorizontal: true,
+ _layout_reflowVertical: true,
+ systemIcon: 'BsFillCollectionFill',
+ },
+});
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index 4523a4f1e..7dc08389b 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/button-has-type */
import { observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -35,11 +36,36 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
return (
<div className="collectionViewBaseChrome-viewSpecsMenu-row">
- <input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft" value={this._key} onChange={e => runInAction(() => (this._key = e.target.value))} placeholder="KEY" />
- <button className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle" style={{ background: this._contains ? '#77dd77' : '#ff6961' }} onClick={() => runInAction(() => (this._contains = !this._contains))}>
+ <input
+ className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"
+ value={this._key}
+ onChange={e =>
+ runInAction(() => {
+ this._key = e.target.value;
+ })
+ }
+ placeholder="KEY"
+ />
+ <button
+ className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
+ style={{ background: this._contains ? '#77dd77' : '#ff6961' }}
+ onClick={() =>
+ runInAction(() => {
+ this._contains = !this._contains;
+ })
+ }>
{this._contains ? 'CONTAINS' : 'DOES NOT CONTAIN'}
</button>
- <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight" value={this._value} onChange={e => runInAction(() => (this._value = e.target.value))} placeholder="VALUE" />
+ <input
+ className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
+ value={this._value}
+ onChange={e =>
+ runInAction(() => {
+ this._value = e.target.value;
+ })
+ }
+ placeholder="VALUE"
+ />
</div>
);
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 5cb7b149b..2d8b2564d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -6,40 +6,182 @@ import { IReactionDisposer, ObservableSet, action, computed, makeObservable, obs
import { observer } from 'mobx-react';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
-import { DashColor, Utils, emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../Utils';
+import { ClientUtils, DashColor, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData } from '../../../fields/DocSymbols';
+import { DocData, DocViews } 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, DocCast, NumCast, StrCast } 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';
-import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { SettingsManager } from '../../util/SettingsManager';
+import { DragManager } from '../../util/DragManager';
+import { dropActionType } from '../../util/DropActionTypes';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { UndoManager, undoable } from '../../util/UndoManager';
import { DashboardView } from '../DashboardView';
import { LightboxView } from '../LightboxView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
+import { PinDocView, PinProps } from '../PinFuncs';
+import { StyleProp } from '../StyleProp';
+import { DefaultStyleProvider } from '../StyleProvider';
import { Colors } from '../global/globalEnums';
-import { DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../nodes/FieldView';
+import { DocumentView, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
import { KeyValueBox } from '../nodes/KeyValueBox';
-import { DashFieldView } from '../nodes/formattedText/DashFieldView';
-import { PinProps, PresBox, PresMovement } from '../nodes/trails';
+import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
+import { PresBox, PresMovement } from '../nodes/trails';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionView } from './CollectionView';
import './TabDocView.scss';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+
const _global = (window /* browser */ || global) /* node */ as any;
+interface TabMinimapViewProps {
+ document: Doc;
+ tabView: () => DocumentView | undefined;
+ addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ background: () => string;
+}
+interface TabMiniThumbProps {
+ miniWidth: () => number;
+ miniHeight: () => number;
+ miniTop: () => number;
+ miniLeft: () => number;
+}
+
+@observer
+class TabMiniThumb extends React.Component<TabMiniThumbProps> {
+ render() {
+ const { miniWidth, miniHeight, miniLeft, miniTop } = this.props;
+ return <div className="miniThumb" style={{ width: `${miniWidth()}%`, height: `${miniHeight()}%`, left: `${miniLeft()}%`, top: `${miniTop()}%` }} />;
+ }
+}
+@observer
+export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> {
+ static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
+ if (doc) {
+ switch (property.split(':')[0]) {
+ case StyleProp.PointerEvents: return 'none';
+ case StyleProp.DocContents: {
+ const background = (() => {
+ switch (doc.type as DocumentType) {
+ case DocumentType.PDF: return 'pink';
+ case DocumentType.AUDIO: return 'lightgreen';
+ case DocumentType.WEB: return 'brown';
+ case DocumentType.IMG: return 'blue';
+ case DocumentType.MAP: return 'orange';
+ case DocumentType.VID: return 'purple';
+ case DocumentType.RTF: return 'yellow';
+ case DocumentType.COL: return undefined;
+ default: return 'gray';
+ } // prettier-ignore
+ })();
+ return !background ? undefined : <div style={{ width: NumCast(doc._width), height: NumCast(doc._height), position: 'absolute', display: 'block', background }} />;
+ }
+ default: return DefaultStyleProvider(doc, props, property);
+ } // prettier-ignore
+ }
+ return undefined;
+ };
+
+ @computed get renderBounds() {
+ const compView = this._props.tabView()?.ComponentView as CollectionFreeFormView;
+ const bounds = compView?.freeformData?.(true)?.bounds;
+ if (!bounds) return undefined;
+ const xbounds = bounds.r - bounds.x;
+ const ybounds = bounds.b - bounds.y;
+ const dim = Math.max(xbounds, ybounds);
+ return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, 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));
+ }
+ @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));
+ }
+ childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
+ returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
+ miniDown = (e: React.PointerEvent) => {
+ const doc = this._props.document;
+ const miniSize = this.returnMiniSize();
+ doc &&
+ setupMoveUpEvents(
+ this,
+ e,
+ action((moveEv, down: number[], delta: number[]) => {
+ const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
+ doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
+ doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
+ return false;
+ }),
+ emptyFunction,
+ emptyFunction
+ );
+ };
+ popup = () => {
+ if (!this.renderBounds) return <div />;
+ const { renderBounds } = this;
+ 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 miniSize = this.returnMiniSize();
+ return (
+ <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
+ <CollectionFreeFormView
+ Document={this._props.document}
+ 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
+ isContentActive={emptyFunction}
+ isAnyChildContentActive={returnFalse}
+ select={emptyFunction}
+ isSelected={returnFalse}
+ dontRegisterView
+ fieldKey={Doc.LayoutFieldKey(this._props.document)}
+ addDocument={returnFalse}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ PanelWidth={this.returnMiniSize}
+ PanelHeight={this.returnMiniSize}
+ ScreenToLocalTransform={Transform.Identity}
+ renderDepth={0}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ styleProvider={TabMinimapView.miniStyleProvider}
+ addDocTab={this._props.addDocTab}
+ // eslint-disable-next-line no-use-before-define
+ pinToPres={TabDocView.PinDoc}
+ childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+ childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+ searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
+ fitContentsToBox={returnTrue}
+ xPadding={this.xPadding}
+ yPadding={this.yPadding}
+ />
+ <div className="miniOverlay" onPointerDown={this.miniDown}>
+ <TabMiniThumb miniLeft={miniLeft} miniTop={miniTop} miniWidth={miniWidth} miniHeight={miniHeight} />
+ </div>
+ </div>
+ );
+ };
+ render() {
+ return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._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>
+ );
+ }
+}
+
interface TabDocViewProps {
documentId: FieldId;
keyValue?: boolean;
@@ -51,9 +193,101 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
_mainCont: HTMLDivElement | null = null;
_tabReaction: IReactionDisposer | undefined;
+ /**
+ * Adds a document to the presentation view
+ * */
+ @action
+ public static PinDoc(docIn: Doc | Doc[], pinProps: PinProps) {
+ 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);
+
+ if (!Doc.ActivePresentation) {
+ Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
+ Doc.ActivePresentation = curPres;
+ }
+
+ docs.forEach(doc => {
+ // Edge Case 1: Cannot pin document to itself
+ if (doc === curPres) {
+ alert('Cannot pin presentation document to itself');
+ return;
+ }
+ const anchorDoc = DocumentView.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
+ const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({});
+ const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc);
+ pinDoc.title = doc.title + ' - Slide';
+ pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
+ pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
+ pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000;
+ pinDoc.presentation_groupWithUp = false;
+ Doc.SetContainer(pinDoc, curPres);
+ // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
+ pinDoc.treeView = ''; // not really needed, but makes key value pane look better
+ pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+ pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
+ 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);
+
+ if (pinProps.pinViewport) PinDocView(pinDoc, pinProps, anchorDoc ?? doc);
+ if (!pinProps?.audioRange && duration !== undefined) {
+ pinDoc.presentation_mediaStart = 'manual';
+ pinDoc.presentation_mediaStop = 'manual';
+ }
+ if (pinProps?.activeFrame !== undefined) {
+ pinDoc.config_activeFrame = pinProps?.activeFrame;
+ pinDoc.title = doc.title + ' (move)';
+ pinDoc.presentation_movement = PresMovement.Pan;
+ }
+ if (pinProps?.currentFrame !== undefined) {
+ pinDoc.config_currentFrame = pinProps?.currentFrame;
+ pinDoc.title = doc.title + ' (move)';
+ pinDoc.presentation_movement = PresMovement.Pan;
+ }
+ if (pinDoc.stroke_isInkMask) {
+ pinDoc.presentation_hideAfter = true;
+ pinDoc.presentation_hideBefore = true;
+ pinDoc.presentation_movement = PresMovement.None;
+ }
+ if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true;
+ Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement());
+ PresBox.Instance?.clearSelectedArray();
+ pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); // Update selected array
+ });
+ if (
+ // open the presentation trail if it's not already opened
+ !Array.from(CollectionDockingView.Instance?.tabMap ?? [])
+ .map(d => d.DashDoc)
+ .includes(curPres)
+ ) {
+ if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres);
+ CollectionDockingView.AddSplit(curPres, OpenWhereMod.right);
+ setTimeout(() => DocumentView.showDocument(docs.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things
+ }
+ setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
+ }
+
+ static Activate = (tabDoc: Doc) => {
+ const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(findTab => findTab.DashDoc === tabDoc && !findTab.contentItem.config.props.keyValue);
+ tab?.header.parent.setActiveContentItem(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)
+ return tab !== undefined;
+ };
+ // static ActivateTabView(doc: Doc) {
+ // const tabView = Array.from(TabDocView._allTabs).find(view => view._document === doc);
+ // if (!tabView?._activated && tabView?._document) {
+ // TabDocView.Activate(tabView?._document);
+ // return tabView;
+ // }
+ // return undefined;
+ // }
constructor(props: any) {
super(props);
makeObservable(this);
+ DocumentView.activateTabView = TabDocView.Activate;
+ DocumentView.PinDoc = TabDocView.PinDoc;
}
@observable _activated: boolean = false;
@@ -62,8 +296,14 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
@observable _hovering = false;
@observable _isActive: boolean = false;
@observable _isAnyChildContentActive = false;
+ public static IsSelected = (doc?: Doc) => {
+ if (Array.from(doc?.[DocViews] ?? []).some(dv => dv?.IsSelected)) {
+ return true;
+ }
+ return false;
+ };
@computed get _isUserActivated() {
- return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive;
+ return TabDocView.IsSelected(this._document) || this._isAnyChildContentActive;
}
get _isContentActive() {
return this._isUserActivated || this._hovering;
@@ -90,7 +330,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
@action
init = (tab: any, doc: Opt<Doc>) => {
if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true;
- if (tab.DashDoc !== doc && doc && tab.hasOwnProperty('contentItem') && tab.contentItem.config.type !== 'stack') {
+ if (tab.DashDoc !== doc && doc && tab.contentItem?.config.type !== 'stack') {
tab._disposers = {} as { [name: string]: IReactionDisposer };
tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
tab.DashDoc = doc;
@@ -105,7 +345,6 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
while (child?.children.length) {
const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
- if (next?.className?.toString().includes(DashFieldView.name)) break;
if (next) child = next;
else break;
}
@@ -129,37 +368,37 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
setupMoveUpEvents(
this,
e,
- e =>
- !e.defaultPrevented &&
- DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY, undefined, () => {
+ moveEv =>
+ !moveEv.defaultPrevented &&
+ DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), moveEv.clientX, moveEv.clientY, undefined, () => {
CollectionDockingView.CloseSplit(doc);
}),
returnFalse,
- action(e => {
+ action(clickEv => {
if (this.view) {
- SelectionManager.SelectView(this.view, false);
+ DocumentView.SelectView(this.view, false);
const child = getChild();
- simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ simulateMouseClick(child, clickEv.clientX, clickEv.clientY + 30, clickEv.screenX, clickEv.screenY + 30);
} else {
this._activated = true;
- setTimeout(() => this.view && SelectionManager.SelectView(this.view, false));
+ setTimeout(() => this.view && DocumentView.SelectView(this.view, false));
}
})
);
};
const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
- const closeIcon = <FontAwesomeIcon icon={'eye'} />;
+ const closeIcon = <FontAwesomeIcon icon="eye" />;
ReactDOM.createRoot(iconWrap).render(docIcon);
ReactDOM.createRoot(closeWrap).render(closeIcon);
tab.reactComponents = [iconWrap, closeWrap];
tab.element[0].prepend(iconWrap);
tab._disposers.color = reaction(
- () => ({ variant: SettingsManager.userVariantColor, degree: Doc.GetBrushStatus(doc), highlight: DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting) }),
+ () => ({ variant: SnappingManager.userVariantColor, degree: Doc.GetBrushStatus(doc), highlight: DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting) }),
({ variant, degree, highlight }) => {
const color = highlight?.highlightIndex === Doc.DocBrushStatus.highlighted ? highlight.highlightColor : degree ? ['transparent', variant, variant, 'orange'][degree] : variant;
- const textColor = color === variant ? SettingsManager.userColor : lightOrDark(color);
+ const textColor = color === variant ? SnappingManager.userColor ?? '' : lightOrDark(color);
titleEle.style.color = textColor;
iconWrap.style.color = textColor;
closeWrap.style.color = textColor;
@@ -185,19 +424,19 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
);
}
// shifts the focus to this tab when another tab is dragged over it
- tab.element[0].onmouseenter = (e: MouseEvent) => {
+ tab.element[0].onmouseenter = () => {
if (SnappingManager.IsDragging && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
tab.header.parent.setActiveContentItem(tab.contentItem);
tab.setActive(true);
}
this._document && Doc.BrushDoc(this._document);
};
- tab.element[0].onmouseleave = (e: MouseEvent) => {
+ tab.element[0].onmouseleave = () => {
this._document && Doc.UnBrushDoc(this._document);
};
tab.element[0].oncontextmenu = (e: MouseEvent) => {
- let child = getChild();
+ const child = getChild();
if (child) {
simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
e.stopPropagation();
@@ -208,7 +447,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
// select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
titleEle.onpointerdown = action((e: any) => {
if (e.target.className !== 'lm_iconWrap') {
- if (this.view) SelectionManager.SelectView(this.view, false);
+ if (this.view) DocumentView.SelectView(this.view, false);
else this._activated = true;
if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
titleEle.lastClick = Date.now();
@@ -216,15 +455,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
}
});
tab._disposers.selectionDisposer = reaction(
- () => SelectionManager.IsSelected(this._document),
+ () => TabDocView.IsSelected(this._document),
action(selected => {
if (selected) this._activated = true;
- const toggle = tab.element[0].children[2].children[0] as HTMLInputElement;
if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
undoable(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch')();
}
- //toggle.style.fontWeight = selected ? 'bold' : '';
- // toggle.style.textTransform = selected ? "uppercase" : "";
}),
{ fireImmediately: true }
);
@@ -232,101 +468,27 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
// highlight the tab when the tab document is brushed in any part of the UI
tab._disposers.reactionDisposer = reaction(
() => doc?.title,
- title => (titleEle.value = title),
+ title => {
+ titleEle.value = title;
+ },
{ fireImmediately: true }
);
// clean up the tab when it is closed
tab.closeElement
- .off('click') //unbind the current click handler
- .click(function () {
+ .off('click') // unbind the current click handler
+ .click(() => {
Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
UndoManager.RunInBatch(() => tab.contentItem.remove(), 'delete tab');
});
}
};
- /**
- * Adds a document to the presentation view
- **/
- @action
- public static PinDoc(docs: Doc | Doc[], pinProps: PinProps) {
- const docList = docs instanceof Doc ? [docs] : docs;
-
- const batch = UndoManager.StartBatch('Pin doc to pres trail');
- const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
-
- if (!Doc.ActivePresentation) {
- Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
- Doc.ActivePresentation = curPres;
- }
-
- docList.forEach(doc => {
- // Edge Case 1: Cannot pin document to itself
- if (doc === curPres) {
- alert('Cannot pin presentation document to itself');
- return;
- }
- const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
- const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({});
- const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc);
- pinDoc.title = doc.title + ' - Slide';
- pinDoc.data = targDoc.type === DocumentType.PRES ? ComputedField.MakeFunction('copyField(this.presentation_targetDoc.data') : new List<Doc>(); // the children of the embedding's layout are the presentation slide children. the embedding's data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
- pinDoc.presentation_movement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
- pinDoc.presentation_duration = pinDoc.presentation_duration ?? 1000;
- pinDoc.presentation_groupWithUp = false;
- Doc.SetContainer(pinDoc, curPres);
- // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
- pinDoc.treeView = ''; // not really needed, but makes key value pane look better
- pinDoc.treeView_RenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
- pinDoc.treeView_HeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
- 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);
-
- if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
- if (!pinProps?.audioRange && duration !== undefined) {
- pinDoc.presentation_mediaStart = 'manual';
- pinDoc.presentation_mediaStop = 'manual';
- }
- if (pinProps?.activeFrame !== undefined) {
- pinDoc.config_activeFrame = pinProps?.activeFrame;
- pinDoc.title = doc.title + ' (move)';
- pinDoc.presentation_movement = PresMovement.Pan;
- }
- if (pinProps?.currentFrame !== undefined) {
- pinDoc.config_currentFrame = pinProps?.currentFrame;
- pinDoc.title = doc.title + ' (move)';
- pinDoc.presentation_movement = PresMovement.Pan;
- }
- if (pinDoc.stroke_isInkMask) {
- pinDoc.presentation_hideAfter = true;
- pinDoc.presentation_hideBefore = true;
- pinDoc.presentation_movement = PresMovement.None;
- }
- if (curPres.expandBoolean) pinDoc.presentation_expandInlineButton = true;
- Doc.AddDocToList(curPres, 'data', pinDoc, PresBox.Instance?.sortArray()?.lastElement());
- PresBox.Instance?.clearSelectedArray();
- pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
- });
- if (
- // open the presentation trail if it's not already opened
- !Array.from(CollectionDockingView.Instance?.tabMap ?? [])
- .map(d => d.DashDoc)
- .includes(curPres)
- ) {
- if (Doc.IsInMyOverlay(curPres)) Doc.RemFromMyOverlay(curPres);
- CollectionDockingView.AddSplit(curPres, OpenWhereMod.right);
- setTimeout(() => DocumentManager.Instance.showDocument(docList.lastElement(), { willPan: true }), 100); // keeps the pinned doc in view since the sidebar shifts things
- }
- setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
- }
-
componentDidMount() {
new _global.ResizeObserver(
action((entries: any) => {
+ // eslint-disable-next-line no-restricted-syntax
for (const entry of entries) {
this._panelWidth = entry.contentRect.width;
this._panelHeight = entry.contentRect.height;
@@ -342,12 +504,12 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
}
componentDidUpdate(prevProps: Readonly<TabDocViewProps>) {
super.componentDidUpdate(prevProps);
- this._view && DocumentManager.Instance.AddView(this._view);
+ this._view && DocumentView.addView(this._view);
}
componentWillUnmount() {
this._tabReaction?.();
- this._view && DocumentManager.Instance.RemoveView(this._view);
+ this._view && DocumentView.removeView(this._view);
runInAction(() => TabDocView._allTabs.delete(this));
this._props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
@@ -361,7 +523,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
private onActiveContentItemChanged(contentItem: any) {
if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
- if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => SelectionManager.SelectView(this._view, false));
+ if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => DocumentView.SelectView(this._view, false));
!this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
}
}
@@ -373,50 +535,45 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
// "replace:right" - will replace the stack on the right named "right" if it exists, or create a stack on the right with that name,
// "replace:monkeys" - will replace any tab that has the label 'monkeys', or a tab with that label will be created by default on the right
// lightbox - will add the document to any collection along the path from the document to the docking view that has a field isLightbox. if none is found, it adds to the full screen lightbox
- addDocTab = (doc: Doc, location: OpenWhere) => {
- SelectionManager.DeselectAll();
+ addDocTab = (docsIn: Doc | Doc[], location: OpenWhere) => {
+ const docs = toList(docsIn);
+ DocumentView.DeselectAll();
const whereFields = location.split(':');
const keyValue = whereFields.includes(OpenWhereMod.keyvalue);
const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none;
const panelName = whereFields.length > 1 ? whereFields.lastElement() : '';
- if (doc.dockingConfig && !keyValue) return DashboardView.openDashboard(doc);
+ if (docs[0]?.dockingConfig && !keyValue) return DashboardView.openDashboard(docs[0]);
// prettier-ignore
switch (whereFields[0]) {
case undefined:
case OpenWhere.lightbox: if (this.layoutDoc?._isLightbox) {
- const lightboxView = !doc.annotationOn && DocCast(doc.embedContainer) ? DocumentManager.Instance.getFirstDocumentView(DocCast(doc.embedContainer)) : undefined;
+ const lightboxView = !docs[0].annotationOn && DocCast(docs[0].embedContainer) ? DocumentView.getFirstDocumentView(DocCast(docs[0].embedContainer)) : undefined;
const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)];
if (lightboxView && (!data || data instanceof List)) {
- lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>([doc]);
+ lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>(docs);
return true;
}
}
- return LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
- case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods);
- case OpenWhere.replace: return CollectionDockingView.ReplaceTab(doc, whereMods, this.stack, panelName, undefined, keyValue);
- case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, TabDocView.DontSelectOnActivate, keyValue);
- case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods, this.stack, undefined, keyValue);
+ return LightboxView.Instance.AddDocTab(docs[0], OpenWhere.lightbox);
+ case OpenWhere.close: return CollectionDockingView.CloseSplit(docs[0], whereMods);
+ case OpenWhere.replace: return CollectionDockingView.ReplaceTab(docs[0], whereMods, this.stack, panelName, undefined, keyValue);
+ case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(docs[0], whereMods, this.stack, TabDocView.DontSelectOnActivate, keyValue);
+ case OpenWhere.add:default:return CollectionDockingView.AddSplit(docs[0], whereMods, this.stack, undefined, keyValue);
}
};
remDocTab = (doc: Doc | Doc[]) => {
if (doc === this._document) {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
CollectionDockingView.CloseSplit(this._document);
return true;
}
return false;
};
- getCurrentFrame = () => {
- return NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
- };
- static Activate = (tabDoc: Doc) => {
- const tab = Array.from(CollectionDockingView.Instance?.tabMap!).find(tab => tab.DashDoc === tabDoc && !tab.contentItem.config.props.keyValue);
- tab?.header.parent.setActiveContentItem(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)
- return tab !== undefined;
- };
+ getCurrentFrame = () => NumCast(Cast(PresBox.Instance.activeItem.presentation_targetDoc, Doc, null)._currentFrame);
+
@action
- focusFunc = (doc: Doc, options: FocusViewOptions) => {
+ 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)
}
@@ -426,7 +583,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
@observable _forceInvalidateScreenToLocal = 0;
ScreenToLocalTransform = () => {
this._forceInvalidateScreenToLocal;
- const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
+ const { translateX, translateY } = ClientUtils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
return CollectionDockingView.Instance?.ScreenToLocalBoxXf().translate(-translateX, -translateY) ?? Transform.Identity();
};
PanelWidth = () => this._panelWidth;
@@ -434,7 +591,9 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
miniMapColor = () => Colors.MEDIUM_GRAY;
tabView = () => this._view;
disableMinimap = () => !this._document;
- whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
+ whenChildContentActiveChanges = (isActive: boolean) => {
+ this._isAnyChildContentActive = isActive;
+ };
isContentActive = () => this._isContentActive;
waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined);
@computed get docView() {
@@ -443,7 +602,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
<DocumentView
key={this._document[Id]}
ref={action((r: DocumentView) => {
- this._lastView && DocumentManager.Instance.RemoveView(this._lastView);
+ this._lastView && DocumentView.removeView(this._lastView);
this._view = r;
this._lastView = this._view;
})}
@@ -452,7 +611,6 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
hideTitle={this._props.keyValue}
Document={this._document}
TemplateDataDocument={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
- onBrowseClickScript={DocumentView.exploreMode}
waitForDoubleClickToClick={this.waitForDoubleClick}
isContentActive={this.isContentActive}
isDocumentActive={returnFalse}
@@ -465,9 +623,9 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
addDocument={undefined}
removeDocument={this.remDocTab}
addDocTab={this.addDocTab}
- suppressSetHeight={this._document._layout_fitWidth ? true : false}
+ suppressSetHeight={!!this._document._layout_fitWidth}
ScreenToLocalTransform={this.ScreenToLocalTransform}
- dontCenter={'y'}
+ dontCenter="y"
whenChildContentsActiveChanged={this.whenChildContentActiveChanges}
focus={this.focusFunc}
containerViewPath={returnEmptyDoclist}
@@ -485,19 +643,24 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
style={{
fontFamily: Doc.UserDoc().renderStyle === 'comic' ? 'Comic Sans MS' : undefined,
}}
- onPointerOver={action(() => (this._hovering = true))}
- onPointerLeave={action(() => (this._hovering = false))}
- onDragOver={action(() => (this._hovering = true))}
- onDragLeave={action(() => (this._hovering = false))}
+ onPointerOver={action(() => { this._hovering = true; })} // prettier-ignore
+ onPointerLeave={action(() => { this._hovering = false; })} // prettier-ignore
+ onDragOver={action(() => { this._hovering = true; })} // prettier-ignore
+ onDragLeave={action(() => { this._hovering = false; })} // prettier-ignore
ref={ref => {
- if ((this._mainCont = ref)) {
+ this._mainCont = ref;
+ if (this._mainCont) {
if (this._lastTab) {
- this._view && DocumentManager.Instance.RemoveView(this._view);
+ this._view && DocumentView.removeView(this._view);
}
this._lastTab = this.tab;
(this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
- DocServer.GetRefField(this._props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
- new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
+ DocServer.GetRefField(this._props.documentId).then(
+ action(doc => {
+ doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document);
+ })
+ );
+ new _global.ResizeObserver(action(() => this._forceInvalidateScreenToLocal++)).observe(ref);
}
}}>
{this.docView}
@@ -505,142 +668,3 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
);
}
}
-
-interface TabMinimapViewProps {
- document: Doc;
- tabView: () => DocumentView | undefined;
- addDocTab: (doc: Doc, where: OpenWhere) => boolean;
- PanelWidth: () => number;
- PanelHeight: () => number;
- background: () => string;
-}
-interface TabMiniThumbProps {
- miniWidth: () => number;
- miniHeight: () => number;
- miniTop: () => number;
- miniLeft: () => number;
-}
-
-@observer
-class TabMiniThumb extends React.Component<TabMiniThumbProps> {
- render() {
- return <div className="miniThumb" style={{ width: `${this.props.miniWidth()}% `, height: `${this.props.miniHeight()}% `, left: `${this.props.miniLeft()}% `, top: `${this.props.miniTop()}% ` }} />;
- }
-}
-@observer
-export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> {
- static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
- if (doc) {
- switch (property.split(':')[0]) {
- default:
- return DefaultStyleProvider(doc, props, property);
- case StyleProp.PointerEvents:
- return 'none';
- case StyleProp.DocContents:
- const background = ((type: DocumentType) => {
- // prettier-ignore
- switch (type) {
- case DocumentType.PDF: return 'pink';
- case DocumentType.AUDIO: return 'lightgreen';
- case DocumentType.WEB: return 'brown';
- case DocumentType.IMG: return 'blue';
- case DocumentType.MAP: return 'orange';
- case DocumentType.VID: return 'purple';
- case DocumentType.RTF: return 'yellow';
- case DocumentType.COL: return undefined;
- default: return 'gray';
- }
- })(doc.type as DocumentType);
- return !background ? undefined : <div style={{ width: NumCast(doc._width), height: NumCast(doc._height), position: 'absolute', display: 'block', background }} />;
- }
- }
- };
-
- @computed get renderBounds() {
- const compView = this._props.tabView()?.ComponentView as CollectionFreeFormView;
- const bounds = compView?.freeformData?.(true)?.bounds;
- if (!bounds) return undefined;
- const xbounds = bounds.r - bounds.x;
- const ybounds = bounds.b - bounds.y;
- const dim = Math.max(xbounds, ybounds);
- return { l: bounds.x + xbounds / 2 - dim / 2, t: bounds.y + ybounds / 2 - dim / 2, cx: bounds.x + xbounds / 2, cy: bounds.y + ybounds / 2, 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));
- }
- @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));
- }
- childLayoutTemplate = () => Cast(this._props.document.childLayoutTemplate, Doc, null);
- returnMiniSize = () => NumCast(this._props.document._miniMapSize, 150);
- miniDown = (e: React.PointerEvent) => {
- const doc = this._props.document;
- const miniSize = this.returnMiniSize();
- doc &&
- setupMoveUpEvents(
- this,
- e,
- action((e: PointerEvent, down: number[], delta: number[]) => {
- const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
- doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
- doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
- return false;
- }),
- emptyFunction,
- emptyFunction
- );
- };
- popup = () => {
- if (!this.renderBounds) return <></>;
- const renderBounds = this.renderBounds;
- 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 miniSize = this.returnMiniSize();
- return (
- <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
- <CollectionFreeFormView
- Document={this._props.document}
- 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={true} // don't render overlay Docs since they won't scale
- isContentActive={emptyFunction}
- isAnyChildContentActive={returnFalse}
- select={emptyFunction}
- isSelected={returnFalse}
- dontRegisterView={true}
- fieldKey={Doc.LayoutFieldKey(this._props.document)}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelWidth={this.returnMiniSize}
- PanelHeight={this.returnMiniSize}
- ScreenToLocalTransform={Transform.Identity}
- renderDepth={0}
- whenChildContentsActiveChanged={emptyFunction}
- focus={emptyFunction}
- styleProvider={TabMinimapView.miniStyleProvider}
- addDocTab={this._props.addDocTab}
- pinToPres={TabDocView.PinDoc}
- childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
- childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
- searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
- fitContentsToBox={returnTrue}
- xPadding={this.xPadding}
- yPadding={this.yPadding}
- />
- <div className="miniOverlay" onPointerDown={this.miniDown}>
- <TabMiniThumb miniLeft={miniLeft} miniTop={miniTop} miniWidth={miniWidth} miniHeight={miniHeight} />
- </div>
- </div>
- );
- };
- render() {
- return this._props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._props.document)) || this._props.document?._type_collection !== CollectionViewType.Freeform ? null : (
- <div className="miniMap-hidden">
- <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement="top-end" popup={this.popup} />
- </div>
- );
- }
-}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 4fd49f8fe..6ea6bbfbd 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,44 +1,51 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IconButton, Size } from 'browndash-components';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../Utils';
-import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc';
+import { ClientUtils, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
+import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast, toList } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
+import { DocUtils } from '../../documents/DocUtils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DocUtils, Docs } from '../../documents/Documents';
-import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../util/DragManager';
-import { LinkManager } from '../../util/LinkManager';
+import { Docs } from '../../documents/Documents';
+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 { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
-import { StyleProp } from '../StyleProvider';
-import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
+import { StyleProp } from '../StyleProp';
+import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView';
import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView';
-import { KeyValueBox } from '../nodes/KeyValueBox';
+import { OpenWhere } from '../nodes/OpenWhere';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
+import { CollectionTreeView } from './CollectionTreeView';
+import { TreeViewType } from './CollectionTreeViewType';
import { CollectionView } from './CollectionView';
import { TreeSort } from './TreeSort';
import './TreeView.scss';
+
const { TREE_BULLET_WIDTH } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
export interface TreeViewProps {
treeView: CollectionTreeView;
+ // eslint-disable-next-line no-use-before-define
parentTreeView: TreeView | CollectionTreeView | undefined;
observeHeight: (ref: any) => void;
unobserveHeight: (ref: any) => void;
@@ -48,7 +55,7 @@ export interface TreeViewProps {
treeViewParent: Doc;
renderDepth: number;
dragAction: dropActionType;
- addDocTab: (doc: Doc, where: OpenWhere) => boolean;
+ addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean;
panelWidth: () => number;
panelHeight: () => number;
addDocument: (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => boolean;
@@ -87,6 +94,7 @@ const treeBulletWidth = function () {
*/
@observer
export class TreeView extends ObservableReactComponent<TreeViewProps> {
+ // eslint-disable-next-line no-use-before-define
static _editTitleOnLoad: Opt<{ id: string; parent: TreeView | CollectionTreeView | undefined }>;
static _openTitleScript: Opt<ScriptField | undefined>;
static _openLevelScript: Opt<ScriptField | undefined>;
@@ -101,6 +109,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
get treeViewOpenIsTransient() {
return this.treeView.Document.treeView_OpenIsTransient || Doc.IsDataProto(this.Document);
}
+ @computed get treeViewOpen() {
+ return (!this.treeViewOpenIsTransient && Doc.GetT(this.Document, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
+ }
set treeViewOpen(c: boolean) {
if (this.treeViewOpenIsTransient) this._transientOpenState = c;
else {
@@ -137,9 +148,6 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
@computed get Document() {
return this._props.Document;
}
- @computed get treeViewOpen() {
- return (!this.treeViewOpenIsTransient && Doc.GetT(this.Document, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
- }
@computed get treeViewExpandedView() {
return this.validExpandViewTypes.includes(StrCast(this.Document.treeView_ExpandedView)) ? StrCast(this.Document.treeView_ExpandedView) : this.defaultExpandedView;
}
@@ -192,12 +200,13 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
}
return false;
};
- @undoBatch remove = (doc: Doc | Doc[], key: string) => {
+ @undoBatch remove = (docIn: Doc | Doc[], key: string) => {
+ const docs = toList(docIn);
this.treeView._props.select(false);
- const ind = DocListCast(this.dataDoc[key]).indexOf(doc instanceof Doc ? doc : doc.lastElement());
+ const ind = DocListCast(this.dataDoc[key]).indexOf(docs.lastElement());
- const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
- res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false);
+ const res = docs.reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
+ res && ind > 0 && DocumentView.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false);
return res;
};
@@ -221,7 +230,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
this.treeViewOpen = !this.treeViewOpen;
} else {
// choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding
- const bestEmbedding = docView.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.Document) ? docView.Document : Doc.BestEmbedding(docView.Document);
+ const bestEmbedding = docView.Document.author === ClientUtils.CurrentUserEmail() && !Doc.IsDataProto(docView.Document) ? docView.Document : Doc.BestEmbedding(docView.Document);
this._props.addDocTab(bestEmbedding, OpenWhere.lightbox);
}
};
@@ -230,7 +239,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
recurToggle = (childList: Doc[]) => {
if (childList.length > 0) {
childList.forEach(child => {
- child.runProcess = !!!child.runProcess;
+ child.runProcess = !child.runProcess;
TreeView.ToggleChildrenRun.get(child)?.();
});
}
@@ -273,9 +282,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
this.recurToggle(this.childDocs);
});
- TreeView.GetRunningChildren.set(this.Document, () => {
- return this.getRunningChildren(this.childDocs);
- });
+ TreeView.GetRunningChildren.set(this.Document, () => this.getRunningChildren(this.childDocs));
}
_treeEle: any;
@@ -301,7 +308,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
super.componentDidUpdate(prevProps);
this._disposers.opening = reaction(
() => this.treeViewOpen,
- open => !open && (this._renderCount = 20)
+ open => {
+ !open && (this._renderCount = 20);
+ }
);
this._props.hierarchyIndex !== undefined && this._props.AddToMap?.(this.Document, this._props.hierarchyIndex);
}
@@ -310,7 +319,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
this._props.hierarchyIndex !== undefined && this._props.AddToMap?.(this.Document, this._props.hierarchyIndex);
}
- onDragUp = (e: PointerEvent) => {
+ onDragUp = () => {
document.removeEventListener('pointerup', this.onDragUp, true);
document.removeEventListener('pointermove', this.onDragMove, true);
};
@@ -324,7 +333,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
document.addEventListener('pointerup', this.onDragUp, true);
}
};
- onPointerLeave = (e: React.PointerEvent): void => {
+ onPointerLeave = (): void => {
Doc.UnBrushDoc(this.dataDoc);
if (this._header.current?.className !== 'treeView-header-editing') {
this._header.current!.className = 'treeView-header';
@@ -368,7 +377,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const bulletData = bullet[DocData];
bulletData.title = ComputedField.MakeFunction('this.text?.Text');
bulletData.data = new List<Doc>([]);
- DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
+ DocumentView.addViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
return bullet;
}
@@ -385,7 +394,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return this.localAdd(folder);
};
- preTreeDrop = (e: Event, de: DragManager.DropEvent, docDropAction: dropActionType) => {
+ preTreeDrop = () => {
// fall through and let the CollectionTreeView handle this since treeView items have no special properties of their own
};
@@ -395,7 +404,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
if (!this._header.current) return false;
const rect = this._header.current.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
+ const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || !!(!before && this.treeViewOpen && this.childDocs?.length);
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
const destDoc = this.Document;
@@ -403,7 +412,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
e.stopPropagation();
return true;
}
- const docDragData = de.complete.docDragData;
+ const { docDragData } = de.complete;
if (docDragData && pt[0] < rect.left + rect.width) {
if (docDragData.draggedDocuments[0] === this.Document) return true;
const added = this.dropDocuments(
@@ -423,14 +432,14 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return false;
};
- localAdd = (doc: Doc | Doc[]) => {
- const innerAdd = (doc: Doc) => {
+ localAdd = (docs: Doc | Doc[]): boolean => {
+ const innerAdd = (doc: Doc): boolean => {
const dataIsComputed = ComputedField.WithoutComputed(() => 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));
return added;
};
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
+ return toList(docs).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
};
dropping: boolean = false;
@@ -462,8 +471,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
refTransform = (ref: HTMLDivElement | undefined | null) => {
if (!ref) return this.ScreenToLocalTransform();
- const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
- const outerXf = Utils.GetScreenTransform(this.treeView.MainEle());
+ const { translateX, translateY } = ClientUtils.GetScreenTransform(ref);
+ const outerXf = ClientUtils.GetScreenTransform(this.treeView.MainEle());
const offset = this.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
return this.ScreenToLocalTransform().translate(offset[0], offset[1]);
};
@@ -490,29 +499,34 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const ids: { [key: string]: string } = {};
const rows: JSX.Element[] = [];
const doc = this.Document;
- doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
+ doc &&
+ Object.keys(doc).forEach(key => {
+ !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key);
+ });
+ // eslint-disable-next-line no-restricted-syntax
for (const key of Object.keys(ids).slice().sort()) {
+ // eslint-disable-next-line no-continue
if (this._props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') continue;
const contents = doc[key];
let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- let leftOffset = observable({ width: 0 });
+ const leftOffset = observable({ width: 0 });
const expandedWidth = () => this._props.panelWidth() - leftOffset.width;
if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc)) {
- const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
- const moveDoc = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.move(doc, target, addDoc);
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => {
- const innerAdd = (doc: Doc) => {
+ const remDoc = (docs: Doc | Doc[]) => this.remove(docs, key);
+ 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 added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
- dataIsComputed && Doc.SetContainer(doc, DocCast(this.Document.embedContainer));
+ 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));
return added;
};
- return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
+ return toList(docs).reduce((flg, iDoc) => flg && innerAdd(iDoc), true as boolean);
};
contentElement = TreeView.GetChildElements(
- contents instanceof Doc ? [contents] : DocListCast(contents),
+ toList(contents as any),
this.treeView,
this,
doc,
@@ -549,11 +563,11 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
contentElement = (
<EditableView
key="editableView"
- contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+ contents={contents !== undefined ? Field.toString(contents as FieldType) : 'null'}
height={13}
fontSize={12}
GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+ SetValue={(value: string) => Doc.SetField(doc, key, value, true)}
/>
);
}
@@ -572,20 +586,20 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
);
}
rows.push(
- <div style={{ display: 'flex', overflow: 'auto' }} key={'newKeyValue'}>
+ <div style={{ display: 'flex', overflow: 'auto' }} key="newKeyValue">
<EditableView
key="editableView"
- contents={'+key=value'}
+ contents="+key=value"
height={13}
fontSize={12}
GetValue={returnEmptyString}
SetValue={input => {
- const match = input.match(/([a-zA-Z0-9_-]+)(=|=:=)([a-zA-Z,_@\?\+\-\*\/\ 0-9\(\)]+)/);
+ const match = input.match(/([a-zA-Z0-9_-]+)(=|=:=)([a-zA-Z,_@?+\-*/ 0-9()]+)/);
if (match) {
const key = match[1];
const assign = match[2];
const val = match[3];
- KeyValueBox.SetField(doc, key, assign + val, false);
+ Doc.SetField(doc, key, assign + val, false);
return true;
}
return false;
@@ -620,7 +634,9 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
docs.push(doc);
- docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i));
+ docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => {
+ d.zIndex = i;
+ });
}
const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
@@ -628,10 +644,10 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return added;
};
- const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
+ const addDoc = (docs: Doc | Doc[], addBefore?: Doc, before?: boolean) => toList(docs).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
const docs = expandKey === 'embeddings' ? this.childEmbeddings : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
- let downX = 0,
- downY = 0;
+ let downX = 0;
+ let downY = 0;
if (docs?.length && this._renderCount < docs?.length) {
this._renderTimer && clearTimeout(this._renderTimer);
this._renderTimer = setTimeout(
@@ -667,7 +683,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
style={{ cursor: 'inherit' }}
key={expandKey + 'more'}
title={`Sorted by : ${this.Document.treeView_SortCriterion}. click to cycle`}
- className="" //this.doc.treeView_HideTitle ? 'no-indent' : ''}
+ className="" // this.doc.treeView_HideTitle ? 'no-indent' : ''}
onPointerDown={e => {
downX = e.clientX;
downY = e.clientY;
@@ -719,7 +735,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
</ul>
</div>
);
- } else if (this.treeViewExpandedView === 'fields') {
+ }
+ if (this.treeViewExpandedView === 'fields') {
return (
<ul key={this.Document[Id] + this.Document.title} style={{ cursor: 'inherit' }}>
<div>{this.expandedField}</div>
@@ -804,7 +821,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
@computed get validExpandViewTypes() {
const annos = () => (DocListCast(this.Document[this.fieldKey + '_annotations']).length && !this.treeView.dashboardMode ? 'annotations' : '');
- const links = () => (LinkManager.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : '');
+ const links = () => (Doc.Links(this.Document).length && !this.treeView.dashboardMode ? 'links' : '');
const data = () => (this.childDocs || this.treeView.dashboardMode ? this.fieldKey : '');
const embeddings = () => (this.treeView.dashboardMode ? '' : 'embeddings');
const fields = () => (Doc.noviceMode ? '' : 'fields');
@@ -891,14 +908,15 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
titleStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
if (!doc || doc !== this.Document) return this._props?.treeView?._props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
- const treeView = this.treeView;
+ const { treeView } = this;
// prettier-ignore
switch (property.split(':')[0]) {
case StyleProp.Opacity: return this.treeView.outlineMode ? undefined : 1;
- case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined;//StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+ case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : undefined; // StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
case StyleProp.Highlighting: if (this.treeView.outlineMode) return undefined;
+ break;
case StyleProp.BoxShadow: return undefined;
- case StyleProp.DocContents:
+ case StyleProp.DocContents: {
const highlightIndex = this.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.GetBrushHighlightStatus(doc);
const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
return treeView.outlineMode ? null : (
@@ -917,6 +935,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
{StrCast(doc?.title)}
</div>
);
+ }
+ default:
}
return treeView._props.styleProvider?.(doc, props, property);
};
@@ -924,7 +944,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, fieldProps: FieldViewProps) => {
+ onKeyDown = (e: React.KeyboardEvent) => {
if (this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode) {
switch (e.key) {
case 'Tab':
@@ -944,6 +964,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
e.stopPropagation?.();
e.preventDefault?.();
return UndoManager.RunInBatch(this.makeTextCollection, 'bullet');
+ default:
}
}
return false;
@@ -959,22 +980,24 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const view = this._editTitle ? (
<EditableView
key="_editTitle"
- oneLine={true}
- display={'inline-block'}
+ oneLine
+ display="inline-block"
editing={this._editTitle}
- background={'#7089bb'}
+ background="#7089bb"
contents={StrCast(this.Document.title)}
height={12}
- sizeToContent={true}
+ sizeToContent
fontSize={12}
- isEditingCallback={action(e => (this._editTitle = e))}
+ isEditingCallback={action(e => {
+ this._editTitle = e;
+ })}
GetValue={() => StrCast(this.Document.title)}
OnTab={undoBatch((shift?: boolean) => {
if (!shift) this._props.indentDocument?.(true);
else this._props.outdentDocument?.(true);
})}
OnEmpty={undoBatch(() => this.treeView.outlineMode && this._props.removeDoc?.(this.Document))}
- OnFillDown={val => this.treeView.fileSysMode && this.makeFolder()}
+ OnFillDown={() => this.treeView.fileSysMode && this.makeFolder()}
SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
Doc.SetInPlace(this.Document, 'title', value, false);
this.treeView.outlineMode && enterKey && this.makeTextCollection();
@@ -984,7 +1007,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
<DocumentView
key="title"
ref={action((r: any) => {
- this._docRef = r ? r : undefined;
+ this._docRef = r || undefined;
if (this._docRef && TreeView._editTitleOnLoad?.id === this.Document[Id] && TreeView._editTitleOnLoad.parent === this._props.parentTreeView) {
this._docRef.select(false);
this.setEditTitle(this._docRef);
@@ -994,8 +1017,8 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
Document={this.Document}
layout_fitWidth={returnTrue}
scriptContext={this}
- hideDecorations={true}
- hideClickBehaviors={true}
+ hideDecorations
+ hideClickBehaviors
styleProvider={this.titleStyleProvider}
onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
containerViewPath={this.treeView.childContainerViewPath}
@@ -1015,7 +1038,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
PanelHeight={return18}
contextMenuItems={this.contextMenuItems}
renderDepth={1}
- isContentActive={emptyFunction} //this._props.isContentActive}
+ isContentActive={emptyFunction} // this._props.isContentActive}
isDocumentActive={this._props.isContentActive}
focus={this.refocus}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
@@ -1045,99 +1068,101 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
}}>
{view}
</div>
- <div className="treeView-rightButtons" ref={action((r: any) => r && (this.headerEleWidth = r.getBoundingClientRect().width))}>
+ <div
+ className="treeView-rightButtons"
+ ref={action((r: any) => {
+ r && (this.headerEleWidth = r.getBoundingClientRect().width);
+ })}>
{this.titleButtons}
</div>
</>
);
}
- renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
- return (
- <>
+ renderBulletHeader = (contents: JSX.Element, editing: boolean) => (
+ <>
+ <div
+ className={`treeView-header` + (editing ? '-editing' : '')}
+ key="titleheader"
+ ref={this._header}
+ onClick={this.ignoreEvent}
+ onPointerDown={e => {
+ this.treeView.isContentActive() &&
+ setupMoveUpEvents(
+ this,
+ e,
+ () => {
+ (this._dref ?? this._docRef)?.startDragging(e.clientX, e.clientY, '' as any);
+ return true;
+ },
+ returnFalse,
+ emptyFunction
+ );
+ }}
+ onPointerEnter={this.onPointerEnter}
+ onPointerLeave={this.onPointerLeave}>
<div
- className={`treeView-header` + (editing ? '-editing' : '')}
- key="titleheader"
- ref={this._header}
- onClick={this.ignoreEvent}
- onPointerDown={e => {
- this.treeView.isContentActive() &&
- setupMoveUpEvents(
- this,
- e,
- () => {
- (this._dref ?? this._docRef)?.startDragging(e.clientX, e.clientY, '' as any);
- return true;
- },
- returnFalse,
- emptyFunction
- );
+ className="treeView-background"
+ style={{
+ background: SettingsManager.userColor,
}}
- onPointerEnter={this.onPointerEnter}
- onPointerLeave={this.onPointerLeave}>
- <div
- className="treeView-background"
- style={{
- background: SettingsManager.userColor,
- }}
- />
- {contents}
- </div>
- {this.renderBorder}
- </>
- );
- };
-
- fitWidthFilter = (doc: Doc) => (doc.type === DocumentType.IMG ? false : undefined);
- renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
- return (
- <div style={{ height: this.embeddedPanelHeight(), width: this.embeddedPanelWidth() }}>
- <DocumentView
- key={this.Document[Id]}
- ref={action((r: DocumentView | null) => (this._dref = r))}
- Document={this.Document}
- layout_fitWidth={this.fitWidthFilter}
- PanelWidth={this.embeddedPanelWidth}
- PanelHeight={this.embeddedPanelHeight}
- LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
- LayoutTemplate={this.treeView._props.childLayoutTemplate}
- isContentActive={isActive}
- isDocumentActive={isActive}
- styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
- fitContentsToBox={returnTrue}
- hideTitle={asText}
- hideDecorations={true}
- hideClickBehaviors={true}
- hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
- dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
- ScreenToLocalTransform={this.docTransform}
- renderDepth={this._props.renderDepth + 1}
- onClickScript={this.onChildClick}
- onKey={this.onKeyDown}
- containerViewPath={this.treeView.childContainerViewPath}
- childFilters={returnEmptyFilter}
- childFiltersByRanges={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- focus={this.refocus}
- addDocument={this._props.addDocument}
- moveDocument={this.move}
- removeDocument={this._props.removeDoc}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- xPadding={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
- yPadding={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
- addDocTab={this._props.addDocTab}
- pinToPres={this.treeView._props.pinToPres}
- disableBrushing={this.treeView._props.disableBrushing}
- scriptContext={this}
/>
+ {contents}
</div>
- );
- };
+ {this.renderBorder}
+ </>
+ );
+
+ fitWidthFilter = (doc: Doc) => (doc.type === DocumentType.IMG ? false : undefined);
+ renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => (
+ <div style={{ height: this.embeddedPanelHeight(), width: this.embeddedPanelWidth() }}>
+ <DocumentView
+ key={this.Document[Id]}
+ ref={action((r: DocumentView | null) => {
+ this._dref = r;
+ })}
+ Document={this.Document}
+ layout_fitWidth={this.fitWidthFilter}
+ PanelWidth={this.embeddedPanelWidth}
+ PanelHeight={this.embeddedPanelHeight}
+ LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
+ LayoutTemplate={this.treeView._props.childLayoutTemplate}
+ isContentActive={isActive}
+ isDocumentActive={isActive}
+ styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ fitContentsToBox={returnTrue}
+ hideTitle={asText}
+ hideDecorations
+ hideClickBehaviors
+ hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
+ ScreenToLocalTransform={this.docTransform}
+ renderDepth={this._props.renderDepth + 1}
+ onClickScript={this.onChildClick}
+ onKey={this.onKeyDown}
+ containerViewPath={this.treeView.childContainerViewPath}
+ childFilters={returnEmptyFilter}
+ childFiltersByRanges={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ focus={this.refocus}
+ addDocument={this._props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this._props.removeDoc}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ xPadding={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
+ yPadding={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this.treeView._props.pinToPres}
+ disableBrushing={this.treeView._props.disableBrushing}
+ scriptContext={this}
+ />
+ </div>
+ );
// renders the text version of a document as the header. This is used in the file system mode and in other vanilla tree views.
@computed get renderTitleAsHeader() {
return this.treeView.Document.treeView_HideUnrendered && this.Document.layout_unrendered && !this.Document.treeView_FieldKey ? (
- <div></div>
+ <div />
) : (
<>
{this.renderBullet}
@@ -1147,14 +1172,12 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
}
// renders the document in the header field instead of a text proxy.
- renderDocumentAsHeader = (asText: boolean) => {
- return (
- <>
- {this.renderBullet}
- {this.renderEmbeddedDocument(asText, this._props.isContentActive)}
- </>
- );
- };
+ renderDocumentAsHeader = (asText: boolean) => (
+ <>
+ {this.renderBullet}
+ {this.renderEmbeddedDocument(asText, this._props.isContentActive)}
+ </>
+ );
@computed get renderBorder() {
const sorting = StrCast(this.Document.treeView_SortCriterion, TreeSort.WhenAdded);
@@ -1170,7 +1193,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const pt = [de.clientX, de.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
+ const inside = this.treeView.fileSysMode && !this.Document.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || !!(!before && this.treeViewOpen && this.childDocs?.length);
this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, dropActionType.copy, undefined, undefined, false, false));
};
@@ -1185,7 +1208,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
className={`treeView-container${this._props.isContentActive() ? '-active' : ''}`}
ref={this.createTreeDropTarget}
onDrop={this.onTreeDrop}
- //onPointerDown={e => this._props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
+ // onPointerDown={e => this._props.isContentActive(true) && DocumentView.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
// onKeyDown={this.onKeyDown}
>
<li className="collection-child">
@@ -1209,11 +1232,10 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const aN = parseInt(a.match(reN)![0], 10);
const bN = parseInt(b.match(reN)![0], 10);
return aN === bN ? 0 : aN > bN ? 1 : -1;
- } else {
- return aA > bA ? 1 : -1;
}
+ return aA > bA ? 1 : -1;
};
- docs.sort(function (d1, d2): 0 | 1 | -1 {
+ docs.sort((d1, d2): 0 | 1 | -1 => {
const a = criterion === TreeSort.AlphaUp ? d2 : d1;
const b = criterion === TreeSort.AlphaUp ? d1 : d2;
const first = a[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
@@ -1230,7 +1252,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
childDocs: Doc[],
treeView: CollectionTreeView,
parentTreeView: CollectionTreeView | TreeView | undefined,
- treeView_Parent: Doc,
+ treeViewParent: Doc,
dataDoc: Doc | undefined,
parentCollectionDoc: Doc | undefined,
containerPrevSibling: Doc | undefined,
@@ -1238,13 +1260,13 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
remove: undefined | ((doc: Doc | Doc[]) => boolean),
move: DragManager.MoveFunction,
dragAction: dropActionType,
- addDocTab: (doc: Doc, where: OpenWhere) => boolean,
+ addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean,
styleProvider: undefined | StyleProviderFuncType,
screenToLocalXf: () => Transform,
isContentActive: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
renderDepth: number,
- treeView_HideHeaderFields: () => boolean,
+ treeViewHideHeaderFields: () => boolean,
renderedIds: string[],
onCheckedClick: undefined | (() => ScriptField),
onChildClick: undefined | (() => ScriptField),
@@ -1261,19 +1283,14 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
hierarchyIndex?: number[],
renderCount?: number
) {
- const viewSpecScript = Cast(treeView_Parent.viewSpecScript, ScriptField);
- if (viewSpecScript) {
- childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result);
- }
-
- const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.WhenAdded));
+ const docs = TreeView.sortDocs(childDocs, StrCast(treeViewParent.treeView_SortCriterion, TreeSort.WhenAdded));
const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView._props.NativeDimScaling?.() || 1);
- const treeView_Refs = new Map<Doc, TreeView | undefined>();
+ const treeViewRefs = new Map<Doc, TreeView | undefined>();
return docs
.filter(child => child instanceof Doc)
.map((child, i) => {
if (renderCount && i > renderCount) return null;
- const pair = Doc.GetLayoutDataDocPair(treeView_Parent, dataDoc, child);
+ const pair = Doc.GetLayoutDataDocPair(treeViewParent, dataDoc, child);
if (!pair.layout || pair.data instanceof Promise) {
return null;
}
@@ -1283,14 +1300,14 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
const fieldKey = Doc.LayoutFieldKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
- FormattedTextBox.SetSelectOnLoad(child);
+ Doc.SetSelectOnLoad(child);
TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
newParent.treeView_Open = true;
Doc.SetContainer(child, treeView.Document);
}
};
- const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeView_Refs.get(docs[i - 1]));
+ 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);
@@ -1301,12 +1318,11 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return (
<TreeView
key={child[Id]}
- ref={r => treeView_Refs.set(child, r ? r : undefined)}
+ ref={r => treeViewRefs.set(child, r || undefined)}
Document={pair.layout}
dataDoc={pair.data}
- treeViewParent={treeView_Parent}
+ treeViewParent={treeViewParent}
prevSibling={docs[i]}
- // TODO: [AL] add these
hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
AddToMap={AddToMap}
RemFromMap={RemFromMap}
@@ -1316,7 +1332,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
onCheckedClick={onCheckedClick}
onChildClick={onChildClick}
renderDepth={renderDepth}
- removeDoc={StrCast(treeView_Parent.treeView_FreezeChildren).includes('remove') ? undefined : remove}
+ removeDoc={StrCast(treeViewParent.treeView_FreezeChildren).includes('remove') ? undefined : remove}
addDocument={addDocument}
styleProvider={styleProvider}
panelWidth={rowWidth}
@@ -1327,7 +1343,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
addDocTab={addDocTab}
ScreenToLocalTransform={screenToLocalXf}
isContentActive={isContentActive}
- treeViewHideHeaderFields={treeView_HideHeaderFields}
+ treeViewHideHeaderFields={treeViewHideHeaderFields}
renderedIds={renderedIds}
skipFields={skipFields}
firstLevel={firstLevel}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
index 0acc99360..d2ce17f99 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -10,6 +10,7 @@ export interface CollectionFreeFormViewBackgroundGridProps {
PanelWidth: () => number;
PanelHeight: () => number;
color: () => string;
+ // eslint-disable-next-line react/require-default-props
isAnnotationOverlay?: boolean;
nativeDimScaling: () => number;
zoomScaling: () => number;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
new file mode 100644
index 000000000..26a52cd2a
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormClusters.ts
@@ -0,0 +1,223 @@
+import { action, observable } from 'mobx';
+import { CollectionFreeFormView } from '.';
+import { intersectRect } from '../../../../Utils';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
+import { StyleProp } from '../../StyleProp';
+import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
+import './CollectionFreeFormView.scss';
+
+export class CollectionFreeFormClusters {
+ private _view: CollectionFreeFormView;
+ private _clusterDistance: number = 75;
+ private _hitCluster: number = -1;
+ @observable _clusterSets: Doc[][] = [];
+
+ constructor(view: CollectionFreeFormView) {
+ this._view = view;
+ }
+ get Document() { return this._view.Document; } // prettier-ignore
+ get DocumentView() { return this._view.DocumentView?.(); } // prettier-ignore
+ get childDocs() { return this._view.childDocs; } // prettier-ignore
+ get childLayoutPairs() { return this._view.childLayoutPairs; } // prettier-ignore
+ get screenToContentsXf() { return this._view.screenToFreeformContentsXf; } // prettier-ignore
+ get viewStyleProvider() { return this._view._props.styleProvider; } // prettier-ignore
+ get viewMoveDocument() { return this._view._props.moveDocument; } // prettier-ignore
+ get selectDocuments() { return this._view.selectDocuments; } // prettier-ignore
+
+ static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
+ const doc2Layout = Doc.Layout(doc2);
+ const doc1Layout = Doc.Layout(doc1);
+ const x2 = NumCast(doc2.x) - clusterDistance;
+ const y2 = NumCast(doc2.y) - clusterDistance;
+ const w2 = NumCast(doc2Layout._width) + clusterDistance;
+ const h2 = NumCast(doc2Layout._height) + clusterDistance;
+ const x = NumCast(doc1.x) - clusterDistance;
+ const y = NumCast(doc1.y) - clusterDistance;
+ const w = NumCast(doc1Layout._width) + clusterDistance;
+ const h = NumCast(doc1Layout._height) + clusterDistance;
+ return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
+ }
+ handlePointerDown(probe: number[]) {
+ this._hitCluster = this.childLayoutPairs
+ .map(pair => pair.layout)
+ .reduce((cluster, cd) => {
+ const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
+ if (grouping !== -1) {
+ const layoutDoc = Doc.Layout(cd);
+ const cx = NumCast(cd.x) - this._clusterDistance / 2;
+ const cy = NumCast(cd.y) - this._clusterDistance / 2;
+ const cw = NumCast(layoutDoc._width) + this._clusterDistance;
+ const ch = NumCast(layoutDoc._height) + this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ }
+ return cluster;
+ }, -1);
+ return this._hitCluster;
+ }
+
+ tryToDrag(e: PointerEvent) {
+ const cluster = this._hitCluster;
+ if (cluster !== -1) {
+ const ptsParent = e;
+ if (ptsParent) {
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
+ const clusterDocs = eles.map(ele => DocumentView.getDocumentView(ele, this.DocumentView)!);
+ const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
+ const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined);
+ de.moveDocument = this.viewMoveDocument;
+ de.offset = this.screenToContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
+ DragManager.StartDocumentDrag(
+ clusterDocs.map(v => v.ContentDiv!),
+ de,
+ ptsParent.clientX,
+ ptsParent.clientY,
+ { hideSource: !de.dropAction }
+ );
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ initLayout() {
+ if (this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length) {
+ return this.updateClusters(true);
+ }
+ return false;
+ }
+ @action
+ updateClusters(useClusters: boolean) {
+ this.Document._freeform_useClusters = useClusters;
+ this._clusterSets.length = 0;
+ this.childLayoutPairs.map(pair => pair.layout).map(c => this.addDocument(c));
+ }
+
+ @action
+ addDocuments(docs: Doc[]) {
+ const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+ if (this.Document._freeform_useClusters) {
+ const docFirst = docs[0];
+ docs.forEach(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
+ const preferredInd = NumCast(docFirst.layout_cluster);
+ docs.forEach(doc => {
+ doc.layout_cluster = -1;
+ });
+ docs.map(doc =>
+ this._clusterSets.map((set, i) =>
+ set.forEach(member => {
+ if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormClusters.overlapping(doc, member, this._clusterDistance)) {
+ docFirst.layout_cluster = i;
+ }
+ })
+ )
+ );
+ if (
+ docFirst.layout_cluster === -1 &&
+ preferredInd !== -1 &&
+ this._clusterSets.length > preferredInd &&
+ (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
+ ) {
+ docFirst.layout_cluster = preferredInd;
+ }
+ this._clusterSets.forEach((set, i) => {
+ if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+ docFirst.layout_cluster = i;
+ }
+ });
+ if (docFirst.layout_cluster === -1) {
+ docs.forEach(doc => {
+ doc.layout_cluster = this._clusterSets.length;
+ this._clusterSets.push([doc]);
+ });
+ } else if (this._clusterSets.length) {
+ for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
+ docs.forEach(doc => {
+ this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc);
+ });
+ }
+ childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.addDocument(child));
+ }
+ }
+
+ @action
+ addDocument = (doc: Doc) => {
+ const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
+ if (this.Document._freeform_useClusters) {
+ this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
+ const preferredInd = NumCast(doc.layout_cluster);
+ doc.layout_cluster = -1;
+ this._clusterSets.forEach((set, i) =>
+ set.forEach(member => {
+ if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormClusters.overlapping(doc, member, this._clusterDistance)) {
+ doc.layout_cluster = i;
+ }
+ })
+ );
+ if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
+ doc.layout_cluster = preferredInd;
+ }
+ this._clusterSets.forEach((set, i) => {
+ if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
+ doc.layout_cluster = i;
+ }
+ });
+ if (doc.layout_cluster === -1) {
+ doc.layout_cluster = this._clusterSets.length;
+ this._clusterSets.push([doc]);
+ } else if (this._clusterSets.length) {
+ for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
+ this._clusterSets[doc.layout_cluster ?? 0].push(doc);
+ }
+ }
+ };
+
+ styleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
+ let styleProp = this.viewStyleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
+ if (doc && this.childDocs?.includes(doc))
+ switch (property.split(':')[0]) {
+ case StyleProp.BackgroundColor:
+ {
+ const cluster = NumCast(doc?.layout_cluster);
+ if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
+ if (this._clusterSets.length <= cluster) {
+ setTimeout(() => doc && this.addDocument(doc));
+ } else {
+ // choose a cluster color from a palette
+ const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
+ styleProp = colors[cluster % colors.length];
+ const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
+ // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
+ set?.forEach(s => {
+ styleProp = StrCast(s.backgroundColor);
+ });
+ }
+ }
+ }
+ break;
+ case StyleProp.FillColor:
+ if (doc && this.Document._currentFrame !== undefined) {
+ return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
+ }
+ break;
+ default:
+ }
+ return styleProp;
+ };
+
+ tryToSelect = (addToSel: boolean) => {
+ if (addToSel && this._hitCluster !== -1) {
+ !addToSel && DocumentView.DeselectAll();
+ const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+ this.selectDocuments(eles);
+ return true;
+ }
+ return false;
+ };
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index 73dd7fea3..fc39cafaa 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -5,13 +5,13 @@ import * as React from 'react';
import { SettingsManager } from '../../../util/SettingsManager';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
-import { Doc } from '../../../../fields/Doc';
/**
* An Fsa Arc. The first array element is a test condition function that will be observed.
* The second array element is a function that will be invoked when the first test function
* returns a truthy value
*/
+// eslint-disable-next-line no-use-before-define
export type infoArc = [() => any, (res?: any) => infoState];
export const StateMessage = Symbol('StateMessage');
@@ -46,6 +46,7 @@ export function InfoState(
gif?: string,
entryFunc?: () => any
) {
+ // eslint-disable-next-line new-cap
return new infoState(msg, arcs, gif, entryFunc);
}
@@ -73,14 +74,15 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent<Collec
}
clearState = () => this._disposers.map(disposer => disposer());
- initState = () => (this._disposers =
- this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map(
- arc => reaction(
- arc.test,
- res => res && this._props.next(arc.act(res)),
- { fireImmediately: true }
- )
- )); // prettier-ignore
+ initState = () => {
+ this._disposers = this.Arcs
+ .map(arc => ({ test: arc[0], act: arc[1] }))
+ .map(arc => reaction(
+ arc.test,
+ res => res && this._props.next(arc.act(res)),
+ { fireImmediately: true }
+ )
+ )}; // prettier-ignore
componentDidMount() {
this.initState();
@@ -97,10 +99,15 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent<Collec
render() {
const gif = this.State?.[StateMessageGIF];
return (
- <div className={'collectionFreeform-infoUI'}>
+ <div className="collectionFreeform-infoUI">
<p className="collectionFreeform-infoUI-msg">
{this.State?.[StateMessage]}
- <button className={'collectionFreeform-' + (!gif ? 'hidden' : 'infoUI-button')} onClick={action(() => (this._expanded = !this._expanded))}>
+ <button
+ type="button"
+ className={'collectionFreeform-' + (!gif ? 'hidden' : 'infoUI-button')}
+ onClick={action(() => {
+ this._expanded = !this._expanded;
+ })}>
{this._expanded ? 'Less...' : 'More...'}
</button>
</p>
@@ -108,7 +115,7 @@ export class CollectionFreeFormInfoState extends ObservableReactComponent<Collec
<img src={`/assets/${gif}`} alt="state message gif" />
</div>
<div className="collectionFreeform-infoUI-close">
- <IconButton icon="x" color={SettingsManager.userColor} size={Size.XSMALL} type={Type.TERT} background={SettingsManager.userBackgroundColor} onClick={action(e => this.props.close())} />
+ <IconButton icon="x" color={SettingsManager.userColor} size={Size.XSMALL} type={Type.TERT} background={SettingsManager.userBackgroundColor} onClick={action(() => this.props.close())} />
</div>
</div>
);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
index cc729decc..5d8373fc7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -1,26 +1,32 @@
import { makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, Field, FieldResult } from '../../../../fields/Doc';
+import { Doc, DocListCast, FieldType, FieldResult } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { StrCast } from '../../../../fields/Types';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { LinkManager } from '../../../util/LinkManager';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocButtonState, DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { TopBar } from '../../topbar/TopBar';
import { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
-import { CollectionFreeFormView } from './CollectionFreeFormView';
import './CollectionFreeFormView.scss';
+import { DocData } from '../../../../fields/DocSymbols';
+import { CollectionFreeFormView } from './CollectionFreeFormView';
export interface CollectionFreeFormInfoUIProps {
Document: Doc;
- Freeform: CollectionFreeFormView;
- close: () => boolean;
+ LayoutDoc: Doc;
+ childDocs: () => Doc[];
+ close: () => void;
}
@observer
export class CollectionFreeFormInfoUI extends ObservableReactComponent<CollectionFreeFormInfoUIProps> {
+ public static Init() {
+ CollectionFreeFormView.SetInfoUICreator((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => (
+ //
+ <CollectionFreeFormInfoUI Document={doc} LayoutDoc={layout} childDocs={childDocs} close={close} />
+ ));
+ }
_firstDocPos = { x: 0, y: 0 };
constructor(props: any) {
@@ -32,10 +38,10 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
@observable _currState: infoState | undefined = undefined;
get currState() { return this._currState; } // prettier-ignore
- set currState(val) { runInAction(() => (this._currState = val)); } // prettier-ignore
+ set currState(val) { runInAction(() => {this._currState = val;}); } // prettier-ignore
componentWillUnmount(): void {
- this._props.Freeform.dataDoc.backgroundColor = this._originalbackground;
+ this._props.Document[DocData].backgroundColor = this._originalbackground;
}
setCurrState = (state: infoState) => {
@@ -46,16 +52,16 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
};
setupStates = () => {
- this._originalbackground = StrCast(this._props.Freeform.dataDoc.backgroundColor);
+ this._originalbackground = StrCast(this._props.Document[DocData].backgroundColor);
// state entry functions
- const setBackground = (colour: string) => () => (this._props.Freeform.dataDoc.backgroundColor = colour);
- const setOpacity = (opacity: number) => () => (this._props.Freeform.layoutDoc.opacity = opacity);
+ // const setBackground = (colour: string) => () => {this._props.Document[DocData].backgroundColor = colour;} // prettier-ignore
+ // const setOpacity = (opacity: number) => () => {this._props.LayoutDoc.opacity = opacity;} // prettier-ignore
// arc transition trigger conditions
- const firstDoc = () => (this._props.Freeform.childDocs.length ? this._props.Freeform.childDocs[0] : undefined);
- const numDocs = () => this._props.Freeform.childDocs.length;
+ const firstDoc = () => (this._props.childDocs().length ? this._props.childDocs()[0] : undefined);
+ const numDocs = () => this._props.childDocs().length;
- let docX: FieldResult<Field>;
- let docY: FieldResult<Field>;
+ let docX: FieldResult<FieldType>;
+ let docY: FieldResult<FieldType>;
const docNewX = () => firstDoc()?.x;
const docNewY = () => firstDoc()?.y;
@@ -63,7 +69,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
const linkStart = () => DocumentLinksButton.StartLink;
const linkUnstart = () => !DocumentLinksButton.StartLink;
- const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length;
+ const numDocLinks = () => Doc.Links(firstDoc())?.length;
const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView;
const activeTool = () => Doc.ActiveTool;
@@ -72,7 +78,6 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
let trail: number;
- const trailView = () => DocumentManager.Instance.DocumentViews.find(view => view.Document === Doc.MyTrails);
const presentationMode = () => Doc.ActivePresentation?.presentation_status;
// set of states
@@ -82,6 +87,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
docCreated: [() => numDocs(), () => {
docX = firstDoc()?.x;
docY = firstDoc()?.y;
+ // eslint-disable-next-line no-use-before-define
return oneDoc;
}],
}
@@ -92,18 +98,20 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
{
// docCreated: [() => numDocs() > 1, () => multipleDocs],
docDeleted: [() => numDocs() < 1, () => start],
- docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docMoved: [() => (docX && docX !== docNewX()) || (docY && docY !== docNewY()), () => {
docX = firstDoc()?.x;
docY = firstDoc()?.y;
+ // eslint-disable-next-line no-use-before-define
return movedDoc;
}],
}
); // prettier-ignore
const movedDoc = InfoState(
- 'Great moves. Try creating a second document. You can see the list of supported document types by typing a colon (\":\")',
+ 'Great moves. Try creating a second document. You can see the list of supported document types by typing a colon (":")',
{
- docCreated: [() => numDocs() == 2, () => multipleDocs],
+ // eslint-disable-next-line no-use-before-define
+ docCreated: [() => numDocs() === 2, () => multipleDocs],
docDeleted: [() => numDocs() < 1, () => start],
},
'dash-colon-menu.gif',
@@ -113,6 +121,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
const multipleDocs = InfoState(
'Let\'s create a new link. Click the link icon on one of your documents.',
{
+ // eslint-disable-next-line no-use-before-define
linkStarted: [() => linkStart(), () => startedLink],
docRemoved: [() => numDocs() < 2, () => oneDoc],
},
@@ -123,6 +132,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
'Now click the highlighted link icon on your other document.',
{
linkUnstart: [() => linkUnstart(), () => multipleDocs],
+ // eslint-disable-next-line no-use-before-define
linkCreated: [() => numDocLinks(), () => madeLink],
docRemoved: [() => numDocs() < 2, () => oneDoc],
},
@@ -135,6 +145,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
linkCreated: [() => !numDocLinks(), () => multipleDocs],
linkViewed: [() => linkMenuOpen(), () => {
alert(numDocLinks() + " cheer for " + numDocLinks() + " link!");
+ // eslint-disable-next-line no-use-before-define
return viewedLink;
}],
},
@@ -146,10 +157,12 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
{
linkDeleted: [() => !numDocLinks(), () => multipleDocs],
docRemoved: [() => numDocs() < 2, () => oneDoc],
- docCreated: [() => numDocs() == 3, () => {
+ docCreated: [() => numDocs() === 3, () => {
trail = pin().length;
+ // eslint-disable-next-line no-use-before-define
return presentDocs;
}],
+ // eslint-disable-next-line no-use-before-define
activePen: [() => activeTool() === InkTool.Pen, () => penMode],
},
'documentation.png',
@@ -163,6 +176,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
() => pin().length > trail,
() => {
trail = pin().length;
+ // eslint-disable-next-line no-use-before-define
return pinnedDoc1;
},
],
@@ -185,11 +199,13 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
() => pin().length > trail,
() => {
trail = pin().length;
+ // eslint-disable-next-line no-use-before-define
return pinnedDoc2;
},
],
// editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
// manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ // eslint-disable-next-line no-use-before-define
autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
docRemoved: [() => numDocs() < 3, () => viewedLink],
});
@@ -199,11 +215,13 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
() => pin().length > trail,
() => {
trail = pin().length;
+ // eslint-disable-next-line no-use-before-define
return pinnedDoc3;
},
],
// editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
// manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ // eslint-disable-next-line no-use-before-define
autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
docRemoved: [() => numDocs() < 3, () => viewedLink],
});
@@ -218,6 +236,7 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
],
// editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
// manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ // eslint-disable-next-line no-use-before-define
autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
docRemoved: [() => numDocs() < 3, () => viewedLink],
});
@@ -235,21 +254,24 @@ export class CollectionFreeFormInfoUI extends ObservableReactComponent<Collectio
const manualPresentationMode = InfoState("You're in manual presentation mode.", {
// editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // eslint-disable-next-line no-use-before-define
autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
docRemoved: [() => numDocs() < 3, () => viewedLink],
- docCreated: [() => numDocs() == 4, () => completed],
+ // eslint-disable-next-line no-use-before-define
+ docCreated: [() => numDocs() === 4, () => completed],
});
const autoPresentationMode = InfoState("You're in auto presentation mode.", {
// editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
docRemoved: [() => numDocs() < 3, () => viewedLink],
- docCreated: [() => numDocs() == 4, () => completed],
+ // eslint-disable-next-line no-use-before-define
+ docCreated: [() => numDocs() === 4, () => completed],
});
const completed = InfoState(
'Eager to learn more? Click the ? icon in the top right corner to read our full documentation.',
- { docRemoved: [() => numDocs() == 1, () => oneDoc] },
+ { docRemoved: [() => numDocs() === 1, () => oneDoc] },
'documentation.png',
() => TopBar.Instance.FlipDocumentationIcon()
); // prettier-ignore
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index c83c26509..a4496a417 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,4 +1,5 @@
-import { Doc, Field, FieldResult } from '../../../../fields/Doc';
+/* eslint-disable no-use-before-define */
+import { Doc, Field, FieldType, FieldResult } from '../../../../fields/Doc';
import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
@@ -48,9 +49,9 @@ export interface PoolData {
export interface ViewDefResult {
ele: JSX.Element;
bounds?: ViewDefBounds;
- inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transparent (hidden, as in during a presentation when you want to smoothly animate it into being a mask), >0 = mask layer and not hidden
+ inkMask?: number; // sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transparent (hidden, as in during a presentation when you want to smoothly animate it into being a mask), >0 = mask layer and not hidden
}
-function toLabel(target: FieldResult<Field>) {
+function toLabel(target: FieldResult<FieldType>) {
if (typeof target === 'number' || Number(target)) {
const truncated = Number(Number(target).toFixed(0));
const precise = Number(Number(target).toFixed(2));
@@ -84,9 +85,9 @@ interface PivotColumn {
filters: string[];
}
-export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
+export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
const docMap = new Map<string, PoolData>();
- childPairs.forEach(({ layout, data }, i) => {
+ childPairs.forEach(({ layout, data }) => {
docMap.set(layout[Id], {
x: NumCast(layout.x),
y: NumCast(layout.y),
@@ -97,10 +98,15 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
replica: '',
});
});
+ // eslint-disable-next-line no-use-before-define
return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
-export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
+function toNumber(val: FieldResult<FieldType>) {
+ return val === undefined ? undefined : 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 */) {
const docMap = new Map<string, PoolData>();
const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
const burstScale = NumCast(pivotDoc._starburstDocScale, 1);
@@ -128,23 +134,23 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
const fieldKey = 'data';
- const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
+ const pivotColumnGroups = new Map<FieldResult<FieldType>, PivotColumn>();
let nonNumbers = 0;
const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
- childPairs.map(pair => {
+ childPairs.forEach(pair => {
const listValue = Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
const num = toNumber(pair.layout[pivotFieldKey]);
- if (num === undefined || Number.isNaN(num)) {
+ if (num === undefined || isNaN(num)) {
nonNumbers++;
}
- const val = Field.toString(pair.layout[pivotFieldKey] as Field);
+ const val = Field.toString(pair.layout[pivotFieldKey] as FieldType);
if (listValue) {
- listValue.forEach((val, i) => {
- !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
- pivotColumnGroups.get(val)!.docs.push(pair.layout);
- pivotColumnGroups.get(val)!.replicas.push(i.toString());
+ listValue.forEach((lval, i) => {
+ !pivotColumnGroups.get(lval) && pivotColumnGroups.set(lval, { docs: [], filters: [lval], replicas: [] });
+ pivotColumnGroups.get(lval)!.docs.push(pair.layout);
+ pivotColumnGroups.get(lval)!.replicas.push(i.toString());
});
} else if (val) {
!pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
@@ -184,11 +190,11 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
const textlen = Array.from(pivotColumnGroups.keys())
.map(c => getTextWidth(toLabel(c), desc))
.reduce((p, c) => Math.max(p, c), 0 as number);
- const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
+ const maxText = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
const colWidth = panelDim[0] / pivotColumnGroups.size;
- const colHeight = panelDim[1] - max_text;
+ const colHeight = panelDim[1] - maxText;
let numCols = 0;
let bestArea = 0;
let pivotAxisWidth = 0;
@@ -212,7 +218,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
let x = 0;
const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
sortedPivotKeys.forEach(key => {
- const val = pivotColumnGroups.get(key)!;
+ const val = pivotColumnGroups.get(key);
let y = 0;
let xCount = 0;
const text = toLabel(key);
@@ -222,11 +228,11 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
x,
y: pivotAxisWidth,
width: pivotAxisWidth * expander * numCols,
- height: max_text,
+ height: maxText,
fontSize,
payload: val,
});
- val.docs.forEach((doc, i) => {
+ val?.docs.forEach((doc, i) => {
const layoutDoc = Doc.Layout(doc);
let wid = pivotAxisWidth;
let hgt = pivotAxisWidth / (Doc.NativeAspect(layoutDoc) || 1);
@@ -262,19 +268,16 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
payload: pivotColumnGroups.get(key)!.filters,
}));
groupNames.push(...dividers);
- return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
-}
-
-function toNumber(val: FieldResult<Field>) {
- return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
+ // eslint-disable-next-line no-use-before-define
+ return normalizeResults(panelDim, maxText, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
}
-export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps?: any) {
+export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps?: any */) {
const fieldKey = 'data';
const pivotDateGroups = new Map<number, Doc[]>();
const docMap = new Map<string, PoolData>();
const groupNames: ViewDefBounds[] = [];
- const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
+ const timelineFieldKey = Field.toString(pivotDoc._pivotField as FieldType);
const curTime = toNumber(pivotDoc[fieldKey + '-timelineCur']);
const curTimeSpan = Cast(pivotDoc[fieldKey + '-timelineSpan'], 'number', null);
const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMinReq'], 'number', null) : curTime && curTime - curTimeSpan;
@@ -290,7 +293,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
childPairs.forEach(pair => {
const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey])));
- if (!Number.isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
+ if (!isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
pivotDateGroups.get(num)!.push(pair.layout);
minTime = Math.min(num, minTime);
@@ -340,6 +343,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
}
+ // eslint-disable-next-line no-use-before-define
layoutDocsAtTime(keyDocs, key);
});
if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
@@ -400,11 +404,11 @@ function normalizeResults(
const height = aggBounds.b - aggBounds.y === 0 ? 1 : aggBounds.b - aggBounds.y;
const wscale = panelDim[0] / width;
let scale = wscale * height > panelDim[1] ? panelDim[1] / height : wscale;
- if (Number.isNaN(scale)) scale = 1;
+ if (isNaN(scale)) scale = 1;
Array.from(docMap.entries())
.filter(ele => ele[1].pair)
- .map(ele => {
+ .forEach(ele => {
const newPosRaw = ele[1];
if (newPosRaw) {
const newPos: PoolData = {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
index 69cbae86f..65a2fe0aa 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -3,27 +3,32 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { ScriptField } from '../../../../fields/ScriptField';
-import { PresBox } from '../../nodes/trails/PresBox';
-import { CollectionFreeFormView } from './CollectionFreeFormView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormView.scss';
+
export interface CollectionFreeFormPannableContentsProps {
Document: Doc;
viewDefDivClick?: ScriptField;
children?: React.ReactNode | undefined;
- transition?: string;
+ transition: () => string;
isAnnotationOverlay: boolean | undefined;
+ showPresPaths: () => boolean;
transform: () => string;
brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
}
@observer
-export class CollectionFreeFormPannableContents extends React.Component<CollectionFreeFormPannableContentsProps> {
+export class CollectionFreeFormPannableContents extends ObservableReactComponent<CollectionFreeFormPannableContentsProps> {
+ static _overlayPlugin: ((fform: Doc) => React.JSX.Element) | null = null;
+ public static SetOverlayPlugin(plugin: ((fform: Doc) => React.JSX.Element) | null) {
+ CollectionFreeFormPannableContents._overlayPlugin = plugin;
+ }
constructor(props: CollectionFreeFormPannableContentsProps) {
super(props);
makeObservable(this);
}
@computed get presPaths() {
- return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.Document) : null;
+ return this._props.showPresPaths() ? CollectionFreeFormPannableContents._overlayPlugin?.(this._props.Document) : 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) =>
@@ -42,7 +47,7 @@ export class CollectionFreeFormPannableContents extends React.Component<Collecti
render() {
return (
<div
- className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
+ className={'collectionfreeformview' + (this._props.viewDefDivClick ? '-viewDef' : '-none')}
onScroll={e => {
const target = e.target as any;
if (getComputedStyle(target)?.overflow === 'visible') {
@@ -50,13 +55,13 @@ export class CollectionFreeFormPannableContents extends React.Component<Collecti
}
}}
style={{
- transform: this.props.transform(),
- transition: this.props.transition,
- width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
+ transform: this._props.transform(),
+ transition: this._props.transition(),
+ width: this._props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
}}>
{this.props.children}
{this.presPaths}
- {this.showViewport(this.props.brushedView())}
+ {this.showViewport(this._props.brushedView())}
</div>
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index fa8218bdd..f64c6715b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -4,26 +4,22 @@ import * as mobxUtils from 'mobx-utils';
import * as React from 'react';
import * as uuid from 'uuid';
import CursorField from '../../../../fields/CursorField';
-import { Doc, FieldResult } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
-import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { Cast } from '../../../../fields/Types';
-import { CollectionViewProps } from '../CollectionView';
+import { CollectionViewProps } from '../CollectionSubView';
import './CollectionFreeFormView.scss';
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@computed protected get cursors(): CursorField[] {
- const doc = this.props.Document;
-
- let cursors: FieldResult<List<CursorField>>;
- const id = Doc.UserDoc()[Id];
- if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
+ const { Document } = this.props;
+ const cursors = Cast(Document.cursors, listSpec(CursorField));
+ if (!cursors) {
return [];
}
const now = mobxUtils.now();
- return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000);
+ return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== Document[Id] && now - metadata.timestamp < 1000);
}
@computed get renderedCursors() {
@@ -33,46 +29,44 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
metadata,
position: { x, y },
},
- }) => {
- return (
- <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
- <canvas
- className="collectionFreeFormRemoteCursors-canvas"
- ref={el => {
- if (el) {
- const ctx = el.getContext('2d');
- if (ctx) {
- ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22';
- ctx.fillRect(0, 0, 20, 20);
+ }) => (
+ <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
+ <canvas
+ className="collectionFreeFormRemoteCursors-canvas"
+ ref={el => {
+ if (el) {
+ const ctx = el.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillRect(0, 0, 20, 20);
- ctx.fillStyle = 'black';
- ctx.lineWidth = 0.5;
+ ctx.fillStyle = 'black';
+ ctx.lineWidth = 0.5;
- ctx.beginPath();
+ ctx.beginPath();
- ctx.moveTo(10, 0);
- ctx.lineTo(10, 8);
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
- ctx.moveTo(10, 20);
- ctx.lineTo(10, 12);
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
- ctx.moveTo(0, 10);
- ctx.lineTo(8, 10);
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
- ctx.moveTo(20, 10);
- ctx.lineTo(12, 10);
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
- ctx.stroke();
- }
+ ctx.stroke();
}
- }}
- width={20}
- height={20}
- />
- <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
- </div>
- );
- }
+ }
+ }}
+ width={20}
+ height={20}
+ />
+ <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
+ </div>
+ )
);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 079a5d977..64398a60a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,60 +1,68 @@
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
+import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
-import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { ActiveInkWidth, Doc, DocListCast, Field, FieldType, Opt, SetActiveInkColor, SetActiveInkWidth } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
+import { InkData, InkField, InkTool, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast, toList } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
+import { Gestures, PointData } from '../../../../pen-gestures/GestureTypes';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
-import { Docs, DocUtils } from '../../../documents/Documents';
+import { aggregateBounds, emptyFunction, intersectRect, Utils } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../../util/DragManager';
+import { DocUtils } from '../../../documents/DocUtils';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { freeformScrollMode } from '../../../util/SettingsManager';
-import { SnappingManager } from '../../../util/SnappingManager';
+import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { GestureOverlay } from '../../GestureOverlay';
-import { CtrlKey } from '../../GlobalKeyHandler';
-import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { InkingStroke } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
-import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
+import { DocumentView } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { PinProps, PresBox } from '../../nodes/trails/PresBox';
-import { CreateImage } from '../../nodes/WebBoxRenderer';
-import { StyleProp } from '../../StyleProvider';
+import { OpenWhere } from '../../nodes/OpenWhere';
+import { PinDocView, PinProps } from '../../PinFuncs';
+import { StyleProp } from '../../StyleProp';
import { CollectionSubView } from '../CollectionSubView';
-import { TreeViewType } from '../CollectionTreeView';
+import { TreeViewType } from '../CollectionTreeViewType';
import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
-import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
+import { CollectionFreeFormClusters } from './CollectionFreeFormClusters';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
+class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
+ render() {
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
+ }
+}
export interface collectionFreeformViewProps {
NativeWidth?: () => number;
NativeHeight?: () => number;
@@ -71,26 +79,21 @@ export interface collectionFreeformViewProps {
@observer
export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
public get displayName() {
- return 'CollectionFreeFormView(' + this.Document.title?.toString() + ')';
+ return 'CollectionFreeFormView(' + (this.Document.title?.toString() ?? '') + ')';
} // this makes mobx trace() statements more descriptive
-
- @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
- @computed get paintFunc() {
- const field = this.dataDoc[this.fieldKey];
- const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as Field)).trim();
- return !paintFunc
- ? ''
- : paintFunc.includes('dashDiv')
- ? `const dashDiv = document.querySelector('#${this._paintedId}');
- (async () => { ${paintFunc} })()`
- : paintFunc;
+ public unprocessedDocs: Doc[] = [];
+ public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
+ public static from(dv?: DocumentView): CollectionFreeFormView | undefined {
+ const parent = CollectionFreeFormDocumentView.from(dv)?._props.parent;
+ return parent instanceof CollectionFreeFormView ? parent : undefined;
}
+
+ _oldWheel: any;
+ _clusters = new CollectionFreeFormClusters(this);
constructor(props: any) {
super(props);
makeObservable(this);
}
- @observable
- public static ShowPresPaths = false;
private _panZoomTransitionTimer: any;
private _lastX: number = 0;
@@ -98,41 +101,48 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _downX: number = 0;
private _downY: number = 0;
private _downTime = 0;
- private _clusterDistance: number = 75;
- private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
private _batch: UndoManager.Batch | undefined = undefined;
private _brushtimer: any;
private _brushtimer1: any;
+ private _eraserLock = 0;
+ private _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation.
- public get isAnnotationOverlay() {
- return this._props.isAnnotationOverlay;
- }
- public get scaleFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_scale';
- }
- private get panXFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_panX';
- }
- private get panYFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_panY';
- }
- private get autoResetFieldKey() {
- return (this._props.viewField ?? '') + '_freeform_autoReset';
- }
+ private get isAnnotationOverlay() { return this._props.isAnnotationOverlay; } // prettier-ignore
+ private get scaleFieldKey() { return (this._props.viewField ?? '') + '_freeform_scale'; } // prettier-ignore
+ private get panXFieldKey() { return (this._props.viewField ?? '') + '_freeform_panX'; } // prettier-ignore
+ private get panYFieldKey() { return (this._props.viewField ?? '') + '_freeform_panY'; } // prettier-ignore
+ private get autoResetFieldKey() { return (this._props.viewField ?? '') + '_freeform_autoReset'; } // prettier-ignore
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement
@observable _showAnimTimeline = false;
- @observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined = undefined; // highlighted region of freeform canvas used by presentations to indicate a region
@observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
+ @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
+ @observable _lightboxDoc: Opt<Doc> = undefined;
+ @observable _paintedId = 'id' + Utils.GenerateGuid().replace(/-/g, '');
+ @observable _keyframeEditing = false;
+ @computed get layoutEngine() {
+ return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ }
+ @computed get childPointerEvents() {
+ const engine = this._props.layoutEngine?.() || StrCast(this.Document._layoutEngine);
+ return SnappingManager.IsResizing
+ ? 'none'
+ : this._props.childPointerEvents?.() ??
+ (this._props.viewDefDivClick || //
+ (engine === computePassLayout.name && !this._props.isSelected()) ||
+ this.isContentActive() === false
+ ? 'none'
+ : this._props.pointerEvents?.());
+ }
@computed get contentViews() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
@@ -154,14 +164,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get contentBounds() {
- const cb = Cast(this.dataDoc.contentBounds, listSpec('number'));
- return cb
- ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
- : aggregateBounds(
- this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
- NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10),
- NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10)
- );
+ return aggregateBounds(
+ this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, this._props.xPadding ?? 10),
+ NumCast(this.layoutDoc._yPadding, this._props.yPadding ?? 10)
+ );
}
@computed get nativeWidth() {
return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
@@ -192,31 +199,31 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.translate(-this.centeringShiftX, -this.centeringShiftY)
.transform(this.panZoomXf);
}
+ @computed get backgroundColor() {
+ return this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor);
+ }
+ @computed get nativeDimScaling() {
+ if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 1;
+ const nw = this.nativeWidth;
+ const nh = this.nativeHeight;
+ const hscale = nh ? this._props.PanelHeight() / nh : 1;
+ const wscale = nw ? this._props.PanelWidth() / nw : 1;
+ return wscale < hscale || (this._props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
+ }
+ @computed get paintFunc() {
+ const field = this.dataDoc[this.fieldKey];
+ const paintFunc = StrCast(Field.toJavascriptString(Cast(field, RichTextField, null)?.Text as FieldType)).trim();
+ return !paintFunc
+ ? ''
+ : paintFunc.includes('dashDiv')
+ ? `const dashDiv = document.querySelector('#${this._paintedId}');
+ (async () => { ${paintFunc} })()`
+ : paintFunc;
+ }
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true);
}
- public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number) {
- const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, timer, undefined, true);
- const timecode = Math.round(time);
- docs.forEach(doc => {
- CollectionFreeFormDocumentView.animFields.forEach(val => {
- const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as any as number);
- });
- CollectionFreeFormDocumentView.animStringFields.forEach(val => {
- const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as any as string);
- });
- CollectionFreeFormDocumentView.animDataFields(doc).forEach(val => {
- const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as any);
- });
- });
- return newTimer;
- }
-
- _keyTimer: NodeJS.Timeout | undefined; // timer for turning off transition flag when key frame change has completed. Need to clear this if you do a second navigation before first finishes, or else first timer can go off during second naviation.
changeKeyFrame = (back = false) => {
const currentFrame = Cast(this.Document._currentFrame, 'number', null);
if (currentFrame === undefined) {
@@ -227,19 +234,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], 1000);
this.Document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
} else {
- this._keyTimer = CollectionFreeFormView.updateKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], currentFrame || 0);
+ this._keyTimer = CollectionFreeFormDocumentView.updateKeyframe(this._keyTimer, [...this.childDocs, this.layoutDoc], currentFrame || 0);
this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
};
- @observable _keyframeEditing = false;
- @action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
+ @action setKeyFrameEditing = (set: boolean) => {
+ this._keyframeEditing = set;
+ };
getKeyFrameEditing = () => this._keyframeEditing;
- onBrowseClickHandler = () => this._props.onBrowseClickScript?.() || ScriptCast(this.layoutDoc.onBrowseClick);
+
onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
viewTransition = () => (this._panZoomTransition ? '' + this._panZoomTransition : undefined);
+ panZoomTransition = () => (this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null)));
fitContentOnce = () => {
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
@@ -251,41 +260,44 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// 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.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
- zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); //, NumCast(DocCast(this.Document.resolvedDataDoc)?.[this.scaleFieldKey], 1));
+ zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], 1); // , NumCast(DocCast(this.Document.resolvedDataDoc)?.[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);
isAnyChildContentActive = () => this._props.isAnyChildContentActive();
addLiveTextBox = (newDoc: Doc) => {
- FormattedTextBox.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ Doc.SetSelectOnLoad(newDoc); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newDoc);
};
selectDocuments = (docs: Doc[]) => {
- SelectionManager.DeselectAll();
- docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
+ DocumentView.DeselectAll();
+ docs.map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && DocumentView.SelectView(dv, true));
};
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
if (newBox instanceof Doc) {
- if ((retVal = this._props.addDocument?.(newBox) || false)) {
+ retVal = this._props.addDocument?.(newBox) || false;
+ if (retVal) {
this.bringToFront(newBox);
- this.updateCluster(newBox);
+ this._clusters.addDocument(newBox);
}
} else {
retVal = this._props.addDocument?.(newBox) || false;
// bcz: deal with clusters
}
if (retVal) {
- const newBoxes = newBox instanceof Doc ? [newBox] : newBox;
- for (const newBox of newBoxes) {
- if (newBox.activeFrame !== undefined) {
- const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]);
- CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}_indexed`]);
- CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]);
- delete newBox.activeFrame;
- CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i]));
+ const newBoxes = toList(newBox);
+ newBoxes.forEach(box => {
+ if (box.activeFrame !== undefined) {
+ const vals = CollectionFreeFormDocumentView.animFields.map(field => box[field.key]);
+ CollectionFreeFormDocumentView.animFields.forEach(field => delete box[`${field.key}_indexed`]);
+ CollectionFreeFormDocumentView.animFields.forEach(field => delete box[field.key]);
+ delete box.activeFrame;
+ CollectionFreeFormDocumentView.animFields.forEach((field, i) => {
+ field.key !== 'opacity' && (box[field.key] = vals[i]);
+ });
}
- }
+ });
if (this.Document._currentFrame !== undefined && !this._props.isAnnotationOverlay) {
CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
}
@@ -300,22 +312,63 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
}
+ /**
+ * focuses on a specified point in the freeform coordinate space. (alternative to focusing on a Document)
+ * @param options
+ * @returns how long a transition it will be to focus on the point, or undefined the doc is a group or something else already moved
+ */
+ focusOnPoint = (options: FocusViewOptions) => {
+ const { pointFocus, zoomTime, didMove } = options;
+ if (!this.Document.isGroup && pointFocus && !didMove) {
+ const dfltScale = this.isAnnotationOverlay ? 1 : 0.5;
+ if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) {
+ this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime);
+ options.didMove = true;
+ return zoomTime;
+ }
+ }
+ return undefined;
+ };
+
+ /**
+ * Focusing on a member of a group -
+ * Since groups can't pan and zoom like regular collections, this method focuses on a Doc in a group by
+ * focusing on the group with an additional transformation to force the final focus to be on the center of the group item.
+ * @param anchor
+ * @param options
+ * @returns
+ */
groupFocus = (anchor: Doc, options: FocusViewOptions) => {
- options.docTransform = new Transform(-NumCast(this.layoutDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.layoutDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
+ if (options.pointFocus) return undefined;
+ options.docTransform = new Transform(NumCast(anchor.x) + NumCast(anchor._width)/2 - NumCast(this.layoutDoc[this.panXFieldKey]),
+ NumCast(anchor.y) + NumCast(anchor._height)/2- NumCast(this.layoutDoc[this.panYFieldKey]), 1); // prettier-ignore
const res = this._props.focus(this.Document, options);
options.docTransform = undefined;
return res;
};
- focus = (anchor: Doc, options: FocusViewOptions) => {
- if (this._lightboxDoc) return;
- if (anchor === this.Document) {
- // if (options.willZoomCentered && options.zoomScale) {
- // this.fitContentOnce();
- // options.didMove = true;
- // }
+ /**
+ * focuses the freeform view on the anchor subject to options.
+ * If a pointFocus is specified, then groupFocus is triggered instad
+ * Otherwise, this shifts the pan and zoom to the anchor target (as specified by options).
+ * NOTE: focusing on a group only has an effet if the options contextPath is empty.
+ * @param anchor
+ * @param options
+ * @returns
+ */
+ focus = (anchor: Doc, options: FocusViewOptions): any => {
+ if (anchor.isGroup && !options.docTransform && options.contextPath?.length) {
+ // don't focus on group if there's a context path because we're about to focus on a group item
+ // which will override any group focus. (If we allowed the group to focus, it would mark didMove even if there were no net movement)
+ return undefined;
+ }
+ 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 anchorInChildViews = this.childLayoutPairs.map(pair => pair.layout).includes(anchor);
+ if (!anchorInCollection && !anchorInChildViews) {
+ return undefined;
}
- if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor) && !this.childLayoutPairs.map(pair => pair.layout).includes(anchor)) return;
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
@@ -331,13 +384,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.setPan(panX, panY, focusTime, true); // docs that are floating in their collection can't be panned to from their collection -- need to propagate the pan to a parent freeform somehow
return focusTime;
}
+ return undefined;
};
getView = async (doc: Doc, options: FocusViewOptions): Promise<Opt<DocumentView>> =>
new Promise<Opt<DocumentView>>(res => {
if (doc.hidden && this._lightboxDoc !== doc) options.didMove = !(doc.hidden = false);
- if (doc === this.Document) return res(this.DocumentView?.());
- const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
+ if (doc === this.Document) {
+ res(this.DocumentView?.());
+ return;
+ }
+ const findDoc = (finish: (dv: DocumentView) => void) => DocumentView.addViewRenderedCb(doc, dv => finish(dv));
findDoc(dv => res(dv));
});
@@ -352,7 +409,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.map(pair => pair.layout)
.slice()
.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- zsorted.forEach((doc, index) => (doc.zIndex = doc.stroke_isInkMask ? 5000 : index + 1));
+ zsorted.forEach((doc, index) => {
+ doc.zIndex = doc.stroke_isInkMask ? 5000 : index + 1;
+ });
const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
@@ -378,7 +437,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!d._keepZWhenDragged && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
- (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
+ (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this._clusters.addDocuments(docDragData.droppedDocuments);
return true;
}
@@ -405,7 +464,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
let added = false;
// do nothing if link is dropped into any freeform view parent of dragged document
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x, y, title: 'dropped annotation' });
- added = this._props.addDocument?.(source) ? true : false;
+ added = !!this._props.addDocument?.(source);
de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
if (de.complete.linkDocument) {
de.complete.linkDocument.layout_isSvg = true;
@@ -420,186 +479,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de, de.complete.annoDragData);
- else if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData);
- else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
+ 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);
return false;
};
onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.screenToFreeformContentsXf.transformPoint(e.pageX, e.pageY));
- static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
- const doc2Layout = Doc.Layout(doc2);
- const doc1Layout = Doc.Layout(doc1);
- const x2 = NumCast(doc2.x) - clusterDistance;
- const y2 = NumCast(doc2.y) - clusterDistance;
- const w2 = NumCast(doc2Layout._width) + clusterDistance;
- const h2 = NumCast(doc2Layout._height) + clusterDistance;
- const x = NumCast(doc1.x) - clusterDistance;
- const y = NumCast(doc1.y) - clusterDistance;
- const w = NumCast(doc1Layout._width) + clusterDistance;
- const h = NumCast(doc1Layout._height) + clusterDistance;
- return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
- }
- pickCluster(probe: number[]) {
- return this.childLayoutPairs
- .map(pair => pair.layout)
- .reduce((cluster, cd) => {
- const grouping = this.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
- if (grouping !== -1) {
- const layoutDoc = Doc.Layout(cd);
- const cx = NumCast(cd.x) - this._clusterDistance / 2;
- const cy = NumCast(cd.y) - this._clusterDistance / 2;
- const cw = NumCast(layoutDoc._width) + this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
- }
- return cluster;
- }, -1);
- }
-
- tryDragCluster(e: PointerEvent, cluster: number) {
- if (cluster !== -1) {
- const ptsParent = e;
- if (ptsParent) {
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
- const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.DocumentView?.())!);
- const { left, top } = clusterDocs[0].getBounds || { left: 0, top: 0 };
- const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? dropActionType.embed : undefined);
- de.moveDocument = this._props.moveDocument;
- de.offset = this.screenToFreeformContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
- DragManager.StartDocumentDrag(
- clusterDocs.map(v => v.ContentDiv!),
- de,
- ptsParent.clientX,
- ptsParent.clientY,
- { hideSource: !de.dropAction }
- );
- return true;
- }
- }
-
- return false;
- }
-
- @action
- updateClusters(_freeform_useClusters: boolean) {
- this.Document._freeform_useClusters = _freeform_useClusters;
- this._clusterSets.length = 0;
- this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
- }
-
- @action
- updateClusterDocs(docs: Doc[]) {
- const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.Document._freeform_useClusters) {
- const docFirst = docs[0];
- docs.map(doc => this._clusterSets.map(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1)));
- const preferredInd = NumCast(docFirst.layout_cluster);
- docs.map(doc => (doc.layout_cluster = -1));
- docs.map(doc =>
- this._clusterSets.map((set, i) =>
- set.map(member => {
- if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
- docFirst.layout_cluster = i;
- }
- })
- )
- );
- if (
- docFirst.layout_cluster === -1 &&
- preferredInd !== -1 &&
- this._clusterSets.length > preferredInd &&
- (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)
- ) {
- docFirst.layout_cluster = preferredInd;
- }
- this._clusterSets.map((set, i) => {
- if (docFirst.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
- docFirst.layout_cluster = i;
- }
- });
- if (docFirst.layout_cluster === -1) {
- docs.map(doc => {
- doc.layout_cluster = this._clusterSets.length;
- this._clusterSets.push([doc]);
- });
- } else if (this._clusterSets.length) {
- for (let i = this._clusterSets.length; i <= NumCast(docFirst.layout_cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
- docs.map(doc => this._clusterSets[(doc.layout_cluster = NumCast(docFirst.layout_cluster))].push(doc));
- }
- childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.layout_cluster === i) && this.updateCluster(child));
- }
- }
-
- @action
- updateCluster = (doc: Doc) => {
- const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.Document._freeform_useClusters) {
- this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
- const preferredInd = NumCast(doc.layout_cluster);
- doc.layout_cluster = -1;
- this._clusterSets.forEach((set, i) =>
- set.forEach(member => {
- if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
- doc.layout_cluster = i;
- }
- })
- );
- if (doc.layout_cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
- doc.layout_cluster = preferredInd;
- }
- this._clusterSets.forEach((set, i) => {
- if (doc.layout_cluster === -1 && !set.filter(member => Doc.IndexOf(member, childLayouts) !== -1).length) {
- doc.layout_cluster = i;
- }
- });
- if (doc.layout_cluster === -1) {
- doc.layout_cluster = this._clusterSets.length;
- this._clusterSets.push([doc]);
- } else if (this._clusterSets.length) {
- for (let i = this._clusterSets.length; i <= doc.layout_cluster; i++) !this._clusterSets[i] && this._clusterSets.push([]);
- this._clusterSets[doc.layout_cluster ?? 0].push(doc);
- }
- }
- };
-
- clusterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
- let styleProp = this._props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
- if (doc && this.childDocList?.includes(doc))
- switch (property.split(':')[0]) {
- case StyleProp.BackgroundColor:
- const cluster = NumCast(doc?.layout_cluster);
- if (this.Document._freeform_useClusters && doc?.type !== DocumentType.IMG) {
- if (this._clusterSets.length <= cluster) {
- setTimeout(() => doc && this.updateCluster(doc));
- } else {
- // choose a cluster color from a palette
- const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)'];
- styleProp = colors[cluster % colors.length];
- const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
- // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document
- set?.map(s => (styleProp = StrCast(s.backgroundColor)));
- }
- }
- break;
- case StyleProp.FillColor:
- if (doc && this.Document._currentFrame !== undefined) {
- return CollectionFreeFormDocumentView.getStringValues(doc, NumCast(this.Document._currentFrame))?.fillColor;
- }
- }
- return styleProp;
- };
-
- trySelectCluster = (addToSel: boolean) => {
- if (addToSel && this._hitCluster !== -1) {
- !addToSel && SelectionManager.DeselectAll();
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
- this.selectDocuments(eles);
- return true;
- }
- return false;
- };
-
@action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
@@ -620,27 +506,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
break;
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- this._hitCluster = this.pickCluster(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, this._hitCluster !== -1 ? true : false, false);
+ const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
}
break;
+ default:
}
}
}
};
- public unprocessedDocs: Doc[] = [];
- public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@undoBatch
onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
switch (ge.gesture) {
- default:
- case GestureUtils.Gestures.Line:
- case GestureUtils.Gestures.Circle:
- case GestureUtils.Gestures.Rectangle:
- case GestureUtils.Gestures.Triangle:
- case GestureUtils.Gestures.Stroke:
- const points = ge.points;
+ case Gestures.Text:
+ if (ge.text) {
+ const B = this.screenToFreeformContentsXf.transformPoint(ge.points[0].X, ge.points[0].Y);
+ this.addDocument(Docs.Create.TextDocument(ge.text, { title: ge.text, x: B[0], y: B[1] }));
+ e.stopPropagation();
+ }
+ break;
+ case Gestures.Line:
+ case Gestures.Circle:
+ case Gestures.Rectangle:
+ case Gestures.Triangle:
+ case Gestures.Stroke:
+ default: {
+ const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
const inkDoc = Docs.Create.InkDocument(
@@ -659,29 +551,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
this.addDocument(inkDoc);
e.stopPropagation();
- break;
- case GestureUtils.Gestures.Rectangle:
- const strokes = this.getActiveDocuments()
- .filter(doc => doc.type === DocumentType.INK)
- .map(i => {
- const d = Cast(i.stroke, InkField);
- const x = NumCast(i.x) - Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
- const y = NumCast(i.y) - Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
- return !d ? [] : d.inkData.map(pd => ({ X: x + pd.X, Y: y + pd.Y }));
- });
-
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {});
- break;
- case GestureUtils.Gestures.Text:
- if (ge.text) {
- const B = this.screenToFreeformContentsXf.transformPoint(ge.points[0].X, ge.points[0].Y);
- this.addDocument(Docs.Create.TextDocument(ge.text, { title: ge.text, x: B[0], y: B[1] }));
- e.stopPropagation();
- }
+ }
}
};
@action
- onEraserUp = (e: PointerEvent): void => {
+ onEraserUp = (): void => {
this._deleteList.forEach(ink => ink._props.removeDocument?.(ink.Document));
this._deleteList = [];
this._batch?.end();
@@ -690,12 +564,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onClick = (e: React.MouseEvent) => {
if (this._lightboxDoc) this._lightboxDoc = undefined;
- if (Utils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) {
- if (this.onBrowseClickHandler()) {
- this.onBrowseClickHandler().script.run({ documentView: this.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
- e.stopPropagation();
- e.preventDefault();
- } else if (this.isContentActive() && e.shiftKey) {
+ if (ClientUtils.isClick(e.pageX, e.pageY, this._downX, this._downY, this._downTime)) {
+ if (this.isContentActive() && e.shiftKey) {
// reset zoom of freeform view to 1-to-1 on a shift + double click
this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY), 1);
e.stopPropagation();
@@ -704,9 +574,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- @action
scrollPan = (e: WheelEvent | { deltaX: number; deltaY: number }): void => {
- PresBox.Instance?.pauseAutoPres();
+ SnappingManager.TriggerUserPanned();
this.setPan(NumCast(this.Document[this.panXFieldKey]) - e.deltaX, NumCast(this.Document[this.panYFieldKey]) - e.deltaY, 0, true);
};
@@ -714,7 +583,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
pan = (e: PointerEvent): void => {
const ctrlKey = e.ctrlKey && !e.shiftKey;
const shiftKey = e.shiftKey && !e.ctrlKey;
- PresBox.Instance?.pauseAutoPres();
+ SnappingManager.TriggerUserPanned();
this.DocumentView?.().clearViewTransition();
const [dxi, dyi] = this.screenToFreeformContentsXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.ScreenToLocalBoxXf().Rotate);
@@ -723,7 +592,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._lastY = e.clientY;
};
- _eraserLock = 0;
/**
* Erases strokes by intersecting them with an invisible "eraser stroke".
* By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
@@ -746,7 +614,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
segments.forEach(segment =>
this.forceStrokeGesture(
e,
- GestureUtils.Gestures.Stroke,
+ Gestures.Stroke,
segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
)
);
@@ -759,12 +627,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
return false;
};
- forceStrokeGesture = (e: PointerEvent, gesture: GestureUtils.Gestures, points: InkData, text?: any) => {
+ forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => {
this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, GestureOverlay.getBounds(points), text));
};
onPointerMove = (e: PointerEvent) => {
- if (this.tryDragCluster(e, this._hitCluster)) {
+ if (this._clusters.tryToDrag(e)) {
e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over
return true;
}
@@ -783,26 +651,25 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
-
- return this.childDocs
- .map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.()))
+ // prettier-ignore
+ return this.childDocs
+ .map(doc => DocumentView.getDocumentView(doc, this.DocumentView?.()))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
- .map(inkView => ({ inkViewBounds: inkView!.getBounds, inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
- .filter(
- ({ inkViewBounds }) =>
+ .map(inkView => inkView!)
+ .map(inkView => ({ inkViewBounds: inkView.getBounds, inkStroke: inkView.ComponentView as InkingStroke, inkView }))
+ .filter(({ inkViewBounds }) =>
inkViewBounds && // bounding box of eraser segment and ink stroke overlap
eraserMin.X <= inkViewBounds.right &&
eraserMin.Y <= inkViewBounds.bottom &&
eraserMax.X >= inkViewBounds.left &&
- eraserMax.Y >= inkViewBounds.top
- )
+ eraserMax.Y >= inkViewBounds.top)
.reduce(
(intersections, { inkStroke, inkView }) => {
const { inkData } = inkStroke.inkScaledData();
// Convert from screen space to ink space for the intersection.
const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
- for (var i = 0; i < inkData.length - 3; i += 4) {
+ for (let i = 0; i < inkData.length - 3; i += 4) {
const rawIntersects = InkField.Segment(inkData, i).intersects({
// compute all unique intersections
p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
@@ -828,16 +695,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
segmentInkStroke = (ink: DocumentView, excludeT: number): Segment[] => {
const segments: Segment[] = [];
- var segment: Segment = [];
- var startSegmentT = 0;
+ let segment: Segment = [];
+ let startSegmentT = 0;
const { inkData } = (ink?.ComponentView as InkingStroke).inkScaledData();
// This iterates through all segments of the curve and splits them where they intersect another curve.
// if 'excludeT' is specified, then any segment containing excludeT will be skipped (ie, deleted)
- for (var i = 0; i < inkData.length - 3; i += 4) {
+ for (let i = 0; i < inkData.length - 3; i += 4) {
const inkSegment = InkField.Segment(inkData, i);
// Getting all t-value intersections of the current curve with all other curves.
const tVals = this.getInkIntersections(i, ink, inkSegment).sort();
if (tVals.length) {
+ // eslint-disable-next-line no-loop-func
tVals.forEach((t, index) => {
const docCurveTVal = t + Math.floor(i / 4);
if (excludeT < startSegmentT || excludeT > docCurveTVal) {
@@ -886,13 +754,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs
.filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect)
.forEach(doc => {
- const otherInk = DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke;
+ const otherInk = DocumentView.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke;
const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] };
const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point));
const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt));
- for (var j = 0; j < otherCtrlPts.length - 3; j += 4) {
+ for (let j = 0; j < otherCtrlPts.length - 3; j += 4) {
const neighboringSegment = i === j || i === j - 4 || i === j + 4;
// Ensuring that the curve intersected by the eraser is not checked for further ink intersections.
+ // eslint-disable-next-line no-continue
if (ink?.Document === otherInk.Document && neighboringSegment) continue;
const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
@@ -903,7 +772,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (apt.d !== undefined && apt.d < 1 && apt.t !== undefined && !tVals.includes(apt.t)) {
tVals.push(apt.t);
}
- this.bintersects(curve, otherCurve).forEach((val: string | number, i: number) => {
+ this.bintersects(curve, otherCurve).forEach((val: string | number /* , i: number */) => {
// Converting the Bezier.js Split type to a t-value number.
const t = +val.toString().split('/')[0];
if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
@@ -948,7 +817,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerWheel = (e: React.WheelEvent): void => {
if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
- PresBox.Instance?.pauseAutoPres();
+ 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);
@@ -956,7 +825,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
switch (
!e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
Doc.UserDoc().freeformScrollMode : // no modifiers, do assigned mode
- e.ctrlKey && !CtrlKey? // otherwise, if ctrl key (pinch gesture) try to zoom else pan
+ e.ctrlKey && !SnappingManager.CtrlKey? // otherwise, if ctrl key (pinch gesture) try to zoom else pan
freeformScrollMode.Zoom : freeformScrollMode.Pan // prettier-ignore
) {
case freeformScrollMode.Pan:
@@ -966,8 +835,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.scrollPan({ deltaX: -deltaX * this.screenToFreeformContentsXf.Scale, deltaY: e.shiftKey ? 0 : -deltaY * this.screenToFreeformContentsXf.Scale });
break;
}
- default:
+ // eslint-disable-next-line no-fallthrough
case freeformScrollMode.Zoom:
+ default:
if ((e.ctrlKey || !scrollable) && this._props.isContentActive()) {
this.zoom(e.clientX, e.clientY, Math.max(-1, Math.min(1, e.deltaY))); // if (!this._props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
// e.preventDefault();
@@ -977,7 +847,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
+ setPan(panXIn: number, panYIn: number, panTime: number = 0, clamp: boolean = false) {
+ let panX = panXIn;
+ let panY = panYIn;
// this is the easiest way to do this -> will talk with Bob about using mobx to do this to remove this line of code.
if (Doc.UserDoc()?.presentationMode === 'watching') ReplayMovements.Instance.pauseFromInteraction();
@@ -1029,14 +901,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
scale * NumCast(this.dataDoc._panY_max, nativeHeight) +
(!this._props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
let newPanY = Math.max(minPanY, Math.min(maxPanY, panY));
- if (false && NumCast(this.layoutDoc.layout_scrollTop) && NumCast(this.layoutDoc._freeform_scale, minScale) !== minScale) {
- const relTop = NumCast(this.layoutDoc.layout_scrollTop) / maxScrollTop;
- this.layoutDoc.layout_scrollTop = undefined;
- newPanY = minPanY + relTop * (maxPanY - minPanY);
- } else if (fitYscroll > 2 && this.layoutDoc.layout_scrollTop === undefined && NumCast(this.layoutDoc._freeform_scale, minScale) === minScale) {
- const maxPanY = minPanY + fitYscroll;
- const relTop = (panY - minPanY) / (maxPanY - minPanY);
- setTimeout(() => (this.layoutDoc.layout_scrollTop = relTop * maxScrollTop), 10);
+ if (fitYscroll > 2 && this.layoutDoc.layout_scrollTop === undefined && NumCast(this.layoutDoc._freeform_scale, minScale) === minScale) {
+ const maxPanScrollY = minPanY + fitYscroll;
+ const relTop = (panY - minPanY) / (maxPanScrollY - minPanY);
+ setTimeout(() => {
+ this.layoutDoc.layout_scrollTop = relTop * maxScrollTop;
+ }, 10);
newPanY = minPanY;
}
!this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1088,7 +958,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._panZoomTransition = transitionTime;
this._panZoomTransitionTimer && clearTimeout(this._panZoomTransitionTimer);
this._panZoomTransitionTimer = setTimeout(
- action(() => (this._panZoomTransition = 0)),
+ action(() => {
+ this._panZoomTransition = 0;
+ }),
transitionTime
);
};
@@ -1161,7 +1033,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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);
- FormattedTextBox.SetSelectOnLoad(newDoc);
+ Doc.SetSelectOnLoad(newDoc);
FormattedTextBox.DontSelectInitialText = true;
return this.addDocument?.(newDoc);
}, 'copied text note');
@@ -1171,20 +1043,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation?.();
return this.createTextDocCopy(fieldProps, !e.altKey && e.key !== 'Tab');
}
+ return undefined;
};
- @computed get childPointerEvents() {
- const engine = this._props.layoutEngine?.() || StrCast(this.Document._layoutEngine);
- return SnappingManager.IsResizing
- ? 'none'
- : this._props.childPointerEvents?.() ??
- (this._props.viewDefDivClick || //
- (engine === computePassLayout.name && !this._props.isSelected()) ||
- this.isContentActive() === false
- ? 'none'
- : this._props.pointerEvents?.());
- }
-
- @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
childPointerEventsFunc = () => this._childPointerEvents;
childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
@@ -1192,20 +1052,22 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const childData = entry.pair.data;
return (
<CollectionFreeFormDocumentView
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...OmitKeys(entry, ['replica', 'pair']).omit}
key={childLayout[Id] + (entry.replica || '')}
Document={childLayout}
+ parent={this}
containerViewPath={this.DocumentView?.().docViewPath}
- styleProvider={this.clusterStyleProvider}
+ styleProvider={this._clusters.styleProvider}
TemplateDataDocument={childData}
dragStarting={this.dragStarting}
dragEnding={this.dragEnding}
+ isAnyChildContentActive={this.isAnyChildContentActive}
isGroupActive={this._props.isGroupActive}
renderDepth={this._props.renderDepth + 1}
hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
- suppressSetHeight={this.layoutEngine ? true : false}
+ suppressSetHeight={!!this.layoutEngine}
RenderCutoffProvider={this.renderCutoffProvider}
- CollectionFreeFormView={this}
LayoutTemplate={childLayout.z ? undefined : this._props.childLayoutTemplate}
LayoutTemplateString={childLayout.z ? undefined : this._props.childLayoutString}
rootSelected={childData ? this.rootSelected : returnFalse}
@@ -1213,7 +1075,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onClickScript={this.onChildClickHandler}
onKey={this.onKeyDown}
onDoubleClickScript={this.onChildDoubleClickHandler}
- onBrowseClickScript={this.onBrowseClickHandler}
bringToFront={this.bringToFront}
ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
PanelWidth={childLayout[Width]}
@@ -1237,39 +1098,43 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/>
);
}
- addDocTab = action((doc: Doc, where: OpenWhere) => {
- if (this._props.isAnnotationOverlay) return this._props.addDocTab(doc, where);
+ addDocTab = action((docsIn: Doc | Doc[], where: OpenWhere) => {
+ const docs = toList(docsIn);
+ if (this._props.isAnnotationOverlay) return this._props.addDocTab(docs, where);
switch (where) {
case OpenWhere.inParent:
- return this._props.addDocument?.(doc) || false;
- case OpenWhere.inParentFromScreen:
- const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.embedContainer);
+ return this._props.addDocument?.(docs) || false;
+ case OpenWhere.inParentFromScreen: {
+ const docContext = DocCast(docs[0]?.embedContainer);
return (
(this.addDocument?.(
- (doc instanceof Doc ? [doc] : doc).map(doc => {
- const pt = this.screenToFreeformContentsXf.transformPoint(NumCast(doc.x), NumCast(doc.y));
- doc.x = pt[0];
- doc.y = pt[1];
+ toList(docs).map(doc => {
+ [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(NumCast(doc.x), NumCast(doc.y));
return doc;
})
) &&
(!docContext || this._props.removeDocument?.(docContext))) ||
false
);
+ }
case undefined:
case OpenWhere.lightbox:
- if (this.layoutDoc._isLightbox) {
- this._lightboxDoc = doc;
- return true;
- }
- if (doc === this.Document || this.childDocList?.includes(doc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(doc)) {
- if (doc.hidden) doc.hidden = false;
- return true;
+ {
+ const firstDoc = docs[0];
+ if (this.layoutDoc._isLightbox) {
+ this._lightboxDoc = firstDoc;
+ return true;
+ }
+ if (firstDoc === this.Document || this.childDocList?.includes(firstDoc) || this.childLayoutPairs.map(pair => pair.layout)?.includes(firstDoc)) {
+ if (firstDoc.hidden) firstDoc.hidden = false;
+ return true;
+ }
}
+ break;
+ default:
}
- return this._props.addDocTab(doc, where);
+ return this._props.addDocTab(docs, where);
});
- @observable _lightboxDoc: Opt<Doc> = undefined;
getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
@@ -1287,16 +1152,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const rotation = Cast(_rotation,'number',
!this.layoutDoc._rotation_jitter ? null
: NumCast(this.layoutDoc._rotation_jitter) * random(-1, 1, NumCast(x), NumCast(y)) );
- const childProps = { ...this._props, fieldKey: '', styleProvider: this.clusterStyleProvider };
+ const childProps = { ...this._props, fieldKey: '', styleProvider: this._clusters.styleProvider };
return {
- x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
- y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
+ x: isNaN(NumCast(x)) ? 0 : NumCast(x),
+ y: isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
autoDim,
rotation,
- color: Cast(color, 'string') ? StrCast(color) : this.clusterStyleProvider(childDoc, childProps, StyleProp.Color),
- backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, childProps, StyleProp.BackgroundColor),
- opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.clusterStyleProvider?.(childDoc, childProps, StyleProp.Opacity),
+ color: Cast(color, 'string') ? StrCast(color) : this._clusters.styleProvider(childDoc, childProps, StyleProp.Color),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this._clusters.styleProvider(childDoc, childProps, StyleProp.BackgroundColor),
+ opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this._clusters.styleProvider?.(childDoc, childProps, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
@@ -1312,9 +1177,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
};
- viewDefsToJSX = (views: ViewDefBounds[]) => {
- return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!);
- };
+ viewDefsToJSX = (views: ViewDefBounds[]) => (!Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!));
viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
const { x, y, z } = viewDef;
@@ -1335,7 +1198,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
),
bounds: viewDef,
};
- } else if (viewDef.type === 'div') {
+ }
+ if (viewDef.type === 'div') {
return [x, y].some(val => val === undefined)
? undefined
: {
@@ -1351,13 +1215,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
bounds: viewDef,
};
}
+ return undefined;
}
- renderCutoffProvider = computedFn(
- function renderCutoffProvider(this: any, doc: Doc) {
- return this.Document.isTemplateDoc ? false : !this._renderCutoffData.get(doc[Id] + '');
- }.bind(this)
- );
+ /**
+ * Determines whether the passed doc should be rendered
+ * since rendering a large collection of documents can be slow, at startup, docs are rendered in batches.
+ * each doc's render() method will call the cutoff provider which will let the doc know if it should render itself yet, or wait
+ */
+ renderCutoffProvider = computedFn((doc: Doc) => (this.Document.isTemplateDoc ? false : !this._renderCutoffData.get(doc[Id] + '')));
doEngineLayout(
poolData: Map<string, PoolData>,
@@ -1367,27 +1233,22 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
doFreeformLayout(poolData: Map<string, PoolData>) {
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair)));
+ this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair)));
return [] as ViewDefResult[];
}
- @computed get layoutEngine() {
- return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
- }
@computed get doInternalLayoutComputation() {
TraceMobx();
const newPool = new Map<string, PoolData>();
- // prettier-ignore
switch (this.layoutEngine) {
case computePassLayout.name : return { newPool, computedElementData: this.doEngineLayout(newPool, computePassLayout) };
case computeTimelineLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
case computePivotLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
case computeStarburstLayout.name: return { newPool, computedElementData: this.doEngineLayout(newPool, computeStarburstLayout) };
- }
- return { newPool, computedElementData: this.doFreeformLayout(newPool) };
+ default: return { newPool, computedElementData: this.doFreeformLayout(newPool) };
+ } // prettier-ignore
}
- @action
doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
const elements = computedElementData.slice();
Array.from(newPool.entries())
@@ -1400,7 +1261,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
})
);
- this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
+ this._clusters.initLayout();
return elements;
};
@@ -1412,7 +1273,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
presentation_transition: 500,
annotationOn: this.Document,
});
- PresBox.pinDocView(
+ PinDocView(
anchor,
{ pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } },
this.Document
@@ -1428,8 +1289,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return anchor;
};
- @action closeInfo = () => (Doc.IsInfoUIDisabled = true);
- infoUI = () => (Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} close={this.closeInfo} />);
+ childDocsFunc = () => this.childDocs;
+ closeInfo = action(() => { Doc.IsInfoUIDisabled = true }); // prettier-ignore
+ static _infoUI: ((doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) | null = null;
+ static SetInfoUICreator(func: (doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => JSX.Element) {
+ CollectionFreeFormView._infoUI = func;
+ }
+ infoUI = () =>
+ Doc.IsInfoUIDisabled || this.Document.annotationOn || this._props.renderDepth
+ ? null //
+ : CollectionFreeFormView._infoUI?.(this.Document, this.layoutDoc, this.childDocsFunc, this.closeInfo) || null;
componentDidMount() {
this._props.setContentViewBox?.(this);
@@ -1470,7 +1339,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.pointerevents = reaction(
() => this.childPointerEvents,
- pointerevents => (this._childPointerEvents = pointerevents as any),
+ pointerevents => {
+ this._childPointerEvents = pointerevents as any;
+ },
{ fireImmediately: true }
);
@@ -1487,6 +1358,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!code.includes('dashDiv')) {
const script = CompileScript(code, { params: { docView: 'any' }, typecheck: false, editable: true });
if (script.compiled) script.run({ this: this.DocumentView?.() });
+ // eslint-disable-next-line no-eval
} else code && !first && eval?.(code);
},
{ fireImmediately: true }
@@ -1495,45 +1367,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.layoutElements = reaction(
// layoutElements can't be a computed value because doLayoutComputation() is an action that has side effect of updating clusters
() => this.doInternalLayoutComputation,
- computation => (this._layoutElements = this.doLayoutComputation(computation.newPool, computation.computedElementData)),
+ computation => {
+ this._layoutElements = this.doLayoutComputation(computation.newPool, computation.computedElementData);
+ },
{ fireImmediately: true }
);
}
- static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
- if (oldDiv.childNodes && newDiv) {
- for (let i = 0; i < oldDiv.childNodes.length; i++) {
- this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
- }
- }
- if (oldDiv instanceof HTMLCanvasElement) {
- if (oldDiv.className === 'collectionFreeFormView-grid') {
- const newCan = newDiv as HTMLCanvasElement;
- const parEle = newCan.parentElement as HTMLElement;
- parEle.removeChild(newCan);
- parEle.appendChild(document.createElement('div'));
- } else {
- const canvas = oldDiv;
- const img = document.createElement('img'); // create a Image Element
- try {
- img.src = canvas.toDataURL(); //image source
- } catch (e) {
- console.log(e);
- }
- img.style.width = canvas.style.width;
- img.style.height = canvas.style.height;
- const newCan = newDiv as HTMLCanvasElement;
- if (newCan) {
- const parEle = newCan.parentElement as HTMLElement;
- parEle.removeChild(newCan);
- parEle.appendChild(img);
- }
- }
- }
+ componentWillUnmount() {
+ this.dataDoc[this.autoResetFieldKey] && this.resetView();
+ Object.values(this._disposers).forEach(disposer => disposer?.());
}
updateIcon = () =>
- CollectionFreeFormView.UpdateIcon(
+ UpdateIcon(
this.layoutDoc[Id] + '-icon' + new Date().getTime(),
this.DocumentView?.().ContentDiv!,
NumCast(this.layoutDoc._width),
@@ -1551,55 +1398,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
);
- public static UpdateIcon(
- filename: string,
- docViewContent: HTMLElement,
- width: number,
- height: number,
- panelWidth: number,
- panelHeight: number,
- scrollTop: number,
- realNativeHeight: number,
- noSuffix: boolean,
- replaceRootFilename: string | undefined,
- cb: (iconFile: string, nativeWidth: number, nativeHeight: number) => any
- ) {
- const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
- newDiv.style.width = width.toString();
- newDiv.style.height = height.toString();
- this.replaceCanvases(docViewContent, newDiv);
- const htmlString = new XMLSerializer().serializeToString(newDiv);
- const nativeWidth = width;
- const nativeHeight = height;
- return CreateImage(Utils.prepend(''), document.styleSheets, htmlString, nativeWidth, (nativeWidth * panelHeight) / panelWidth, (scrollTop * panelHeight) / realNativeHeight)
- .then(async (data_url: any) => {
- const returnedFilename = await Utils.convertDataUri(data_url, filename, noSuffix, replaceRootFilename);
- cb(returnedFilename as string, nativeWidth, nativeHeight);
- })
- .catch(function (error: any) {
- console.error('oops, something went wrong!', error);
- });
- }
-
- componentWillUnmount() {
- this.dataDoc[this.autoResetFieldKey] && this.resetView();
- Object.values(this._disposers).forEach(disposer => disposer?.());
- }
-
- @action
- onCursorMove = (e: React.PointerEvent) => {
+ onCursorMove = () => {
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@undoBatch
promoteCollection = () => {
const childDocs = this.childDocs.slice();
- childDocs.forEach(doc => {
+ childDocs.forEach(docIn => {
+ const doc = docIn;
const scr = this.screenToFreeformContentsXf.inverse().transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = scr?.[0];
doc.y = scr?.[1];
});
- this._props.addDocTab(childDocs as any as Doc, OpenWhere.inParentFromScreen);
+ this._props.addDocTab(childDocs, OpenWhere.inParentFromScreen);
};
@undoBatch
@@ -1608,7 +1420,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const width = Math.max(...docs.map(doc => NumCast(doc._width))) + 20;
const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
const dim = Math.ceil(Math.sqrt(docs.length));
- docs.forEach((doc, i) => {
+ docs.forEach((docIn, i) => {
+ const doc = docIn;
doc.x = NumCast(this.Document[this.panXFieldKey]) + (i % dim) * width - (width * dim) / 2;
doc.y = NumCast(this.Document[this.panYFieldKey]) + Math.floor(i / dim) * height - (height * dim) / 2;
});
@@ -1641,7 +1454,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- onContextMenu = (e: React.MouseEvent) => {
+ onContextMenu = () => {
if (this._props.isAnnotationOverlay || !ContextMenu.Instance) return;
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
@@ -1652,7 +1465,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
return;
}
- !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
+ !Doc.noviceMode &&
+ Doc.UserDoc().defaultTextLayout &&
+ appearanceItems.push({
+ description: 'Reset default note style',
+ event: () => {
+ Doc.UserDoc().defaultTextLayout = undefined;
+ },
+ icon: 'eye',
+ });
appearanceItems.push({ description: `Pin View`, event: () => this._props.pinToPres(this.Document, { pinViewport: MarqueeView.CurViewBounds(this.dataDoc, this._props.PanelWidth(), this._props.PanelHeight()) }), icon: 'map-pin' });
!Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
@@ -1660,15 +1481,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
!Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
- !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
+ !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this._clusters.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
const options = ContextMenu.Instance.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
!this._props.isAnnotationOverlay &&
!Doc.noviceMode &&
- optionItems.push({ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline', event: action(() => (this._showAnimTimeline = !this._showAnimTimeline)), icon: 'eye' });
- this._props.renderDepth && optionItems.push({ description: 'Use Background Color as Default', event: () => (Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor)), icon: 'palette' });
+ optionItems.push({
+ description: (this._showAnimTimeline ? 'Close' : 'Open') + ' Animation Timeline',
+ event: action(() => {
+ this._showAnimTimeline = !this._showAnimTimeline;
+ }),
+ icon: 'eye',
+ });
+ this._props.renderDepth &&
+ optionItems.push({
+ description: 'Use Background Color as Default',
+ event: () => {
+ Cast(Doc.UserDoc().emptyCollection, Doc, null).backgroundColor = StrCast(this.layoutDoc.backgroundColor);
+ },
+ icon: 'palette',
+ });
this._props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
if (!Doc.noviceMode) {
optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
@@ -1707,15 +1541,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
- activeDocs
- .filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))
- .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
+ activeDocs.filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)).forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.screenToFreeformContentsXf.inverse();
snappableDocs
- .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))))
+ .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = invXf.transformPoint(left, top);
@@ -1731,29 +1563,41 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
incrementalRender = action(() => {
if (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.())) {
- const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
+ const layoutUnrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = this.Document.isTemplateDoc ? Number.MAX_VALUE : 5;
- for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) {
- this._renderCutoffData.set(layout_unrendered[i][Id] + '', true);
+ for (let i = 0; i < Math.min(layoutUnrendered.length, loadIncrement); i++) {
+ this._renderCutoffData.set(layoutUnrendered[i][Id] + '', true);
}
}
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
- @computed get placeholder() {
- return (
- <div className="collectionfreeformview-placeholder" style={{ background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.Document.annotationOn ? '' : this.Document.title?.toString()}</span>
- </div>
- );
- }
-
+ showPresPaths = () => SnappingManager.ShowPresPaths;
brushedView = () => this._brushedView;
- gridColor = () =>
- DashColor(lightOrDark(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor)))
- .fade(0.5)
- .toString();
- @computed get backgroundGrid() {
+ gridColor = () => DashColor(lightOrDark(this.backgroundColor)).fade(0.5).toString(); // prettier-ignore
+ nativeDim = () => this.nativeDimScaling;
+
+ brushView = action((viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => {
+ this._brushtimer1 && clearTimeout(this._brushtimer1);
+ this._brushtimer && clearTimeout(this._brushtimer);
+ this._brushedView = undefined;
+ this._brushtimer1 = setTimeout(
+ action(() => {
+ this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
+ this._brushtimer = setTimeout(action(() => { this._brushedView = undefined; }), holdTime); // prettier-ignore
+ }),
+ transTime + 1
+ );
+ });
+ lightboxPanelWidth = () => Math.max(0, this._props.PanelWidth() - 30);
+ lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
+ lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
+ onPassiveWheel = (e: WheelEvent) => {
+ const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
+ this._props.isSelected() && !scrollable && e.preventDefault();
+ };
+ get backgroundGrid() {
return (
<div>
<CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render when taking snapshot of a dashboard and the background grid is on!!?
@@ -1780,7 +1624,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.PanZoomCenterXf}
- transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.Document._viewTransition, 'string', null))}
+ showPresPaths={this.showPresPaths}
+ transition={this.panZoomTransition}
viewDefDivClick={this._props.viewDefDivClick}>
{this.props.children ?? null} {/* most likely case of children is document content that's being annoated: eg., an image */}
{this.contentViews}
@@ -1797,7 +1642,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
- trySelectCluster={this.trySelectCluster}
+ trySelectCluster={this._clusters.tryToSelect}
activeDocuments={this.getActiveDocuments}
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
@@ -1813,39 +1658,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</MarqueeView>
);
}
-
- @computed get nativeDimScaling() {
- if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 1;
- const nw = this.nativeWidth;
- const nh = this.nativeHeight;
- const hscale = nh ? this._props.PanelHeight() / nh : 1;
- const wscale = nw ? this._props.PanelWidth() / nw : 1;
- return wscale < hscale || (this._props.layout_fitWidth?.(this.Document) ?? this.layoutDoc.layout_fitWidth) ? wscale : hscale;
- }
- nativeDim = () => this.nativeDimScaling;
-
- @action
- brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => {
- this._brushtimer1 && clearTimeout(this._brushtimer1);
- this._brushtimer && clearTimeout(this._brushtimer);
- this._brushedView = undefined;
- this._brushtimer1 = setTimeout(
- action(() => {
- this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
- this._brushtimer = setTimeout(action(() => (this._brushedView = undefined)), holdTime); // prettier-ignore
- }),
- transTime + 1
+ get placeholder() {
+ return (
+ <div className="collectionfreeformview-placeholder" style={{ background: this.backgroundColor }}>
+ <span className="collectionfreeformview-placeholderSpan">{this.Document.annotationOn ? '' : this.Document.title?.toString()}</span>
+ </div>
);
- };
- lightboxPanelWidth = () => Math.max(0, this._props.PanelWidth() - 30);
- lightboxPanelHeight = () => Math.max(0, this._props.PanelHeight() - 30);
- lightboxScreenToLocal = () => this.ScreenToLocalBoxXf().translate(-15, -15);
- onPassiveWheel = (e: WheelEvent) => {
- const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
- const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling;
- this._props.isSelected() && !scrollable && e.preventDefault();
- };
- _oldWheel: any;
+ }
render() {
TraceMobx();
return (
@@ -1889,7 +1708,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onClickScript={this.onChildClickHandler}
onKey={this.onKeyDown}
onDoubleClickScript={this.onChildDoubleClickHandler}
- onBrowseClickScript={this.onBrowseClickHandler}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
@@ -1912,47 +1730,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
- render() {
- return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
- }
-}
-
-export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
- const browseTransitionTime = 500;
- SelectionManager.DeselectAll();
- dv &&
- DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
- if (!focused) {
- const selfFfview = !dv.Document.isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
- let containers = dv.containerViewPath?.() ?? [];
- let parFfview = dv.CollectionFreeFormView;
- for (var cont of containers) {
- parFfview = parFfview ?? cont.CollectionFreeFormView;
- }
- while (parFfview?.Document.isGroup) parFfview = parFfview.DocumentView?.().CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.layoutDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
- ffview?.zoomSmoothlyAboutPt(ffview.screenToFreeformContentsXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
- Doc.linkFollowHighlight(dv?.Document, false);
- }
- });
-}
-ScriptingGlobals.add(CollectionBrowseClick);
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+ !readOnly && (DocumentView.Selected()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+ !readOnly && (DocumentView.Selected()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
- const selView = SelectionManager.Views;
+ const selView = DocumentView.Selected();
if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
+ return undefined;
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
- SelectionManager.Views.forEach(view =>
+ DocumentView.Selected().forEach(view =>
view._props.pinToPres(view.Document, {
currentFrame: Cast(view.Document.currentFrame, 'number', null),
pinData: {
@@ -1963,29 +1758,33 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
})
);
});
+// eslint-disable-next-line prefer-arrow-callback
ScriptingGlobals.add(function bringToFront() {
- SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
+ DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document));
});
-ScriptingGlobals.add(function sendToBack(doc: Doc) {
- SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function sendToBack() {
+ DocumentView.Selected().forEach(view => CollectionFreeFormView.from(view)?.bringToFront(view.Document, true));
});
-ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function datavizFromSchema() {
// creating a dataviz doc to represent the schema table
- SelectionManager.Views.forEach(view => {
+ DocumentView.Selected().forEach(viewIn => {
+ const view = viewIn;
if (!view.layoutDoc.schema_columnKeys) {
view.layoutDoc.schema_columnKeys = new List<string>(['title', 'type', 'author', 'author_date']);
}
- const keys = Cast(view.layoutDoc.schema_columnKeys, listSpec('string'))?.filter(key => key != 'text');
+ 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)]);
- let csvRows = [];
+ const csvRows = [];
csvRows.push(keys.join(','));
for (let i = 0; i < children.length; i++) {
- let eachRow = [];
+ const eachRow = [];
for (let j = 0; j < keys.length; j++) {
- var cell = children[i][keys[j]]?.toString();
- if (cell) cell = cell.toString().replace(/\,/g, '');
+ let cell = children[i][keys[j]]?.toString();
+ if (cell) cell = cell.toString().replace(/,/g, '');
eachRow.push(cell);
}
csvRows.push(eachRow);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 79cc534dc..adac5a102 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -9,6 +9,7 @@ import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@observer
export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
+ // eslint-disable-next-line no-use-before-define
static Instance: MarqueeOptionsMenu;
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction;
@@ -29,14 +30,13 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
}
render() {
- const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ width: 19 }} />;
const buttons = (
<>
- <IconButton tooltip={'Create a Collection'} onPointerDown={this.createCollection} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
- <IconButton tooltip={'Create a Grouping'} onPointerDown={e => this.createCollection(e, true)} icon={<FontAwesomeIcon icon="layer-group" />} color={this.userColor} />
- <IconButton tooltip={'Summarize Documents'} onPointerDown={this.summarize} icon={<FontAwesomeIcon icon="compress-arrows-alt" />} color={this.userColor} />
- <IconButton tooltip={'Delete Documents'} onPointerDown={this.delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={this.userColor} />
- <IconButton tooltip={'Pin selected region'} onPointerDown={this.pinWithView} icon={<FontAwesomeIcon icon="map-pin" />} color={this.userColor} />
+ <IconButton tooltip="Create a Collection" onPointerDown={this.createCollection} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
+ <IconButton tooltip="Create a Grouping" onPointerDown={e => this.createCollection(e, true)} icon={<FontAwesomeIcon icon="layer-group" />} color={this.userColor} />
+ <IconButton tooltip="Summarize Documents" onPointerDown={this.summarize} icon={<FontAwesomeIcon icon="compress-arrows-alt" />} color={this.userColor} />
+ <IconButton tooltip="Delete Documents" onPointerDown={this.delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={this.userColor} />
+ <IconButton tooltip="Pin selected region" onPointerDown={this.pinWithView} icon={<FontAwesomeIcon icon="map-pin" />} color={this.userColor} />
</>
);
return this.getElement(buttons);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 6eca91e9d..b96444024 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,9 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, intersectRect, lightOrDark, returnFalse } from '../../../../Utils';
+import { ClientUtils, lightOrDark, returnFalse } from '../../../../ClientUtils';
+import { intersectRect } from '../../../../Utils';
import { Doc, Opt } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
@@ -12,22 +14,24 @@ import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { DocUtils } from '../../../documents/DocUtils';
import { DocumentType } from '../../../documents/DocumentTypes';
-import { DocUtils, Docs, DocumentOptions } from '../../../documents/Documents';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { freeformScrollMode } from '../../../util/SettingsManager';
-import { SnappingManager } from '../../../util/SnappingManager';
+import { Docs, DocumentOptions } from '../../../documents/Documents';
+import { SnappingManager, freeformScrollMode } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { MarqueeViewBounds } from '../../PinFuncs';
import { PreviewCursor } from '../../PreviewCursor';
-import { OpenWhere } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
+import { OpenWhere } from '../../nodes/OpenWhere';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { SubCollectionViewProps } from '../CollectionSubView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
+
interface MarqueeViewProps {
getContainerTransform: () => Transform;
getTransform: () => Transform;
@@ -44,13 +48,6 @@ interface MarqueeViewProps {
slowLoadDocuments: (files: File[] | string, options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => Promise<void>;
}
-export interface MarqueeViewBounds {
- left: number;
- top: number;
- width: number;
- height: number;
-}
-
@observer
export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps & MarqueeViewProps> {
public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
@@ -102,7 +99,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
@action
onKeyDown = (e: KeyboardEvent) => {
- //make textbox and add it to this collection
+ // make textbox and add it to this collection
// tslint:disable-next-line:prefer-const
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
@@ -141,7 +138,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
}
}
let ypos = y;
- ns.map(line => {
+ ns.forEach(line => {
const indent = line.search(/\S|$/);
const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + (indent / 3) * 10, y: ypos, title: line });
this._props.addDocument?.(newBox);
@@ -155,7 +152,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
pasteImageBitmap((data: any, error: any) => {
error && console.log(error);
data &&
- Utils.convertDataUri(data, this._props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+ ClientUtils.convertDataUri(data, this._props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
this._props.Document['thumb-frozen'] = new ImageField(returnedfilename);
});
})
@@ -165,11 +162,11 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
slide.x = x;
slide.y = y;
- FormattedTextBox.SelectOnLoad = slide[Id];
+ Doc.SetSelectOnLoad(slide);
TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
this._props.addDocument?.(slide);
e.stopPropagation();
- }*/ else if (e.key === 'p' && e.ctrlKey) {
+ } */ else if (e.key === 'p' && e.ctrlKey) {
e.preventDefault();
(async () => {
const text: string = await navigator.clipboard.readText();
@@ -177,14 +174,14 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this.pasteTable(ns, x, y);
})();
e.stopPropagation();
- } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.length < 2) {
+ } else if (!e.ctrlKey && !e.metaKey && DocumentView.Selected().length < 2) {
FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100));
e.stopPropagation();
}
};
- //heuristically converts pasted text into a table.
+ // heuristically converts pasted text into a table.
// assumes each entry is separated by a tab
// skips all rows until it gets to a row with more than one entry
// assumes that 1st row has header entry for each column
@@ -192,15 +189,15 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
// any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
// assumes each cell is a string or a number
pasteTable(ns: string[], x: number, y: number) {
- let csvRows = [];
+ const csvRows = [];
const headers = ns[0].split('\t');
csvRows.push(headers.join(','));
ns[0] = '';
const eachCell = ns.join('\t').split('\t');
let eachRow = [];
for (let i = 1; i < eachCell.length; i++) {
- eachRow.push(eachCell[i].replace(/\,/g, ''));
- if (i % headers.length == 0) {
+ eachRow.push(eachCell[i].replace(/,/g, ''));
+ if (i % headers.length === 0) {
csvRows.push(eachRow);
eachRow = [];
}
@@ -233,7 +230,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this._lastY = e.pageY;
this._lassoPts.push([e.clientX, e.clientY]);
if (!e.cancelBubble) {
- if (!Utils.isClick(this._lastX, this._lastY, this._downX, this._downY, Date.now())) {
+ if (!ClientUtils.isClick(this._lastX, this._lastY, this._downX, this._downY, Date.now())) {
if (!this._commandExecuted) {
this.showMarquee();
}
@@ -251,7 +248,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
if (this._visible) {
const mselect = this.marqueeSelect();
if (!e.shiftKey) {
- SelectionManager.DeselectAll(mselect.length ? undefined : this._props.Document);
+ DocumentView.DeselectAll(mselect.length ? undefined : this._props.Document);
}
const docs = mselect.length ? mselect : [this._props.Document];
this._props.selectDocuments(docs);
@@ -320,7 +317,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
@action
onClick = (e: React.MouseEvent): void => {
if (this._props.pointerEvents?.() === 'none') return;
- if (Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
+ if (ClientUtils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
if (Doc.ActiveTool === InkTool.None) {
if (!this._props.trySelectCluster(e.shiftKey)) {
!SnappingManager.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document);
@@ -335,15 +332,21 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
};
@action
- showMarquee = () => (this._visible = true);
+ showMarquee = () => {
+ this._visible = true;
+ };
@action
- hideMarquee = () => (this._visible = false);
+ hideMarquee = () => {
+ this._visible = false;
+ };
@undoBatch
delete = action((e?: React.PointerEvent<Element> | KeyboardEvent | undefined, hide?: boolean) => {
const selected = this.marqueeSelect(false);
- SelectionManager.DeselectAll();
- selected.forEach(doc => (hide ? (doc.hidden = true) : this._props.removeDocument?.(doc)));
+ DocumentView.DeselectAll();
+ selected.forEach(doc => {
+ hide ? (doc.hidden = true) : this._props.removeDocument?.(doc);
+ });
this.cleanupInteractions(false);
MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -374,9 +377,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
});
@undoBatch
- pileup = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
+ pileup = action(() => {
const selected = this.marqueeSelect(false);
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
selected.forEach(d => this._props.removeDocument?.(d));
const newCollection = DocUtils.pileup(selected, this.Bounds.left + this.Bounds.width / 2, this.Bounds.top + this.Bounds.height / 2)!;
this._props.addDocument?.(newCollection);
@@ -386,7 +389,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
});
/**
- * This triggers the TabDocView.PinDoc method which is the universal method
+ * This triggers the DocumentView.PinDoc method which is the universal method
* used to pin documents to the currently active presentation trail.
*
* This one is unique in that it includes the bounds associated with marquee view.
@@ -482,7 +485,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
});
@undoBatch
- summary = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
+ summary = action(() => {
const selected = this.marqueeSelect(false).map(d => {
this._props.removeDocument?.(d);
d.x = NumCast(d.x) - this.Bounds.left;
@@ -527,8 +530,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
(e as any).propagationIsStopped = true;
if (e.key === 'g') this.collection(e, true);
if (e.key === 'c' || e.key === 't') this.collection(e);
- if (e.key === 's' || e.key === 'S') this.summary(e);
- if (e.key === 'p') this.pileup(e);
+ if (e.key === 's' || e.key === 'S') this.summary();
+ if (e.key === 'p') this.pileup();
this.cleanupInteractions(false);
}
if (e.key === 'r' || e.key === ' ') {
@@ -540,6 +543,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
};
touchesLine(r1: { left: number; top: number; width: number; height: number }) {
+ // eslint-disable-next-line no-restricted-syntax
for (const lassoPt of this._lassoPts) {
const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
@@ -560,6 +564,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
let hasLeft = false;
let hasBottom = false;
let hasRight = false;
+ // eslint-disable-next-line no-restricted-syntax
for (const lassoPt of this._lassoPts) {
const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
@@ -662,6 +667,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
};
render() {
return (
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events
<div
className="marqueeView"
ref={r => {
@@ -673,7 +679,9 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}
- onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}
+ onScroll={e => {
+ e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0;
+ }}
onClick={this.onClick}
onPointerDown={this.onPointerDown}>
{this._visible ? this.marqueeDiv : null}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index f25872c2b..2d9191dd7 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,10 +1,11 @@
import { action, computed, Lambda, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { returnFalse, returnZero, setupMoveUpEvents } from '../../../../ClientUtils';
import { Doc, Opt } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
+import { emptyFunction } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
@@ -12,10 +13,10 @@ import { undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionGridView.scss';
import Grid, { Layout } from './Grid';
+
@observer
export class CollectionGridView extends CollectionSubView() {
private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -76,15 +77,13 @@ export class CollectionGridView extends CollectionSubView() {
pairs.forEach((pair, i) => {
const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
if (existing) newLayouts.push(existing);
- else {
- if (Object.keys(this.dropLocation).length) {
- // external drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
- this.dropLocation = {};
- } else {
- // internal drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
- }
+ else if (Object.keys(this.dropLocation).length) {
+ // external drop
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
+ this.dropLocation = {};
+ } else {
+ // internal drop
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
}
});
pairs?.length && this.setLayoutList(newLayouts);
@@ -139,9 +138,7 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Creates a layout object for a grid item
*/
- makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => {
- return { i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static };
- };
+ makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
/**
* Adds a layout to the list of layouts.
@@ -189,6 +186,7 @@ export class CollectionGridView extends CollectionSubView() {
getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
return (
<DocumentView
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
@@ -218,7 +216,7 @@ export class CollectionGridView extends CollectionSubView() {
if (this.flexGrid) {
const savedLayouts = this.savedLayoutList;
this.childLayoutPairs.forEach(({ layout: doc }) => {
- const gridLayout = savedLayouts.find(gridLayout => gridLayout.i === doc[Id]);
+ const gridLayout = savedLayouts.find(layout => layout.i === doc[Id]);
if (gridLayout) Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
});
@@ -317,7 +315,9 @@ export class CollectionGridView extends CollectionSubView() {
e,
returnFalse,
action(() => {
- undoBatch(() => (this.Document.gridRowHeight = this._rowHeight))();
+ undoBatch(() => {
+ this.Document.gridRowHeight = this._rowHeight;
+ })();
this._rowHeight = undefined;
}),
emptyFunction,
@@ -331,8 +331,20 @@ export class CollectionGridView extends CollectionSubView() {
*/
onContextMenu = () => {
const displayOptionsMenu: ContextMenuProps[] = [];
- displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.Document.display = this.Document.display ? undefined : 'contents'), icon: 'copy' });
- displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.Document.centerY = !this.Document.centerY), icon: 'copy' });
+ displayOptionsMenu.push({
+ description: 'Toggle Content Display Style',
+ event: () => {
+ this.Document.display = this.Document.display ? undefined : 'contents';
+ },
+ icon: 'copy',
+ });
+ displayOptionsMenu.push({
+ description: 'Toggle Vertical Centering',
+ event: () => {
+ this.Document.centerY = !this.Document.centerY;
+ },
+ icon: 'copy',
+ });
ContextMenu.Instance.addItem({ description: 'Display', subitems: displayOptionsMenu, icon: 'tv' });
};
@@ -346,14 +358,14 @@ export class CollectionGridView extends CollectionSubView() {
e,
returnFalse,
returnFalse,
- (e: PointerEvent, doubleTap?: boolean) => {
- if (doubleTap && !e.button) {
+ (clickEv: PointerEvent, doubleTap?: boolean) => {
+ if (doubleTap && !clickEv.button) {
undoBatch(
action(() => {
const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
- FormattedTextBox.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
+ Doc.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
Doc.AddDocToList(this.Document, this._props.fieldKey, text);
- this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
+ this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY))));
})
)();
}
@@ -386,7 +398,7 @@ export class CollectionGridView extends CollectionSubView() {
width={this._props.PanelWidth()}
nodeList={this.contents.length ? this.contents : null}
layout={this.contents.length ? this.renderedLayoutList : undefined}
- childrenDraggable={this._props.isSelected() ? true : false}
+ childrenDraggable={!!this._props.isSelected()}
numCols={this.numCols}
rowHeight={this.rowHeight}
setLayout={this.setLayout}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 228af78aa..eac0dc0e1 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,25 +1,27 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from 'browndash-components';
import { IReactionDisposer, action, makeObservable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, returnEmptyDoclist, returnTrue } from '../../../../Utils';
+import { ClientUtils, returnTrue } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { Doc, Opt } from '../../../../fields/Doc';
import { Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../../util/DragManager';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { UndoStack } from '../../UndoStack';
import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { DocumentView } from '../../nodes/DocumentView';
import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
-import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionLinearView.scss';
@@ -46,13 +48,15 @@ export class CollectionLinearView extends CollectionSubView() {
this._dropDisposer?.();
this._widthDisposer?.();
this._selectedDisposer?.();
- this.childLayoutPairs.map((pair, ind) => ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
+ this.childLayoutPairs.map(pair => ScriptCast(DocCast(pair.layout.proto)?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
}
componentDidMount() {
this._widthDisposer = reaction(
() => 5 + NumCast(this.dataDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
- width => this.childDocs.length && (this.layoutDoc._width = width),
+ width => {
+ this.childDocs.length && (this.layoutDoc._width = width);
+ },
{ fireImmediately: true }
);
}
@@ -64,14 +68,14 @@ export class CollectionLinearView extends CollectionSubView() {
dimension = () => NumCast(this.layoutDoc._height);
getTransform = (ele: Opt<HTMLDivElement>) => {
if (!ele) return Transform.Identity();
- const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
+ const { translateX, translateY } = ClientUtils.GetScreenTransform(ele);
return new Transform(-translateX, -translateY, 1);
};
@action
exitLongLinks = () => {
if (DocumentLinksButton.StartLink?.Document) {
- action((e: React.PointerEvent<HTMLDivElement>) => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc));
+ action(() => Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc));
}
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
@@ -97,8 +101,8 @@ export class CollectionLinearView extends CollectionSubView() {
e.preventDefault();
};
- getLinkUI = () => {
- return !DocumentLinksButton.StartLink ? null : (
+ getLinkUI = () =>
+ !DocumentLinksButton.StartLink ? null : (
<span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
<span className="bottomPopup-text">
Creating link from:{' '}
@@ -108,7 +112,7 @@ export class CollectionLinearView extends CollectionSubView() {
</b>
</span>
- <Tooltip title={<div className="dash-tooltip">{'Toggle description pop-up'} </div>} placement="top">
+ <Tooltip title={<div className="dash-tooltip">Toggle description pop-up </div>} placement="top">
<span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
Labels: {LinkDescriptionPopup.Instance.showDescriptions ? LinkDescriptionPopup.Instance.showDescriptions : 'ON'}
</span>
@@ -121,16 +125,15 @@ export class CollectionLinearView extends CollectionSubView() {
</Tooltip>
</span>
);
- };
- getCurrentlyPlayingUI = () => {
- return !CollectionStackedTimeline.CurrentlyPlaying?.length ? null : (
+ getCurrentlyPlayingUI = () =>
+ !DocumentView.CurrentlyPlaying?.length ? null : (
<span className="bottomPopup-background">
<span className="bottomPopup-text">
Currently playing:
- {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
+ {DocumentView.CurrentlyPlaying.map((clip, i) => (
<>
- <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}>
- {clip.Document.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')}
+ <span className="audio-title" onPointerDown={() => DocumentView.showDocument(clip.Document, { willZoomCentered: true })}>
+ {clip.Document.title + (i === DocumentView.CurrentlyPlaying.length - 1 ? ' ' : ',')}
</span>
<FontAwesomeIcon icon={!clip.ComponentView?.IsPlaying?.() ? 'play' : 'pause'} size="lg" onPointerDown={() => clip.ComponentView?.TogglePause?.()} />{' '}
<FontAwesomeIcon icon="times" size="lg" onPointerDown={() => clip.ComponentView?.Pause?.()} />{' '}
@@ -139,7 +142,6 @@ export class CollectionLinearView extends CollectionSubView() {
</span>
</span>
);
- };
getDisplayDoc = (doc: Doc, preview: boolean = false) => {
// hack to avoid overhead of making UndoStack,etc into DocumentView style Boxes. If the UndoStack is ever intended to become part of the persisten state of the dashboard, then this would have to change.
@@ -149,6 +151,7 @@ export class CollectionLinearView extends CollectionSubView() {
case '<CurrentlyPlayingUI>': return this.getCurrentlyPlayingUI();
case '<UndoStack>': return <UndoStack key={doc[Id]}/>;
case '<Branching>': return Doc.UserDoc().isBranchingMode ? <BranchingTrailManager key={doc[Id]} /> : null;
+ default:
}
const nested = doc._type_collection === CollectionViewType.Linear;
@@ -161,7 +164,9 @@ export class CollectionLinearView extends CollectionSubView() {
<div
className={preview ? 'preview' : `collectionLinearView-docBtn`}
key={doc[Id]}
- ref={r => (dref = r || undefined)}
+ ref={r => {
+ dref = r || undefined;
+ }}
style={{
pointerEvents: 'all',
width: NumCast(doc._width),
@@ -194,7 +199,7 @@ export class CollectionLinearView extends CollectionSubView() {
childFilters={this._props.childFilters}
childFiltersByRanges={this._props.childFiltersByRanges}
searchFilterDocs={this._props.searchFilterDocs}
- hideResizeHandles={true}
+ hideResizeHandles
/>
</div>
);
@@ -220,30 +225,26 @@ export class CollectionLinearView extends CollectionSubView() {
ScriptCast(this.Document.onClick)?.script.run({ this: this.Document }, console.log);
}}
tooltip={isExpanded ? 'Close' : 'Open'}
- fillWidth={true}
- align={'center'}
+ fillWidth
+ align="center"
/>
);
return (
<div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsOpen ? undefined : 'transparent' }}>
<div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
- {
- <>
- {!this.layoutDoc.linearView_Expandable ? null : menuOpener}
- {!this.layoutDoc.linearView_IsOpen ? null : (
- <div
- className="collectionLinearView-content"
- style={{
- height: this.dimension(),
- flexDirection: flexDir as any,
- gap: flexGap,
- }}>
- {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
- </div>
- )}
- </>
- }
+ {!this.layoutDoc.linearView_Expandable ? null : menuOpener}
+ {!this.layoutDoc.linearView_IsOpen ? null : (
+ <div
+ className="collectionLinearView-content"
+ style={{
+ height: this.dimension(),
+ flexDirection: flexDir as any,
+ gap: flexGap,
+ }}>
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+ </div>
+ )}
</div>
</div>
);
diff --git a/src/client/views/collections/collectionLinear/index.ts b/src/client/views/collections/collectionLinear/index.ts
index ff73e14ae..ab3b4b0b5 100644
--- a/src/client/views/collections/collectionLinear/index.ts
+++ b/src/client/views/collections/collectionLinear/index.ts
@@ -1 +1 @@
-export * from "./CollectionLinearView"; \ No newline at end of file
+export * from './CollectionLinearView';
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 125dd2781..b8509a005 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,3 +1,5 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Button, IconButton } from 'browndash-components';
@@ -7,7 +9,7 @@ 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, dropActionType } from '../../../util/DragManager';
+import { DragManager } from '../../../util/DragManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, undoable } from '../../../util/UndoManager';
@@ -16,6 +18,7 @@ import { CollectionSubView } from '../CollectionSubView';
import './CollectionMulticolumnView.scss';
import ResizeBar from './MulticolumnResizer';
import WidthLabel from './MulticolumnWidthLabel';
+import { dropActionType } from '../../../util/DropActionTypes';
interface WidthSpecifier {
magnitude: number;
@@ -80,7 +83,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
private get resolvedLayoutInformation(): LayoutData {
let starSum = 0;
const widthSpecifiers: WidthSpecifier[] = [];
- this.childLayouts.map(layout => {
+ this.childLayouts.forEach(layout => {
const unit = StrCast(layout._dimUnit, '*');
const magnitude = NumCast(layout._dimMagnitude, this.minimumDim);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
@@ -140,6 +143,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._xMargin);
}
+ return undefined;
}
/**
@@ -159,6 +163,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) {
return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum;
}
+ return undefined;
}
/**
@@ -175,7 +180,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
* or the ratio width evaluated to a pixel value
*/
private lookupPixels = (layout: Doc): number => {
- const columnUnitLength = this.columnUnitLength;
+ const { columnUnitLength } = this;
if (columnUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
@@ -193,19 +198,19 @@ export class CollectionMulticolumnView extends CollectionSubView() {
* documents before the target.
*/
private lookupIndividualTransform = (layout: Doc) => {
- const columnUnitLength = this.columnUnitLength;
+ const { columnUnitLength } = this;
if (columnUnitLength === undefined) {
return Transform.Identity(); // we're still waiting on promises to resolve
}
let offset = 0;
- var xf = Transform.Identity();
- this.childLayouts.map(candidate => {
+ // eslint-disable-next-line no-restricted-syntax
+ for (const { layout: candidate } of this.childLayoutPairs) {
if (candidate === layout) {
- return (xf = this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0));
+ return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
}
offset += this.lookupPixels(candidate) + resizerWidth;
- });
- return xf;
+ }
+ return Transform.Identity();
};
@undoBatch
@@ -213,7 +218,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
let dropInd = -1;
if (de.complete.docDragData && this._mainCont) {
let curInd = -1;
- de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
+ de.complete.docDragData?.droppedDocuments.forEach(d => {
+ curInd = this.childDocs.indexOf(d);
+ });
Array.from(this._mainCont.children).forEach((child, index) => {
const brect = child.getBoundingClientRect();
if (brect.x < de.x && brect.x + brect.width > de.x) {
@@ -265,7 +272,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
this.lookupIndividualTransform(childLayout)
.translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
.scale(this._props.NativeDimScaling?.() || 1);
- const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(childLayout.freeform_fitContentsToBox);
return (
<DocumentView
Document={childLayout}
@@ -281,11 +287,11 @@ export class CollectionMulticolumnView extends CollectionSubView() {
dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
onClickScript={this.onChildClickHandler}
onDoubleClickScript={this.onChildDoubleClickHandler}
- suppressSetHeight={true}
+ 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 ? true : false}
+ hideResizeHandles={!!(childLayout.layout_fitWidth || this._props.childHideResizeHandles)}
hideDecorationTitle={this._props.childHideDecorationTitle}
fitContentsToBox={this._props.fitContentsToBox}
focus={this._props.focus}
@@ -312,17 +318,19 @@ 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={'Tab: ' + StrCast(layout.title)} key={'wrapper' + i}>
<div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
{this.getDisplayDoc(layout)}
{this.layoutDoc._chromeHidden ? null : (
- <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this._props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
+ <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(() => this._props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
)}
<WidthLabel layout={layout} collectionDoc={this.Document} />
</div>
</Tooltip>,
<ResizeBar
width={resizerWidth}
+ // eslint-disable-next-line react/no-array-index-key
key={'resizer' + i}
styleProvider={this._props.styleProvider}
isContentActive={this._props.isContentActive}
@@ -353,14 +361,29 @@ export class CollectionMulticolumnView extends CollectionSubView() {
{this.contents}
{!this._startIndex ? null : (
<Tooltip title="scroll back">
- <div style={{ position: 'absolute', bottom: 0, left: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
- <Button tooltip="Scroll back" icon={<FontAwesomeIcon icon="chevron-left" size="lg" />} onClick={action(e => (this._startIndex = Math.max(0, this._startIndex - this.maxShown)))} color={SettingsManager.userColor} />
+ <div
+ style={{ position: 'absolute', bottom: 0, left: 0, background: SettingsManager.userVariantColor }}
+ onClick={action(() => {
+ this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown);
+ })}>
+ <Button
+ tooltip="Scroll back"
+ icon={<FontAwesomeIcon icon="chevron-left" size="lg" />}
+ onClick={action(() => {
+ this._startIndex = Math.max(0, this._startIndex - this.maxShown);
+ })}
+ color={SettingsManager.userColor}
+ />
</div>
</Tooltip>
)}
{this._startIndex > this.childLayoutPairs.length - 1 || !this.maxShown ? null : (
<Tooltip title="scroll forward">
- <div style={{ position: 'absolute', bottom: 0, right: 0, background: SettingsManager.userVariantColor }} onClick={action(e => (this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown)))}>
+ <div
+ style={{ position: 'absolute', bottom: 0, right: 0, background: SettingsManager.userVariantColor }}
+ onClick={action(() => {
+ this._startIndex = Math.min(this.childLayoutPairs.length - 1, this._startIndex + this.maxShown);
+ })}>
<IconButton icon={<FaChevronRight />} color={SettingsManager.userColor} />
</div>
</Tooltip>
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 17bf3e50c..3fe3d5343 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { DragManager, dropActionType } from '../../../util/DragManager';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
import { DocumentView } from '../../nodes/DocumentView';
@@ -11,6 +12,7 @@ import { CollectionSubView } from '../CollectionSubView';
import './CollectionMultirowView.scss';
import HeightLabel from './MultirowHeightLabel';
import ResizeBar from './MultirowResizer';
+
interface HeightSpecifier {
magnitude: number;
unit: string;
@@ -63,7 +65,7 @@ export class CollectionMultirowView extends CollectionSubView() {
private get resolvedLayoutInformation(): LayoutData {
let starSum = 0;
const heightSpecifiers: HeightSpecifier[] = [];
- this.childLayoutPairs.map(pair => {
+ this.childLayoutPairs.forEach(pair => {
const unit = StrCast(pair.layout._dimUnit, '*');
const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim);
if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) {
@@ -123,6 +125,7 @@ export class CollectionMultirowView extends CollectionSubView() {
if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._yMargin);
}
+ return undefined;
}
/**
@@ -142,6 +145,7 @@ export class CollectionMultirowView extends CollectionSubView() {
if (this.resolvedLayoutInformation && this.totalRatioAllocation !== undefined) {
return this.totalRatioAllocation / this.resolvedLayoutInformation.starSum;
}
+ return undefined;
}
/**
@@ -158,13 +162,12 @@ export class CollectionMultirowView extends CollectionSubView() {
* or the ratio width evaluated to a pixel value
*/
private lookupPixels = (layout: Doc): number => {
- const rowUnitLength = this.rowUnitLength;
- if (rowUnitLength === undefined) {
+ if (this.rowUnitLength === undefined) {
return 0; // we're still waiting on promises to resolve
}
let height = NumCast(layout._dimMagnitude, this.minimumDim);
if (StrCast(layout._dimUnit, '*') === DimUnit.Ratio) {
- height *= rowUnitLength;
+ height *= this.rowUnitLength;
}
return height;
};
@@ -176,11 +179,11 @@ export class CollectionMultirowView extends CollectionSubView() {
* documents before the target.
*/
private lookupIndividualTransform = (layout: Doc) => {
- const rowUnitLength = this.rowUnitLength;
- if (rowUnitLength === undefined) {
+ 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));
@@ -195,7 +198,9 @@ export class CollectionMultirowView extends CollectionSubView() {
let dropInd = -1;
if (de.complete.docDragData && this._mainCont) {
let curInd = -1;
- de.complete.docDragData?.droppedDocuments.forEach(d => (curInd = this.childDocs.indexOf(d)));
+ de.complete.docDragData?.droppedDocuments.forEach(d => {
+ curInd = this.childDocs.indexOf(d);
+ });
Array.from(this._mainCont.children).forEach((child, index) => {
const brect = child.getBoundingClientRect();
if (brect.y < de.y && brect.y + brect.height > de.y) {
@@ -247,7 +252,6 @@ export class CollectionMultirowView extends CollectionSubView() {
this.lookupIndividualTransform(layout)
.translate(-NumCast(this.layoutDoc._xMargin), -NumCast(this.layoutDoc._yMargin))
.scale(this._props.NativeDimScaling?.() || 1);
- const shouldNotScale = () => this._props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
return (
<DocumentView
Document={layout}
@@ -266,7 +270,7 @@ export class CollectionMultirowView extends CollectionSubView() {
ScreenToLocalTransform={dxf}
isContentActive={this.isChildContentActive}
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
- hideResizeHandles={layout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
+ hideResizeHandles={!!(layout.layout_fitWidth || this._props.childHideResizeHandles)}
hideDecorationTitle={this._props.childHideDecorationTitle}
fitContentsToBox={this._props.fitContentsToBox}
focus={this._props.focus}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index d580d9c52..931e2c5e0 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,10 +1,11 @@
+/* eslint-disable react/require-default-props */
import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { UndoManager } from '../../../util/UndoManager';
-import { StyleProp } from '../../StyleProvider';
+import { StyleProp } from '../../StyleProp';
import { StyleProviderFuncType } from '../../nodes/FieldView';
import { DimUnit } from './CollectionMulticolumnView';
@@ -52,27 +53,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
}
};
- private get isActivated() {
- const { toLeft, toRight } = this.props;
- if (toLeft && toRight) {
- if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel && StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- } else if (toLeft) {
- if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- } else if (toRight) {
- if (StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- }
- return false;
- }
-
@action
private onPointerUp = () => {
window.removeEventListener('pointermove', this.onPointerMove);
@@ -90,7 +70,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
width: this.props.width,
backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
}}>
- <div className={'multiColumnResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
+ <div className="multiColumnResizer-hdl" onPointerDown={e => this.registerResizing(e)} />
</div>
);
}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
index a9579d931..a7a0b3457 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -19,7 +19,7 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
const getUnit = () => StrCast(layout.dimUnit);
const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(3));
return (
- <div className={'label-wrapper'}>
+ <div className="label-wrapper">
<EditableView
GetValue={getMagnitude}
SetValue={value => {
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index 878c7ff3c..66215f109 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/require-default-props */
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -20,7 +21,7 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
const getUnit = () => StrCast(layout.dimUnit);
const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
return (
- <div className={'label-wrapper'}>
+ <div className="label-wrapper">
<EditableView
GetValue={getMagnitude}
SetValue={value => {
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 73d08d5ef..cff0a8b4c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,10 +1,11 @@
+/* eslint-disable react/require-default-props */
import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { UndoManager } from '../../../util/UndoManager';
-import { StyleProp } from '../../StyleProvider';
+import { StyleProp } from '../../StyleProp';
import { StyleProviderFuncType } from '../../nodes/FieldView';
import { DimUnit } from './CollectionMultirowView';
@@ -33,7 +34,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
};
private onPointerMove = ({ movementY }: PointerEvent) => {
- const { toTop: toTop, toBottom: toBottom, columnUnitLength } = this.props;
+ const { toTop, toBottom, columnUnitLength } = this.props;
const movingDown = movementY > 0;
const toNarrow = movingDown ? toBottom : toTop;
const toWiden = movingDown ? toTop : toBottom;
@@ -50,27 +51,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
}
};
- private get isActivated() {
- const { toTop, toBottom } = this.props;
- if (toTop && toBottom) {
- if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel && StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- } else if (toTop) {
- if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- } else if (toBottom) {
- if (StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) {
- return false;
- }
- return true;
- }
- return false;
- }
-
@action
private onPointerUp = () => {
window.removeEventListener('pointermove', this.onPointerMove);
@@ -88,7 +68,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
height: this.props.height,
backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
}}>
- <div className={'multiRowResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
+ <div className="multiRowResizer-hdl" onPointerDown={e => this.registerResizing(e)} />
</div>
);
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 0244db891..023b72778 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,45 +1,39 @@
+/* eslint-disable no-restricted-syntax */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, PopupTrigger, Type } from 'browndash-components';
import { ObservableMap, action, computed, makeObservable, observable, observe } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
-import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
+import { returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
+import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
+import { ColumnType } from '../../../../fields/SchemaHeaderField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
-import { DocUtils, Docs, DocumentOptions, FInfo } from '../../../documents/Documents';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../../util/DragManager';
-import { SelectionManager } from '../../../util/SelectionManager';
+import { DocUtils } from '../../../documents/DocUtils';
+import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { EditableView } from '../../EditableView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
+import { StyleProp } from '../../StyleProp';
+import { DefaultStyleProvider } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
-const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
-export enum ColumnType {
- Number,
- String,
- Boolean,
- Date,
- Image,
- RTF,
- Enumeration,
- Any,
-}
+const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
export const FInfotoColType: { [key: string]: ColumnType } = {
string: ColumnType.String,
@@ -102,17 +96,13 @@ export class CollectionSchemaView extends CollectionSubView() {
@computed get _selectedDocs() {
// get all selected documents then filter out any whose parent is not this schema document
- const selected = SelectionManager.Docs.filter(doc => this.childDocs.includes(doc));
- // SelectionManager... filter(doc => this.childDocs.includes(doc))
- //DocCast(doc.embedContainer)[DocData] === this.dataDoc
- //SelectionManager.Docs.forEach(doc => console.log("index: " + this.rowIndex(doc) + " equal: " + Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document)));
+ const selected = DocumentView.SelectedDocs().filter(doc => this.childDocs.includes(doc));
if (!selected.length) {
- for (const sel of SelectionManager.Docs) {
- const contextPath = DocumentManager.GetContextPath(sel, true);
- if (contextPath.includes(this.Document)) {
- const parentInd = contextPath.indexOf(this.Document);
- return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
- }
+ // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window)
+ const childOfSchemaDoc = DocumentView.SelectedDocs().find(sel => DocumentView.getContextPath(sel, true).includes(this.Document));
+ if (childOfSchemaDoc) {
+ const contextPath = DocumentView.getContextPath(childOfSchemaDoc, true);
+ return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected
}
}
return selected;
@@ -176,8 +166,8 @@ export class CollectionSchemaView extends CollectionSubView() {
Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
this._keysDisposer = observe(
this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
- change => {
- switch (change.type as any) {
+ (change: any) => {
+ switch (change.type) {
case 'splice':
// prettier-ignore
(change as any).added.forEach((doc: Doc) => // for each document added
@@ -185,7 +175,9 @@ export class CollectionSchemaView extends CollectionSubView() {
Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
!this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author'))))));
break;
- case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ case 'update': // let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ break;
+ default:
}
},
true
@@ -197,6 +189,9 @@ export class CollectionSchemaView extends CollectionSubView() {
document.removeEventListener('keydown', this.onKeyDown);
}
+ // ViewBoxInterface overrides
+ override isUnstyledView = returnTrue; // used by style provider : turns off opacity, animation effects, scaling
+
rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc);
@action
@@ -211,7 +206,7 @@ export class CollectionSchemaView extends CollectionSubView() {
if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
const newDoc = this.sortedDocs.docs[lastIndex + 1];
if (this._selectedDocs.includes(newDoc)) {
- SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc));
this.deselectCell(curDoc);
} else {
const shift: boolean = e.shiftKey;
@@ -232,7 +227,7 @@ export class CollectionSchemaView extends CollectionSubView() {
if (firstIndex > 0 && firstIndex < this.childDocs.length) {
const newDoc = this.sortedDocs.docs[firstIndex - 1];
if (this._selectedDocs.includes(newDoc)) {
- SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc));
this.deselectCell(curDoc);
} else {
const shift: boolean = e.shiftKey;
@@ -265,7 +260,9 @@ export class CollectionSchemaView extends CollectionSubView() {
}
case 'Escape': {
this.deselectAllCells();
+ break;
}
+ default:
}
}
};
@@ -287,7 +284,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.addNewKey(newKey, defaultVal);
}
- let currKeys = [...this.columnKeys];
+ const currKeys = [...this.columnKeys];
currKeys[index] = newKey;
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -304,13 +301,16 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(0, 0, key);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@action
- addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[DocData][key] = defaultVal));
+ addNewKey = (key: string, defaultVal: any) =>
+ this.childDocs.forEach(doc => {
+ doc[DocData][key] = defaultVal;
+ });
@undoBatch
removeColumn = (index: number) => {
@@ -320,7 +320,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(index, 1);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -328,7 +328,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
startResize = (e: any, index: number) => {
this._displayColumnWidths = this.storedColumnWidths;
- setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index), this.finishResize, emptyFunction);
+ setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction);
};
@action
@@ -370,11 +370,11 @@ export class CollectionSchemaView extends CollectionSubView() {
if (this._selectedCol === fromIndex) this._selectedCol = toIndex;
else if (toIndex === this._selectedCol) this._selectedCol = fromIndex; //keeps selected cell consistent
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
- let currWidths = this.storedColumnWidths.slice();
+ const currWidths = this.storedColumnWidths.slice();
currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths);
@@ -389,15 +389,6 @@ export class CollectionSchemaView extends CollectionSubView() {
const dragEles = [this._colEles[index]];
this.childDocs.forEach(doc => dragEles.push(this._rowEles.get(doc).children[1].children[index]));
DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y);
-
- // document.removeEventListener('pointermove', this.highlightDropColumn);
- // document.addEventListener('pointermove', this.highlightDropColumn);
- // let stopHighlight = (e: PointerEvent) => {
- // document.removeEventListener('pointermove', this.highlightDropColumn);
- // document.removeEventListener('pointerup', stopHighlight);
- // };
- // document.addEventListener('pointerup', stopHighlight);
-
return true;
};
@@ -498,14 +489,14 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
- const rowDocView = DocumentManager.Instance.getDocumentView(doc);
- if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
+ addDocToSelection = (doc: Doc, extendSelection: boolean) => {
+ const rowDocView = DocumentView.getDocumentView(doc);
+ if (rowDocView) DocumentView.SelectView(rowDocView, extendSelection);
};
@action
clearSelection = () => {
- SelectionManager.DeselectAll();
+ DocumentView.DeselectAll();
this.deselectAllCells();
};
@@ -534,10 +525,10 @@ export class CollectionSchemaView extends CollectionSubView() {
if (shiftKey && lastSelected && !this._selectedDocs.includes(doc)) this.selectRows(doc, lastSelected);
else if (ctrlKey) {
if (lastSelected && this._selectedDocs.includes(doc)) {
- SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(doc));
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(doc));
this.deselectCell(doc);
- } else this.addDocToSelection(doc, true, index);
- } else this.addDocToSelection(doc, false, index);
+ } else this.addDocToSelection(doc, true);
+ } else this.addDocToSelection(doc, false);
this._selectedCol = col;
if (this._lowestSelectedIndex === -1 || index < this._lowestSelectedIndex) this._lowestSelectedIndex = index;
@@ -595,7 +586,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...this.sortedDocs.docs]);
this.clearSelection();
draggedDocs.forEach(doc => {
- DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true));
+ DocumentView.addViewRenderedCb(doc, dv => dv.select(true));
});
this._lowestSelectedIndex = Math.min(...draggedDocs?.map(doc => this.rowIndex(doc)));
return true;
@@ -604,13 +595,13 @@ export class CollectionSchemaView extends CollectionSubView() {
};
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
+ super.onExternalDrop(e, {}, undoBatch(action(docs => docs.map((doc: Doc) => this.addDocument(doc)))));
};
onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
@action
- onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ onDividerMove = (e: PointerEvent) => {
const nativeWidth = this._previewRef!.getBoundingClientRect();
const minWidth = 40;
const maxWidth = 1000;
@@ -640,37 +631,76 @@ export class CollectionSchemaView extends CollectionSubView() {
const rect = found.getBoundingClientRect();
const localRect = this.ScreenToLocalBoxXf().transformBounds(rect.left, rect.top, rect.width, rect.height);
if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this._props.PanelHeight()) {
- let focusSpeed = options.zoomTime ?? 50;
+ const focusSpeed = options.zoomTime ?? 50;
smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
return focusSpeed;
}
}
+ return undefined;
};
@computed get fieldDefaultInput() {
switch (this._newFieldType) {
case ColumnType.Number:
- return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="number"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? 0}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
case ColumnType.Boolean:
return (
<>
- <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} />
+ <input
+ type="checkbox"
+ name=""
+ id=""
+ value={this._newFieldDefault}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.checked;
+ })}
+ />
{this._newFieldDefault ? 'true' : 'false'}
</>
);
case ColumnType.String:
- return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="text"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? ''}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
+ default:
+ return undefined;
}
}
onSearchKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'Enter':
- this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))();
+ this._menuKeys.length > 0 && this._menuValue.length > 0
+ ? this.setKey(this._menuKeys[0])
+ : action(() => {
+ this._makeNewField = true;
+ })();
break;
case 'Escape':
this.closeColumnMenu();
break;
+ default:
}
};
@@ -693,9 +723,9 @@ export class CollectionSchemaView extends CollectionSubView() {
}
});
if (selectedDocs.length === 1) {
- this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value));
+ this.childDocs.forEach(doc => Doc.SetField(doc, key, value));
} else {
- selectedDocs.forEach(doc => KeyValueBox.SetField(doc, key, value));
+ selectedDocs.forEach(doc => Doc.SetField(doc, key, value));
}
return true;
};
@@ -704,7 +734,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.childDocs.forEach(doc => {
let docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0);
if (docIsSelected) {
- KeyValueBox.SetField(doc, key, value);
+ Doc.SetField(doc, key, value);
}
});
return true;
@@ -723,7 +753,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeColumnMenu = () => (this._columnMenuIndex = undefined);
+ closeColumnMenu = () => {
+ this._columnMenuIndex = undefined;
+ };
@action
openFilterMenu = (index: number) => {
@@ -732,7 +764,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeFilterMenu = () => (this._filterColumnIndex = undefined);
+ closeFilterMenu = () => {
+ this._filterColumnIndex = undefined;
+ };
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
@@ -762,7 +796,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
};
- getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field);
+ getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] === field);
removeFieldFilters = (field: string) => {
this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove'));
@@ -774,11 +808,14 @@ export class CollectionSchemaView extends CollectionSubView() {
case 'Escape':
this.closeFilterMenu();
break;
+ default:
}
};
@action
- updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterSearchValue = e.target.value);
+ updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._filterSearchValue = e.target.value;
+ };
@computed get newFieldMenu() {
return (
@@ -787,7 +824,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Number}
+ checked={this._newFieldType === ColumnType.Number}
onChange={action(() => {
this._newFieldType = ColumnType.Number;
this._newFieldDefault = 0;
@@ -799,7 +836,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Boolean}
+ checked={this._newFieldType === ColumnType.Boolean}
onChange={action(() => {
this._newFieldType = ColumnType.Boolean;
this._newFieldDefault = false;
@@ -811,7 +848,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.String}
+ checked={this._newFieldType === ColumnType.String}
onChange={action(() => {
this._newFieldType = ColumnType.String;
this._newFieldDefault = '';
@@ -823,7 +860,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="schema-key-warning">{this._newFieldWarning}</div>
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action(() => {
if (this.documentKeys.includes(this._menuValue)) {
this._newFieldWarning = 'Field already exists';
} else if (this._menuValue.length === 0) {
@@ -850,7 +887,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="schema-key-search">
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action((e: any) => {
e.stopPropagation();
this._makeNewField = true;
})}>
@@ -888,7 +925,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get renderColumnMenu() {
- const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
+ const x = this._columnMenuIndex! === -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
return (
<div className="schema-column-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
<input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
@@ -897,7 +934,6 @@ export class CollectionSchemaView extends CollectionSubView() {
);
}
get renderKeysMenu() {
- //console.log('RNDERMENUT:' + this._columnMenuIndex);
return (
<div className="schema-column-menu" style={{ left: 0, minWidth: CollectionSchemaView._minColWidth }}>
<input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
@@ -931,7 +967,7 @@ export class CollectionSchemaView extends CollectionSubView() {
type="checkbox"
onPointerDown={e => e.stopPropagation()}
onClick={e => e.stopPropagation()}
- onChange={action(e => {
+ onChange={action((e: any) => {
if (e.target.checked) {
Doc.setDocFilter(this.Document, columnKey, key, 'check');
} else {
@@ -954,7 +990,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this.renderFilterOptions}
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action((e: any) => {
e.stopPropagation();
this.closeFilterMenu();
})}>
@@ -985,9 +1021,9 @@ export class CollectionSchemaView extends CollectionSubView() {
? staticDocs
: [...staticDocs].sort((docA, docB) => {
// this sorts the documents based on the selected field. returning -1 for a before b, 0 for a = b, 1 for a > b
- const aStr = Field.toString(docA[field] as Field);
- const bStr = Field.toString(docB[field] as Field);
- var out = 0;
+ const aStr = Field.toString(docA[field] as FieldType);
+ const bStr = Field.toString(docB[field] as FieldType);
+ let out = 0;
if (aStr < bStr) out = -1;
if (aStr > bStr) out = 1;
if (desc) out *= -1;
@@ -1024,7 +1060,7 @@ export class CollectionSchemaView extends CollectionSubView() {
placement="right"
background={SettingsManager.userBackgroundColor}
color={SettingsManager.userColor}
- toggle={<FontAwesomeIcon onPointerDown={e => this.openColumnMenu(-1, true)} icon="plus" />}
+ toggle={<FontAwesomeIcon onPointerDown={() => this.openColumnMenu(-1, true)} icon="plus" />}
trigger={PopupTrigger.CLICK}
type={Type.TERT}
isOpen={this._columnMenuIndex !== -1 ? false : undefined}
@@ -1033,6 +1069,7 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this.columnKeys.map((key, index) => (
<SchemaColumnHeader
+ // eslint-disable-next-line react/no-array-index-key
key={index}
columnIndex={index}
columnKeys={this.columnKeys}
@@ -1052,28 +1089,42 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
- <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
+ {
+ // eslint-disable-next-line no-use-before-define
+ <CollectionSchemaViewDocs
+ schema={this}
+ childDocs={this.sortedDocsFunc}
+ rowHeight={this.rowHeightFunc}
+ setRef={(ref: HTMLDivElement | null) => {
+ this._tableContentRef = ref;
+ }}
+ />
+ }
{this.layoutDoc.chromeHidden ? null : (
<div className="schema-add">
<EditableView
GetValue={returnEmptyString}
SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
placeholder={"Type text to create note or ':' to create specific type"}
- contents={'+ New Node'}
+ contents="+ New Node"
menuCallback={this.menuCallback}
height={CollectionSchemaView._newNodeInputHeight}
/>
</div>
)}
</div>
- {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
+ {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown} />}
{this.previewWidth > 0 && (
- <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
+ <div
+ style={{ width: `${this.previewWidth}px` }}
+ ref={ref => {
+ this._previewRef = ref;
+ }}>
{Array.from(this._selectedDocs).lastElement() && (
<DocumentView
Document={Array.from(this._selectedDocs).lastElement()}
fitContentsToBox={returnTrue}
- dontCenter={'y'}
+ dontCenter="y"
onClickScriptDisable="always"
focus={emptyFunction}
defaultDoubleClick={returnIgnore}
@@ -1118,7 +1169,10 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
<div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
{this.props.childDocs().docs.map((doc: Doc, index: number) => (
<div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
- <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ {
+ // eslint-disable-next-line no-use-before-define
+ <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ }
</div>
))}
</div>
@@ -1151,6 +1205,7 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
return (
<DocumentView
key={this._props.doc[Id]}
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props.schema._props}
containerViewPath={this._props.schema.childContainerViewPath}
LayoutTemplate={this._props.schema._props.childLayoutTemplate}
@@ -1170,14 +1225,14 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
searchFilterDocs={this._props.schema.searchFilterDocs}
rootSelected={this._props.schema.rootSelected}
ScreenToLocalTransform={this.screenToLocalXf}
- dragWhenActive={true}
+ dragWhenActive
isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive}
isContentActive={this.isRowContentActive}
whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
+ hideDecorations
+ hideTitle
+ hideDocumentButtonBar
+ hideLinkAnchors
layout_fitWidth={returnTrue}
/>
);
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 5f8b412be..6b5a34ec0 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,8 +1,10 @@
+/* eslint-disable react/no-unused-prop-types */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, observable } from 'mobx';
+import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { setupMoveUpEvents } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { Colors } from '../../global/globalEnums';
import './CollectionSchemaView.scss';
@@ -24,8 +26,6 @@ export interface SchemaColumnHeaderProps {
@observer
export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
- @observable _ref: HTMLDivElement | null = null;
-
get fieldKey() {
return this.props.columnKeys[this.props.columnIndex];
}
@@ -34,9 +34,9 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
sortClicked = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- if (this.props.sortField == this.fieldKey && this.props.sortDesc) {
+ if (this.props.sortField === this.fieldKey && this.props.sortDesc) {
this.props.setSort(undefined);
- } else if (this.props.sortField == this.fieldKey) {
+ } else if (this.props.sortField === this.fieldKey) {
this.props.setSort(this.fieldKey, true);
} else {
this.props.setSort(this.fieldKey, false);
@@ -45,7 +45,7 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
@action
onPointerDown = (e: React.PointerEvent) => {
- this.props.isContentActive(true) && setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction, false);
+ this.props.isContentActive(true) && setupMoveUpEvents(this, e, moveEv => this.props.dragColumn(moveEv, this.props.columnIndex), emptyFunction, emptyFunction);
};
render() {
@@ -58,19 +58,18 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
onPointerDown={this.onPointerDown}
ref={col => {
if (col) {
- this._ref = col;
this.props.setColRef(this.props.columnIndex, col);
}
}}>
- <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)}></div>
+ <div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)} />
<div className="schema-column-title">{this.fieldKey}</div>
<div className="schema-header-menu">
<div className="schema-header-button" onPointerDown={e => this.props.openContextMenu(e.clientX, e.clientY, this.props.columnIndex)}>
<FontAwesomeIcon icon="ellipsis-h" />
</div>
- <div className="schema-sort-button" onPointerDown={this.sortClicked} style={this.props.sortField == this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}>
- <FontAwesomeIcon icon="caret-right" style={this.props.sortField == this.fieldKey ? { transform: `rotate(${this.props.sortDesc ? '270deg' : '90deg'})` } : {}} />
+ <div className="schema-sort-button" onPointerDown={this.sortClicked} style={this.props.sortField === this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}>
+ <FontAwesomeIcon icon="caret-right" style={this.props.sortField === this.fieldKey ? { transform: `rotate(${this.props.sortDesc ? '270deg' : '90deg'})` } : {}} />
</div>
</div>
</div>
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index f5618db96..059aa912e 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -5,15 +5,16 @@ import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { CgClose, CgLock, CgLockUnlock } from 'react-icons/cg';
import { FaExternalLinkAlt } from 'react-icons/fa';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+import { returnFalse, setupMoveUpEvents } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { Doc } from '../../../../fields/Doc';
import { BoolCast } from '../../../../fields/Types';
import { Transform } from '../../../util/Transform';
import { undoable } from '../../../util/UndoManager';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
-import { OpenWhere } from '../../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../../nodes/FieldView';
+import { OpenWhere } from '../../nodes/OpenWhere';
import { CollectionSchemaView } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaTableCell } from './SchemaTableCell';
@@ -78,7 +79,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
}}>
<IconButton
tooltip="close"
- icon={<CgClose size={'16px'} />}
+ icon={<CgClose size="16px" />}
size={Size.XSMALL}
onPointerDown={e =>
setupMoveUpEvents(
@@ -86,8 +87,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
e,
returnFalse,
emptyFunction,
- undoable(e => {
- e.stopPropagation();
+ undoable(clickEv => {
+ clickEv.stopPropagation();
this._props.removeDocument?.(this.Document);
}, 'Delete Row')
)
@@ -119,8 +120,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
e,
returnFalse,
emptyFunction,
- undoable(e => {
- e.stopPropagation();
+ undoable(clickEv => {
+ clickEv.stopPropagation();
this._props.addDocTab(this.Document, OpenWhere.addRight);
}, 'Open schema Doc preview')
)
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 0085f9938..48c86ac27 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable no-use-before-define */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, Size, Type } from 'browndash-components';
import { action, computed, makeObservable, observable } from 'mobx';
@@ -7,16 +10,16 @@ import * as React from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import Select from 'react-select';
-import { StopEvent, Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils';
+import { ClientUtils, StopEvent, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field } from '../../../../fields/Doc';
import { RichTextField } from '../../../../fields/RichTextField';
-import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast } from '../../../../fields/Types';
+import { ColumnType } from '../../../../fields/SchemaHeaderField';
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { FInfo, FInfoFieldType } from '../../../documents/Documents';
-import { DocFocusOrOpen } from '../../../util/DocumentManager';
-import { dropActionType } from '../../../util/DragManager';
-import { SettingsManager } from '../../../util/SettingsManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, undoable } from '../../../util/UndoManager';
@@ -24,11 +27,10 @@ import { EditableView } from '../../EditableView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DefaultStyleProvider } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
-import { OpenWhere, returnEmptyDocViewList } from '../../nodes/DocumentView';
+import { DocFocusOrOpen, returnEmptyDocViewList } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
export interface SchemaTableCellProps {
@@ -64,8 +66,8 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
makeObservable(this);
}
- static addFieldDoc = (doc: Doc, where: OpenWhere) => {
- DocFocusOrOpen(doc);
+ static addFieldDoc = (docs: Doc | Doc[] /* , where: OpenWhere */) => {
+ DocFocusOrOpen(toList(docs)[0]);
return true;
};
public static renderProps(props: SchemaTableCellProps) {
@@ -144,7 +146,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
this._props.finishEdit?.();
return true;
}
- const ret = KeyValueBox.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined);
+ const ret = Doc.SetField(fieldProps.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(fieldProps.Document) ? true : undefined);
this._props.finishEdit?.();
return ret;
}, 'edit schema cell')}
@@ -177,7 +179,7 @@ export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellPro
case ColumnType.Image: return <SchemaImageCell {...this._props} />;
case ColumnType.Boolean: return <SchemaBoolCell {...this._props} />;
case ColumnType.RTF: return <SchemaRTFCell {...this._props} />;
- case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => val.toString())} />;
+ case ColumnType.Enumeration: return <SchemaEnumerationCell {...this._props} options={this._props.getFinfo(this._props.fieldKey)?.values?.map(val => Field.toString(val))} />;
case ColumnType.Date: return <SchemaDateCell {...this._props} />;
default: return this.defaultCellContent;
}
@@ -217,8 +219,8 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
choosePath(url: URL) {
if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
- if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
- if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
+ if (url.href.indexOf(window.location.origin) === -1) return ClientUtils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
+ if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; // Why is this here — good question
const ext = extname(url.href);
return url.href.replace(ext, '_s' + ext);
@@ -233,7 +235,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
.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 : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
+ const url = paths.length ? paths : [ClientUtils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
return url[0];
}
@@ -257,7 +259,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
};
@action
- removeHoverPreview = (e: React.PointerEvent) => {
+ removeHoverPreview = () => {
if (!this._previewRef) return;
document.body.removeChild(this._previewRef);
};
@@ -269,7 +271,7 @@ export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellPro
const height = this._props.rowHeight() ? this._props.rowHeight() - (this._props.padding || 6) * 2 : undefined;
const width = height ? height * aspect : undefined; // increase the width of the image if necessary to maintain proportionality
- return <img src={this.url} width={width ? width : undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
+ return <img src={this.url} width={width || undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
}
}
@@ -293,26 +295,26 @@ export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProp
// } 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 change');
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
+ const { pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<>
<div style={{ pointerEvents: 'none' }}>
- <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={e => {}} />
+ <DatePicker dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={emptyFunction} />
</div>
{pointerEvents === 'none' ? null : (
<Popup
icon={<FontAwesomeIcon size="sm" icon="caret-down" />}
size={Size.XSMALL}
type={Type.TERT}
- color={SettingsManager.userColor}
- background={SettingsManager.userBackgroundColor}
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
popup={
<div style={{ width: 'fit-content', height: '200px' }}>
- <DatePicker open={true} dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
+ <DatePicker open dateFormat="Pp" selected={this.date?.date ?? Date.now()} onChange={this.handleChange} />
</div>
}
/>
@@ -341,7 +343,7 @@ export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps
fieldProps.isContentActive = this.selectedFunc;
return (
<div className="schemaRTFCell" style={{ fontStyle: this.selected ? undefined : 'italic', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox {...fieldProps} autoFocus={true} onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ {this.selected ? <FormattedTextBox {...fieldProps} autoFocus onBlur={() => this._props.finishEdit?.()} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
@@ -367,8 +369,8 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
checked={BoolCast(this._props.Document[this._props.fieldKey])}
onChange={undoBatch((value: React.ChangeEvent<HTMLInputElement> | undefined) => {
if ((value?.nativeEvent as any).shiftKey) {
- this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
- } else KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
+ 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() ?? ''));
})}
/>
<EditableView
@@ -382,7 +384,7 @@ export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProp
this._props.finishEdit?.();
return true;
}
- const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
+ const set = Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value, Doc.IsDataProto(this._props.Document) ? true : undefined);
this._props.finishEdit?.();
return set;
})}
@@ -403,7 +405,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
return this._props.isRowActive() && selected && selected?.filter(doc => doc === this._props.Document).length !== 0 && this._props.selectedCol() === this._props.col;
}
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
+ const { color, textDecoration, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
const options = this._props.options?.map(facet => ({ value: facet, label: facet }));
return (
<div className="schemaSelectionCell" style={{ color, textDecoration, cursor, pointerEvents }}>
@@ -448,7 +450,7 @@ export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableC
placeholder={StrCast(this._props.Document[this._props.fieldKey], 'select...')}
options={options}
isMulti={false}
- onChange={val => KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
+ onChange={val => Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
/>
</div>
</div>