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.tsx100
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss7
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx54
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx87
-rw-r--r--src/client/views/collections/CollectionDockingView.scss41
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx125
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx136
-rw-r--r--src/client/views/collections/CollectionMenu.scss4
-rw-r--r--src/client/views/collections/CollectionMenu.tsx927
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.scss2
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx187
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx133
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewDivider.tsx15
-rw-r--r--src/client/views/collections/CollectionPileView.tsx49
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx284
-rw-r--r--src/client/views/collections/CollectionStackingView.scss4
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx296
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx157
-rw-r--r--src/client/views/collections/CollectionStaffView.scss13
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx53
-rw-r--r--src/client/views/collections/CollectionSubView.tsx178
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx79
-rw-r--r--src/client/views/collections/CollectionTreeView.scss3
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx230
-rw-r--r--src/client/views/collections/CollectionView.scss10
-rw-r--r--src/client/views/collections/CollectionView.tsx156
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx37
-rw-r--r--src/client/views/collections/TabDocView.scss3
-rw-r--r--src/client/views/collections/TabDocView.tsx220
-rw-r--r--src/client/views/collections/TreeView.scss2
-rw-r--r--src/client/views/collections/TreeView.tsx652
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx75
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx114
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx255
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx80
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx63
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss10
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss48
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1400
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx224
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx97
-rw-r--r--src/client/views/collections/collectionGrid/Grid.tsx2
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss8
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx78
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss4
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx131
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss7
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx123
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx21
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx27
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx22
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx21
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss5
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx225
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx6
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx76
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx175
-rw-r--r--src/client/views/collections/goldenLayoutTheme.css132
63 files changed, 3715 insertions, 4007 deletions
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
new file mode 100644
index 000000000..cbcc980a9
--- /dev/null
+++ b/src/client/views/collections/CollectionCalendarView.tsx
@@ -0,0 +1,100 @@
+import { computed, makeObservable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { dateRangeStrToDates, emptyFunction, returnTrue } from '../../../Utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { CollectionStackingView } from './CollectionStackingView';
+import { CollectionSubView } from './CollectionSubView';
+
+@observer
+export class CollectionCalendarView extends CollectionSubView() {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ componentDidMount(): void {}
+
+ componentWillUnmount(): void {}
+
+ @computed get allCalendars() {
+ return this.childDocs; // returns a list of docs (i.e. calendars)
+ }
+
+ removeCalendar = () => {};
+
+ addCalendar = (doc: Doc | Doc[], annotationKey?: string | undefined): boolean => {
+ // bring up calendar modal with option to create a calendar
+ return 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
+ };
+
+ // most recent calendar should come first
+ sortByMostRecentDate = (calendarA: Doc, calendarB: Doc) => {
+ const aDateRangeStr = StrCast(DocListCast(calendarA.data).lastElement()?.date_range);
+ const bDateRangeStr = StrCast(DocListCast(calendarB.data).lastElement()?.date_range);
+
+ const [aFromDate, aToDate] = dateRangeStrToDates(aDateRangeStr);
+ const [bFromDate, bToDate] = dateRangeStrToDates(bDateRangeStr);
+
+ if (aFromDate > bFromDate) {
+ return -1; // a comes first
+ } else if (aFromDate < bFromDate) {
+ 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
+ }
+ }
+ };
+
+ screenToLocalTransform = () =>
+ this._props
+ .ScreenToLocalTransform()
+ .translate(Doc.NativeWidth(this.Document), 0)
+ .scale(this._props.NativeDimScaling?.() || 1);
+
+ get calendarsKey() {
+ return this._props.fieldKey;
+ }
+
+ render() {
+ return (
+ <div className="collectionCalendarView">
+ <CollectionStackingView
+ {...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}
+ // select={emptyFunction} What does this mean?
+ isAnyChildContentActive={returnTrue} // ??
+ // childDocumentsActive={}
+ // whenChildContentsActiveChanged={}
+ childHideDecorationTitle={false}
+ removeDocument={this.removeDocument} // will calendar automatically be removed from myCalendars
+ moveDocument={this.moveDocument}
+ addDocument={this.addCalendar}
+ ScreenToLocalTransform={this.screenToLocalTransform}
+ renderDepth={this._props.renderDepth + 1}
+ fieldKey={this.calendarsKey}
+ />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index 8319f19ca..a556d0fa7 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -1,8 +1,9 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.collectionCarousel3DView-outer {
height: 100%;
position: relative;
background-color: white;
+ overflow: hidden;
}
.carousel-wrapper {
@@ -16,7 +17,9 @@
.collectionCarousel3DView-item,
.collectionCarousel3DView-item-active {
flex: 1;
- transition: opacity 0.3s linear, transform 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955);
+ transition:
+ opacity 0.3s linear,
+ transform 0.5s cubic-bezier(0.455, 0.03, 0.515, 0.955);
pointer-events: none;
opacity: 0.5;
z-index: 1;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index a8d080953..7e484f3df 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -1,25 +1,29 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed } from 'mobx';
+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 { Id } from '../../../fields/FieldSymbols';
import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { returnFalse, returnZero, Utils } from '../../../Utils';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
-import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
-import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
import { StyleProp } from '../StyleProvider';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FieldView';
import './CollectionCarousel3DView.scss';
import { CollectionSubView } from './CollectionSubView';
-
+const { default: { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
@observer
export class CollectionCarousel3DView extends CollectionSubView() {
@computed get scrollSpeed() {
return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
}
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -35,18 +39,18 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
centerScale = Number(CAROUSEL3D_CENTER_SCALE);
- panelWidth = () => this.props.PanelWidth() / 3;
- panelHeight = () => this.props.PanelHeight() * Number(CAROUSEL3D_SIDE_SCALE);
+ panelWidth = () => this._props.PanelWidth() / 3;
+ panelHeight = () => this._props.PanelHeight() * Number(CAROUSEL3D_SIDE_SCALE);
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
- isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
isChildContentActive = () => (this.isContentActive() ? true : false);
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)
+ 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)
+ .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: DocFocusOptions) => {
+ focus = (anchor: Doc, options: FocusViewOptions) => {
const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
options.didMove = true;
@@ -60,23 +64,23 @@ export class CollectionCarousel3DView extends CollectionSubView() {
const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
return (
<DocumentView
- {...this.props}
+ {...this._props}
+ Document={childPair.layout}
+ TemplateDataDocument={childPair.data}
+ //suppressSetHeight={true}
NativeWidth={returnZero}
NativeHeight={returnZero}
- //suppressSetHeight={true}
- onDoubleClick={this.onChildDoubleClick}
- renderDepth={this.props.renderDepth + 1}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- Document={childPair.layout}
- DataDoc={childPair.data}
+ 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}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.panelWidth}
PanelHeight={this.panelHeight}
- bringToFront={returnFalse}
/>
);
};
@@ -139,10 +143,10 @@ export class CollectionCarousel3DView extends CollectionSubView() {
const whichButton = this.layoutDoc.showScrollButton;
return (
<>
- <div className={`carousel3DView-back-scroll${whichButton === 'back' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(-1)}>
+ <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'} />}
</div>
- <div className={`carousel3DView-fwd-scroll${whichButton === 'fwd' ? '' : '-hidden'}`} style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={() => this.toggleAutoScroll(1)}>
+ <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'} />}
</div>
</>
@@ -163,8 +167,8 @@ export class CollectionCarousel3DView extends CollectionSubView() {
className="collectionCarousel3DView-outer"
ref={this.createDashEventsTarget}
style={{
- background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
- color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
+ color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
}}>
<div className="carousel-wrapper" style={{ transform: `translateX(${this.translateX}px)` }}>
{this.content}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 33a92d406..dae16bafb 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -1,14 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed } from 'mobx';
+import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { StopEvent, emptyFunction, returnFalse, returnOne, returnZero } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnOne, returnZero, StopEvent } from '../../../Utils';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DragManager } from '../../util/DragManager';
-import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
+import { DocumentView } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView } from './CollectionSubView';
@@ -16,6 +17,11 @@ import { CollectionSubView } from './CollectionSubView';
export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentWillUnmount() {
this._dropDisposer?.();
}
@@ -35,58 +41,59 @@ export class CollectionCarouselView extends CollectionSubView() {
e.stopPropagation();
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length;
};
- captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<DocumentViewProps>, property: string): any => {
+ 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;
- return childValue ?? this.props.styleProvider?.(this.layoutDoc, captionProps, property);
+ 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);
+ panelHeight = () => this._props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
@computed get marginX() {
return NumCast(this.layoutDoc.caption_xMargin, 50);
}
- captionWidth = () => this.props.PanelWidth() - 2 * this.marginX;
+ captionWidth = () => this._props.PanelWidth() - 2 * this.marginX;
@computed get content() {
const index = NumCast(this.layoutDoc._carousel_index);
const curDoc = this.childLayoutPairs?.[index];
- const captionProps = { ...this.props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
- const show_captions = StrCast(this.layoutDoc._layout_showCaption);
+ const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
+ const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption);
return !(curDoc?.layout instanceof Doc) ? null : (
<>
<div className="collectionCarouselView-image" key="image">
<DocumentView
- {...this.props}
+ {...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
- setContentView={undefined}
- onDoubleClick={this.onContentDoubleClick}
- onClick={this.onContentClick}
- isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive}
- isContentActive={this.props.childContentsActive ?? this.props.isContentActive() === false ? returnFalse : emptyFunction}
- hideCaptions={show_captions ? true : false}
- renderDepth={this.props.renderDepth + 1}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
+ layout_fitWidth={undefined}
+ setContentViewBox={undefined}
+ onDoubleClickScript={this.onContentDoubleClick}
+ onClickScript={this.onContentClick}
+ isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive}
+ isContentActive={this._props.childContentsActive ?? this._props.isContentActive() === false ? returnFalse : emptyFunction}
+ hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions
+ renderDepth={this._props.renderDepth + 1}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
Document={curDoc.layout}
- DataDoc={curDoc.layout.resolvedDataDoc as Doc}
+ TemplateDataDocument={DocCast(curDoc.layout.resolvedDataDoc)}
PanelHeight={this.panelHeight}
- bringToFront={returnFalse}
/>
</div>
- <div
- className="collectionCarouselView-caption"
- key="caption"
- onWheel={StopEvent}
- style={{
- display: show_captions ? undefined : 'none',
- borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
- marginRight: this.marginX,
- marginLeft: this.marginX,
- width: `calc(100% - ${this.marginX * 2}px)`,
- }}>
- <FormattedTextBox key={index} {...captionProps} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
- </div>
+ {!carouselShowsCaptions ? null : (
+ <div
+ className="collectionCarouselView-caption"
+ key="caption"
+ onWheel={StopEvent}
+ style={{
+ borderRadius: this._props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
+ marginRight: this.marginX,
+ marginLeft: this.marginX,
+ width: `calc(100% - ${this.marginX * 2}px)`,
+ }}>
+ <FormattedTextBox key={index} {...captionProps} fieldKey={carouselShowsCaptions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} TemplateDataDocument={undefined} />
+ </div>
+ )}
</>
);
}
@@ -109,11 +116,11 @@ export class CollectionCarouselView extends CollectionSubView() {
className="collectionCarouselView-outer"
ref={this.createDashEventsTarget}
style={{
- background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
- color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color),
+ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor),
+ color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color),
}}>
{this.content}
- {this.props.Document._chromeHidden ? null : this.buttons}
+ {this.Document._chromeHidden ? null : this.buttons}
</div>
);
}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index c0530ab81..a747ef45f 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.lm_root {
position: relative;
@@ -28,10 +28,6 @@
position: relative;
z-index: 20;
}
-.lm_splitter:hover,
-.lm_splitter.lm_dragging {
- background: orange;
-}
.lm_splitter.lm_vertical .lm_drag_handle {
width: 100%;
position: absolute;
@@ -56,7 +52,7 @@
// }
.lm_header .lm_controls {
position: absolute;
- right: 3px;
+ right: 0px;
}
.lm_header .lm_controls > li {
cursor: pointer;
@@ -64,16 +60,12 @@
width: 18px;
height: 18px;
text-align: center;
- top: 3px;
}
.lm_header ul {
margin: 0;
padding: 0;
list-style-type: none;
}
-.lm_header .lm_tabs {
- position: absolute;
-}
.lm_header .lm_tab {
cursor: pointer;
float: left;
@@ -280,7 +272,7 @@
z-index: 20;
} /*# sourceMappingURL=goldenlayout-base.css.map */
-@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css';
+@import './goldenLayoutTheme.css';
.lm_title {
-webkit-appearance: none;
@@ -329,8 +321,9 @@
}
.lm_header .lm_tabs {
+ position: absolute;
overflow-y: hidden;
- width: 100%;
+ width: calc(100% - 5px);
}
ul.lm_tabs::before {
content: ' ';
@@ -484,8 +477,6 @@ ul.lm_tabs::before {
.collectiondockingview-container {
width: 100%;
height: 100%;
- border-style: solid;
- border-width: $COLLECTION_BORDER_WIDTH;
position: absolute;
top: 0;
left: 0;
@@ -508,6 +499,7 @@ ul.lm_tabs::before {
display: flex;
align-content: center;
justify-content: center;
+ background: transparent !important;
}
.lm_controls > li {
@@ -518,7 +510,11 @@ ul.lm_tabs::before {
.lm_controls .lm_popout {
background-image: unset;
- left: -3;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ background: #93939347;
+ z-index: 100;
+ //left: -3;
&:hover {
background: gray;
color: white !important;
@@ -528,7 +524,7 @@ ul.lm_tabs::before {
content: '+';
margin: auto;
font-size: x-large;
- top: -6;
+ top: -4;
position: relative;
}
.lm_maximise {
@@ -681,11 +677,6 @@ ul.lm_tabs::before {
height: 8px;
}
- .flexlayout__tab_button:hover .flexlayout__tab_button_trailing,
- .flexlayout__tab_button--selected .flexlayout__tab_button_trailing {
- background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center;
- }
-
.flexlayout__tab_button_overflow {
float: left;
width: 20px;
@@ -696,7 +687,6 @@ ul.lm_tabs::before {
font-size: 10px;
color: lightgray;
font-family: Arial, sans-serif;
- background: transparent url('../../../../node_modules/flexlayout-react/images/more.png') no-repeat left;
}
.flexlayout__tabset_header {
@@ -751,7 +741,6 @@ ul.lm_tabs::before {
height: 20px;
border: none;
outline-width: 0;
- background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center;
}
.flexlayout__tab_toolbar_button-max {
@@ -759,7 +748,6 @@ ul.lm_tabs::before {
height: 20px;
border: none;
outline-width: 0;
- background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center;
}
.flexlayout__popup_menu_item {
@@ -877,11 +865,6 @@ ul.lm_tabs::before {
height: 8px;
}
- .flexlayout__border_button:hover .flexlayout__border_button_trailing,
- .flexlayout__border_button--selected .flexlayout__border_button_trailing {
- background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center;
- }
-
.flexlayout__border_toolbar_left {
position: absolute;
display: flex;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4873a61ff..87973fd81 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,9 +1,10 @@
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { action, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import * as GoldenLayout from '../../../client/goldenLayout';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { AclAdmin, AclEdit } from '../../../fields/DocSymbols';
+import { AclAdmin, AclEdit, DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -19,7 +20,8 @@ import { DragManager } from '../../util/DragManager';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
import { DashboardView } from '../DashboardView';
import { LightboxView } from '../LightboxView';
import { OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
@@ -28,15 +30,13 @@ import { ScriptingRepl } from '../ScriptingRepl';
import { UndoStack } from '../UndoStack';
import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { CollectionSubView } from './CollectionSubView';
import { TabDocView } from './TabDocView';
-import React = require('react');
-import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
export class CollectionDockingView extends CollectionSubView() {
- @observable public static Instance: CollectionDockingView | undefined;
+ @observable public static Instance: CollectionDockingView | undefined = undefined;
public static makeDocumentConfig(document: Doc, panelName?: string, width?: number, keyValue?: boolean) {
return {
type: 'react-component',
@@ -62,14 +62,16 @@ export class CollectionDockingView extends CollectionSubView() {
}
private _goldenLayout: any = null;
static _highlightStyleSheet: any = addStyleSheet();
- constructor(props: SubCollectionViewProps) {
+
+ constructor(props: any) {
super(props);
- if (this.props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
+ makeObservable(this);
+ if (this._props.renderDepth < 0) CollectionDockingView.Instance = this;
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
DragManager.StartWindowDrag = this.StartOtherDrag;
- this.rootDoc.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
+ this.Document.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
}
/**
@@ -85,6 +87,7 @@ export class CollectionDockingView extends CollectionSubView() {
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 });
+ return true;
};
tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
@@ -130,7 +133,7 @@ export class CollectionDockingView extends CollectionSubView() {
@undoBatch
@action
- public static ReplaceTab(document: Doc, panelName: OpenWhereMod, stack: any, addToSplit?: boolean, keyValue?: boolean): boolean {
+ 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);
@@ -151,7 +154,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
return false;
}
- return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
+ return CollectionDockingView.AddSplit(document, mods, stack, panelName);
}
@undoBatch
@@ -273,8 +276,9 @@ export class CollectionDockingView extends CollectionSubView() {
return true;
}
setupGoldenLayout = async () => {
- //const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document)));
- const config = StrCast(this.props.Document.dockingConfig);
+ if (this._unmounting) return;
+ //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 docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
@@ -310,6 +314,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
componentDidMount: () => void = async () => {
+ this._unmounting = false;
if (this._containerRef.current) {
this._lightboxReactionDisposer = reaction(
() => LightboxView.LightboxDoc,
@@ -317,7 +322,7 @@ export class CollectionDockingView extends CollectionSubView() {
);
new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
this._reactionDisposer = reaction(
- () => StrCast(this.props.Document.dockingConfig),
+ () => StrCast(this.Document.dockingConfig),
config => {
if (!this._goldenLayout || this._ignoreStateChange !== config) {
// bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one.
@@ -327,7 +332,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
);
reaction(
- () => this.props.PanelWidth(),
+ () => this._props.PanelWidth(),
width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
@@ -344,7 +349,9 @@ export class CollectionDockingView extends CollectionSubView() {
}
};
+ _unmounting = false;
componentWillUnmount: () => void = () => {
+ this._unmounting = true;
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
@@ -374,14 +381,14 @@ export class CollectionDockingView extends CollectionSubView() {
.map(id => DocServer.GetCachedRefField(id))
.filter(f => f)
.map(f => f as Doc);
- const changesMade = this.props.Document.dockingConfig !== json;
+ const changesMade = this.Document.dockingConfig !== json;
if (changesMade) {
if (![AclAdmin, AclEdit].includes(GetEffectiveAcl(this.dataDoc))) {
this.layoutDoc.dockingConfig = json;
this.layoutDoc.data = new List<Doc>(docs);
} else {
- Doc.SetInPlace(this.rootDoc, 'dockingConfig', json, true);
- Doc.SetInPlace(this.rootDoc, 'data', new List<Doc>(docs), true);
+ Doc.SetInPlace(this.Document, 'dockingConfig', json, true);
+ Doc.SetInPlace(this.Document, 'data', new List<Doc>(docs), true);
}
}
this._flush?.end();
@@ -406,8 +413,9 @@ export class CollectionDockingView extends CollectionSubView() {
window.addEventListener('mouseup', this.onPointerUp);
if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
- if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize');
- else {
+ if (className.includes('lm_maximise')) {
+ // this._flush = UndoManager.StartBatch('tab maximize');
+ } else {
const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget);
if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) {
@@ -423,7 +431,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
public CaptureThumbnail() {
- const content = this.props.DocumentView?.()?.ContentDiv;
+ const content = this.DocumentView?.()?.ContentDiv;
if (content) {
const _width = Number(getComputedStyle(content).width.replace('px', ''));
const _height = Number(getComputedStyle(content).height.replace('px', ''));
@@ -441,7 +449,7 @@ export class CollectionDockingView extends CollectionSubView() {
if (clone) {
const cloned = await Doc.MakeClone(doc);
Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
- Doc.GetProto(cloned.clone).dockingConfig = json;
+ cloned.clone[DocData].dockingConfig = json;
return DashboardView.openDashboard(cloned.clone);
}
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -455,7 +463,7 @@ export class CollectionDockingView extends CollectionSubView() {
const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeEmbedding(origtab);
const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeEmbedding(origtabdoc));
if (newtabdocs.length) {
- Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
+ newtab[DocData].data = new List<Doc>(newtabdocs);
newtabdocs.forEach(ntab => Doc.SetContainer(ntab, newtab));
}
json = json.replace(origtab[Id], newtab[Id]);
@@ -463,6 +471,7 @@ export class CollectionDockingView extends CollectionSubView() {
});
const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
DashboardView.SetupDashboardTrails(copy);
+ DashboardView.SetupDashboardCalendars(copy); // Zaul TODO: needed?
return DashboardView.openDashboard(copy);
}
@@ -470,21 +479,21 @@ export class CollectionDockingView extends CollectionSubView() {
stateChanged = () => {
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
const json = JSON.stringify(this._goldenLayout.toConfig());
- const changesMade = this.props.Document.dockingConfig !== json;
+ const changesMade = this.Document.dockingConfig !== json;
return changesMade;
};
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);
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.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.rootDoc) tab.DashDoc.embedContainer = undefined;
+ 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.RemoveDocFromList(Doc.GetProto(tab.DashDoc), 'proto_embeddings', tab.DashDoc);
+ Doc.RemoveDocFromList(tab.DashDoc[DocData], 'proto_embeddings', tab.DashDoc);
}
if (CollectionDockingView.Instance) {
- const dview = CollectionDockingView.Instance.props.Document;
+ const dview = CollectionDockingView.Instance.Document;
const fieldKey = CollectionDockingView.Instance.props.fieldKey;
Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
this.tabMap.delete(tab);
@@ -504,34 +513,37 @@ export class CollectionDockingView extends CollectionSubView() {
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(),
- _height: this.props.PanelHeight(),
+ _width: this._props.PanelWidth(),
+ _height: this._props.PanelHeight(),
_freeform_backgroundGrid: true,
_layout_fitWidth: true,
title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
});
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
- inheritParentAcls(this.rootDoc, docToAdd, false);
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd, undefined, undefined, true);
+ inheritParentAcls(this.Document, docToAdd, false);
CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
}
});
- let addNewDoc = 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);
- inheritParentAcls(this.dataDoc, docToAdd, false);
- CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
- }
- });
+ 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'
+ );
stack.header?.controlsContainer
.find('.lm_close') //get the close icon
@@ -567,15 +579,15 @@ export class CollectionDockingView extends CollectionSubView() {
};
render() {
- const href = ImageCast(this.rootDoc.thumb)?.url?.href;
- return this.props.renderDepth > -1 ? (
+ const href = ImageCast(this.Document.thumb)?.url?.href;
+ return this._props.renderDepth > -1 ? (
<div>
{href ? (
<img
style={{ background: 'white', top: 0, position: 'absolute' }}
src={href} // + '?d=' + (new Date()).getTime()}
- width={this.props.PanelWidth()}
- height={this.props.PanelHeight()}
+ width={this._props.PanelWidth()}
+ height={this._props.PanelHeight()}
/>
) : (
<p>nested dashboards has no thumbnail</p>
@@ -589,7 +601,7 @@ export class CollectionDockingView extends CollectionSubView() {
ScriptingGlobals.add(
function openInLightbox(doc: any) {
- LightboxView.AddDocTab(doc, OpenWhere.lightbox);
+ LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
},
'opens up document in a lightbox',
'(doc: any)'
@@ -603,7 +615,7 @@ ScriptingGlobals.add(
// 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: 'Scripting REPL' });
+ case "<UndoStack />": return OverlayView.Instance.addWindow(<UndoStack />, { x: 300, y: 100, width: 200, height: 200, title: 'Undo stack' });
}
Doc.AddToMyOverlay(doc);
}
@@ -618,6 +630,3 @@ ScriptingGlobals.add(
'opens up document in screen overlay layer',
'(doc: any)'
);
-ScriptingGlobals.add(function useRightSplit(doc: any, addToRightSplit?: boolean) {
- CollectionDockingView.ReplaceTab(doc, OpenWhereMod.right, undefined, addToRightSplit);
-});
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 06522b85e..41c5d5b42 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -1,13 +1,12 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction } from 'mobx';
+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 { DocData } from '../../../fields/DocSymbols';
-import { Id } from '../../../fields/FieldSymbols';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
-import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { CompileScript } from '../../util/Scripting';
@@ -15,12 +14,10 @@ import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { CollectionStackingView } from './CollectionStackingView';
import './CollectionStackingView.scss';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
interface CMVFieldRowProps {
rows: () => number;
@@ -42,7 +39,12 @@ interface CMVFieldRowProps {
}
@observer
-export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowProps> {
+export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVFieldRowProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@observable private _background = 'inherit';
@observable private _createEmbeddingSelected: boolean = false;
@observable private heading: string = '';
@@ -50,13 +52,13 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@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;
@@ -68,28 +70,28 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
this._dropDisposer?.();
if (ele) {
this._ele = ele;
- this.props.observeHeight(ele);
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
+ this._props.observeHeight(ele);
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Document);
}
};
@action
componentDidMount() {
- this.heading = this.props.headingObject?.heading || '';
- this.color = this.props.headingObject?.color || '#f1efeb';
- this.collapsed = this.props.headingObject?.collapsed || false;
+ this.heading = this._props.headingObject?.heading || '';
+ this.color = this._props.headingObject?.color || '#f1efeb';
+ this.collapsed = this._props.headingObject?.collapsed || false;
}
componentWillUnmount() {
- this.props.unobserveHeight(this._ele);
+ this._props.unobserveHeight(this._ele);
}
getTrueHeight = () => {
if (this.collapsed) {
- this.props.setDocHeight(this.heading, 20);
+ this._props.setDocHeight(this.heading, 20);
} else {
const rawHeight = this._contRef.current!.getBoundingClientRect().height + 15; //+ 15 accounts for the group header
- const transformScale = this.props.screenToLocalTransform().Scale;
+ const transformScale = this._props.screenToLocalTransform().Scale;
const trueHeight = rawHeight * transformScale;
- this.props.setDocHeight(this.heading, trueHeight);
+ this._props.setDocHeight(this.heading, trueHeight);
}
};
@@ -97,10 +99,10 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
rowDrop = action((e: Event, de: DragManager.DropEvent) => {
this._createEmbeddingSelected = false;
if (de.complete.docDragData) {
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
const castedValue = this.getValue(this.heading);
const onLayoutDoc = this.onLayoutDoc(key);
- if (this.props.parent.onInternalDrop(e, de)) {
+ if (this._props.parent.onInternalDrop(e, de)) {
de.complete.docDragData.droppedDocuments.forEach(d => Doc.SetInPlace(d, key, castedValue, !onLayoutDoc));
}
return true;
@@ -119,15 +121,15 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@action
headingChanged = (value: string, shiftDown?: boolean) => {
this._createEmbeddingSelected = false;
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
const castedValue = this.getValue(value);
if (castedValue) {
- if (this.props.parent.colHeaderData) {
- if (this.props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
+ if (this._props.parent.colHeaderData) {
+ if (this._props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
return false;
}
}
- this.props.docList.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
+ this._props.docList.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
this._heading = castedValue.toString();
return true;
}
@@ -140,7 +142,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
this._color = color;
};
- pointerEnteredRow = action(() => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4'));
+ pointerEnteredRow = action(() => SnappingManager.IsDragging && (this._background = '#b4b4b4'));
@action
pointerLeaveRow = () => {
@@ -152,24 +154,24 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
addDocument = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
this._createEmbeddingSelected = false;
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
const newDoc = Docs.Create.TextDocument('', { _layout_autoHeight: true, _width: 200, _layout_fitWidth: true, title: value });
const onLayoutDoc = this.onLayoutDoc(key);
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ FormattedTextBox.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = value;
- (onLayoutDoc ? newDoc : newDoc[DocData])[key] = 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)
+ (onLayoutDoc ? newDoc : newDoc[DocData])[key] = 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)
};
deleteRow = undoBatch(
action(() => {
this._createEmbeddingSelected = false;
- const key = this.props.pivotField;
- this.props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
- if (this.props.parent.colHeaderData && this.props.headingObject) {
- const index = this.props.parent.colHeaderData.indexOf(this.props.headingObject);
- this.props.parent.colHeaderData.splice(index, 1);
+ const key = this._props.pivotField;
+ this._props.docList.forEach(d => Doc.SetInPlace(d, key, undefined, true));
+ if (this._props.parent.colHeaderData && this._props.headingObject) {
+ const index = this._props.parent.colHeaderData.indexOf(this._props.headingObject);
+ this._props.parent.colHeaderData.splice(index, 1);
}
})
);
@@ -182,8 +184,8 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
};
headerMove = (e: PointerEvent) => {
- const embedding = Doc.MakeEmbedding(this.props.Document);
- const key = this.props.pivotField;
+ const embedding = Doc.MakeEmbedding(this._props.Document);
+ const key = this._props.pivotField;
let value = this.getValue(this.heading);
value = typeof value === 'string' ? `"${value}"` : value;
const script = `return doc.${key} === ${value}`;
@@ -198,7 +200,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
@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, e => !this._props.chromeHidden && this.collapseSection(e));
this._createEmbeddingSelected = false;
}
};
@@ -207,7 +209,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
* 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 => {
+ DocListCast(this._props.parent.Document.data).forEach(doc => {
if (Doc.Get(doc, key, true)) return true;
});
return false;
@@ -246,37 +248,25 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
toggleEmbedding = action(() => (this._createEmbeddingSelected = true));
toggleVisibility = () => (this._collapsed = !this.collapsed);
- renderMenu = () => {
- const selected = this._createEmbeddingSelected;
- return (
- <div className="collectionStackingView-optionPicker">
- <div className="optionOptions">
- <div className={'optionPicker' + (selected === true ? ' active' : '')} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}>
- Delete
- </div>
- </div>
- </div>
- );
- };
@action
textCallback = (char: string) => {
return 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))));
- const showChrome = !this.props.chromeHidden;
- const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `;
+ 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))));
+ const showChrome = !this._props.chromeHidden;
+ const stackPad = showChrome ? `0px ${this._props.parent.xMargin}px` : `${this._props.parent.yMargin}px ${this._props.parent.xMargin}px 0px ${this._props.parent.xMargin}px `;
return this.collapsed ? null : (
<div style={{ position: 'relative' }}>
- {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : null}
+ {this._props.showHandle && this._props.parent._props.isContentActive() ? this._props.parent.columnDragger : null}
{showChrome ? (
<div
className="collectionStackingView-addDocumentButton"
style={
{
//width: style.columnWidth / style.numGroupColumns,
- //padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`,
+ //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'} />
@@ -287,25 +277,25 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
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`, ''),
+ 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`, ''),
}}>
- {this.props.parent.children(this.props.docList)}
+ {this._props.parent.children(this._props.docList)}
</div>
</div>
);
}
@computed get headingView() {
- 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 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} />;
- return this.props.Document.miniHeaders ? (
+ return this._props.Document.miniHeaders ? (
<div className="collectionStackingView-miniHeader">{editableHeaderView}</div>
- ) : !this.props.headingObject ? null : (
+ ) : !this._props.headingObject ? null : (
<div className="collectionStackingView-sectionHeader" ref={this._headerRef}>
<div
className="collectionStackingView-sectionHeader-subCont"
@@ -338,11 +328,9 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
)}
{noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : (
<div className="collectionStackingView-sectionOptions" onPointerDown={e => e.stopPropagation()}>
- <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={this.renderMenu()}>
- <button className="collectionStackingView-sectionOptionButton">
- <FontAwesomeIcon icon="ellipsis-v" size="lg" />
- </button>
- </Flyout>
+ <button className="collectionStackingView-sectionOptionButton" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}>
+ <FontAwesomeIcon icon="trash" size="lg" />
+ </button>
</div>
)}
</div>
@@ -352,7 +340,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr
render() {
const background = this._background;
return (
- <div className="collectionStackingView-masonrySection" style={{ width: this.props.parent.NodeWidth, background }} ref={this.createRowDropRef} onPointerEnter={this.pointerEnteredRow} onPointerLeave={this.pointerLeaveRow}>
+ <div className="collectionStackingView-masonrySection" style={{ width: this._props.parent.NodeWidth, background }} ref={this.createRowDropRef} onPointerEnter={this.pointerEnteredRow} onPointerLeave={this.pointerLeaveRow}>
{this.headingView}
{this.contentLayout}
</div>
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 6eeccc94e..5d46649b2 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables.module.scss';
.collectionMenu-container {
display: flex;
@@ -19,4 +19,4 @@
display: flex;
flex-direction: row;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index ec9d86c1a..0f90818ef 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,74 +1,56 @@
-import React = require('react');
-import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import * as React from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from 'browndash-components';
-import { Lambda, action, computed, observable, reaction, runInAction } from 'mobx';
+import { action, computed, Lambda, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
-import { ColorState } from 'react-color';
-import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
-import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
import { RichTextField } from '../../../fields/RichTextField';
-import { listSpec } from '../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { Document } from '../../../fields/documentSchemas';
-import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
import { SettingsManager } from '../../util/SettingsManager';
import { Transform } from '../../util/Transform';
import { undoBatch } from '../../util/UndoManager';
import { AntimodeMenu } from '../AntimodeMenu';
import { EditableView } from '../EditableView';
-import { GestureOverlay } from '../GestureOverlay';
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
-import { LightboxView } from '../LightboxView';
import { MainView } from '../MainView';
+import { DocumentView, DocumentViewInternal, returnEmptyDocViewList } from '../nodes/DocumentView';
import { DefaultStyleProvider } from '../StyleProvider';
-import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView';
-import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { CollectionDockingView } from './CollectionDockingView';
-import './CollectionMenu.scss';
-import { COLLECTION_BORDER_WIDTH } from './CollectionView';
-import { TabDocView } from './TabDocView';
-import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionLinearView } from './collectionLinear';
-import { media_state } from '../nodes/AudioBox';
+import './CollectionMenu.scss';
+import { DocData } from '../../../fields/DocSymbols';
interface CollectionMenuProps {
panelHeight: () => number;
panelWidth: () => number;
+ toggleTopBar: () => void;
+ topBarHeight: () => number;
}
@observer
export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
@observable static Instance: CollectionMenu;
-
- @observable SelectedCollection: DocumentView | undefined;
- @observable FieldKey: string;
+ @observable SelectedCollection: DocumentView | undefined = undefined;
private _docBtnRef = React.createRef<HTMLDivElement>();
constructor(props: any) {
super(props);
- this.FieldKey = '';
- runInAction(() => (CollectionMenu.Instance = this));
+ makeObservable(this);
+ CollectionMenu.Instance = this;
this._canFade = false; // don't let the inking menu fade away
- runInAction(() => (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().length && SelectionManager.Views()[0],
+ () => SelectionManager.Views.lastElement(),
view => view && this.SetSelection(view)
);
}
@@ -87,20 +69,11 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
};
@action
- toggleTopBar = () => {
- if (SettingsManager.headerBarHeight > 0) {
- SettingsManager.headerBarHeight = 0;
- } else {
- SettingsManager.headerBarHeight = 60;
- }
- };
-
- @action
toggleProperties = () => {
if (MainView.Instance.propertiesWidth() > 0) {
- SettingsManager.propertiesWidth = 0;
+ SettingsManager.Instance.propertiesWidth = 0;
} else {
- SettingsManager.propertiesWidth = 300;
+ SettingsManager.Instance.propertiesWidth = 300;
}
};
@@ -113,29 +86,25 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
@computed get contMenuButtons() {
const selDoc = Doc.MyContextMenuBtns;
return !(selDoc instanceof Doc) ? null : (
- <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }}>
+ <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this._props.panelHeight() }}>
<CollectionLinearView
Document={selDoc}
- DataDoc={undefined}
+ docViewPath={returnEmptyDocViewList}
fieldKey="data"
dropAction="embed"
- setHeight={returnFalse}
styleProvider={DefaultStyleProvider}
- rootSelected={returnTrue}
- bringToFront={emptyFunction}
select={emptyFunction}
isContentActive={returnTrue}
isAnyChildContentActive={returnFalse}
isSelected={returnFalse}
- docViewPath={returnEmptyDoclist}
moveDocument={returnFalse}
addDocument={returnFalse}
addDocTab={DocumentViewInternal.addDocTabFunc}
pinToPres={emptyFunction}
removeDocument={returnFalse}
ScreenToLocalTransform={this.buttonBarXf}
- PanelWidth={this.props.panelWidth}
- PanelHeight={this.props.panelHeight}
+ PanelWidth={this._props.panelWidth}
+ PanelHeight={this._props.panelHeight}
renderDepth={0}
focus={emptyFunction}
whenChildContentsActiveChanged={emptyFunction}
@@ -148,10 +117,10 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
}
render() {
- const headerIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
- const headerTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
- const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
- const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Properties' : 'Open Properties';
+ 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 hardCodedButtons = (
<div className={`hardCodedButtons`}>
@@ -159,8 +128,8 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
color={SettingsManager.userColor}
- onClick={this.toggleTopBar}
- toggleStatus={SettingsManager.headerBarHeight > 0}
+ onClick={this.props.toggleTopBar}
+ toggleStatus={this.props.topBarHeight() > 0}
icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
tooltip={headerTitle}
/>
@@ -169,14 +138,13 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
type={Type.PRIM}
color={SettingsManager.userColor}
onClick={this.toggleProperties}
- toggleStatus={SettingsManager.propertiesWidth > 0}
+ toggleStatus={SettingsManager.Instance.propertiesWidth > 0}
icon={<FontAwesomeIcon icon={propIcon} size="lg" />}
tooltip={propTitle}
/>
</div>
);
- // NEW BUTTONS
//dash col linear view buttons
const contMenuButtons = (
<div
@@ -191,21 +159,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
);
return contMenuButtons;
-
- // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
- // <button className="antimodeMenu-button" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}>
- // <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} />
- // </button>
- // </Tooltip>;
-
- // //OLD BUTTONS
- // return this.getElement(!this.SelectedCollection ? [/*button*/] :
- // [<CollectionViewBaseChrome key="chrome"
- // docView={this.SelectedCollection}
- // fieldKey={this.SelectedCollection.LayoutFieldKey}
- // type={StrCast(this.SelectedCollection?.props.Document._type_collection, CollectionViewType.Invalid) as CollectionViewType} />,
- // prop,
- // /*button*/]);
}
}
@@ -222,7 +175,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
get document() {
- return this.props.docView?.props.Document;
+ return this.props.docView?.Document;
}
get target() {
return this.document;
@@ -230,7 +183,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
_templateCommand = {
params: ['target', 'source'],
title: 'item view',
- script: 'self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])',
+ script: 'this.target.childLayoutTemplate = getDocTemplate(this.source?.[0])',
immediate: undoBatch((source: Doc[]) => {
let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
try {
@@ -252,24 +205,24 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
_narrativeCommand = {
params: ['target', 'source'],
title: 'child click view',
- script: 'self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])',
+ script: 'this.target.childClickedOpenTemplateView = getDocTemplate(this.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(self.target).data = copyField(self.source);',
- immediate: undoBatch((source: Doc[]) => (Doc.GetProto(this.target).data = new List<Doc>(source))),
+ script: 'getProto(this.target).data = copyField(this.source);',
+ immediate: undoBatch((source: Doc[]) => (this.target[DocData].data = new List<Doc>(source))),
initialize: emptyFunction,
};
_onClickCommand = {
params: ['target', 'proxy'],
title: 'copy onClick',
- script: `{ if (self.proxy?.[0]) {
- getProto(self.proxy[0]).onClick = copyField(self.target.onClick);
- getProto(self.proxy[0]).target = self.target.target;
- getProto(self.proxy[0]).source = copyField(self.target.source);
+ script: `{ if (this.proxy?.[0]) {
+ getProto(this.proxy[0]).onClick = copyField(this.target.onClick);
+ getProto(this.proxy[0]).target = this.target.target;
+ getProto(this.proxy[0]).source = copyField(this.target.source);
}}`,
immediate: undoBatch((source: Doc[]) => {}),
initialize: emptyFunction,
@@ -277,7 +230,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
_viewCommand = {
params: ['target'],
title: 'bookmark view',
- script: "self.target._freeform_panX = self['target-freeform_panX']; self.target._freeform_panY = self['target-freeform_panY']; self.target._freeform_scale = self['target_freeform_scale']; gotoFrame(self.target, self['target-currentFrame']);",
+ 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[]) => {
this.target._freeform_panX = 0;
this.target._freeform_panY = 0;
@@ -294,22 +247,22 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
_clusterCommand = {
params: ['target'],
title: 'fit content',
- script: 'self.target._freeform_fitContentsToBox = !self.target._freeform_fitContentsToBox;',
+ script: 'this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox;',
immediate: undoBatch((source: Doc[]) => (this.target._freeform_fitContentsToBox = !this.target._freeform_fitContentsToBox)),
initialize: emptyFunction,
};
_fitContentCommand = {
params: ['target'],
title: 'toggle clusters',
- script: 'self.target._freeform_useClusters = !self.target._freeform_useClusters;',
+ script: 'this.target._freeform_useClusters = !this.target._freeform_useClusters;',
immediate: undoBatch((source: Doc[]) => (this.target._freeform_useClusters = !this.target._freeform_useClusters)),
initialize: emptyFunction,
};
_saveFilterCommand = {
params: ['target'],
title: 'save filter',
- script: `self.target._childFilters = compareLists(self['target-childFilters'],self.target._childFilters) ? undefined : copyField(self['target-childFilters']);
- self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
+ 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[]) => {
this.target._childFilters = undefined;
this.target._searchFilterDocs = undefined;
@@ -392,57 +345,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
this.document._facetWidth = 0;
};
- @computed get subChrome() {
- switch (
- this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type // bcz: ARgh! hack to get menu for tree view outline items
- ) {
- default:
- return this.otherSubChrome;
- case CollectionViewType.Invalid:
- case CollectionViewType.Freeform:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />;
- case CollectionViewType.Stacking:
- return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.NoteTaking:
- return <CollectionNoteTakingViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Schema:
- return <CollectionSchemaViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Tree:
- return <CollectionTreeViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Masonry:
- return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Carousel:
- case CollectionViewType.Carousel3D:
- return <Collection3DCarouselViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Grid:
- return <CollectionGridViewChrome key="collchrome" {...this.props} />;
- case CollectionViewType.Docking:
- return <CollectionDockingChrome key="collchrome" {...this.props} />;
- }
- }
-
- @computed get otherSubChrome() {
- const docType = this.props.docView.Document.type;
- switch (docType) {
- default:
- return null;
- case DocumentType.IMG:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- case DocumentType.PDF:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- case DocumentType.INK:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- case DocumentType.WEB:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- case DocumentType.VID:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- case DocumentType.RTF:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />;
- case DocumentType.MAP:
- return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
- }
- }
-
private dropDisposer?: DragManager.DragDropDisposer;
protected createDropTarget = (ele: HTMLDivElement) => {
this.dropDisposer?.();
@@ -519,612 +421,11 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
</div>
);
}
-
- @computed get viewModes() {
- const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
- const isPres: boolean = this.document && this.document.type === DocumentType.PRES;
- return isPres ? null : (
- <div className="collectionViewBaseChrome-viewModes">
- <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
- <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
- <button className={'antimodeMenu-button'}>
- <FontAwesomeIcon icon="bullseye" size="lg" />
- </button>
- <select className="collectionViewBaseChrome-viewPicker" onPointerDown={stopPropagation} onChange={this.viewChanged} value={StrCast(this.props.type)}>
- {Object.values(CollectionViewType)
- .filter(type => !excludedViewTypes.includes(type))
- .map(type => (
- <option key={Utils.GenerateGuid()} className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value={type}>
- {type[0].toUpperCase() + type.substring(1)}
- </option>
- ))}
- </select>
- </div>
- </Tooltip>
- </div>
- );
- }
-
- @computed get selectedDocumentView() {
- return SelectionManager.Views().lastElement();
- }
- @computed get selectedDoc() {
- return SelectionManager.Docs().lastElement();
- }
- @computed get notACollection() {
- if (this.selectedDoc) {
- const layoutField = Doc.LayoutField(this.selectedDoc);
- return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
- } else return false;
- }
- @computed
- get pinButton() {
- const targetDoc = this.selectedDoc;
- const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
- return !targetDoc ? null : (
- <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? 'Unpin from presentation' : 'Pin to presentation'}</div>} placement="top">
- <button
- className="antimodeMenu-button"
- style={{ backgroundColor: isPinned ? '121212' : undefined, borderLeft: '1px solid gray' }}
- onClick={e =>
- TabDocView.PinDoc(targetDoc, {
- /* unpin: isPinned*/
- })
- }>
- <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
- </button>
- </Tooltip>
- );
- }
-
- @undoBatch
- @action
- startRecording = () => {
- const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: media_state.PendingRecording });
- CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
- };
-
- @computed
- get recordButton() {
- const targetDoc = this.selectedDoc;
- return (
- <Tooltip key="record" title={<div className="dash-tooltip">{'Capture screen'}</div>} placement="top">
- <button className="antimodeMenu-button" onClick={e => this.startRecording()}>
- <div className="recordButtonOutline" style={{}}>
- <div className="recordButtonInner" style={{}}></div>
- </div>
- </button>
- </Tooltip>
- );
- }
-
- @undoBatch
- onEmbed = () => {
- if (this.selectedDoc && this.selectedDocumentView) {
- // const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
- // copy.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
- // copy.y = NumCast(this.selectedDoc.y) + 30;
- // this.selectedDocumentView.props.addDocument?.(copy);
- const embedding = Doc.MakeEmbedding(this.selectedDoc);
- embedding.x = NumCast(this.selectedDoc.x) + NumCast(this.selectedDoc._width);
- embedding.y = NumCast(this.selectedDoc.y) + 30;
- this.selectedDocumentView.props.addDocument?.(embedding);
- }
- };
- onEmbedButtonDown = (e: React.PointerEvent): void => {
- setupMoveUpEvents(this, e, this.onEmbedButtonMoved, emptyFunction, emptyFunction);
- };
-
- @undoBatch
- onEmbedButtonMoved = (e: PointerEvent) => {
- const contentDiv = this.selectedDocumentView?.ContentDiv;
- if (contentDiv && this.selectedDoc) {
- const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
- const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
- dragData.defaultDropAction = 'embed';
- dragData.canEmbed = true;
- DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
- offsetX: offset[0],
- offsetY: offset[1],
- hideSource: false,
- });
- return true;
- }
- return false;
- };
-
- @computed
- get embedButton() {
- const targetDoc = this.selectedDoc;
- return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an embedding'}</div>} placement="top">
- <button className="antimodeMenu-button" onPointerDown={this.onEmbedButtonDown} onClick={this.onEmbed} style={{ cursor: 'drag' }}>
- <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
- </button>
- </Tooltip>
- );
- }
-
- @computed get lightboxButton() {
- const targetDoc = this.selectedDoc;
- return !targetDoc ? null : (
- <Tooltip title={<div className="dash-tooltip">{'View in Lightbox'}</div>} placement="top">
- <button
- className="antimodeMenu-button"
- onPointerDown={() => {
- const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
- }}>
- <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
- </button>
- </Tooltip>
- );
- }
-
- @computed get toggleOverlayButton() {
- return (
- <>
- <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
- <button
- className={'antimodeMenu-button'}
- key="float"
- style={{
- backgroundColor: this.props.docView.layoutDoc.z ? '121212' : undefined,
- pointerEvents: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'none' : undefined,
- color: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
- }}
- onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
- <FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
- </button>
- </Tooltip>
- </>
- );
- }
-
render() {
return (
<div className="collectionMenu-cont">
<div className="collectionMenu">
- <div className="collectionViewBaseChrome">
- {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
- <div className="collectionMenu-divider" key="divider1"></div>
- {this.embedButton}
- {/* {this.pinButton} */}
- {this.toggleOverlayButton}
- <div className="collectionMenu-divider" key="divider2"></div>
- {this.subChrome}
- <div className="collectionMenu-divider" key="divider3"></div>
- {this.lightboxButton}
- {this.recordButton}
- {!this._buttonizableCommands ? null : this.templateChrome}
- </div>
- </div>
- </div>
- );
- }
-}
-
-@observer
-export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
- render() {
- return null;
- }
-}
-
-@observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean; isDoc?: boolean }> {
- public static Instance: CollectionFreeFormViewChrome;
- constructor(props: any) {
- super(props);
- CollectionFreeFormViewChrome.Instance = this;
- }
- get document() {
- return this.props.docView.props.Document;
- }
- @computed get dataField() {
- return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '_annotations' : '')];
- }
- @computed get childDocs() {
- return DocListCast(this.dataField);
- }
- @computed get selectedDocumentView() {
- return SelectionManager.Views().lastElement();
- }
- @computed get selectedDoc() {
- return SelectionManager.Docs().lastElement();
- }
- @computed get isText() {
- return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
- }
-
- public static gotoKeyFrame(doc: Doc, newFrame: number) {
- if (doc) {
- const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
- const currentFrame = Cast(doc._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- doc._currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
- }
- CollectionFreeFormView.updateKeyframe(undefined, [...childDocs, doc], currentFrame || 0);
- doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
- }
- }
-
- _keyTimer: NodeJS.Timeout | undefined;
- @undoBatch
- @action
- nextKeyframe = (): void => {
- const currentFrame = Cast(this.document._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- this.document._currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
- }
- this._keyTimer = CollectionFreeFormView.updateKeyframe(this._keyTimer, [...this.childDocs, this.document], currentFrame || 0);
- this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
- this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
- };
- @undoBatch
- @action
- prevKeyframe = (): void => {
- const currentFrame = Cast(this.document._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- this.document._currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
- }
- this._keyTimer = CollectionFreeFormView.gotoKeyframe(this._keyTimer, [...this.childDocs, this.document], 1000);
- this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
- };
-
- private _palette = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', ''];
- private _width = ['1', '5', '10', '100'];
- private _dotsize = [10, 20, 30, 40];
- private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
- private _head = ['', '', '', '', 'arrow', '', ''];
- private _end = ['', '', '', 'arrow', 'arrow', '', ''];
- private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'] as GestureUtils.Gestures[];
- private _title = ['pen', 'highlighter', 'line', 'line with arrow', 'line with double arrows', 'square', 'circle'];
- private _faName = ['pen-fancy', 'highlighter', 'minus', 'long-arrow-alt-right', 'arrows-alt-h', 'square', 'circle'];
- @observable _selectedPrimitive = this._shapePrims.length;
- @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
- @observable _colorBtn = false;
- @observable _widthBtn = false;
- @observable _fillBtn = false;
-
- @action clearKeepPrimitiveMode() {
- this._selectedPrimitive = this._shapePrims.length;
- }
- @action primCreated() {
- if (!this._keepPrimitiveMode) {
- //get out of ink mode after each stroke=
- if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
- Doc.ActiveTool = InkTool.None;
- this._selectedPrimitive = this._shapePrims.length;
- SetActiveArrowStart('none');
- SetActiveArrowEnd('none');
- }
- }
-
- @action
- changeColor = (color: string, type: string) => {
- const col: ColorState = {
- hex: color,
- hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
- hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
- rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
- oldHue: 0,
- source: '',
- };
- if (type === 'color') {
- SetActiveInkColor(Utils.colorString(col));
- } else if (type === 'fill') {
- SetActiveFillColor(Utils.colorString(col));
- }
- };
-
- @action
- editProperties = (value: any, field: string) => {
- SelectionManager.Views().forEach(
- action((element: DocumentView) => {
- const doc = Document(element.rootDoc);
- if (doc.type === DocumentType.INK) {
- switch (field) {
- case 'width':
- doc.stroke_width = Number(value);
- break;
- case 'color':
- doc.color = String(value);
- break;
- case 'fill':
- doc.fillColor = String(value);
- break;
- case 'dash':
- doc.stroke_dash = value;
- }
- }
- })
- );
- };
-
- @computed get drawButtons() {
- const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
- this._keepPrimitiveMode = keep;
- // these are for shapes
- if (this._selectedPrimitive !== i) {
- this._selectedPrimitive = i;
- if (this._title[i] === 'highlighter') {
- Doc.ActiveTool = InkTool.Highlighter;
- GestureOverlay.Instance.SavedColor = ActiveInkColor();
- SetActiveInkColor('rgba(245, 230, 95, 0.75)');
- } else {
- Doc.ActiveTool = InkTool.Pen;
- }
- SetActiveArrowStart(this._head[i]);
- SetActiveArrowEnd(this._end[i]);
- SetActiveBezierApprox('300');
-
- if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = this._shapePrims[i];
- } else {
- this._selectedPrimitive = this._shapePrims.length;
- Doc.ActiveTool = InkTool.None;
- SetActiveArrowStart('');
- SetActiveArrowEnd('');
- if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = undefined;
- SetActiveBezierApprox('0');
- }
- e.stopPropagation();
- });
- return (
- <div className="btn-draw" key="draw">
- {this._draw.map((icon, i) => (
- <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
- <button className="antimodeMenu-button" onPointerDown={e => func(e, i, false)} onDoubleClick={e => func(e, i, true)} style={{ backgroundColor: i === this._selectedPrimitive ? '525252' : '', fontSize: '20' }}>
- <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
- </button>
- </Tooltip>
- ))}
- </div>
- );
- }
-
- toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps['icon'], ele: JSX.Element | null) => {
- return (
- <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
- <button className="antimodeMenu-button" key={key} onPointerDown={action(e => setter())} style={{ backgroundColor: value ? '121212' : '' }}>
- <FontAwesomeIcon icon={icon} size="lg" />
- {ele}
- </button>
- </Tooltip>
- );
- };
-
- @computed get widthPicker() {
- const widthPicker = this.toggleButton('stroke width', this._widthBtn, () => (this._widthBtn = !this._widthBtn), 'bars', null);
- return !this._widthBtn ? (
- widthPicker
- ) : (
- <div className="btn2-group" key="width">
- {widthPicker}
- {this._width.map((wid, i) => (
- <Tooltip title={<div className="dash-tooltip">change width</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- key={wid}
- onPointerDown={action(() => {
- SetActiveInkWidth(wid);
- this._widthBtn = false;
- this.editProperties(wid, 'width');
- })}
- style={{ backgroundColor: this._widthBtn ? '121212' : '', zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: 'center' }}>
- •
- </button>
- </Tooltip>
- ))}
- </div>
- );
- }
-
- @computed get colorPicker() {
- const colorPicker = this.toggleButton('stroke color', this._colorBtn, () => (this._colorBtn = !this._colorBtn), 'pen-nib', <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? '121212' }} />);
- return !this._colorBtn ? (
- colorPicker
- ) : (
- <div className="btn-group" key="color">
- {colorPicker}
- {this._palette.map(color => (
- <button
- className="antimodeMenu-button"
- key={color}
- onPointerDown={action(() => {
- this.changeColor(color, 'color');
- this._colorBtn = false;
- this.editProperties(color, 'color');
- })}
- style={{ backgroundColor: this._colorBtn ? '121212' : '', zIndex: 1001 }}>
- {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
- <div className="color-previewII" style={{ backgroundColor: color }}>
- {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
- </div>
- </button>
- ))}
- </div>
- );
- }
- @computed get fillPicker() {
- const fillPicker = this.toggleButton('shape fill color', this._fillBtn, () => (this._fillBtn = !this._fillBtn), 'fill-drip', <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? '121212' }} />);
- return !this._fillBtn ? (
- fillPicker
- ) : (
- <div className="btn-group" key="fill">
- {fillPicker}
- {this._palette.map(color => (
- <button
- className="antimodeMenu-button"
- key={color}
- onPointerDown={action(() => {
- this.changeColor(color, 'fill');
- this._fillBtn = false;
- this.editProperties(color, 'fill');
- })}
- style={{ backgroundColor: this._fillBtn ? '121212' : '', zIndex: 1001 }}>
- <div className="color-previewII" style={{ backgroundColor: color }}>
- {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
- </div>
- </button>
- ))}
- </div>
- );
- }
-
- render() {
- return !this.props.docView.layoutDoc ? null : (
- <div className="collectionFreeFormMenu-cont">
- <RichTextMenu key="rich" />
- {!this.isText ? (
- <>
- {this.drawButtons}
- {this.widthPicker}
- {this.colorPicker}
- {this.fillPicker}
- {Doc.noviceMode || this.props.isDoc ? null : (
- <>
- <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
- <div className="backKeyframe" onClick={this.prevKeyframe}>
- <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
- </div>
- </Tooltip>
- <Tooltip key="num" title={<div className="dash-tooltip">Frame number</div>} placement="bottom">
- <div
- className="numKeyframe"
- style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? 'white' : 'black', backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? '#5B9FDD' : '#AEDDF8' }}
- onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))}>
- {NumCast(this.document._currentFrame)}
- </div>
- </Tooltip>
- <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
- <div className="fwdKeyframe" onClick={this.nextKeyframe}>
- <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
- </div>
- </Tooltip>
- </>
- )}
- </>
- ) : null}
- {!this.selectedDocumentView?.ComponentView?.menuControls ? null : this.selectedDocumentView?.ComponentView?.menuControls?.()}
- </div>
- );
- }
-}
-@observer
-export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
- @observable private _currentKey: string = '';
- @observable private suggestions: string[] = [];
-
- get document() {
- return this.props.docView.props.Document;
- }
-
- @computed private get descending() {
- return StrCast(this.document._columnsSort) === 'descending';
- }
- @computed get pivotField() {
- return StrCast(this.document._pivotField);
- }
-
- getKeySuggestions = async (value: string): Promise<string[]> => {
- const val = value.toLowerCase();
- const docs = DocListCast(this.document[this.props.fieldKey]);
-
- if (Doc.noviceMode) {
- 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);
- }
- 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);
- }
- };
-
- @action
- onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
- this._currentKey = newValue;
- };
-
- getSuggestionValue = (suggestion: string) => suggestion;
-
- renderSuggestion = (suggestion: string) => {
- return <p>{suggestion}</p>;
- };
-
- onSuggestionFetch = async ({ value }: { value: string }) => {
- const sugg = await this.getKeySuggestions(value);
- runInAction(() => {
- this.suggestions = sugg;
- });
- };
-
- @action
- onSuggestionClear = () => {
- this.suggestions = [];
- };
-
- @action
- setValue = (value: string) => {
- this.document._pivotField = value;
- return true;
- };
-
- @action toggleSort = () => {
- this.document._columnsSort = this.document._columnsSort === 'descending' ? 'ascending' : this.document._columnsSort === 'ascending' ? undefined : 'descending';
- };
- @action resetValue = () => {
- this._currentKey = this.pivotField;
- };
-
- render() {
- const doctype = this.props.docView.Document.type;
- const isPres: boolean = doctype === DocumentType.PRES;
- return isPres ? null : (
- <div className="collectionStackingViewChrome-cont">
- <div className="collectionStackingViewChrome-pivotField-cont">
- <div className="collectionStackingViewChrome-pivotField-label">GROUP BY:</div>
- <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? '180' : '0'}deg)` }}>
- <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
- </div>
- <div className="collectionStackingViewChrome-pivotField">
- <EditableView
- GetValue={() => this.pivotField}
- autosuggestProps={{
- resetValue: this.resetValue,
- value: this._currentKey,
- onChange: this.onKeyChange,
- autosuggestProps: {
- inputProps: {
- value: this._currentKey,
- onChange: this.onKeyChange,
- },
- getSuggestionValue: this.getSuggestionValue,
- suggestions: this.suggestions,
- alwaysRenderSuggestions: true,
- renderSuggestion: this.renderSuggestion,
- onSuggestionsFetchRequested: this.onSuggestionFetch,
- onSuggestionsClearRequested: this.onSuggestionClear,
- },
- }}
- oneLine
- SetValue={this.setValue}
- contents={this.pivotField ? this.pivotField : 'N/A'}
- />
- </div>
+ <div className="collectionViewBaseChrome">{!this._buttonizableCommands ? null : this.templateChrome}</div>
</div>
</div>
);
@@ -1137,7 +438,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
@observable private suggestions: string[] = [];
get document() {
- return this.props.docView.props.Document;
+ return this.props.docView.Document;
}
@computed private get descending() {
@@ -1230,6 +531,7 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
autosuggestProps: {
inputProps: {
value: this._currentKey,
+ // @ts-ignore
onChange: this.onKeyChange,
},
getSuggestionValue: this.getSuggestionValue,
@@ -1251,123 +553,6 @@ export class CollectionNoteTakingViewChrome extends React.Component<CollectionVi
}
}
-@observer
-export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
- // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
- get document() {
- return this.props.docView.props.Document;
- }
-
- @undoBatch
- togglePreview = () => {
- const dividerWidth = 4;
- const borderWidth = Number(COLLECTION_BORDER_WIDTH);
- const panelWidth = this.props.docView.props.PanelWidth();
- const previewWidth = NumCast(this.document.schema_previewWidth);
- const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
- this.document.schema_previewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
- };
-
- @undoBatch
- @action
- toggleTextwrap = async () => {
- const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []);
- if (textwrappedRows.length) {
- this.document.textwrappedSchemaRows = new List<string>([]);
- } else {
- const docs = DocListCast(this.document[this.props.fieldKey]);
- const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
- this.document.textwrappedSchemaRows = new List<string>(allRows);
- }
- };
-
- render() {
- const previewWidth = NumCast(this.document.schema_previewWidth);
- const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
-
- return (
- <div className="collectionSchemaViewChrome-cont">
- <div className="collectionSchemaViewChrome-toggle">
- <div className="collectionSchemaViewChrome-label">Show Preview: </div>
- <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}>
- <div className={'collectionSchemaViewChrome-togglerButton' + (previewWidth !== 0 ? ' on' : ' off')}>{previewWidth !== 0 ? 'on' : 'off'}</div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-@observer
-export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
- get document() {
- return this.props.docView.props.Document;
- }
- get sortAscending() {
- return this.document[this.props.fieldKey + '-sortAscending'];
- }
- set sortAscending(value) {
- this.document[this.props.fieldKey + '-sortAscending'] = value;
- }
- @computed private get ascending() {
- return Cast(this.sortAscending, 'boolean', null);
- }
-
- @action toggleSort = () => {
- if (this.sortAscending) this.sortAscending = undefined;
- else if (this.sortAscending === undefined) this.sortAscending = false;
- else this.sortAscending = true;
- };
-
- render() {
- return (
- <div className="collectionTreeViewChrome-cont">
- <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
- <div className="collectionTreeViewChrome-sortLabel">Sort</div>
- <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? '90' : this.ascending ? '180' : '0'}deg)` }}>
- <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
- </div>
- </button>
- </div>
- );
- }
-}
-
-// Enter scroll speed for 3D Carousel
-@observer
-export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
- get document() {
- return this.props.docView.props.Document;
- }
- @computed get scrollSpeed() {
- return this.document._autoScrollSpeed;
- }
-
- @action
- setValue = (value: string) => {
- const numValue = Number(StrCast(value));
- if (numValue > 0) {
- this.document._autoScrollSpeed = numValue;
- return true;
- }
- return false;
- };
-
- render() {
- return (
- <div className="collection3DCarouselViewChrome-cont">
- <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
- {/* {FormattedTextBox.Focused ? <RichTextMenu /> : null} */}
- <div className="collectionStackingViewChrome-scrollSpeed-label">AUTOSCROLL SPEED:</div>
- <div className="collection3DCarouselViewChrome-scrollSpeed">
- <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
- </div>
- </div>
- </div>
- );
- }
-}
-
/**
* Chrome for grid view.
*/
@@ -1379,16 +564,21 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
@observable private resize = false;
private resizeListenerDisposer: Opt<Lambda>;
get document() {
- return this.props.docView.props.Document;
+ return this.props.docView.Document;
+ }
+
+ @computed get panelWidth() {
+ return this.props.docView.props.PanelWidth();
}
componentDidMount() {
runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
// listener to reduce text on chrome resize (panel resize)
- this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => {
- runInAction(() => (this.resize = newValue < 700));
- });
+ this.resizeListenerDisposer = reaction(
+ () => this.panelWidth,
+ newValue => (this.resize = newValue < 700)
+ );
}
componentWillUnmount() {
@@ -1513,12 +703,6 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
<input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
<input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
</span>
- {/* <span className="grid-control">
- <span className="grid-icon">
- <FontAwesomeIcon icon="text-height" size="1x" />
- </span>
- <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
- </span> */}
<span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
<input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.document.gridPreventCollision} />
<label className="flexLabel">{this.resize ? 'Coll' : 'Collisions'}</label>
@@ -1547,6 +731,3 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
);
}
}
-ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
- CollectionFreeFormViewChrome.gotoKeyFrame(doc, newFrame);
-});
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index be1800d81..91a82d40f 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.collectionNoteTakingView-DocumentButtons {
display: none;
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index afeef5a8f..b8133806f 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,9 +1,8 @@
-import React = require('react');
-import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction, trace } from 'mobx';
+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 { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -19,14 +18,15 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { LightboxView } from '../LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import './CollectionNoteTakingView.scss';
import { CollectionNoteTakingViewColumn } from './CollectionNoteTakingViewColumn';
import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivider';
import { CollectionSubView } from './CollectionSubView';
+import { JsxElement } from 'typescript';
const _global = (window /* browser */ || global) /* node */ as any;
/**
@@ -44,10 +44,15 @@ export class CollectionNoteTakingView extends CollectionSubView() {
notetakingCategoryField = 'NotetakingCategory';
public DividerWidth = 16;
@observable docsDraggedRowCol: number[] = [];
- @observable _cursor: CursorProperty = 'grab';
@observable _scroll = 0;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get chromeHidden() {
- return BoolCast(this.layoutDoc.chromeHidden) || this.props.onBrowseClick?.() ? true : false;
+ return BoolCast(this.layoutDoc.chromeHidden) || this._props.onBrowseClickScript?.() ? true : false;
}
// columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
@computed get colHeaderData() {
@@ -66,7 +71,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return colHeaderData ?? ([] as SchemaHeaderField[]);
}
@computed get headerMargin() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin);
}
@computed get xMargin() {
return NumCast(this.layoutDoc._xMargin, 5);
@@ -83,11 +88,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
// PanelWidth returns the size of the total available space the view occupies
@computed get PanelWidth() {
- return this.props.PanelWidth();
+ return this._props.PanelWidth();
}
// maxColWidth returns the maximum column width, which is slightly less than the total available space.
@computed get maxColWidth() {
- return this.props.PanelWidth();
+ return this._props.PanelWidth();
}
// availableWidth is the total amount of non-divider width. Since widths are stored relatively,
// we use availableWidth to convert from a percentage to a pixel count.
@@ -153,7 +158,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
this._disposers.layout_autoHeight = reaction(
() => this.layoutDoc._layout_autoHeight,
layout_autoHeight =>
- layout_autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
+ layout_autoHeight && this._props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
);
}
@@ -163,9 +168,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
Object.keys(this._disposers).forEach(key => this._disposers[key]());
}
- @action
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;
+ return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
};
createRef = (ele: HTMLDivElement | null) => {
@@ -174,11 +178,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
@computed get onChildClickHandler() {
- return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+ return () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
}
@computed get onChildDoubleClickHandler() {
- return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+ return () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
}
scrollToBottom = () => {
@@ -186,13 +190,13 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
// let's dive in and get the actual document we want to drag/move around
- focusDocument = (doc: Doc, options: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: FocusViewOptions) => {
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 localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
- if (Math.floor(localTop[1]) !== 0 && Math.ceil(this.props.PanelHeight()) < (this._mainCont?.scrollHeight || 0)) {
+ 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;
smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
return focusSpeed;
@@ -200,7 +204,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
};
- styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
+ styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
switch (property) {
case StyleProp.BoxShadow:
if (doc && DragManager.docsBeingDragged.includes(doc)) {
@@ -208,20 +212,20 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
break;
case StyleProp.Opacity:
- if (doc && this.props.childOpacity) {
- return this.props.childOpacity();
+ if (doc && this._props.childOpacity) {
+ return this._props.childOpacity();
}
break;
}
- return this.props.styleProvider?.(doc, props, property);
+ return this._props.styleProvider?.(doc, props, property);
};
- isContentActive = () => this.props.isContentActive();
+ isContentActive = () => this._props.isContentActive();
blockPointerEventsWhenDragging = () => (this.docsDraggedRowCol.length ? 'none' : undefined);
// getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView)
getDisplayDoc(doc: Doc, width: () => number) {
- const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
+ const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this._props.TemplateDataDocument;
const height = () => this.getDocHeight(doc);
let dref: Opt<DocumentView>;
const noteTakingDocTransform = () => this.getDocTransform(doc, dref);
@@ -229,47 +233,45 @@ export class CollectionNoteTakingView extends CollectionSubView() {
<DocumentView
ref={r => (dref = r || undefined)}
Document={doc}
+ TemplateDataDocument={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
pointerEvents={this.blockPointerEventsWhenDragging}
- DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
PanelWidth={width}
PanelHeight={height}
styleProvider={this.styleProvider}
- docViewPath={this.props.docViewPath}
- layout_fitWidth={this.props.childLayoutFitWidth}
+ containerViewPath={this.childContainerViewPath}
+ 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}
isDocumentActive={this.isContentActive}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
- NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
- dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
- dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ NativeWidth={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+ NativeHeight={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+ dontCenter={this._props.childIgnoreNativeSize ? 'xy' : undefined}
+ dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this._props.dontRegisterView)}
rootSelected={this.rootSelected}
- layout_showTitle={this.props.childlayout_showTitle}
+ layout_showTitle={this._props.childlayout_showTitle}
dragAction={StrCast(this.layoutDoc.childDragAction) as dropActionType}
- onClick={this.onChildClickHandler}
- onBrowseClick={this.props.onBrowseClick}
- onDoubleClick={this.onChildDoubleClickHandler}
+ onClickScript={this.onChildClickHandler}
+ onBrowseClickScript={this._props.onBrowseClickScript}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
ScreenToLocalTransform={noteTakingDocTransform}
focus={this.focusDocument}
childFilters={this.childDocFilters}
- hideDecorationTitle={this.props.childHideDecorationTitle?.()}
- hideResizeHandles={this.props.childHideResizeHandles?.()}
- hideTitle={this.props.childHideTitle?.()}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ hideResizeHandles={this._props.childHideResizeHandles}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents) as any}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- bringToFront={returnFalse}
- pinToPres={this.props.pinToPres}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ contentPointerEvents={StrCast(this.layoutDoc.childContentPointerEvents) as any}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
/>
);
}
@@ -279,7 +281,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
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);
// 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.props.ScreenToLocalTransform().Scale);
+ return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.ScreenToLocalBoxXf().Scale);
}
// how to get the width of a document. Currently returns the width of the column (minus margins)
@@ -289,24 +291,24 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
const existingWidth = existingHeader?.width ? existingHeader.width : 0;
const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
- const width = d.layout_fitWidth ? maxWidth : d[Width]();
+ const width = d.layout_fitWidth ? maxWidth : NumCast(d._width);
return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
}
// how to get the height of a document. Nothing special here.
getDocHeight(d?: Doc) {
if (!d || d.hidden) return 0;
- const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
- const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
- const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
- const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
- const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
+ const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+ const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
+ const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
+ const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
if (nw && nh) {
const docWid = this.getDocWidth(d);
return Math.min(maxHeight, (docWid * nh) / nw);
}
const childHeight = NumCast(childLayoutDoc._height);
- const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+ const panelHeight = childLayoutDoc._layout_fitWidth || this._props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this._props.PanelHeight() - 2 * this.yMargin;
return Math.min(childHeight, maxHeight, panelHeight);
}
@@ -336,12 +338,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// onPointerMove is used to preview where a document will drop in a column once a drag is complete.
@action
onPointerMove = (force: boolean, ex: number, ey: number) => {
- if (this.childDocList && (this.childDocList.includes(DragManager.DocDragData?.draggedDocuments.lastElement()!) || force || this.isContentActive())) {
+ if (this.childDocList?.includes(DragManager.DocDragData?.draggedDocuments?.lastElement() as any) || force || SnappingManager.CanEmbed) {
// get the current docs for the column based on the mouse's x coordinate
- const xCoord = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[0] - 2 * this.gridGap;
+ const xCoord = this.ScreenToLocalBoxXf().transformPoint(ex, ey)[0] - 2 * this.gridGap;
const colDocs = this.getDocsFromXCoord(xCoord);
// get the index for where you need to insert the doc you are currently dragging
- const clientY = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[1];
+ const clientY = this.ScreenToLocalBoxXf().transformPoint(ex, ey)[1];
let dropInd = -1;
let pos0 = (this.refList.lastElement() as HTMLDivElement).children[0].getBoundingClientRect().height + this.yMargin * 2;
colDocs.forEach((doc, i) => {
@@ -404,14 +406,12 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
@undoBatch
- @action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- const docView = fieldProps.DocumentView?.();
- if (docView && (e.ctrlKey || docView.rootDoc._createDocOnCR) && ['Enter'].includes(e.key)) {
+ if ((e.ctrlKey || fieldProps.Document._createDocOnCR) && ['Enter'].includes(e.key)) {
e.stopPropagation?.();
- const newDoc = Doc.MakeCopy(docView.rootDoc, true);
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ const newDoc = Doc.MakeCopy(fieldProps.Document, true);
+ newDoc[DocData].text = undefined;
+ FormattedTextBox.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
};
@@ -419,7 +419,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// onInternalDrop is used when dragging and dropping a document within the view, such as dragging
// a document to a new column or changing its order within the column.
@undoBatch
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData) {
if (super.onInternalDrop(e, de)) {
@@ -435,7 +434,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
if (rowCol[0] <= 0) {
docs.splice(0, 0, ...newDocs);
} else {
- const colDocs = this.getDocsFromXCoord(this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0]);
+ const colDocs = this.getDocsFromXCoord(this.ScreenToLocalBoxXf().transformPoint(de.x, de.y)[0]);
const previousDoc = colDocs[rowCol[0] - 1];
const previousDocIndex = docs.indexOf(previousDoc);
docs.splice(previousDocIndex + 1, 0, ...newDocs);
@@ -443,9 +442,9 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
- if (!this.props.addDocument?.(source)) e.preventDefault();
+ 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
e.stopPropagation();
return true;
@@ -458,7 +457,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
@undoBatch
internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
const dropCreator = annoDragData.dropDocCreator;
- annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => dropCreator(annotationOn) || this.rootDoc;
+ annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => dropCreator(annotationOn) || this.Document;
return true;
}
@@ -503,16 +502,17 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const type = 'number';
return (
<CollectionNoteTakingViewColumn
+ key={heading?.heading ?? 'unset'}
unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
observeHeight={ref => {
if (ref) {
this.refList.push(ref);
this.observer = new _global.ResizeObserver(
action((entries: any) => {
- if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
- if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
- this.props.setHeight?.(height);
+ if (!LightboxView.Contains(this.DocumentView?.())) {
+ this._props.setHeight?.(height);
}
}
})
@@ -520,13 +520,13 @@ export class CollectionNoteTakingView extends CollectionSubView() {
this.observer.observe(ref);
}
}}
- PanelWidth={this.props.PanelWidth}
- select={this.props.select}
+ PanelWidth={this._props.PanelWidth}
+ select={this._props.select}
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.props.Document}
- DataDoc={this.props.DataDoc}
+ Document={this.Document}
+ TemplateDataDocument={this._props.TemplateDataDocument}
resizeColumns={this.resizeColumns}
renderChildren={this.children}
numGroupColumns={this.numGroupColumns}
@@ -536,7 +536,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
dividerWidth={this.DividerWidth}
maxColWidth={this.maxColWidth}
availableWidth={this.availableWidth}
- key={heading?.heading ?? 'unset'}
headings={this.headings}
heading={heading?.heading ?? 'unset'}
headingObject={heading}
@@ -544,7 +543,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
yMargin={this.yMargin}
type={type}
createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.props.ScreenToLocalTransform}
+ screenToLocalTransform={this.ScreenToLocalBoxXf}
editableViewProps={this.editableViewProps}
/>
);
@@ -553,7 +552,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of
// columnHeaders and resizing the existing columns to make room for our new one.
@undoBatch
- @action
addGroup = (value: string) => {
if (this.colHeaderData) {
for (const header of this.colHeaderData) {
@@ -584,7 +582,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// setColumnStartXCoords is used to update column widths when using the drag handlers between columns
@action
setColumnStartXCoords = (movementXScreen: number, colIndex: number) => {
- const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0];
+ const movementX = this.ScreenToLocalBoxXf().transformDirection(movementXScreen, 0)[0];
const leftHeader = this.colHeaderData[colIndex];
const rightHeader = this.colHeaderData[colIndex + 1];
leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
@@ -599,14 +597,11 @@ export class CollectionNoteTakingView extends CollectionSubView() {
@computed get renderedSections() {
TraceMobx();
const sections = Array.from(this.Sections.entries());
- return sections.map((sec, i) => (
- <>
- {this.sectionNoteTaking(sec[0], sec[1])}
- {i === sections.length - 1 ? null : ( //
- <CollectionNoteTakingViewDivider key={`divider${i}`} isContentActive={this.isContentActive} index={i} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />
- )}
- </>
- ));
+ return sections.reduce((list, sec, i) => {
+ list.push(this.sectionNoteTaking(sec[0], sec[1]));
+ 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[]);
}
@computed get nativeWidth() {
@@ -617,7 +612,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
@computed get scaling() {
- return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight;
+ return !this.nativeWidth ? 1 : this._props.PanelHeight() / this.nativeHeight;
}
@computed get backgroundEvents() {
@@ -634,8 +629,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
ref={this.createRef}
key="notes"
style={{
- overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
- background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+ overflowY: this._props.isContentActive() ? 'auto' : 'hidden',
+ background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor),
pointerEvents: this.backgroundEvents,
}}
onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
@@ -644,8 +639,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
onDragOver={e => this.onPointerMove(true, e.clientX, e.clientY)}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
- onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}>
- {this.renderedSections}
+ onWheel={e => this._props.isContentActive() && e.stopPropagation()}>
+ <>{this.renderedSections}</>
</div>
);
}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 52cc21903..38846c79d 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -1,18 +1,17 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnEmptyString } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Copy, Id } from '../../../fields/FieldSymbols';
+import { Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { returnEmptyString } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
+import { DocUtils, Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
@@ -20,12 +19,13 @@ import { undoBatch } 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';
interface CSVFieldColumnProps {
Document: Doc;
- DataDoc: Opt<Doc>;
+ TemplateDataDocument: Opt<Doc>;
docList: Doc[];
heading: string;
pivotField: string;
@@ -58,43 +58,43 @@ interface CSVFieldColumnProps {
* majority of functions here are for rendering styles.
*/
@observer
-export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColumnProps> {
+export class CollectionNoteTakingViewColumn extends ObservableReactComponent<CSVFieldColumnProps> {
@observable private _background = 'inherit';
// columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- if (!this.props.colHeaderData || !this.props.headingObject || this.props.colHeaderData.length === 1) return `${(this.props.availableWidth / this.props.PanelWidth()) * 100}%`;
- const i = this.props.colHeaderData.indexOf(this.props.headingObject);
- return ((this.props.colHeaderData[i].width * this.props.availableWidth) / this.props.PanelWidth()) * 100 + '%';
+ if (!this._props.colHeaderData || !this._props.headingObject || this._props.colHeaderData.length === 1) return `${(this._props.availableWidth / this._props.PanelWidth()) * 100}%`;
+ const i = this._props.colHeaderData.findIndex(hd => hd.heading === this._props.headingObject?.heading && hd.color === this._props.headingObject.color);
+ return ((this._props.colHeaderData[i].width * this._props.availableWidth) / this._props.PanelWidth()) * 100 + '%';
}
private dropDisposer?: DragManager.DragDropDisposer;
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
public static ColumnMargin = 10;
- @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
- @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
+ @observable _heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading;
+ @observable _color = this._props.headingObject ? this._props.headingObject.color : '#f1efeb';
_ele: HTMLElement | null = null;
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
if (ele) {
this._ele = ele;
- this.props.observeHeight(ele);
- this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
+ this._props.observeHeight(ele);
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
}
};
componentWillUnmount() {
- this.props.unobserveHeight(this._ele);
+ this._props.unobserveHeight(this._ele);
}
@undoBatch
- columnDrop = action((e: Event, de: DragManager.DropEvent) => {
+ columnDrop = (e: Event, de: DragManager.DropEvent) => {
const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
- drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+ drop.docs?.forEach(d => Doc.SetInPlace(d, this._props.pivotField, drop.val, false));
return true;
- });
+ };
getValue = (value: string): any => {
const parsed = parseInt(value);
@@ -108,20 +108,20 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
headingChanged = (value: string, shiftDown?: boolean) => {
const castedValue = this.getValue(value);
if (castedValue) {
- if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+ if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- 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;
+ 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;
}
return true;
}
return false;
};
- @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4');
+ @action pointerEntered = () => SnappingManager.IsDragging && (this._background = '#b4b4b4');
@action pointerLeave = () => (this._background = 'inherit');
@undoBatch
addTextNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
@@ -130,26 +130,25 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
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);
+ const colValue = this.getValue(this._props.heading);
newDoc[key] = colValue;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ FormattedTextBox.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
- return this.props.addDocument?.(newDoc) || false;
+ return this._props.addDocument?.(newDoc) || false;
};
// deleteColumn is called when a user deletes a column using the 'trash' icon in the button area.
// If the user deletes the first column, the documents get moved to the second column. Otherwise,
// all docs are added to the column directly to the left.
@undoBatch
- @action
deleteColumn = () => {
- const colHdrData = Array.from(Cast(this.props.Document[this.props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
- if (this.props.headingObject) {
- this.props.docList.forEach(d => (d[this.props.pivotField] = undefined));
- colHdrData.splice(colHdrData.indexOf(this.props.headingObject), 1);
- this.props.resizeColumns(colHdrData);
+ const colHdrData = Array.from(Cast(this._props.Document[this._props.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null));
+ if (this._props.headingObject) {
+ this._props.docList.forEach(d => (d[this._props.pivotField] = undefined));
+ colHdrData.splice(colHdrData.indexOf(this._props.headingObject), 1);
+ this._props.resizeColumns(colHdrData);
}
};
@@ -157,21 +156,21 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
- const dataDoc = this.props.DataDoc || this.props.Document;
- const pivotValue = this.getValue(this.props.heading);
+ const dataDoc = this._props.TemplateDataDocument || this._props.Document;
+ const pivotValue = this.getValue(this._props.heading);
DocUtils.addDocumentCreatorMenuItems(
doc => {
- const key = this.props.pivotField;
- doc[key] = this.getValue(this.props.heading);
- FormattedTextBox.SelectOnLoad = doc[Id];
- return this.props.addDocument?.(doc);
+ const key = this._props.pivotField;
+ doc[key] = this.getValue(this._props.heading);
+ FormattedTextBox.SetSelectOnLoad(doc);
+ return this._props.addDocument?.(doc);
},
- this.props.addDocument,
+ this._props.addDocument,
x,
y,
true,
- this.props.pivotField,
+ this._props.pivotField,
pivotValue
);
@@ -181,12 +180,12 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document));
+ const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
if (created) {
- if (this.props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+ if (this._props.Document.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
- return this.props.addDocument?.(created);
+ return this._props.addDocument?.(created);
}
},
icon: 'compress-arrows-alt',
@@ -200,12 +199,12 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
event: () => {
const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
if (created) {
- const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document;
+ const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
if (container.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, container);
return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
}
- return this.props.addDocument?.(created) || false;
+ return this._props.addDocument?.(created) || false;
}
},
icon: 'compress-arrows-alt',
@@ -214,13 +213,13 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
!Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
!Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this.props.Document)[name] = '';
+ Doc.GetProto(this._props.Document)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this.props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+ if (this._props.Document.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
- this.props.addDocument?.(created);
+ this._props.addDocument?.(created);
}
});
ContextMenu.Instance.displayMenu(x, y, undefined, true);
@@ -228,26 +227,26 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
@computed get innards() {
TraceMobx();
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
const heading = this._heading;
- const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin;
+ const columnYMargin = this._props.headingObject ? 0 : this._props.yMargin;
const evContents = heading ? heading : '25';
- const headingView = this.props.headingObject ? (
+ const headingView = this._props.headingObject ? (
<div
key={heading}
className="collectionNoteTakingView-sectionHeader"
ref={this._headerRef}
style={{
- marginTop: 2 * this.props.yMargin,
+ marginTop: 2 * this._props.yMargin,
width: 'calc(100% - 5px)',
}}>
<div
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={true} />
</div>
- {(this.props.colHeaderData?.length ?? 0) > 1 && (
+ {(this._props.colHeaderData?.length ?? 0) > 1 && (
<button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>
@@ -255,7 +254,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
</div>
) : null;
const templatecols = this.columnWidth;
- const type = this.props.Document.type;
+ const type = this._props.Document.type;
return (
<>
{headingView}
@@ -265,20 +264,20 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
key={`${heading}-stack`}
className={`collectionNoteTakingView-Nodes`}
style={{
- padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
- gridGap: this.props.gridGap,
+ padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
+ gridGap: this._props.gridGap,
gridTemplateColumns: templatecols,
}}>
- {this.props.renderChildren(this.props.docList)}
+ {this._props.renderChildren(this._props.docList)}
</div>
- {!this.props.chromeHidden ? (
+ {!this._props.chromeHidden ? (
<div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
<div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
<EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.addTextNote} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
</div>
- <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
- <EditableView {...this.props.editableViewProps()} />
+ <div key={`${this._props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
+ <EditableView {...this._props.editableViewProps()} />
</div>
</div>
) : null}
@@ -298,7 +297,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
style={{
width: this.columnWidth,
background: this._background,
- marginLeft: this.props.headings().findIndex((h: any) => h[0] === this.props.headingObject) === 0 ? NumCast(this.props.Document.xMargin) : 0,
+ marginLeft: this._props.headings().findIndex((h: any) => h[0] === this._props.headingObject) === 0 ? NumCast(this._props.Document.xMargin) : 0,
}}
ref={this.createColumnDropRef}
onPointerEnter={this.pointerEntered}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index af822d917..5e4bce19d 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,8 +1,9 @@
-import { action, observable, trace } from 'mobx';
+import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
import { UndoManager } from '../../util/UndoManager';
+import { ObservableReactComponent } from '../ObservableReactComponent';
interface DividerProps {
index: number;
@@ -17,8 +18,7 @@ interface DividerProps {
* are two simple vertical lines that allow the user to alter the widths of CollectionNoteTakingViewColumns.
*/
@observer
-export class CollectionNoteTakingViewDivider extends React.Component<DividerProps> {
- @observable private isHoverActive = false;
+export class CollectionNoteTakingViewDivider extends ObservableReactComponent<DividerProps> {
@observable private isResizingActive = false;
@action
@@ -29,12 +29,11 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
e,
(e, down, delta) => {
if (!batch) batch = UndoManager.StartBatch('resizing');
- this.props.setColumnStartXCoords(delta[0], this.props.index);
+ this._props.setColumnStartXCoords(delta[0], this._props.index);
return false;
},
action(() => {
this.isResizingActive = false;
- this.isHoverActive = false;
batch?.end();
}),
emptyFunction
@@ -50,10 +49,8 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
display: 'flex',
alignItems: 'center',
cursor: 'col-resize',
- pointerEvents: this.props.isContentActive() ? 'all' : 'none',
- }}
- onPointerEnter={action(() => (this.isHoverActive = true))}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+ pointerEvents: this._props.isContentActive() ? 'all' : 'none',
+ }}>
<div
className="columnResizer-handler"
onPointerDown={e => this.registerResizing(e)}
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 91701b213..d0df77cbe 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,7 +1,7 @@
-import { action, computed, IReactionDisposer } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { ScriptField } from '../../../fields/ScriptField';
import { NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
@@ -13,13 +13,17 @@ import { computePassLayout, computeStarburstLayout } from './collectionFreeForm'
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import './CollectionPileView.scss';
import { CollectionSubView } from './CollectionSubView';
-import React = require('react');
@observer
export class CollectionPileView extends CollectionSubView() {
_originalChrome: any = '';
_disposers: { [name: string]: IReactionDisposer } = {};
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentDidMount() {
if (this.layoutEngine() !== computePassLayout.name && this.layoutEngine() !== computeStarburstLayout.name) {
this.Document._freeform_pileEngine = computePassLayout.name;
@@ -37,14 +41,14 @@ export class CollectionPileView extends CollectionSubView() {
@undoBatch
addPileDoc = (doc: Doc | Doc[]) => {
(doc instanceof Doc ? [doc] : doc).map(d => DocUtils.iconify(d));
- return this.props.addDocument?.(doc) || false;
+ return this._props.addDocument?.(doc) || 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;
- if (ret && !DocListCast(this.rootDoc[this.fieldKey ?? 'data']).length) this.props.DocumentView?.().props.removeDocument?.(this.rootDoc);
+ const ret = this._props.moveDocument?.(doc, targetCollection, addDoc) || false;
+ if (ret && !DocListCast(this.dataDoc[this.fieldKey ?? 'data']).length) this.DocumentView?.()._props.removeDocument?.(this.Document);
return ret;
};
@@ -53,7 +57,7 @@ export class CollectionPileView extends CollectionSubView() {
}
@computed get contentEvents() {
const isStarburst = this.layoutEngine() === computeStarburstLayout.name;
- return this.props.isContentActive() && isStarburst ? undefined : 'none';
+ return this._props.isContentActive() && isStarburst ? undefined : 'none';
}
// returns the contents of the pileup in a CollectionFreeFormView
@@ -61,13 +65,13 @@ export class CollectionPileView extends CollectionSubView() {
return (
<div className="collectionPileView-innards" style={{ pointerEvents: this.contentEvents }}>
<CollectionFreeFormView
- {...this.props} //
+ {...this._props} //
layoutEngine={this.layoutEngine}
addDocument={this.addPileDoc}
moveDocument={this.removePileDoc}
// pile children never have their contents active, but will be document active whenever the entire pile is.
childContentsActive={returnFalse}
- childDocumentsActive={this.props.isDocumentActive}
+ childDocumentsActive={this._props.isDocumentActive}
childDragAction="move"
childClickScript={this.toggleIcon}
/>
@@ -79,28 +83,28 @@ export class CollectionPileView extends CollectionSubView() {
toggleStarburst = action(() => {
this.layoutDoc._freeform_scale = undefined;
if (this.layoutEngine() === computeStarburstLayout.name) {
- if (this.rootDoc[Width]() !== NumCast(this.rootDoc._starburstDiameter, 500)) {
- this.rootDoc._starburstDiameter = this.rootDoc[Width]();
+ if (NumCast(this.layoutDoc._width) !== NumCast(this.Document._starburstDiameter, 500)) {
+ this.Document._starburstDiameter = NumCast(this.layoutDoc._width);
}
const defaultSize = 110;
- this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
- this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
+ this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
+ this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize);
this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize);
DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
this.layoutDoc._freeform_panX = 0;
this.layoutDoc._freeform_panY = -10;
- this.props.Document._freeform_pileEngine = computePassLayout.name;
+ this.Document._freeform_pileEngine = computePassLayout.name;
} else {
- const defaultSize = NumCast(this.rootDoc._starburstDiameter, 400);
- this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2;
- this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2;
- this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width]();
- this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height]();
+ const defaultSize = NumCast(this.Document._starburstDiameter, 400);
+ this.Document.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
+ this.Document.y = NumCast(this.Document.y) + NumCast(this.layoutDoc._height) / 2 - defaultSize / 2;
+ this.layoutDoc._freeform_pileWidth = NumCast(this.layoutDoc._width);
+ this.layoutDoc._freeform_pileHeight = NumCast(this.layoutDoc._height);
this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
this.layoutDoc._width = this.layoutDoc._height = defaultSize;
this.layoutDoc.background;
- this.props.Document._freeform_pileEngine = computeStarburstLayout.name;
+ this.Document._freeform_pileEngine = computeStarburstLayout.name;
}
});
@@ -121,7 +125,7 @@ export class CollectionPileView extends CollectionSubView() {
const doc = this.childDocs[0];
doc.x = e.clientX;
doc.y = e.clientY;
- this.props.addDocTab(doc, OpenWhere.inParentFromScreen) && (this.props.removeDocument?.(doc) || false);
+ this._props.addDocTab(doc, OpenWhere.inParentFromScreen) && (this._props.removeDocument?.(doc) || false);
dist = 0;
}
}
@@ -139,7 +143,6 @@ export class CollectionPileView extends CollectionSubView() {
// onClick for toggling the pileup view
@undoBatch
- @action
onClick = (e: React.MouseEvent) => {
if (e.button === 0) {
SelectionManager.DeselectAll();
@@ -150,7 +153,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.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index a19d8e696..0ced3f9e3 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.collectionStackedTimeline-timelineContainer {
height: 100%;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 7c61bc4da..5c47d4b9e 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,8 +1,9 @@
-import React = require('react');
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+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 { Doc, Opt } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -17,24 +18,26 @@ import { DragManager } from '../../util/DragManager';
import { FollowLinkScript, IsFollowLinkScript, LinkFollower } from '../../util/LinkFollower';
import { LinkManager } from '../../util/LinkManager';
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';
-import { AudioWaveform } from '../AudioWaveform';
import { CollectionSubView } from '../collections/CollectionSubView';
import { LightboxView } from '../LightboxView';
-import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../nodes/DocumentView';
+import { AudioWaveform } from '../nodes/audio/AudioWaveform';
+import { DocumentView, OpenWhere } from '../nodes/DocumentView';
+import { FocusFuncType, FocusViewOptions, StyleProviderFuncType } from '../nodes/FieldView';
import { LabelBox } from '../nodes/LabelBox';
import { VideoBox } from '../nodes/VideoBox';
+import { ObservableReactComponent } from '../ObservableReactComponent';
import './CollectionStackedTimeline.scss';
export type CollectionStackedTimelineProps = {
Play: () => void;
Pause: () => void;
- playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
+ playLink: (linkDoc: Doc, options: FocusViewOptions) => void;
playFrom: (seekTimeInSeconds: number, endTime?: number) => void;
playing: () => boolean;
+ thumbnails?: () => string[];
setTime: (time: number) => void;
startTag: string;
endTag: string;
@@ -43,7 +46,6 @@ export type CollectionStackedTimelineProps = {
rawDuration: number;
dataFieldKey: string;
fieldKey: string;
- thumbnails?: () => string[];
};
// trimming state: shows full clip, current trim bounds, or not trimming
@@ -55,8 +57,15 @@ export enum TrimScope {
@observer
export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
- @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
- @observable public static CurrentlyPlaying: DocumentView[];
+ public static SelectingRegions: Set<CollectionStackedTimeline> = new Set();
+ public static StopSelecting() {
+ this.SelectingRegions.forEach(action(region => (region._selectingRegion = false)));
+ this.SelectingRegions.clear();
+ }
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
static LabelScript: ScriptField;
static LabelPlayScript: ScriptField;
@@ -64,7 +73,9 @@ 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 _markerEnd: number | undefined;
+ @observable public static CurrentlyPlaying: DocumentView[] = [];
+ @observable _selectingRegion = false;
+ @observable _markerEnd: number | undefined = undefined;
@observable _trimming: number = TrimScope.None;
@observable _trimStart: number = 0; // trim controls start pos
@observable _trimEnd: number = 0; // trim controls end pos
@@ -74,14 +85,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@observable _hoverTime: number = 0;
- @observable _thumbnail: string | undefined;
+ @observable _thumbnail: string | undefined = undefined;
// ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore
get minTrimLength() {
return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
}
@computed get thumbnails() {
- return this.props.thumbnails?.();
+ return this._props.thumbnails?.();
}
@computed get trimStart() {
@@ -101,7 +112,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
return this.clipEnd - this.clipStart;
}
@computed get clipEnd() {
- return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration);
+ return this.IsTrimming === TrimScope.All ? this._props.rawDuration : NumCast(this.layoutDoc.clipEnd, this._props.rawDuration);
}
@computed get currentTime() {
@@ -116,11 +127,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
document.addEventListener('keydown', this.keyEvents, true);
}
- @action
componentWillUnmount() {
document.removeEventListener('keydown', this.keyEvents, true);
- if (CollectionStackedTimeline.SelectingRegion === this) {
- CollectionStackedTimeline.SelectingRegion = undefined;
+ if (this._selectingRegion) {
+ runInAction(() => (this._selectingRegion = false));
+ CollectionStackedTimeline.SelectingRegions.delete(this);
}
}
@@ -150,8 +161,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._zoomFactor = zoom;
}
- anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
- anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val) ?? null);
+ makeDocUnfiltered = (doc: Doc) => this.childDocList?.some(item => item === doc);
+
+ 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));
+ findDoc(dv => res(dv));
+ });
+
+ anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this._props.startTag]));
+ 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) => {
@@ -178,23 +198,25 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
if (
// need to include range inputs because after dragging video time slider it becomes target element
!(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
- this.props.isContentActive()
+ this._props.isContentActive()
) {
// if shift pressed scrub 1 second otherwise 1/10th
const jump = e.shiftKey ? 1 : 0.1;
switch (e.key) {
case ' ':
- this.props.playing() ? this.props.Pause() : this.props.Play();
+ this._props.playing() ? this._props.Pause() : this._props.Play();
break;
case '^':
- if (!CollectionStackedTimeline.SelectingRegion) {
+ if (!this._selectingRegion) {
this._markerStart = this._markerEnd = this.currentTime;
- CollectionStackedTimeline.SelectingRegion = this;
+ this._selectingRegion = true;
+ CollectionStackedTimeline.SelectingRegions.add(this);
} else {
this._markerEnd = this.currentTime;
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
+ CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
this._markerEnd = undefined;
- CollectionStackedTimeline.SelectingRegion = undefined;
+ this._selectingRegion = false;
+ CollectionStackedTimeline.SelectingRegions.delete(this);
}
e.stopPropagation();
break;
@@ -205,11 +227,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._trimming = TrimScope.None;
break;
case 'ArrowLeft':
- this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
+ this._props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
e.stopPropagation();
break;
case 'ArrowRight':
- this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
+ this._props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
e.stopPropagation();
break;
}
@@ -219,7 +241,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
getLinkData(l: Doc) {
let la1 = l.link_anchor_1 as Doc;
let la2 = l.link_anchor_2 as Doc;
- const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
+ const linkTime = NumCast(la2[this._props.startTag], NumCast(la1[this._props.startTag]));
if (Doc.AreProtosEqual(la1, this.dataDoc)) {
la1 = l.link_anchor_2 as Doc;
la2 = l.link_anchor_1 as Doc;
@@ -234,9 +256,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const clientX = e.clientX;
const diff = rect ? clientX - rect?.x : null;
const shiftKey = e.shiftKey;
- if (rect && this.props.isContentActive()) {
- const wasPlaying = this.props.playing();
- if (wasPlaying) this.props.Pause();
+ if (rect && this._props.isContentActive()) {
+ const wasPlaying = this._props.playing();
+ if (wasPlaying) this._props.Pause();
var wasSelecting = this._markerEnd !== undefined;
setupMoveUpEvents(
this,
@@ -258,7 +280,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._markerEnd = tmp;
}
if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
- const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
+ const anchor = CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this._markerStart, this._markerEnd, undefined, true);
setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
}
(!isClick || !wasSelecting) && (this._markerEnd = undefined);
@@ -266,17 +288,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}),
(e, doubleTap) => {
if (e.button !== 2) {
- this.props.select(false);
- !wasPlaying && doubleTap && this.props.Play();
+ this._props.select(false);
+ !wasPlaying && doubleTap && this._props.Play();
}
},
- this.props.isSelected(true) || this.props.isContentActive(),
+ this._props.isSelected() || this._props.isContentActive(),
undefined,
() => {
if (shiftKey) {
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.currentTime, undefined, undefined, true);
+ CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this._props.fieldKey, this.currentTime, undefined, undefined, true);
} else {
- !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+ !wasPlaying && this._props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
}
);
@@ -291,7 +313,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
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) * VideoBox.numThumbnails);
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;
}
@@ -306,7 +328,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this,
e,
action((e, [], []) => {
- if (rect && this.props.isContentActive()) {
+ 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);
}
return false;
@@ -326,7 +348,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this,
e,
action((e, [], []) => {
- if (rect && this.props.isContentActive()) {
+ 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);
}
return false;
@@ -349,8 +371,8 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@action
scrollToTime = (time: number) => {
if (this._timelineWrapper) {
- if (time > this.toTimeline(this._scroll + this.props.PanelWidth(), this.timelineContentWidth)) {
- this._scroll = Math.min(this._scroll + this.props.PanelWidth(), this.timelineContentWidth - this.props.PanelWidth());
+ if (time > this.toTimeline(this._scroll + this._props.PanelWidth(), this.timelineContentWidth)) {
+ this._scroll = Math.min(this._scroll + this._props.PanelWidth(), this.timelineContentWidth - this._props.PanelWidth());
smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
} else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
this._scroll = (time / this.timelineContentWidth) * this.clipDuration;
@@ -364,15 +386,15 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
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.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
+ const localPt = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y);
const x = localPt[0] - docDragData.offset[0];
const timelinePt = this.toTimeline(x + this._scroll, this.timelineContentWidth);
docDragData.droppedDocuments.forEach(drop => {
const anchorEnd = this.anchorEnd(drop);
if (anchorEnd !== undefined) {
- Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
+ Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this._props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
}
- Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
+ Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this._props.startTag : 'timecodeToShow', timelinePt, false);
});
return true;
@@ -387,27 +409,27 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
// creates marker on timeline
@undoBatch
- @action
- static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
- if (anchorStartTime === undefined) return rootDoc;
+ static createAnchor(doc: Doc, dataDoc: Doc, fieldKey: string, anchorStartTime: Opt<number>, anchorEndTime: Opt<number>, docAnchor: Opt<Doc>, addAsAnnotation: boolean) {
+ if (anchorStartTime === undefined) return doc;
const startTag = '_timecodeToShow';
const endTag = '_timecodeToHide';
const anchor =
docAnchor ??
Docs.Create.LabelDocument({
- title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
+ title: ComputedField.MakeFunction(`this["${endTag}"] ? "#" + formatToTime(this["${startTag}"]) + "-" + formatToTime(this["${endTag}"]) : "#" + formatToTime(this["${startTag}"])`) as any,
_label_minFontSize: 12,
_label_maxFontSize: 24,
_dragOnlyWithinContainer: true,
backgroundColor: 'rgba(128, 128, 128, 0.5)',
layout_hideLinkButton: true,
onClick: FollowLinkScript(),
- annotationOn: rootDoc,
+ _embedContainer: doc,
+ annotationOn: doc,
_isTimelineLabel: true,
layout_borderRounding: anchorEndTime === undefined ? '100%' : undefined,
});
- Doc.GetProto(anchor)[startTag] = anchorStartTime;
- Doc.GetProto(anchor)[endTag] = anchorEndTime;
+ anchor[DocData][startTag] = anchorStartTime;
+ anchor[DocData][endTag] = anchorEndTime;
if (addAsAnnotation) {
if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor);
@@ -423,20 +445,20 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
const endTime = this.anchorEnd(anchorDoc);
if (this.layoutDoc.autoPlayAnchors) {
- if (this.props.playing()) this.props.Pause();
+ if (this._props.playing()) this._props.Pause();
else {
- this.props.playFrom(seekTimeInSeconds, endTime);
+ 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();
+ if (!this.layoutDoc.autoPlayAnchors && this._props.playing()) {
+ this._props.Pause();
} else {
- this.props.Play();
+ this._props.Play();
}
} else {
- this.props.playFrom(seekTimeInSeconds, endTime);
+ this._props.playFrom(seekTimeInSeconds, endTime);
this.scrollToTime(seekTimeInSeconds);
}
}
@@ -451,17 +473,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.05;
const endTime = this.anchorEnd(anchorDoc);
if (seekTimeInSeconds < NumCast(this.layoutDoc._layout_currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._layout_currentTimecode) - 1e-4) {
- if (this.props.playing()) this.props.Pause();
- else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
+ if (this._props.playing()) this._props.Pause();
+ else if (this.layoutDoc.autoPlayAnchors) this._props.Play();
else if (!this.layoutDoc.autoPlayAnchors) {
const rect = this._timeline?.getBoundingClientRect();
- rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+ rect && this._props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
} else {
if (this.layoutDoc.autoPlayAnchors) {
- this.props.playFrom(seekTimeInSeconds, endTime);
+ this._props.playFrom(seekTimeInSeconds, endTime);
} else {
- this.props.setTime(seekTimeInSeconds);
+ this._props.setTime(seekTimeInSeconds);
}
}
return { select: true };
@@ -491,24 +513,24 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
};
dictationHeightPercent = 50;
- dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
+ dictationHeight = () => (this._props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
@computed get timelineContentHeight() {
- return (this.props.PanelHeight() * this.dictationHeightPercent) / 100;
+ return (this._props.PanelHeight() * this.dictationHeightPercent) / 100;
}
@computed get timelineContentWidth() {
- return this.props.PanelWidth() * this.zoomFactor;
+ return this._props.PanelWidth() * this.zoomFactor;
} // subtract size of container border
- dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight);
+ dictationScreenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, -this.timelineContentHeight);
- isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive();
currentTimecode = () => this.currentTime;
// renders selection region on timeline
@computed get selectionContainer() {
- const markerEnd = CollectionStackedTimeline.SelectingRegion === this ? this.currentTime : this._markerEnd;
+ const markerEnd = this._selectingRegion ? this.currentTime : this._markerEnd;
return markerEnd === undefined ? null : (
<div
className="collectionStackedTimeline-selector"
@@ -521,7 +543,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}
@computed get timelineEvents() {
- return this.props.isContentActive() ? 'all' : this.props.isContentActive() === false ? 'none' : undefined;
+ return this._props.isContentActive() ? 'all' : this._props.isContentActive() === false ? 'none' : undefined;
}
render() {
const overlaps: {
@@ -529,17 +551,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
anchorEndTime: number;
level: number;
}[] = [];
- const drawAnchors = this.childDocs.map(anchor => ({
- level: this.getLevel(anchor, overlaps),
- anchor,
+ const drawAnchors = this.childLayoutPairs.map(pair => ({
+ level: this.getLevel(pair.layout, overlaps),
+ anchor: pair.layout,
}));
const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
return this.clipDuration === 0 ? null : (
<div ref={this.createDashEventsTarget} style={{ pointerEvents: this.timelineEvents }}>
<div
className="collectionStackedTimeline-timelineContainer"
- style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }}
- onWheel={e => e.stopPropagation()}
+ style={{ width: this._props.PanelWidth(), cursor: SnappingManager.IsDragging ? 'grab' : '' }}
+ onWheel={e => this.isContentActive() && e.stopPropagation()}
onScroll={this.setScroll}
onMouseMove={e => this.isContentActive() && this.onHover(e)}
ref={wrapper => (this._timelineWrapper = wrapper)}>
@@ -554,11 +576,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
const end = this.anchorEnd(d.anchor, start + (10 / this.timelineContentWidth) * this.clipDuration);
if (end < this.clipStart || start > this.clipEnd) return null;
const left = Math.max(((start - this.clipStart) / this.clipDuration) * this.timelineContentWidth, 0);
- const top = (d.level / maxLevel) * this.props.PanelHeight();
+ const top = (d.level / maxLevel) * this._props.PanelHeight();
const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
const width = (timespan / this.clipDuration) * this.timelineContentWidth;
- const height = this.props.PanelHeight() / maxLevel;
- return this.props.Document.hideAnchors ? null : (
+ const height = this._props.PanelHeight() / maxLevel;
+ return this.Document.hideAnchors ? null : (
<div
className={'collectionStackedTimeline-marker-timeline'}
key={d.anchor[Id]}
@@ -570,7 +592,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
pointerEvents: 'none',
}}>
<StackedTimelineAnchor
- {...this.props}
+ {...this._props}
mark={d.anchor}
rangeClickScript={this.rangeClickScript}
rangePlayScript={this.rangePlayScript}
@@ -591,18 +613,19 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
);
})}
{!this.IsTrimming && this.selectionContainer}
- {!this.props.PanelHeight() ? null : (
+ {!this._props.PanelHeight() ? null : (
<AudioWaveform
- rawDuration={this.props.rawDuration}
- fieldKey={this.props.dataFieldKey}
+ rawDuration={this._props.rawDuration}
+ fieldKey={this._props.dataFieldKey}
duration={this.clipDuration}
- mediaPath={this.props.mediaPath}
+ mediaPath={this._props.mediaPath}
layoutDoc={this.layoutDoc}
clipStart={this.clipStart}
clipEnd={this.clipEnd}
zoomFactor={this.zoomFactor}
PanelHeight={this.timelineContentHeight}
PanelWidth={this.timelineContentWidth}
+ progress={(this.currentTime - this.clipStart) / this.clipDuration}
/>
)}
@@ -669,8 +692,8 @@ interface StackedTimelineAnchorProps {
width: number;
height: number;
toTimeline: (screen_delta: number, width: number) => number;
- styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
- playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
+ styleProvider?: StyleProviderFuncType;
+ playLink: (linkDoc: Doc, options: FocusViewOptions) => void;
setTime: (time: number) => void;
startTag: string;
endTag: string;
@@ -679,50 +702,51 @@ interface StackedTimelineAnchorProps {
isDocumentActive?: () => boolean | undefined;
ScreenToLocalTransform: () => Transform;
_timeline: HTMLDivElement | null;
- focus: DocFocusFunc;
+ focus: FocusFuncType;
currentTimecode: () => number;
- isSelected: (outsideReaction?: boolean) => boolean;
+ isSelected: () => boolean;
stackedTimeline: CollectionStackedTimeline;
trimStart: number;
trimEnd: number;
}
@observer
-class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
+class StackedTimelineAnchor extends ObservableReactComponent<StackedTimelineAnchorProps> {
_lastTimecode: number;
_disposer: IReactionDisposer | undefined;
- constructor(props: any) {
+ constructor(props: StackedTimelineAnchorProps) {
super(props);
- this._lastTimecode = this.props.currentTimecode();
+ makeObservable(this);
+ this._lastTimecode = this._props.currentTimecode();
}
// updates marker document title to reflect correct timecodes
computeTitle = () => {
- if (this.props.mark.type !== DocumentType.LABEL) return undefined;
- const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
- const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
+ if (this._props.mark.type !== DocumentType.LABEL) return undefined;
+ const start = Math.max(NumCast(this._props.mark[this._props.startTag]), this._props.trimStart) - this._props.trimStart;
+ const end = Math.min(NumCast(this._props.mark[this._props.endTag]), this._props.trimEnd) - this._props.trimStart;
return `#${formatTime(start)}-${formatTime(end)}`;
};
componentDidMount() {
this._disposer = reaction(
- () => this.props.currentTimecode(),
+ () => 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))*/
- !this.props.layoutDoc.dontAutoFollowLinks &&
- LinkManager.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
+ /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this._props.layoutDoc))*/
+ !this._props.layoutDoc.dontAutoFollowLinks &&
+ LinkManager.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);
+ LinkFollower.FollowLink(undefined, this._props.mark, false);
}
this._lastTimecode = time;
}
@@ -737,16 +761,16 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
// starting the drag event for anchor resizing
@action
onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
- //this.props._timeline?.setPointerCapture(e.pointerId);
+ //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);
+ return this._props.toTimeline(e.clientX - rect.x, rect.width);
};
const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => {
- const timelineOnly = Cast(anchor[this.props.startTag], 'number', null) !== 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 && 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);
} else {
anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
@@ -760,11 +784,11 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
e,
e => {
if (!undo) undo = UndoManager.StartBatch('drag anchor');
- this.props.setTime(newTime(e));
+ this._props.setTime(newTime(e));
return changeAnchor(anchor, left, newTime(e));
},
action(e => {
- this.props.setTime(newTime(e));
+ this._props.setTime(newTime(e));
undo?.end();
this.noEvents = false;
}),
@@ -775,7 +799,9 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
// context menu
contextMenuItems = () => {
const resetTitle = {
- script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!,
+ script: ScriptField.MakeFunction(
+ `this.title = this["${this._props.endTag}"] ? "#" + formatToTime(this["${this._props.startTag}"]) + "-" + formatToTime(this["${this._props.endTag}"]) : "#" + formatToTime(this["${this._props.startTag}"])`
+ )!,
icon: 'folder-plus',
label: 'Reset Title',
};
@@ -784,9 +810,9 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
// renders anchor LabelBox
renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
- const anchor = observable({ view: undefined as any });
- const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => {
- this.props.playLink(mark, options);
+ const anchor = observable({ view: undefined as Opt<DocumentView> | null });
+ const focusFunc = (doc: Doc, options: FocusViewOptions): number | undefined => {
+ this._props.playLink(mark, options);
return undefined;
};
return {
@@ -794,19 +820,19 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
view: (
<DocumentView
key="view"
- {...this.props}
+ {...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
ref={action((r: DocumentView | null) => (anchor.view = r))}
Document={mark}
- DataDoc={undefined}
- docViewPath={returnEmptyDoclist}
+ TemplateDataDocument={undefined}
+ containerViewPath={returnEmptyDoclist}
pointerEvents={this.noEvents ? returnNone : undefined}
- styleProvider={this.props.styleProvider}
- renderDepth={this.props.renderDepth + 1}
+ styleProvider={this._props.styleProvider}
+ renderDepth={this._props.renderDepth + 1}
LayoutTemplate={undefined}
LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
- isDocumentActive={this.props.isDocumentActive}
+ isDocumentActive={this._props.isDocumentActive}
PanelWidth={width}
PanelHeight={height}
layout_fitWidth={returnTrue}
@@ -817,31 +843,29 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
searchFilterDocs={returnEmptyDoclist}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
- rootSelected={returnFalse}
- onClick={script}
- onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
+ onClickScript={script}
+ onDoubleClickScript={this._props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
ignoreAutoHeight={false}
hideResizeHandles={true}
- bringToFront={emptyFunction}
contextMenuItems={this.contextMenuItems}
/>
),
};
});
- anchorScreenToLocalXf = () => this.props.ScreenToLocalTransform().translate(-this.props.left, -this.props.top);
- width = () => this.props.width;
- height = () => this.props.height;
+ anchorScreenToLocalXf = () => this._props.ScreenToLocalTransform().translate(-this._props.left, -this._props.top);
+ width = () => this._props.width;
+ height = () => this._props.height;
render() {
- const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height);
+ const inner = this.renderInner(this._props.mark, this._props.rangeClickScript, this._props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height);
return (
<div style={{ pointerEvents: this.noEvents ? 'none' : undefined }}>
{inner.view}
- {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+ {!inner.anchor.view || !inner.anchor.view.IsSelected ? null : (
<>
- <div key="left" className="collectionStackedTimeline-left-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
- <div key="right" className="collectionStackedTimeline-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
+ <div key="left" className="collectionStackedTimeline-left-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this._props.mark, true)} />
+ <div key="right" className="collectionStackedTimeline-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this._props.mark, false)} />
</>
)}
</div>
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index dddb3ec71..6225cc52a 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.collectionMasonryView {
display: inline;
@@ -142,7 +142,7 @@
transform-origin: top left;
grid-column-end: span 1;
height: 100%;
- margin: auto;
+ //margin: auto;
display: inline-grid;
}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0b29e7286..89e72152a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,20 +1,21 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+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 { Doc, Opt } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
import { DragManager, dropActionType } from '../../util/DragManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -23,15 +24,14 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { LightboxView } from '../LightboxView';
import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView } from './CollectionSubView';
-import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
@@ -59,12 +59,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// map of node headers to their heights. Used in Masonry
@observable _heightMap = new Map<string, number>();
// Assuming that this is the current css cursor style
- @observable _cursor: CursorProperty = 'ew-resize';
+ @observable _cursor: CSS.Property.Cursor = 'ew-resize';
// gets reset whenever we scroll. Not sure what it is
@observable _scroll = 0; // used to force the document decoration to update when scrolling
// does this mean whether the browser is hidden? Or is chrome something else entirely?
@computed get chromeHidden() {
- return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+ return this._props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
}
// it looks like this gets the column headers that Mehek was showing just now
@computed get colHeaderData() {
@@ -77,18 +77,18 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// filteredChildren is what you want to work with. It's the list of things that you're currently displaying
@computed get filteredChildren() {
const children = this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
- if (this.props.sortFunc) children.sort(this.props.sortFunc);
+ if (this._props.sortFunc) children.sort(this._props.sortFunc);
return children;
}
// how much margin we give the header
@computed get headerMargin() {
- return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
+ return this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.HeaderMargin);
}
@computed get xMargin() {
- return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this.props.PanelWidth()));
+ return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
}
@computed get yMargin() {
- return this.props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this.props.PanelWidth()));
+ return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
}
@computed get gridGap() {
@@ -96,7 +96,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
// are we stacking or masonry?
@computed get isStackingView() {
- return (this.props.type_collection ?? this.layoutDoc._type_collection) === CollectionViewType.Stacking;
+ return (this._props.type_collection ?? this.layoutDoc._type_collection) === CollectionViewType.Stacking;
}
// this is the number of StackingViewFieldColumns that we have
@computed get numGroupColumns() {
@@ -108,16 +108,16 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
// columnWidth handles the margin on the left and right side of the documents
@computed get columnWidth() {
- return Math.min(this.props.PanelWidth() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+ return Math.min(this._props.PanelWidth() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this._props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
}
@computed get NodeWidth() {
- return this.props.PanelWidth() - this.gridGap;
+ return this._props.PanelWidth() - this.gridGap;
}
constructor(props: any) {
super(props);
-
+ makeObservable(this);
if (this.colHeaderData === undefined) {
// TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
// here we're making an empty list of column headers (again, what Mehek showed us)
@@ -138,7 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
// just getting the style
- const style = this.isStackingView ? { margin: this.rootDoc._stacking_alignCenter ? 'auto' : undefined, width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const style = this.isStackingView ? { margin: this.Document._stacking_alignCenter ? 'auto' : undefined, width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
// So we're choosing whether we're going to render a column or a masonry doc
return (
<div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
@@ -203,7 +203,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
componentDidMount() {
super.componentDidMount?.();
- this.props.setContentView?.(this);
+ this._props.setContentViewBox?.(this);
// reset section headers when a new filter is inputted
this._pivotFieldDisposer = reaction(
@@ -214,7 +214,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
() => this.layoutDoc._layout_autoHeight,
layout_autoHeight =>
layout_autoHeight &&
- this.props.setHeight?.(
+ this._props.setHeight?.(
Math.min(
NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
this.headerMargin + (this.isStackingView ? Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))) : this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0))
@@ -229,20 +229,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this._layout_autoHeightDisposer?.();
}
- isAnyChildContentActive = () => this.props.isAnyChildContentActive();
+ isAnyChildContentActive = () => this._props.isAnyChildContentActive();
- @action
moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
- return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
+ return this._props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
};
createRef = (ele: HTMLDivElement | null) => {
this._masonryGridRef = ele;
this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
};
- onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+ onChildClickHandler = () => this._props.childClickScript || ScriptCast(this.Document.onChildClick);
@computed get onChildDoubleClickHandler() {
- return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+ return () => this._props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
}
scrollToBottom = () => {
@@ -250,13 +249,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
};
// let's dive in and get the actual document we want to drag/move around
- focusDocument = (doc: Doc, options: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: FocusViewOptions) => {
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 localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
+ const localTop = this.ScreenToLocalBoxXf().transformPoint(0, top);
if (Math.floor(localTop[1]) !== 0) {
let focusSpeed = options.zoomTime ?? 500;
smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc);
@@ -266,102 +265,100 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return undefined;
};
- styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
+ styleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string) => {
if (property === StyleProp.Opacity && doc) {
- if (this.props.childOpacity) {
- return this.props.childOpacity();
+ if (this._props.childOpacity) {
+ return this._props.childOpacity();
}
if (this.Document._currentFrame !== undefined) {
return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
}
}
- return this.props.styleProvider?.(doc, props, property);
+ return this._props.styleProvider?.(doc, props, property);
};
@undoBatch
- @action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- const docView = fieldProps.DocumentView?.();
- if (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
+ if (['Enter'].includes(e.key) && e.ctrlKey) {
e.stopPropagation?.();
const below = !e.altKey && e.key !== 'Tab';
- const layout_fieldKey = StrCast(docView.LayoutFieldKey);
- const newDoc = Doc.MakeCopy(docView.rootDoc, true);
- const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+ const layout_fieldKey = 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' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
- newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
+ if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
+ newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
}
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ newDoc[DocData].text = undefined;
+ FormattedTextBox.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
};
- isContentActive = () => (this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined);
+ isContentActive = () => (this._props.isContentActive() ? true : this._props.isSelected() === false || this._props.isContentActive() === false ? false : undefined);
@observable _renderCount = 5;
isChildContentActive = () =>
- this.props.isContentActive?.() === false
- ? false
- : this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive))
- ? true
- : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false
+ this._props.isContentActive?.() === false
? false
- : undefined;
- isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined);
+ : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
+ ? true
+ : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ ? false
+ : undefined;
+ isChildButtonContentActive = () => (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined);
@observable docRefs = new ObservableMap<Doc, DocumentView>();
+ childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
// this is what renders the document that you see on the screen
// called in Children: this actually adds a document to our children list
getDisplayDoc(doc: Doc, width: () => number, count: number) {
- const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc;
+ const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined;
const height = () => this.getDocHeight(doc);
-
+ const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this._props.PanelHeight()));
+ const panelWidth = () => (this.isStackingView ? width() : this.columnWidth);
const stackedDocTransform = () => this.getDocTransform(doc);
this._docXfs.push({ stackedDocTransform, width, height });
return count > this._renderCount ? null : (
<DocumentView
ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(doc, r))}
Document={doc}
- DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)}
- renderDepth={this.props.renderDepth + 1}
- PanelWidth={width}
- PanelHeight={height}
- pointerEvents={this.props.DocumentView?.().props.onClick?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
+ TemplateDataDocument={dataDoc ?? (Doc.AreProtosEqual(doc[DocData], doc) ? undefined : doc[DocData])}
+ renderDepth={this._props.renderDepth + 1}
+ PanelWidth={panelWidth}
+ PanelHeight={panelHeight}
+ pointerEvents={this.DocumentView?.()._props.onClickScript?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
styleProvider={this.styleProvider}
- docViewPath={this.props.docViewPath}
- layout_fitWidth={this.props.childLayoutFitWidth}
+ containerViewPath={this.childContainerViewPath}
+ layout_fitWidth={this.childFitWidth}
isContentActive={doc.onClick ? this.isChildButtonContentActive : this.isChildContentActive}
onKey={this.onKeyDown}
- onBrowseClick={this.props.onBrowseClick}
+ onBrowseClickScript={this._props.onBrowseClickScript}
isDocumentActive={this.isContentActive}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
- NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childLayoutFitWidth?.(doc) || (doc._layout_fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
- dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
- dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ NativeWidth={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+ NativeHeight={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeHeight(doc)) ? height : undefined}
+ dontCenter={this._props.childIgnoreNativeSize ? 'xy' : (StrCast(this.layoutDoc.layout_dontCenter) as any)}
+ dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this._props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
rootSelected={this.rootSelected}
- layout_showTitle={this.props.childlayout_showTitle}
- dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
- onClick={this.onChildClickHandler}
- onDoubleClick={this.onChildDoubleClickHandler}
+ layout_showTitle={this._props.childlayout_showTitle}
+ dragAction={(this.layoutDoc.childDragAction ?? this._props.childDragAction) as dropActionType}
+ onClickScript={this.onChildClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
ScreenToLocalTransform={stackedDocTransform}
focus={this.focusDocument}
childFilters={this.childDocFilters}
- hideDecorationTitle={this.props.childHideDecorationTitle?.()}
- hideResizeHandles={this.props.childHideResizeHandles?.()}
- hideTitle={this.props.childHideTitle?.()}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ hideResizeHandles={this._props.childHideResizeHandles}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- xPadding={NumCast(this.layoutDoc._childXPadding, this.props.childXPadding)}
- yPadding={NumCast(this.layoutDoc._childYPadding, this.props.childYPadding)}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents) as any}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- bringToFront={returnFalse}
- pinToPres={this.props.pinToPres}
+ xPadding={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
+ yPadding={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ contentPointerEvents={StrCast(this.layoutDoc.childContentPointerEvents) as any}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
/>
);
}
@@ -371,31 +368,31 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this._scroll; // must be referenced for document decorations to update when the text box container is scrolled
const { translateX, translateY } = Utils.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.props.ScreenToLocalTransform().Scale);
+ return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.ScreenToLocalBoxXf().Scale);
}
getDocWidth(d?: Doc) {
if (!d) return 0;
- const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+ const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
const maxWidth = this.columnWidth / this.numGroupColumns;
- if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
- return Math.min(d[Width](), maxWidth);
+ if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
+ return Math.min(NumCast(d._width), maxWidth);
}
return maxWidth;
}
getDocHeight(d?: Doc) {
if (!d || d.hidden) return 0;
- const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
- const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
- const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
- const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
- const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
+ const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
+ const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
+ const maxHeight = (lim => (lim === 0 ? this._props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
+ const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
+ const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
if (nw && nh) {
const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
return Math.min(maxHeight, (docWid * nh) / nw);
}
const childHeight = NumCast(childLayoutDoc._height);
- const panelHeight = childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+ const panelHeight = this.childFitWidth(childLayoutDoc) ? Number.MAX_SAFE_INTEGER : this._props.PanelHeight() - 2 * this.yMargin;
return Math.min(childHeight, maxHeight, panelHeight);
}
@@ -433,7 +430,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
@undoBatch
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
// Fairly confident that this is where the swapping of nodes in the various arrays happens
const where = [de.x, de.y];
@@ -471,9 +467,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
- if (!this.props.addDocument?.(source)) e.preventDefault();
+ 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
e.stopPropagation();
return true;
@@ -489,14 +485,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const dropCreator = annoDragData.dropDocCreator;
annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
const dropDoc = dropCreator(annotationOn);
- return dropDoc || this.rootDoc;
+ return dropDoc || this.Document;
};
return true;
}
/// an item from outside of Dash is being dropped onto this stacking view (e.g, a document from the file system)
@undoBatch
- @action
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
const where = [e.clientX, e.clientY];
let targInd = -1;
@@ -541,10 +536,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this.refList.push(ref);
this.observer = new _global.ResizeObserver(
action((entries: any) => {
- if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
- if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
- this.props.setHeight?.(height);
+ if (!LightboxView.Contains(this.DocumentView?.())) {
+ this._props.setHeight?.(height);
}
}
})
@@ -555,8 +550,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
- Document={this.props.Document}
- DataDoc={this.props.DataDoc}
+ Document={this.Document}
+ TemplateDataDocument={this._props.TemplateDataDocument}
renderChildren={this.children}
columnWidth={this.columnWidth}
numGroupColumns={this.numGroupColumns}
@@ -570,7 +565,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
yMargin={this.yMargin}
type={type}
createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.props.ScreenToLocalTransform}
+ screenToLocalTransform={this.ScreenToLocalBoxXf}
/>
);
};
@@ -583,11 +578,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
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)))));
+ const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
return (
<CollectionMasonryViewFieldRow
showHandle={first}
- Document={this.props.Document}
+ Document={this.Document}
chromeHidden={this.chromeHidden}
pivotField={this.pivotField}
unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
@@ -596,9 +591,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this.refList.push(ref);
this.observer = new _global.ResizeObserver(
action((entries: any) => {
- if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc._layout_autoHeight && ref && this.refList.length && !SnappingManager.IsDragging) {
const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
- this.props.setHeight?.(2 * this.headerMargin + height); // bcz: added 2x for header to fix problem with scrollbars appearing in Tools panel
+ this._props.setHeight?.(2 * this.headerMargin + height); // bcz: added 2x for header to fix problem with scrollbars appearing in Tools panel
}
})
);
@@ -614,7 +609,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
parent={this}
type={type}
createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.props.ScreenToLocalTransform}
+ screenToLocalTransform={this.ScreenToLocalBoxXf}
setDocHeight={this.setDocHeight}
/>
);
@@ -664,56 +659,49 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return35 = () => 35;
@computed get buttonMenu() {
- const menuDoc: Doc = Cast(this.rootDoc.layout_headerButton, Doc, null);
- // TODO:glr Allow support for multiple buttons
- if (menuDoc) {
- const width: number = NumCast(menuDoc._width, 30);
- const height: number = NumCast(menuDoc._height, 30);
- return (
- <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
- <DocumentView
- Document={menuDoc}
- DataDoc={menuDoc}
- isContentActive={this.isContentActive}
- isDocumentActive={this.isContentActive}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
- onBrowseClick={this.props.onBrowseClick}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={Transform.Identity}
- PanelWidth={this.return35}
- PanelHeight={this.return35}
- renderDepth={this.props.renderDepth}
- focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- childFilters={this.props.childFilters}
- childFiltersByRanges={this.props.childFiltersByRanges}
- searchFilterDocs={this.props.searchFilterDocs}
- />
- </div>
- );
- }
+ const menuDoc = DocCast(this.layoutDoc.layout_headerButton);
+ return !menuDoc ? null : (
+ <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+ <DocumentView
+ Document={menuDoc}
+ isContentActive={this.isContentActive}
+ isDocumentActive={this.isContentActive}
+ 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}
+ ScreenToLocalTransform={Transform.Identity}
+ PanelWidth={this.return35}
+ PanelHeight={this.return35}
+ renderDepth={this._props.renderDepth}
+ focus={emptyFunction}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ childFilters={this._props.childFilters}
+ childFiltersByRanges={this._props.childFiltersByRanges}
+ searchFilterDocs={this._props.searchFilterDocs}
+ />
+ </div>
+ );
}
@computed get nativeWidth() {
- return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+ return this._props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
}
@computed get nativeHeight() {
- return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+ return this._props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
}
@computed get scaling() {
- return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight;
+ return !this.nativeWidth ? 1 : this._props.PanelHeight() / this.nativeHeight;
}
@computed get backgroundEvents() {
- return this.props.isContentActive() === false ? 'none' : undefined;
+ return this._props.isContentActive() === false ? 'none' : undefined;
}
observer: any;
render() {
@@ -723,8 +711,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
SetValue: this.addGroup,
contents: '+ ADD A GROUP',
};
- const buttonMenu = this.rootDoc.layout_headerButton;
- const noviceExplainer = this.rootDoc.layout_explainer;
+ const buttonMenu = this.layoutDoc.layout_headerButton;
+ const noviceExplainer = this.layoutDoc.layout_explainer;
return (
<>
{buttonMenu || noviceExplainer ? (
@@ -739,8 +727,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
ref={this.createRef}
style={{
overflowY: this.isContentActive() ? 'auto' : 'hidden',
- background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
- pointerEvents: (this.props.pointerEvents?.() as any) ?? this.backgroundEvents,
+ 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))}
onDrop={this.onExternalDrop.bind(this)}
@@ -748,7 +736,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
onWheel={e => this.isContentActive() && e.stopPropagation()}>
{this.renderedSections}
{!this.showAddAGroup ? null : (
- <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+ <div key={`${this.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
<EditableView {...editableViewProps} />
</div>
)}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 3598d548a..c455f20d8 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,15 +1,15 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { RichTextField } from '../../../fields/RichTextField';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { BoolCast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, setupMoveUpEvents, returnFalse, returnEmptyString } from '../../../Utils';
+import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
@@ -19,14 +19,14 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
-import './CollectionStackingView.scss';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { Id } from '../../../fields/FieldSymbols';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import './CollectionStackingView.scss';
// So this is how we are storing a column
interface CSVFieldColumnProps {
Document: Doc;
- DataDoc: Opt<Doc>;
+ TemplateDataDocument: Opt<Doc>;
docList: Doc[];
heading: string;
pivotField: string;
@@ -49,16 +49,21 @@ interface CSVFieldColumnProps {
}
@observer
-export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
- @observable private _background = 'inherit';
-
+export class CollectionStackingViewFieldColumn extends ObservableReactComponent<CSVFieldColumnProps> {
private dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
-
+ @observable private _background = 'inherit';
@observable _paletteOn = false;
- @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
- @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
+ @observable _heading = '';
+ @observable _color = '';
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ this._heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading;
+ this._color = this._props.headingObject ? this._props.headingObject.color : '#f1efeb';
+ }
_ele: HTMLElement | null = null;
@@ -68,29 +73,29 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
this.dropDisposer?.();
if (ele) {
this._ele = ele;
- this.props.observeHeight(ele);
- this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
+ this._props.observeHeight(ele);
+ this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Document);
}
};
@action
componentDidMount() {
this._disposers.collapser = reaction(
- () => this.props.headingObject?.collapsed,
+ () => this._props.headingObject?.collapsed,
collapsed => (this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false),
{ fireImmediately: true }
);
}
componentWillUnmount() {
this._disposers.collapser?.();
- this.props.unobserveHeight(this._ele);
+ this._props.unobserveHeight(this._ele);
}
//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) };
- this.props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
+ this._props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this._props.pivotField, drop.val, false));
return true;
});
getValue = (value: string): any => {
@@ -105,13 +110,13 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
headingChanged = (value: string, shiftDown?: boolean) => {
const castedValue = this.getValue(value);
if (castedValue) {
- if (this.props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+ if (this._props.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- 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;
+ 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;
}
return true;
}
@@ -120,41 +125,41 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action
changeColumnColor = (color: string) => {
- this.props.headingObject?.setColor(color);
+ this._props.headingObject?.setColor(color);
this._color = color;
};
- @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = '#b4b4b4');
+ @action pointerEntered = () => SnappingManager.IsDragging && (this._background = '#b4b4b4');
@action pointerLeave = () => (this._background = 'inherit');
@undoBatch typedNote = (char: string) => this.addNewTextDoc('-typed text-', false, true);
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
- const key = this.props.pivotField;
+ const key = this._props.pivotField;
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _layout_fitWidth: true, title: value, _layout_autoHeight: true });
- 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 heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
+ 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 heading = maxHeading === 0 || this._props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
newDoc.heading = heading;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ FormattedTextBox.SetSelectOnLoad(newDoc);
FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? '' : ' ';
- return this.props.addDocument?.(newDoc) || false;
+ return this._props.addDocument?.(newDoc) || false;
};
@action
deleteColumn = () => {
- 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);
+ 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);
}
};
@action
collapseSection = () => {
- this.props.headingObject?.setCollapsed(!this.props.headingObject.collapsed);
- this.collapsed = BoolCast(this.props.headingObject?.collapsed);
+ this._props.headingObject?.setCollapsed(!this._props.headingObject.collapsed);
+ this.collapsed = BoolCast(this._props.headingObject?.collapsed);
};
headerDown = (e: React.PointerEvent<HTMLDivElement>) => setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
@@ -162,12 +167,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
//TODO: I think this is where I'm supposed to edit stuff
startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
// 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);
+ const embedding = Doc.MakeEmbedding(this._props.Document);
+ embedding._width = this._props.columnWidth / (this._props.colHeaderData?.length || 1);
embedding._pivotField = undefined;
let value = this.getValue(this._heading);
value = typeof value === 'string' ? `"${value}"` : value;
- embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
+ embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this._props.pivotField} === ${value}`, { doc: Doc.name });
if (embedding.viewSpecScript) {
DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
return true;
@@ -177,7 +182,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
renderColorPicker = () => {
const gray = '#f1efeb';
- const selected = this.props.headingObject ? this.props.headingObject.color : gray;
+ const selected = this._props.headingObject ? this._props.headingObject.color : gray;
const colors = ['pink2', 'purple4', 'bluegreen1', 'yellow4', 'gray', 'red2', 'bluegreen7', 'bluegreen5', 'orange1'];
return (
<div className="collectionStackingView-colorPicker">
@@ -211,15 +216,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
ContextMenu.Instance.clearItems();
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
- const dataDoc = this.props.DataDoc || this.props.Document;
+ const dataDoc = this._props.TemplateDataDocument || this._props.Document;
const width = this._ele ? Number(getComputedStyle(this._ele).width.replace('px', '')) : 0;
const height = this._ele ? Number(getComputedStyle(this._ele).height.replace('px', '')) : 0;
DocUtils.addDocumentCreatorMenuItems(
doc => {
- FormattedTextBox.SelectOnLoad = doc[Id];
- return this.props.addDocument?.(doc);
+ FormattedTextBox.SetSelectOnLoad(doc);
+ return this._props.addDocument?.(doc);
},
- this.props.addDocument,
+ this._props.addDocument,
0,
0,
true
@@ -231,12 +236,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
docItems.push({
description: ':' + fieldKey,
event: () => {
- const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document));
+ const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this._props.Document));
if (created) {
- if (this.props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+ if (this._props.Document.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
- return this.props.addDocument?.(created);
+ return this._props.addDocument?.(created);
}
},
icon: 'compress-arrows-alt',
@@ -250,12 +255,12 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
event: () => {
const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
if (created) {
- const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document;
+ const container = this._props.Document.resolvedDataDoc ? Doc.GetProto(this._props.Document) : this._props.Document;
if (container.isTemplateDoc) {
Doc.MakeMetadataFieldTemplate(created, container);
return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
}
- return this.props.addDocument?.(created) || false;
+ return this._props.addDocument?.(created) || false;
}
},
icon: 'compress-arrows-alt',
@@ -264,16 +269,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Doc Fields ...', subitems: docItems, icon: 'eye' });
!Doc.noviceMode && ContextMenu.Instance.addItem({ description: 'Containers ...', subitems: layoutItems, icon: 'eye' });
ContextMenu.Instance.setDefaultItem('::', (name: string): void => {
- Doc.GetProto(this.props.Document)[name] = '';
+ Doc.GetProto(this._props.Document)[name] = '';
const created = Docs.Create.TextDocument('', { title: name, _width: 250, _layout_autoHeight: true });
if (created) {
- if (this.props.Document.isTemplateDoc) {
- Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+ if (this._props.Document.isTemplateDoc) {
+ Doc.MakeMetadataFieldTemplate(created, this._props.Document);
}
- this.props.addDocument?.(created);
+ this._props.addDocument?.(created);
}
});
- const pt = this.props
+ const pt = this._props
.screenToLocalTransform()
.inverse()
.transformPoint(width - 30, height);
@@ -282,21 +287,21 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@computed get innards() {
TraceMobx();
- const key = this.props.pivotField;
- const headings = this.props.headings();
+ const key = this._props.pivotField;
+ const headings = this._props.headings();
const heading = this._heading;
- const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin;
+ 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 headingView = this.props.headingObject ? (
+ const evContents = heading ? heading : this._props?.type === 'number' ? '0' : noValueHeader;
+ const headingView = this._props.headingObject ? (
<div
key={heading}
className="collectionStackingView-sectionHeader"
ref={this._headerRef}
style={{
- marginTop: this.props.yMargin,
- width: this.props.columnWidth / (uniqueHeadings.length + (this.props.chromeHidden ? 0 : 1) || 1),
+ marginTop: this._props.yMargin,
+ width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1),
}}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
@@ -326,35 +331,35 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
)} */}
</div>
<div
- className={'collectionStackingView-collapseBar' + (this.props.headingObject.collapsed === true ? ' active' : '')}
- style={{ display: this.props.headingObject.collapsed === true ? 'block' : undefined }}
+ className={'collectionStackingView-collapseBar' + (this._props.headingObject.collapsed === true ? ' active' : '')}
+ style={{ display: this._props.headingObject.collapsed === true ? 'block' : undefined }}
onClick={this.collapseSection}
/>
</div>
) : null;
- const templatecols = `${this.props.columnWidth / this.props.numGroupColumns}px `;
- const type = this.props.Document.type;
+ const templatecols = `${this._props.columnWidth / this._props.numGroupColumns}px `;
+ const type = this._props.Document.type;
return (
<>
- {this.props.Document._columnsHideIfEmpty ? null : headingView}
+ {this._props.Document._columnsHideIfEmpty ? null : headingView}
{this.collapsed ? null : (
<div>
<div
key={`${heading}-stack`}
className={`collectionStackingView-masonrySingle`}
style={{
- padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
+ 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`,
height: 'max-content',
position: 'relative',
- gridGap: this.props.gridGap,
+ gridGap: this._props.gridGap,
gridTemplateColumns: templatecols,
gridAutoRows: '0px',
}}>
- {this.props.renderChildren(this.props.docList)}
+ {this._props.renderChildren(this._props.docList)}
</div>
- {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+ {!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
@@ -365,7 +370,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
key={`${heading}-add-document`}
onKeyDown={e => e.stopPropagation()}
className="collectionStackingView-addDocumentButton"
- style={{ width: 'calc(100% - 25px)', maxWidth: this.props.columnWidth / this.props.numGroupColumns - 25, marginBottom: 10 }}>
+ style={{ width: 'calc(100% - 25px)', maxWidth: this._props.columnWidth / this._props.numGroupColumns - 25, marginBottom: 10 }}>
<EditableView
GetValue={returnEmptyString}
SetValue={this.addNewTextDoc}
@@ -384,15 +389,15 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
render() {
TraceMobx();
- const headings = this.props.headings();
+ const headings = this._props.headings();
const heading = this._heading;
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
return (
<div
- className={'collectionStackingViewFieldColumn' + (SnappingManager.GetIsDragging() ? 'Dragging' : '')}
+ className={'collectionStackingViewFieldColumn' + (SnappingManager.IsDragging ? 'Dragging' : '')}
key={heading}
style={{
- width: `${100 / (uniqueHeadings.length + (this.props.chromeHidden ? 0 : 1) || 1)}%`,
+ width: `${100 / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1)}%`,
height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
background: this._background,
}}
diff --git a/src/client/views/collections/CollectionStaffView.scss b/src/client/views/collections/CollectionStaffView.scss
deleted file mode 100644
index 493a5f670..000000000
--- a/src/client/views/collections/CollectionStaffView.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.collectionStaffView {
- .collectionStaffView-staff {
- width: 100%;
- margin-top: 100px;
- margin-bottom: 100px;
- }
-
- .collectionStaffView-line {
- margin: 10px;
- height: 2px;
- background: black;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
deleted file mode 100644
index c025e94a8..000000000
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { CollectionSubView } from "./CollectionSubView";
-import React = require("react");
-import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { NumCast } from "../../../fields/Types";
-import "./CollectionStaffView.scss";
-import { observer } from "mobx-react";
-
-@observer
-export class CollectionStaffView extends CollectionSubView() {
- private _reactionDisposer: IReactionDisposer | undefined;
- @observable private _staves = NumCast(this.props.Document.staves);
-
- componentWillUnmount() {
- this._reactionDisposer?.();
- }
- componentDidMount = () => {
- this._reactionDisposer = reaction(() => NumCast(this.props.Document.staves),
- (staves) => runInAction(() => this._staves = staves)
- );
-
- this.props.Document.staves = 5;
- }
-
- @computed get addStaffButton() {
- return <div onPointerDown={this.addStaff}>+</div>;
- }
-
- @computed get staves() {
- const staves = [];
- for (let i = 0; i < this._staves; i++) {
- const rows = [];
- for (let j = 0; j < 5; j++) {
- rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>);
- }
- staves.push(<div key={`staff-${i}`} className="collectionStaffView-staff">
- {rows}
- </div>);
- }
- return staves;
- }
-
- @action
- addStaff = (e: React.PointerEvent) => {
- this.props.Document.staves = this._staves + 1;
- }
-
- render() {
- return <div className="collectionStaffView">
- {this.staves}
- {this.addStaffButton}
- </div>;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 09e7cdb32..fdbd1cc90 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,44 +1,55 @@
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
+import * as React from 'react';
import * as rp from 'request-promise';
+import { Utils, returnFalse } from '../../../Utils';
import CursorField from '../../../fields/CursorField';
import { Doc, DocListCast, Field, Opt, 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 { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+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 { returnFalse, Utils } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Networking } from '../../Network';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocUtils, Docs, DocumentOptions } from '../../documents/Documents';
+import { DragManager, dropActionType } from '../../util/DragManager';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { InteractionUtils } from '../../util/InteractionUtils';
-import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { DocComponent } from '../DocComponent';
-import React = require('react');
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { UndoManager, undoBatch } from '../../util/UndoManager';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { LoadingBox } from '../nodes/LoadingBox';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { CollectionView, CollectionViewProps } from './CollectionView';
export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
}
export function CollectionSubView<X>(moreProps?: X) {
- class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() {
+ class CollectionSubView extends ViewBoxBaseComponent<X & SubCollectionViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private gestureDisposer?: GestureUtils.GestureEventDisposer;
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _mainCont?: HTMLDivElement;
- @observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
- @observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ @observable _focusFilters: Opt<string[]> = undefined; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
+ @observable _focusRangeFilters: Opt<string[]> = undefined; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
this.gestureDisposer?.();
- this._multiTouchDisposer?.();
if (ele) {
this._mainCont = ele;
this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
- this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
}
};
protected CreateDropTarget(ele: HTMLDivElement) {
@@ -48,29 +59,30 @@ export function CollectionSubView<X>(moreProps?: X) {
componentWillUnmount() {
this.gestureDisposer?.();
- this._multiTouchDisposer?.();
}
- @computed get dataDoc() {
- return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+ get dataDoc() {
+ return this._props.TemplateDataDocument instanceof Doc && this.Document.isTemplateForField ? Doc.GetProto(this._props.TemplateDataDocument) : this.Document.resolvedDataDoc ? this.Document : Doc.GetProto(this.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
}
- rootSelected = (outsideReaction?: boolean) => {
- return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction));
- };
+ get childContainerViewPath() {
+ return this.DocumentView?.().docViewPath;
+ }
+ // this returns whether either the collection is selected, or the template that it is part of is selected
+ rootSelected = () => this._props.isSelected() || BoolCast(this._props.TemplateDataDocument && this._props.rootSelected?.());
- // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
- // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
+ // The data field for rendering this collection will be on the this.Document unless we're rendering a template in which case we try to use props.TemplateDataDocument.
+ // When a document has a TemplateDataDoc but it's not a template, then it contains its own rendering data, but needs to pass the TemplateDataDoc through
// to its children which may be templates.
// If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey'
@computed get dataField() {
- return this.layoutDoc[this.props.fieldKey];
+ return this.dataDoc[this._props.fieldKey]; // this used to be 'layoutDoc', but then template fields will get ignored since the template is not a proto of the layout. hopefully nothing depending on the previous code.
}
@computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
- const { Document, DataDoc } = this.props;
+ const { Document, TemplateDataDocument } = this._props;
const validPairs = this.childDocs
- .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
+ .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));
@@ -80,14 +92,14 @@ export function CollectionSubView<X>(moreProps?: X) {
@computed get childDocList() {
return Cast(this.dataField, listSpec(Doc));
}
- collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._childFilters);
- collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._childFiltersByRanges, listSpec('string'), []);
+ 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 => Utils.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)) || [])];
- childDocRangeFilters = () => [...(this.props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
- searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
+ unrecursiveDocFilters = () => [...(this._props.childFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
+ childDocRangeFilters = () => [...(this._props.childFiltersByRanges?.() || []), ...this.collectionRangeDocFilters()];
+ searchFilterDocs = () => this._props.searchFilterDocs?.() ?? DocListCast(this.Document._searchFilterDocs);
@computed.struct get childDocs() {
TraceMobx();
let rawdocs: (Doc | Promise<Doc>)[] = [];
@@ -97,31 +109,31 @@ export function CollectionSubView<X>(moreProps?: X) {
} else if (Cast(this.dataField, listSpec(Doc), null)) {
// otherwise, if the collection data is a list, then use it.
rawdocs = Cast(this.dataField, listSpec(Doc), null);
- } else {
+ } else if (this.dataField) {
// Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
// For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
// Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
- const rootDoc = Cast(this.props.Document.rootDocument, Doc, null);
- rawdocs = rootDoc && !this.props.isAnnotationOverlay ? [Doc.GetProto(rootDoc)] : [];
+ const templateRoot = this._props.TemplateDataDocument;
+ rawdocs = templateRoot && !this._props.isAnnotationOverlay ? [Doc.GetProto(templateRoot)] : [];
}
- const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this.props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc);
+ const childDocs = rawdocs.filter(d => !(d instanceof Promise) && GetEffectiveAcl(Doc.GetProto(d)) !== AclPrivate && (this._props.ignoreUnrendered || !d.layout_unrendered)).map(d => d as Doc);
const childDocFilters = this.childDocFilters();
const childFiltersByRanges = this.childDocRangeFilters();
const searchDocs = this.searchFilterDocs();
- if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !childFiltersByRanges.length && !searchDocs.length)) {
+ if (this.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !childFiltersByRanges.length && !searchDocs.length)) {
return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
}
const docsforFilter: Doc[] = [];
childDocs.forEach(d => {
// dragging facets
- const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
- if (dragged && DragManager.docsBeingDragged.includes(d)) return false;
- let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0;
+ const dragged = this._props.childFilters?.().some(f => f.includes(Utils.noDragDocsFilter));
+ if (dragged && SnappingManager.CanEmbed && DragManager.docsBeingDragged.includes(d)) return false;
+ 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.props.Document).length > 0;
+ 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];
@@ -154,7 +166,7 @@ export function CollectionSubView<X>(moreProps?: X) {
@action
protected async setCursorPosition(position: [number, number]) {
let ind;
- const doc = this.props.Document;
+ const doc = this.Document;
const id = Doc.UserDoc()[Id];
const email = Doc.CurrentUserEmail;
const pos = { x: position[0], y: position[1] };
@@ -184,29 +196,27 @@ export function CollectionSubView<X>(moreProps?: X) {
@undoBatch
protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
- protected onInternalPreDrop(e: Event, de: DragManager.DropEvent) {
+ protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, dropAction: dropActionType) {
if (de.complete.docDragData) {
- // override the dropEvent's dropAction
- const dropAction = this.layoutDoc.dropAction as dropActionType;
// if the dropEvent's dragAction is, say 'embed', but we're just dragging within a collection, we may not actually want to make an embedding.
// so we check if our collection has a dropAction set on it and if so, we use that instead.
- if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.props.Document && this.childDocs.includes(d))) {
+ if (dropAction && !de.complete.docDragData.draggedDocuments.some(d => d.embedContainer === this.Document && this.childDocs.includes(d))) {
de.complete.docDragData.dropAction = dropAction;
}
e.stopPropagation();
}
}
- 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);
- @action
+ 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;
+
protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean {
const docDragData = de.complete.docDragData;
if (docDragData) {
let added = undefined;
const dropAction = docDragData.dropAction || docDragData.userDropAction;
- const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
+ const targetDocments = DocListCast(this.dataDoc[this._props.fieldKey]);
const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
@@ -214,28 +224,28 @@ export function CollectionSubView<X>(moreProps?: X) {
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
if (movedDocs.length) {
const canAdd =
- (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
- const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
+ (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.Document)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.Document));
+ const moved = docDragData.moveDocument(movedDocs, this.Document, canAdd ? this.addDocument : returnFalse);
added = canAdd || moved ? moved : undefined;
} else if (addedDocs.length) {
added = this.addDocument(addedDocs);
}
- if (!added && ScriptCast(this.rootDoc.dropConverter)) {
- ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
+ if (!added && ScriptCast(this.Document.dropConverter)) {
+ ScriptCast(this.Document.dropConverter)?.script.run({ dragData: docDragData });
added = addedDocs.length ? this.addDocument(addedDocs) : true;
}
} else {
- ScriptCast(this.rootDoc.dropConverter)?.script.run({ dragData: docDragData });
+ ScriptCast(this.Document.dropConverter)?.script.run({ dragData: docDragData });
added = this.addDocument(docDragData.droppedDocuments);
!added && alert('You cannot perform this move');
}
- added === false && !this.props.isAnnotationOverlay && e.preventDefault();
+ added === false && !this._props.isAnnotationOverlay && e.preventDefault();
added === true && e.stopPropagation();
return added ? true : false;
} else if (de.complete.annoDragData) {
const dropCreator = de.complete.annoDragData.dropDocCreator;
de.complete.annoDragData.dropDocCreator = () => {
- const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
+ const dropped = dropCreator(this._props.isAnnotationOverlay ? this.Document : undefined);
this.addDocument(dropped);
return dropped;
};
@@ -245,7 +255,6 @@ export function CollectionSubView<X>(moreProps?: X) {
}
@undoBatch
- @action
protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: (docs: Doc[]) => void) {
if (e.ctrlKey) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
@@ -297,20 +306,19 @@ export function CollectionSubView<X>(moreProps?: X) {
const cors = img.includes('corsProxy') ? img.match(/http.*corsProxy\//)![0] : '';
img = cors ? img.replace(cors, '') : img;
if (img) {
- const split = img.split('src="')[1].split('"')[0];
- let source = split;
- if (split.startsWith('data:image') && split.includes('base64')) {
- const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
- if (accessPaths.agnostic.client.indexOf('dashblobstore') === -1) {
- source = Utils.prepend(accessPaths.agnostic.client);
- } else {
- source = accessPaths.agnostic.client;
- }
- }
- if (source.startsWith('http')) {
- const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
- ImageUtils.ExtractExif(doc);
- addDocument(doc);
+ const imgSrc = img.split('src="')[1].split('"')[0];
+ const imgOpts = { ...options, _width: 300 };
+ if (imgSrc.startsWith('data:image') && imgSrc.includes('base64')) {
+ const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: [imgSrc] })).lastElement();
+ const newImgSrc =
+ result.accessPaths.agnostic.client.indexOf('dashblobstore') === -1 //
+ ? Utils.prepend(result.accessPaths.agnostic.client)
+ : result.accessPaths.agnostic.client;
+
+ addDocument(ImageUtils.AssignImgInfo(Docs.Create.ImageDocument(newImgSrc, imgOpts), result));
+ } else if (imgSrc.startsWith('http')) {
+ const doc = Docs.Create.ImageDocument(imgSrc, imgOpts);
+ addDocument(ImageUtils.AssignImgInfo(doc, await ImageUtils.ExtractImgInfo(doc)));
}
return;
} else {
@@ -327,7 +335,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
});
} else {
- const srcWeb = SelectionManager.Views().lastElement();
+ 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;
@@ -336,7 +344,7 @@ export function CollectionSubView<X>(moreProps?: X) {
Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
addDocument(htmlDoc);
if (srcWeb) {
- const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+ 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);
@@ -442,13 +450,13 @@ export function CollectionSubView<X>(moreProps?: X) {
if (typeof files === 'string') {
const loading = Docs.Create.LoadingDocument(files, options);
generatedDocuments.push(loading);
- Doc.addCurrentlyLoading(loading);
+ LoadingBox.addCurrentlyLoading(loading);
DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
generatedDocuments.push(
...files.map(file => {
const loading = Docs.Create.LoadingDocument(file, options);
- Doc.addCurrentlyLoading(loading);
+ LoadingBox.addCurrentlyLoading(loading);
DocUtils.uploadFileToDoc(file, {}, loading);
return loading;
})
@@ -456,15 +464,15 @@ export function CollectionSubView<X>(moreProps?: X) {
}
if (generatedDocuments.length) {
// Creating a dash document
- const isFreeformView = this.props.Document._type_collection === CollectionViewType.Freeform;
+ const isFreeformView = this.Document._type_collection === CollectionViewType.Freeform;
const set = !isFreeformView
? generatedDocuments
: generatedDocuments.length > 1
- ? generatedDocuments.map(d => {
- DocUtils.iconify(d);
- return d;
- })
- : [];
+ ? generatedDocuments.map(d => {
+ DocUtils.iconify(d);
+ return d;
+ })
+ : [];
if (completed) completed(set);
else {
if (isFreeformView && generatedDocuments.length > 1) {
@@ -486,11 +494,3 @@ export function CollectionSubView<X>(moreProps?: X) {
return CollectionSubView;
}
-
-import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
-import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
-import { DragManager, dropActionType } from '../../util/DragManager';
-import { SelectionManager } from '../../util/SelectionManager';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index a8f5345b7..ee5147428 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,6 +1,8 @@
import { toUpper } from 'lodash';
-import { action, computed, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
+import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
import { Doc, Opt, StrListCast } from '../../../fields/Doc';
import { List } from '../../../fields/List';
import { ObjectField } from '../../../fields/ObjectField';
@@ -8,34 +10,38 @@ import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
import { Docs } from '../../documents/Documents';
import { DocumentManager } from '../../util/DocumentManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
-import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FocusViewOptions } from '../nodes/FieldView';
import { PresBox } from '../nodes/trails';
-import { computePivotLayout, computeTimelineLayout, ViewDefBounds } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTimeView.scss';
-import React = require('react');
+import { ViewDefBounds, computePivotLayout, computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@observer
export class CollectionTimeView extends CollectionSubView() {
_changing = false;
@observable _layoutEngine = computePivotLayout.name;
@observable _collapsed: boolean = false;
- @observable _childClickedScript: Opt<ScriptField>;
- @observable _viewDefDivClick: Opt<ScriptField>;
- @observable _focusPivotField: Opt<string>;
+ @observable _childClickedScript: Opt<ScriptField> = undefined;
+ @observable _viewDefDivClick: Opt<ScriptField> = undefined;
+ @observable _focusPivotField: Opt<string> = undefined;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
- async componentDidMount() {
- this.props.setContentView?.(this);
+ componentDidMount() {
+ this._props.setContentViewBox?.(this);
runInAction(() => {
- this._childClickedScript = ScriptField.MakeScript('openInLightbox(self)', { this: Doc.name });
+ this._childClickedScript = ScriptField.MakeScript('openInLightbox(this)', { this: Doc.name });
this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
});
}
@@ -47,23 +53,23 @@ export class CollectionTimeView extends CollectionSubView() {
getAnchor = (addAsAnnotation: boolean) => {
const anchor = Docs.Create.HTMLMarkerDocument([], {
title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as any,
- annotationOn: this.rootDoc,
+ annotationOn: this.Document,
});
- PresBox.pinDocView(anchor, { pinData: { type_collection: true, pivot: true, filters: true } }, this.rootDoc);
+ PresBox.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
- if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
} else {
- this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
+ this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
}
return anchor;
};
@action
- scrollPreview = (docView: DocumentView, anchor: Doc, focusSpeed: number, options: DocFocusOptions) => {
+ 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);
@@ -79,10 +85,10 @@ export class CollectionTimeView extends CollectionSubView() {
this,
e,
action((e: PointerEvent, down: number[], delta: number[]) => {
- const minReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMinReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMin'], 0));
- const maxReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMaxReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMax'], 10));
- this.props.Document[this.props.fieldKey + '-timelineMinReq'] = minReq + ((maxReq - minReq) * delta[0]) / this.props.PanelWidth();
- this.props.Document[this.props.fieldKey + '-timelineSpan'] = undefined;
+ 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();
+ this.Document[this._props.fieldKey + '-timelineSpan'] = undefined;
return false;
}),
returnFalse,
@@ -95,9 +101,9 @@ export class CollectionTimeView extends CollectionSubView() {
this,
e,
action((e: PointerEvent, down: number[], delta: number[]) => {
- const minReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMinReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMin'], 0));
- const maxReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMaxReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMax'], 10));
- this.props.Document[this.props.fieldKey + '-timelineMaxReq'] = maxReq + ((maxReq - minReq) * delta[0]) / this.props.PanelWidth();
+ 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();
return false;
}),
returnFalse,
@@ -110,10 +116,10 @@ export class CollectionTimeView extends CollectionSubView() {
this,
e,
action((e: PointerEvent, down: number[], delta: number[]) => {
- const minReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMinReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMin'], 0));
- const maxReq = NumCast(this.props.Document[this.props.fieldKey + '-timelineMaxReq'], NumCast(this.props.Document[this.props.fieldKey + '-timelineMax'], 10));
- this.props.Document[this.props.fieldKey + '-timelineMinReq'] = minReq - ((maxReq - minReq) * delta[0]) / this.props.PanelWidth();
- this.props.Document[this.props.fieldKey + '-timelineMaxReq'] = maxReq - ((maxReq - minReq) * delta[0]) / this.props.PanelWidth();
+ 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();
+ this.Document[this._props.fieldKey + '-timelineMaxReq'] = maxReq - ((maxReq - minReq) * delta[0]) / this._props.PanelWidth();
return false;
}),
returnFalse,
@@ -140,9 +146,9 @@ export class CollectionTimeView extends CollectionSubView() {
@computed get contents() {
return (
- <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this.props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
+ <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
<CollectionFreeFormView
- {...this.props}
+ {...this._props}
engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
fitContentsToBox={returnTrue}
childClickScript={this._childClickedScript}
@@ -189,7 +195,7 @@ export class CollectionTimeView extends CollectionSubView() {
@computed get _allFacets() {
const facets = new Set<string>();
this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key)));
- Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key)));
+ Doc.AreProtosEqual(this.dataDoc, this.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key)));
return Array.from(facets);
}
menuCallback = (x: number, y: number) => {
@@ -206,7 +212,6 @@ export class CollectionTimeView extends CollectionSubView() {
Array.from(keySet).map(fieldKey => docItems.push({ description: ':' + fieldKey, event: () => (this.layoutDoc._pivotField = fieldKey), icon: 'compress-arrows-alt' }));
docItems.push({ description: ':default', event: () => (this.layoutDoc._pivotField = undefined), icon: 'compress-arrows-alt' });
ContextMenu.Instance.addItem({ description: 'Pivot Fields ...', subitems: docItems, icon: 'eye' });
- const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y);
ContextMenu.Instance.displayMenu(x, y, ':');
};
@@ -222,7 +227,7 @@ export class CollectionTimeView extends CollectionSubView() {
}
return false;
}}
- background={'#f1efeb'} // this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+ background={'#f1efeb'} // this._props.headingObject ? this._props.headingObject.color : "#f1efeb";
contents={':' + StrCast(this.layoutDoc._pivotField)}
showMenuOnLoad={true}
display={'inline'}
@@ -241,7 +246,7 @@ export class CollectionTimeView extends CollectionSubView() {
}
});
const forceLayout = StrCast(this.layoutDoc._forceRenderEngine);
- const doTimeline = forceLayout ? forceLayout === computeTimelineLayout.name : nonNumbers / this.childDocs.length < 0.1 && this.props.PanelWidth() / this.props.PanelHeight() > 6;
+ const doTimeline = forceLayout ? forceLayout === computeTimelineLayout.name : nonNumbers / this.childDocs.length < 0.1 && this._props.PanelWidth() / this._props.PanelHeight() > 6;
if (doTimeline !== (this._layoutEngine === computeTimelineLayout.name)) {
if (!this._changing) {
this._changing = true;
@@ -256,10 +261,10 @@ export class CollectionTimeView extends CollectionSubView() {
}
return (
- <div className={'collectionTimeView' + (doTimeline ? '' : '-pivot')} onContextMenu={this.specificMenu} style={{ width: this.props.PanelWidth(), height: '100%' }}>
+ <div className={'collectionTimeView' + (doTimeline ? '' : '-pivot')} onContextMenu={this.specificMenu} style={{ width: this._props.PanelWidth(), height: '100%' }}>
{this.pivotKeyUI}
{this.contents}
- {!this.props.isSelected() || !doTimeline ? null : (
+ {!this._props.isSelected() || !doTimeline ? null : (
<>
<div className="collectionTimeView-thumb-min collectionTimeView-thumb" key="min" onPointerDown={this.onMinDown} />
<div className="collectionTimeView-thumb-max collectionTimeView-thumb" key="mid" onPointerDown={this.onMaxDown} />
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2bf649caf..bbbef78b4 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,10 +1,9 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.collectionTreeView-container {
transform-origin: top left;
}
.collectionTreeView-dropTarget {
- border-width: $COLLECTION_BORDER_WIDTH;
border-color: transparent;
border-style: solid;
border-radius: inherit;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e408c193a..741013148 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,8 @@
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
@@ -26,7 +27,6 @@ import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView } from './CollectionSubView';
import './CollectionTreeView.scss';
import { TreeView } from './TreeView';
-import React = require('react');
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionTreeViewProps = {
@@ -59,27 +59,29 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes
private observer: any; // observer for monitoring tree view items.
- @computed get doc() {
- return this.props.Document;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
}
- @computed get dataDoc() {
- return this.props.DataDoc || this.doc;
+
+ get dataDoc() {
+ return this._props.TemplateDataDocument || this.Document;
}
@computed get treeViewtruncateTitleWidth() {
- return NumCast(this.doc.treeView_TruncateTitleWidth, this.panelWidth());
+ return NumCast(this.Document.treeView_TruncateTitleWidth, this.panelWidth());
}
@computed get treeChildren() {
TraceMobx();
- return this.props.childDocuments || this.childDocs;
+ return this._props.childDocuments || this.childDocs;
}
@computed get outlineMode() {
- return this.doc.treeView_Type === TreeViewType.outline;
+ return this.Document.treeView_Type === TreeViewType.outline;
}
@computed get fileSysMode() {
- return this.doc.treeView_Type === TreeViewType.fileSystem;
+ return this.Document.treeView_Type === TreeViewType.fileSystem;
}
@computed get dashboardMode() {
- return this.doc === Doc.MyDashboards;
+ return this.Document === Doc.MyDashboards;
}
@observable _titleHeight = 0; // height of the title bar
@@ -88,8 +90,8 @@ 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 = (outsideReaction?: boolean) => (this._isAnyChildContentActive ? true : this._props.isContentActive() ? true : false);
componentWillUnmount() {
this._isDisposing = true;
@@ -99,9 +101,9 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
componentDidMount() {
- //this.props.setContentView?.(this);
+ //this._props.setContentView?.(this);
this._disposers.autoheight = reaction(
- () => this.rootDoc.layout_autoHeight,
+ () => this.layoutDoc.layout_autoHeight,
auto => auto && this.computeHeight(),
{ fireImmediately: true }
);
@@ -112,62 +114,62 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6;
this.layoutDoc._layout_autoHeightMargins = bodyHeight;
- !this.props.dontRegisterView && this.props.setHeight?.(bodyHeight + titleHeight);
+ !this._props.dontRegisterView && this._props.setHeight?.(bodyHeight + titleHeight);
}
};
unobserveHeight = (ref: any) => {
this.refList.delete(ref);
- this.rootDoc.layout_autoHeight && this.computeHeight();
+ this.layoutDoc.layout_autoHeight && this.computeHeight();
};
observeHeight = (ref: any) => {
if (ref) {
this.refList.add(ref);
this.observer = new _global.ResizeObserver(
action((entries: any) => {
- if (this.rootDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+ if (this.layoutDoc.layout_autoHeight && ref && this.refList.size && !SnappingManager.IsDragging) {
this.computeHeight();
}
})
);
- this.rootDoc.layout_autoHeight && 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.doc, this.onInternalPreDrop.bind(this));
+ if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.Document, this.onInternalPreDrop.bind(this));
};
- protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent) => {
- const dropAction = this.layoutDoc.dropAction as dropActionType;
+ protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, dropAction: dropActionType) => {
const dragData = de.complete.docDragData;
if (dragData) {
- const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.rootDoc) ? true : false;
- const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.doc && this.childDocs.includes(d));
- if (isAlreadyInTree() !== sameTree) {
- console.log('WHAAAT');
- }
+ const sameTree = Doc.AreProtosEqual(dragData.treeViewDoc, this.Document) ? true : false;
+ const isAlreadyInTree = () => sameTree || dragData.draggedDocuments.some(d => d.embedContainer === this.Document && this.childDocs.includes(d));
dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
e.stopPropagation();
}
};
- screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this._headerHeight);
+ configDrag = (dragData: DragManager.DocumentDragData) => {
+ dragData.treeViewDoc = this.Document;
+ };
+
+ screenToLocalTransform = () => this.ScreenToLocalBoxXf().translate(0, -this._headerHeight);
@action
remove = (doc: Doc | Doc[]): boolean => {
const docs = doc instanceof Doc ? [doc] : doc;
- const targetDataDoc = this.doc[DocData];
- const value = DocListCast(targetDataDoc[this.props.fieldKey]);
+ 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.rootDoc, doc)))) SelectionManager.DeselectAll();
+ if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views.some(dv => Doc.AreProtosEqual(dv.Document, doc)))) SelectionManager.DeselectAll();
if (result.length !== value.length && doc instanceof Doc) {
- const ind = DocListCast(targetDataDoc[this.props.fieldKey]).indexOf(doc);
- const prev = ind && DocListCast(targetDataDoc[this.props.fieldKey])[ind - 1];
- this.props.removeDocument?.(doc);
+ const ind = DocListCast(targetDataDoc[this._props.fieldKey]).indexOf(doc);
+ const prev = ind && DocListCast(targetDataDoc[this._props.fieldKey])[ind - 1];
+ this._props.removeDocument?.(doc);
if (ind > 0 && prev) {
- FormattedTextBox.SelectOnLoad = prev[Id];
- DocumentManager.Instance.getDocumentView(prev, this.props.DocumentView?.())?.select(false);
+ FormattedTextBox.SetSelectOnLoad(prev);
+ DocumentManager.Instance.getDocumentView(prev, this.DocumentView?.())?.select(false);
}
return true;
}
@@ -178,24 +180,28 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
const doAddDoc = (doc: Doc | Doc[]) =>
(doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => {
- const res = flg && Doc.AddDocToList(this.doc[DocData], this.props.fieldKey, doc, relativeTo, before);
- res && Doc.SetContainer(doc, this.props.Document);
+ const res = flg && Doc.AddDocToList(this.Document[DocData], this._props.fieldKey, doc, relativeTo, before);
+ res && Doc.SetContainer(doc, this.Document);
return res;
}, true);
- if (this.doc.resolvedDataDoc instanceof Promise) return false;
- return relativeTo === undefined ? this.props.addDocument?.(docs) || false : doAddDoc(docs);
+ if (this.Document.resolvedDataDoc instanceof Promise) return false;
+ return relativeTo === undefined ? this._props.addDocument?.(docs) || false : doAddDoc(docs);
};
onContextMenu = (e: React.MouseEvent): void => {
// need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
if (!Doc.noviceMode) {
const layoutItems: ContextMenuProps[] = [];
- layoutItems.push({ description: 'Make tree state ' + (this.doc.treeView_OpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeView_OpenIsTransient = !this.doc.treeView_OpenIsTransient), icon: 'paint-brush' });
- layoutItems.push({ description: (this.doc.treeView_HideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeView_HideHeaderFields = !this.doc.treeView_HideHeaderFields), icon: 'paint-brush' });
- layoutItems.push({ description: (this.doc.treeView_HideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeView_HideTitle = !this.doc.treeView_HideTitle), icon: 'paint-brush' });
+ layoutItems.push({
+ description: 'Make tree state ' + (this.Document.treeView_OpenIsTransient ? 'persistent' : 'transient'),
+ event: () => (this.Document.treeView_OpenIsTransient = !this.Document.treeView_OpenIsTransient),
+ 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' });
ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
- onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
+ onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.Document, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
!existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
}
};
@@ -213,7 +219,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
height={'auto'}
GetValue={() => StrCast(this.dataDoc.title)}
SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
- if (enter && this.props.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+ if (enter && this.Document.treeView_Type === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
this.dataDoc.title = value;
return true;
})}
@@ -231,12 +237,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
get documentTitle() {
return (
<FormattedTextBox
- {...this.props}
+ {...this._props}
fieldKey="text"
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
isContentActive={this.isContentActive}
isDocumentActive={this.isContentActive}
- rootSelected={returnTrue}
forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not layout_autoHeight
PanelWidth={this.documentTitleWidth}
PanelHeight={this.documentTitleHeight}
@@ -249,57 +254,56 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
moveDocument={returnFalse}
removeDocument={returnFalse}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- bringToFront={returnFalse}
/>
);
}
childContextMenuItems = () => {
- const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
- const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
- const icons = StrListCast(this.doc.childContextMenuIcons);
- return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
+ const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
+ const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+ const icons = StrListCast(this.Document.childContextMenuIcons);
+ return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
- headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeView_HideHeaderFields);
+ headerFields = () => this._props.treeViewHideHeaderFields || BoolCast(this.Document.treeView_HideHeaderFields);
@observable _renderCount = 1;
@computed get treeViewElements() {
TraceMobx();
- const dragAction = StrCast(this.doc.childDragAction) as dropActionType;
+ const dragAction = StrCast(this.Document.childDragAction) as dropActionType;
const addDoc = (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;
+ 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))));
return TreeView.GetChildElements(
this.treeChildren,
this,
this,
- this.doc,
- this.props.DataDoc,
+ this.Document,
+ this._props.TemplateDataDocument,
undefined,
undefined,
addDoc,
this.remove,
moveDoc,
dragAction,
- this.props.addDocTab,
- this.props.styleProvider,
+ this._props.addDocTab,
+ this._props.styleProvider,
this.screenToLocalTransform,
this.isContentActive,
this.panelWidth,
- this.props.renderDepth,
+ this._props.renderDepth,
this.headerFields,
[],
- this.props.onCheckedClick,
+ this._props.onCheckedClick,
this.onChildClick,
- this.props.treeViewSkipFields,
+ this._props.treeViewSkipFields,
true,
this.whenChildContentsActiveChanged,
- this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, 'boolean', null),
+ this._props.dontRegisterView || Cast(this.Document.childDontRegisterViews, 'boolean', null),
this.observeHeight,
this.unobserveHeight,
this.childContextMenuItems(),
//TODO: [AL] add these
- this.props.AddToMap,
- this.props.RemFromMap,
- this.props.hierarchyIndex,
+ this._props.AddToMap,
+ this._props.RemFromMap,
+ this._props.hierarchyIndex,
this._renderCount
);
}
@@ -307,8 +311,8 @@ 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.props.ScreenToLocalTransform().Scale))}
- key={this.doc[Id]}
+ 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}
</div>
@@ -316,38 +320,37 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
@computed get noviceExplainer() {
- return !Doc.noviceMode || !this.rootDoc.layout_explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.layout_explainer)} </div>;
+ return !Doc.noviceMode || !this.layoutDoc.layout_explainer ? null : <div className="documentExplanation"> {StrCast(this.layoutDoc.layout_explainer)} </div>;
}
return35 = () => 35;
@computed get buttonMenu() {
- const menuDoc = Cast(this.rootDoc.layout_headerButton, Doc, null);
+ const menuDoc = Cast(this.layoutDoc.layout_headerButton, Doc, null);
// To create a multibutton menu add a CollectionLinearView
return !menuDoc ? null : (
<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
<DocumentView
Document={menuDoc}
- DataDoc={menuDoc}
- isContentActive={this.props.isContentActive}
+ TemplateDataDocument={menuDoc}
+ isContentActive={this._props.isContentActive}
isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- addDocTab={this.props.addDocTab}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
+ rootSelected={this.rootSelected}
ScreenToLocalTransform={Transform.Identity}
PanelWidth={this.return35}
PanelHeight={this.return35}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- docViewPath={returnEmptyDoclist}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={returnEmptyDoclist}
whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- childFilters={this.props.childFilters}
- childFiltersByRanges={this.props.childFiltersByRanges}
- searchFilterDocs={this.props.searchFilterDocs}
+ childFilters={this._props.childFilters}
+ childFiltersByRanges={this._props.childFiltersByRanges}
+ searchFilterDocs={this._props.searchFilterDocs}
/>
</div>
);
@@ -364,30 +367,30 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
@computed get nativeDimScaling() {
const nw = this.nativeWidth;
const nh = this.nativeHeight;
- const hscale = nh ? this.props.PanelHeight() / nh : 1;
- const wscale = nw ? this.props.PanelWidth() / nw : 1;
+ const hscale = nh ? this._props.PanelHeight() / nh : 1;
+ const wscale = nw ? this._props.PanelWidth() / nw : 1;
return wscale < hscale ? wscale : hscale;
}
- marginX = () => NumCast(this.doc._xMargin);
- marginTop = () => NumCast(this.doc._yMargin);
- marginBot = () => NumCast(this.doc._yMargin);
- documentTitleWidth = () => Math.min(this.layoutDoc?.[Width](), this.panelWidth());
- documentTitleHeight = () => (this.layoutDoc?.[Height]() || 0) - NumCast(this.layoutDoc.layout_autoHeightMargins);
+ marginX = () => NumCast(this.Document._xMargin);
+ marginTop = () => NumCast(this.Document._yMargin);
+ marginBot = () => NumCast(this.Document._yMargin);
+ documentTitleWidth = () => Math.min(NumCast(this.layoutDoc?._width), this.panelWidth());
+ documentTitleHeight = () => NumCast(this.layoutDoc?._height) - NumCast(this.layoutDoc.layout_autoHeightMargins);
truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
- onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
- panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1));
+ onChildClick = () => this._props.onChildClick?.() || ScriptCast(this.Document.onChildClick);
+ panelWidth = () => Math.max(0, this._props.PanelWidth() - 2 * this.marginX() * (this._props.NativeDimScaling?.() || 1));
- addAnnotationDocument = (doc: Doc | Doc[]) => this.addDocument(doc, `${this.props.fieldKey}_annotations`) || false;
- remAnnotationDocument = (doc: Doc | Doc[]) => this.removeDocument(doc, `${this.props.fieldKey}_annotations`) || false;
+ 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;
+ this.moveDocument(doc, targetCollection, addDocument, `${this._props.fieldKey}_annotations`) || false;
@observable _headerHeight = 0;
@computed get content() {
- const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
- const color = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.Color);
- const pointerEvents = () => (this.props.isContentActive() === false ? 'none' : undefined);
- const titleBar = this.props.treeViewHideTitle || this.doc.treeView_HideTitle ? null : this.titleBar;
+ const background = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor);
+ const color = () => this._props.styleProvider?.(this.Document, this._props, StyleProp.Color);
+ const pointerEvents = () => (this._props.isContentActive() === false ? 'none' : undefined);
+ const titleBar = this._props.treeViewHideTitle || this.Document.treeView_HideTitle ? null : this.titleBar;
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%', pointerEvents: 'all' }}>
{!this.buttonMenu && !this.noviceExplainer ? null : (
@@ -399,7 +402,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
<div
className="collectionTreeView-contents"
key="tree"
- ref={r => !this.doc.treeView_HasOverlay && r && this.createTreeDropTarget(r)}
+ ref={r => !this.Document.treeView_HasOverlay && r && this.createTreeDropTarget(r)}
style={{
...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
color: color(),
@@ -424,7 +427,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={e => (!this.layoutDoc.forceActive ? this._props.select(false) : SelectionManager.DeselectAll())}
onDrop={this.onTreeDrop}>
<ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
</div>
@@ -436,27 +439,26 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
render() {
TraceMobx();
- const scale = this.props.NativeDimScaling?.() || 1;
+ const scale = this._props.NativeDimScaling?.() || 1;
return (
<div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
- {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
+ {!(this.Document instanceof Doc) || !this.treeChildren ? null : this.Document.treeView_HasOverlay ? (
<CollectionFreeFormView
- {...this.props}
- setContentView={emptyFunction}
+ {...this._props}
+ setContentViewBox={emptyFunction}
NativeWidth={returnZero}
NativeHeight={returnZero}
- pointerEvents={this.props.isContentActive() && SnappingManager.GetIsDragging() ? returnAll : returnNone}
+ pointerEvents={this._props.isContentActive() && SnappingManager.IsDragging ? returnAll : returnNone}
isAnnotationOverlay={true}
isAnnotationOverlayScrollable={true}
- childDocumentsActive={this.props.isDocumentActive}
- fieldKey={this.props.fieldKey + '_annotations'}
+ childDocumentsActive={this._props.isContentActive}
+ fieldKey={this._props.fieldKey + '_annotations'}
dropAction="move"
select={emptyFunction}
addDocument={this.addAnnotationDocument}
removeDocument={this.remAnnotationDocument}
moveDocument={this.moveAnnotationDocument}
- bringToFront={emptyFunction}
- renderDepth={this.props.renderDepth + 1}>
+ renderDepth={this._props.renderDepth + 1}>
{this.content}
</CollectionFreeFormView>
) : (
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index 5db489c0a..de53a2c62 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -1,4 +1,4 @@
-@import "../global/globalCssVariables";
+@import '../global/globalCssVariables.module.scss';
.collectionView {
border-width: 0;
@@ -9,7 +9,7 @@
border-radius: inherit;
width: 100%;
height: 100%;
- overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying...
+ //overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying...
.collectionView-filterDragger {
background-color: rgb(140, 139, 139);
@@ -54,8 +54,8 @@
}
}
- >div,
- >div>div {
+ > div,
+ > div > div {
width: 100%;
height: 100%;
}
@@ -80,4 +80,4 @@
border-radius: 50%;
padding: 3px;
background: white;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ce19b3f9b..18eb4dd1f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,47 +1,43 @@
-import { computed, observable, runInAction } from 'mobx';
+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 { ObjectField } from '../../../fields/ObjectField';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { returnEmptyString } from '../../../Utils';
-import { DocUtils } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
+import { DocUtils } from '../../documents/Documents';
import { dropActionType } from '../../util/DragManager';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
-import { InteractionUtils } from '../../util/InteractionUtils';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../DocComponent';
import { OpenWhere } from '../nodes/DocumentView';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import { CollectionCalendarView } from './CollectionCalendarView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './collectionLinear';
-import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
-import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionNoteTakingView } from './CollectionNoteTakingView';
import { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
import { CollectionStackingView } from './CollectionStackingView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
-export const COLLECTION_BORDER_WIDTH = 2;
-const path = require('path');
-
-interface CollectionViewProps_ extends FieldViewProps {
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+import { CollectionGridView } from './collectionGrid/CollectionGridView';
+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;
- setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void;
ignoreUnrendered?: boolean;
// property overrides for child documents
@@ -52,10 +48,9 @@ interface CollectionViewProps_ extends FieldViewProps {
childlayout_showTitle?: () => string;
childOpacity?: () => number;
childContextMenuItems?: () => { script: ScriptField; label: string }[];
- childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
- childHideDecorationTitle?: () => boolean;
- childHideResizeHandles?: () => boolean;
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;
@@ -68,9 +63,8 @@ interface CollectionViewProps_ extends FieldViewProps {
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 CollectionViewProps extends React.PropsWithChildren<CollectionViewProps_> {}
@observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
+export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() implements ViewBoxInterface {
public static LayoutString(fieldStr: string) {
return FieldView.LayoutString(CollectionView, fieldStr);
}
@@ -79,12 +73,28 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
public static SetSafeMode(safeMode: boolean) {
this._safeMode = safeMode;
}
-
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ private reactionDisposer: IReactionDisposer | undefined;
+ @observable _isContentActive: boolean | undefined = undefined;
constructor(props: any) {
super(props);
- runInAction(() => (this._annotationKeySuffix = returnEmptyString));
+ makeObservable(this);
+ this._annotationKeySuffix = returnEmptyString;
+ }
+
+ componentDidMount() {
+ // we use a reaction/observable instead of a computed value to reduce invalidations.
+ // There are many variables that aggregate into this boolean output - a change in any of them
+ // will cause downstream invalidations even if the computed value doesn't change. By making
+ // 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),
+ { fireImmediately: true }
+ );
+ }
+ componentWillUnmount() {
+ this.reactionDisposer?.();
}
get collectionViewType(): CollectionViewType | undefined {
@@ -101,41 +111,41 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
return viewField as any as CollectionViewType;
}
- screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
+ screenToLocalTransform = () => (this._props.renderDepth ? this.ScreenToLocalBoxXf() : this.ScreenToLocalBoxXf().scale(this._props.PanelWidth() / this.bodyPanelWidth()));
// prettier-ignore
private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
TraceMobx();
if (type === undefined) return null;
switch (type) {
default:
- case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
- case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
- case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
- case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
- case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
- case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
- case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
- case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
- case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
- case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
- case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
- case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
- case CollectionViewType.NoteTaking: return <CollectionNoteTakingView key="collview" {...props} />;
- 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.Staff: return <CollectionStaffView key="collview" {...props} />;
+ 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} />;
+ case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
+ case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
+ case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
+ case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
+ case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
+ case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
+ case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
+ case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
+ case CollectionViewType.NoteTaking: return <CollectionNoteTakingView key="collview" {...props} />;
+ case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
+ case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+ case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
}
};
setupViewTypes(category: string, func: (type_collection: CollectionViewType) => Doc) {
- if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._type_collection !== CollectionViewType.Docking && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
+ if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking && !this.dataDoc.isGroup && !this.Document.annotationOn) {
// prettier-ignore
const subItems: ContextMenuProps[] = [
{ 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: 'Calendar', event: () => func(CollectionViewType.Calendar), icon: 'columns'},
{ 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' },
@@ -159,27 +169,27 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
if (cm && !e.isPropagationStopped()) {
// need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
!Doc.noviceMode &&
- this.setupViewTypes('UI Controls...', vtype => {
- const newRendition = Doc.MakeEmbedding(this.rootDoc);
+ this.setupViewTypes('Appearance...', vtype => {
+ const newRendition = Doc.MakeEmbedding(this.Document);
newRendition._type_collection = vtype;
- this.props.addDocTab(newRendition, OpenWhere.addRight);
+ this._props.addDocTab(newRendition, OpenWhere.addRight);
return newRendition;
});
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
- !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.rootDoc.forceActive = !this.rootDoc.forceActive), icon: 'project-diagram' }) : null;
- if (this.rootDoc.childLayout instanceof Doc) {
- optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, OpenWhere.addRight), icon: 'project-diagram' });
+ !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;
+ 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.rootDoc.childClickedOpenTemplateView instanceof Doc) {
- optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView 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.rootDoc._isLightbox ? 'Unset' : 'Set'} is Lightbox`, event: () => (this.rootDoc._isLightbox = !this.rootDoc._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' });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
- if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
+ if (!Doc.noviceMode && !this.Document.annotationOn && !this._props.hideClickBehaviors) {
const existingOnClick = cm.findByDescription('OnClick...');
const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
const funcs = [
@@ -191,9 +201,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
description: `Edit ${func.name} script`,
icon: 'edit',
event: (obj: any) => {
- const embedding = Doc.MakeEmbedding(this.rootDoc);
+ const embedding = Doc.MakeEmbedding(this.Document);
DocUtils.makeCustomViewClicked(embedding, undefined, func.key);
- this.props.addDocTab(embedding, OpenWhere.addRight);
+ this._props.addDocTab(embedding, OpenWhere.addRight);
},
})
);
@@ -201,51 +211,51 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
onClicks.push({
description: `Set child ${childClick.title}`,
icon: 'edit',
- event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
+ event: () => (this.dataDoc[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
})
);
- !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
+ !Doc.IsSystem(this.Document) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
}
if (!Doc.noviceMode) {
const more = cm.findByDescription('More...');
const moreItems = more && 'subitems' in more ? more.subitems : [];
- moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
+ moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.Document) });
!more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
}
}
};
- bodyPanelWidth = () => this.props.PanelWidth();
+ bodyPanelWidth = () => this._props.PanelWidth();
- childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
- childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
- childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
- isContentActive = (outsideReaction?: boolean) => (this.isAnyChildContentActive() ? true : this.props.isContentActive());
+ childLayoutTemplate = () => this._props.childLayoutTemplate?.() || Cast(this.Document.childLayoutTemplate, Doc, null);
+ isContentActive = (outsideReaction?: boolean) => this._isContentActive;
+
+ pointerEvents = () =>
+ this.layoutDoc._lockedPosition && //
+ this.Document?._type_collection === CollectionViewType.Freeform;
render() {
TraceMobx();
+ const pointerEvents = this.pointerEvents() ? 'none' : undefined;
const props: SubCollectionViewProps = {
- ...this.props,
+ ...this._props,
addDocument: this.addDocument,
moveDocument: this.moveDocument,
removeDocument: this.removeDocument,
isContentActive: this.isContentActive,
isAnyChildContentActive: this.isAnyChildContentActive,
- whenChildContentsActiveChanged: this.whenChildContentsActiveChanged,
PanelWidth: this.bodyPanelWidth,
- PanelHeight: this.props.PanelHeight,
+ PanelHeight: this._props.PanelHeight,
ScreenToLocalTransform: this.screenToLocalTransform,
childLayoutTemplate: this.childLayoutTemplate,
- childLayoutString: StrCast(this.rootDoc.childLayoutString, this.props.childLayoutString),
- childHideResizeHandles: this.childHideResizeHandles,
- childHideDecorationTitle: this.childHideDecorationTitle,
+ whenChildContentsActiveChanged: this.whenChildContentsActiveChanged,
+ childLayoutString: StrCast(this.Document.childLayoutString, this._props.childLayoutString),
+ childHideResizeHandles: this._props.childHideResizeHandles ?? BoolCast(this.Document.childHideResizeHandles),
+ childHideDecorationTitle: this._props.childHideDecorationTitle ?? BoolCast(this.Document.childHideDecorationTitle),
};
return (
- <div
- className="collectionView"
- onContextMenu={this.onContextMenu}
- style={{ pointerEvents: this.props.DocumentView?.()?.props.docViewPath().lastElement()?.rootDoc?._type_collection === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
+ <div className="collectionView" onContextMenu={this.onContextMenu} style={{ pointerEvents }}>
{this.renderSubView(this.collectionViewType, props)}
</div>
);
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
index f3071b316..4523a4f1e 100644
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -1,6 +1,6 @@
-import * as React from "react";
-import { observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
interface IKeyRestrictionProps {
contains: boolean;
@@ -19,37 +19,28 @@ export default class KeyRestrictionRow extends React.Component<IKeyRestrictionPr
if (this._key && this._value) {
let parsedValue: string | number = `"${this._value}"`;
const parsed = parseInt(this._value);
- let type = "string";
+ let type = 'string';
if (!isNaN(parsed)) {
parsedValue = parsed;
- type = "number";
+ type = 'number';
}
- const scriptText = `${this._contains ? "" : "!"}(((doc.${this._key} && (doc.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))) ||
- ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === "string" ? ".includes" : "<="}(${parsedValue}))))`;
+ const scriptText = `${this._contains ? '' : '!'}(((doc.${this._key} && (doc.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))) ||
+ ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))))`;
// let doc = new Doc();
// ((doc.data_ext && doc.data_ext!.text) && (doc.data_ext!.text as string).includes("hello"));
this.props.script(scriptText);
- }
- else {
- this.props.script("");
+ } else {
+ this.props.script('');
}
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)}>
- {this._contains ? "CONTAINS" : "DOES NOT CONTAIN"}
+ <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>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 13bb3a577..dd4c0b881 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.scss';
+@import '../global/globalCssVariables.module.scss';
.tabDocView-content {
height: 100%;
@@ -15,7 +15,6 @@ input.lm_title {
}
input.lm_title {
- transition-delay: 0.35s;
width: max-content;
cursor: pointer;
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 26aa5a121..9bc3ef822 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -2,38 +2,42 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, Type } from 'browndash-components';
import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, ObservableSet, reaction } from 'mobx';
+import { IReactionDisposer, ObservableSet, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
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 { Doc, Opt } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } 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 { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
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 { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoable, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoable } from '../../util/UndoManager';
import { DashboardView } from '../DashboardView';
-import { Colors } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
-import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere, OpenWhereMod } from '../nodes/DocumentView';
-import { DashFieldView } from '../nodes/formattedText/DashFieldView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
+import { Colors } from '../global/globalEnums';
+import { DocumentView, OpenWhere, OpenWhereMod, returnEmptyDocViewList } from '../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../nodes/FieldView';
import { KeyValueBox } from '../nodes/KeyValueBox';
+import { DashFieldView } from '../nodes/formattedText/DashFieldView';
import { PinProps, PresBox, PresMovement } from '../nodes/trails';
-import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionView } from './CollectionView';
import './TabDocView.scss';
-import React = require('react');
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
const _global = (window /* browser */ || global) /* node */ as any;
interface TabDocViewProps {
@@ -42,10 +46,16 @@ interface TabDocViewProps {
glContainer: any;
}
@observer
-export class TabDocView extends React.Component<TabDocViewProps> {
+export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
static _allTabs = new ObservableSet<TabDocView>();
_mainCont: HTMLDivElement | null = null;
_tabReaction: IReactionDisposer | undefined;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@observable _activated: boolean = false;
@observable _panelWidth = 0;
@observable _panelHeight = 0;
@@ -53,41 +63,23 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@observable _isActive: boolean = false;
@observable _isAnyChildContentActive = false;
@computed get _isUserActivated() {
- return SelectionManager.Views().some(view => view.rootDoc === this._document) || this._isAnyChildContentActive;
+ return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive;
}
- @computed get _isContentActive() {
+ get _isContentActive() {
return this._isUserActivated || this._hovering;
}
- @observable _document: Doc | undefined;
- @observable _view: DocumentView | undefined;
+ @observable _document: Doc | undefined = undefined;
+ @observable _view: DocumentView | undefined = undefined;
@computed get layoutDoc() {
return this._document && Doc.Layout(this._document);
}
- @computed get tabBorderColor() {
- const highlight = DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting);
- if (highlight?.highlightIndex === Doc.DocBrushStatus.highlighted) return highlight.highlightColor;
- return 'transparent';
- }
- @computed get tabColor() {
- return this._isUserActivated ? Colors.WHITE : this._hovering ? Colors.LIGHT_GRAY : Colors.MEDIUM_GRAY;
- }
- @computed get tabTextColor() {
- return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color)));
- }
- // @computed get renderBounds() {
- // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
- // const xbounds = bounds[2] - bounds[0];
- // const ybounds = bounds[3] - bounds[1];
- // const dim = Math.max(xbounds, ybounds);
- // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
- // }
get stack() {
- return (this.props as any).glContainer.parent.parent;
+ return this._props.glContainer.parent.parent;
}
get tab() {
- return (this.props as any).glContainer.tab;
+ return this._props.glContainer.tab;
}
get view() {
return this._view;
@@ -126,7 +118,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
titleEle.onchange = (e: any) => {
undoable(() => {
titleEle.size = e.currentTarget.value.length + 3;
- Doc.GetProto(doc).title = e.currentTarget.value;
+ doc[DocData].title = e.currentTarget.value;
}, 'edit tab title')();
};
@@ -163,24 +155,45 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.reactComponents = [iconWrap, closeWrap];
tab.element[0].prepend(iconWrap);
tab._disposers.color = reaction(
- () => ({ color: this.tabColor, borderColor: this.tabBorderColor }),
- coloring => {
- const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
+ () => ({ variant: SettingsManager.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);
titleEle.style.color = textColor;
- titleEle.style.backgroundColor = coloring.borderColor;
iconWrap.style.color = textColor;
closeWrap.style.color = textColor;
- tab.element[0].style.background = coloring.color;
+ tab.element[0].style.background =
+ color === variant
+ ? DashColor(color)
+ .fade(
+ this._isUserActivated
+ ? 0
+ : this._hovering
+ ? 0.25
+ : degree === Doc.DocBrushStatus.selfBrushed
+ ? 0.5
+ : degree === Doc.DocBrushStatus.protoBrushed //
+ ? 0.7
+ : 0.9
+ )
+ .rgb()
+ .toString()
+ : color;
},
{ fireImmediately: true }
);
}
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: MouseEvent) => {
- if (SnappingManager.GetIsDragging() && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
+ 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) => {
+ this._document && Doc.UnBrushDoc(this._document);
};
tab.element[0].oncontextmenu = (e: MouseEvent) => {
@@ -203,29 +216,25 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
});
tab._disposers.selectionDisposer = reaction(
- () => SelectionManager.Views().some(view => view.rootDoc === this._document),
+ () => SelectionManager.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.fontWeight = selected ? 'bold' : '';
// toggle.style.textTransform = selected ? "uppercase" : "";
}),
{ fireImmediately: true }
);
// highlight the tab when the tab document is brushed in any part of the UI
- // tab._disposers.reactionDisposer = reaction(
- // () => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }),
- // ({ title, degree }) => {
- // titleEle.value = title;
- // titleEle.style.padding = degree ? 0 : 2;
- // titleEle.style.border = `${['gray', 'gray', 'gray'][degree]} ${['none', 'dashed', 'solid'][degree]} 2px`;
- // },
- // { fireImmediately: true }
- // );
+ tab._disposers.reactionDisposer = reaction(
+ () => doc?.title,
+ title => (titleEle.value = title),
+ { fireImmediately: true }
+ );
// clean up the tab when it is closed
tab.closeElement
@@ -260,10 +269,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return;
}
const anchorDoc = DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.getAnchor?.(false, pinProps);
- const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Doc.MakeDelegate(anchorDoc && anchorDoc !== doc ? anchorDoc : doc);
- pinDoc.presentation_targetDoc = anchorDoc ?? doc;
+ const pinDoc = anchorDoc?.type === DocumentType.CONFIG ? anchorDoc : Docs.Create.ConfigDocument({});
+ const targDoc = (pinDoc.presentation_targetDoc = anchorDoc ?? doc);
pinDoc.title = doc.title + ' - Slide';
- pinDoc.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.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;
@@ -272,7 +281,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
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_ChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
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
@@ -316,7 +324,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
}
- @action
componentDidMount() {
new _global.ResizeObserver(
action((entries: any) => {
@@ -325,32 +332,36 @@ export class TabDocView extends React.Component<TabDocViewProps> {
this._panelHeight = entry.contentRect.height;
}
})
- ).observe(this.props.glContainer._element[0]);
- this.props.glContainer.layoutManager.on('activeContentItemChanged', this.onActiveContentItemChanged);
- this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined);
+ ).observe(this._props.glContainer._element[0]);
+ this._props.glContainer.layoutManager.on('activeContentItemChanged', this.onActiveContentItemChanged);
+ this._props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined);
// this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
// ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
// { fireImmediately: true });
- TabDocView._allTabs.add(this);
+ runInAction(() => TabDocView._allTabs.add(this));
}
- componentDidUpdate() {
+ componentDidUpdate(prevProps: Readonly<TabDocViewProps>) {
+ super.componentDidUpdate(prevProps);
this._view && DocumentManager.Instance.AddView(this._view);
}
- @action
componentWillUnmount() {
this._tabReaction?.();
this._view && DocumentManager.Instance.RemoveView(this._view);
- TabDocView._allTabs.delete(this);
+ runInAction(() => TabDocView._allTabs.delete(this));
- this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
+ this._props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
}
+ // Flag indicating that when a tab is activated, it should not select it's document.
+ // this is used by the link properties menu when it wants to display the link target without selecting the target (which would make the link property window go away since it would no longer be selected)
+ public static DontSelectOnActivate = 'dontSelectOnActivate';
+
@action.bound
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 !== 'dontSelectOnActivate') setTimeout(() => SelectionManager.SelectView(this._view, false));
+ if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => SelectionManager.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.
}
}
@@ -365,24 +376,25 @@ export class TabDocView extends React.Component<TabDocViewProps> {
addDocTab = (doc: Doc, location: OpenWhere) => {
SelectionManager.DeselectAll();
const whereFields = location.split(':');
- const keyValue = whereFields[1]?.includes('KeyValue');
- const whereMods: OpenWhereMod = whereFields.length > 1 ? (whereFields[1].replace('KeyValue', '') as OpenWhereMod) : OpenWhereMod.none;
+ 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);
// 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 data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)];
+ const data = lightboxView?.dataDoc[Doc.LayoutFieldKey(lightboxView.Document)];
if (lightboxView && (!data || data instanceof List)) {
- lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.rootDoc)] = new List<Doc>([doc]);
+ lightboxView.layoutDoc[Doc.LayoutFieldKey(lightboxView.Document)] = new List<Doc>([doc]);
return true;
}
}
- return LightboxView.AddDocTab(doc, location);
+ 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, undefined, keyValue);
- case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, "dontSelectOnActivate", keyValue);
+ 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);
}
};
@@ -404,7 +416,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return tab !== undefined;
};
@action
- focusFunc = (doc: Doc, options: DocFocusOptions) => {
+ focusFunc = (doc: Doc, options: FocusViewOptions) => {
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)
}
@@ -415,7 +427,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
ScreenToLocalTransform = () => {
this._forceInvalidateScreenToLocal;
const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
- return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY) ?? Transform.Identity();
+ return CollectionDockingView.Instance?.ScreenToLocalBoxXf().translate(-translateX, -translateY) ?? Transform.Identity();
};
PanelWidth = () => this._panelWidth;
PanelHeight = () => this._panelHeight;
@@ -424,7 +436,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
disableMinimap = () => !this._document;
whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
isContentActive = () => this._isContentActive;
- waitForDoubleClick = () => (DocumentView.ExploreMode ? 'never' : undefined);
+ waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined);
@computed get docView() {
return !this._activated || !this._document ? null : (
<>
@@ -436,11 +448,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
this._lastView = this._view;
})}
renderDepth={0}
- LayoutTemplateString={this.props.keyValue ? KeyValueBox.LayoutString() : undefined}
- hideTitle={this.props.keyValue}
+ LayoutTemplateString={this._props.keyValue ? KeyValueBox.LayoutString() : undefined}
+ hideTitle={this._props.keyValue}
Document={this._document}
- DataDoc={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
- onBrowseClick={DocumentView.exploreMode}
+ TemplateDataDocument={!Doc.AreProtosEqual(this._document[DocData], this._document) ? this._document[DocData] : undefined}
+ onBrowseClickScript={DocumentView.exploreMode}
waitForDoubleClickToClick={this.waitForDoubleClick}
isContentActive={this.isContentActive}
isDocumentActive={returnFalse}
@@ -455,11 +467,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
addDocTab={this.addDocTab}
ScreenToLocalTransform={this.ScreenToLocalTransform}
dontCenter={'y'}
- rootSelected={returnTrue}
whenChildContentsActiveChanged={this.whenChildContentActiveChanges}
focus={this.focusFunc}
- docViewPath={returnEmptyDoclist}
- bringToFront={emptyFunction}
+ containerViewPath={returnEmptyDoclist}
pinToPres={TabDocView.PinDoc}
/>
{this.disableMinimap() ? null : <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />}
@@ -485,7 +495,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
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)));
+ 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);
}
}}>
@@ -509,15 +519,15 @@ interface TabMiniThumbProps {
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 React.Component<TabMinimapViewProps> {
- static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+export class TabMinimapView extends ObservableReactComponent<TabMinimapViewProps> {
+ static miniStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string): any => {
if (doc) {
switch (property.split(':')[0]) {
default:
@@ -539,12 +549,13 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
default: return 'gray';
}
})(doc.type as DocumentType);
- return !background ? undefined : <div style={{ width: doc[Width](), height: doc[Height](), position: 'absolute', display: 'block', background }} />;
+ 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 compView = this._props.tabView()?.ComponentView as CollectionFreeFormView;
const bounds = compView?.freeformData?.(true)?.bounds;
if (!bounds) return undefined;
const xbounds = bounds.r - bounds.x;
@@ -552,10 +563,10 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
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 };
}
- childLayoutTemplate = () => Cast(this.props.document.childLayoutTemplate, Doc, null);
- returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150);
+ 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 doc = this._props.document;
const miniSize = this.returnMiniSize();
doc &&
setupMoveUpEvents(
@@ -574,27 +585,24 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
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 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() }}>
+ <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this._props.background() }}>
<CollectionFreeFormView
- Document={this.props.document}
- docViewPath={returnEmptyDoclist}
+ 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
- setHeight={returnFalse}
isContentActive={emptyFunction}
isAnyChildContentActive={returnFalse}
select={emptyFunction}
isSelected={returnFalse}
dontRegisterView={true}
- fieldKey={Doc.LayoutFieldKey(this.props.document)}
- bringToFront={emptyFunction}
- rootSelected={returnTrue}
+ fieldKey={Doc.LayoutFieldKey(this._props.document)}
addDocument={returnFalse}
moveDocument={returnFalse}
removeDocument={returnFalse}
@@ -605,7 +613,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
whenChildContentsActiveChanged={emptyFunction}
focus={emptyFunction}
styleProvider={TabMinimapView.miniStyleProvider}
- addDocTab={this.props.addDocTab}
+ addDocTab={this._props.addDocTab}
pinToPres={TabDocView.PinDoc}
childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
@@ -619,7 +627,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
);
};
render() {
- return this.props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this.props.document)) || this.props.document?._type_collection !== CollectionViewType.Freeform ? null : (
+ return this._props.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.scss b/src/client/views/collections/TreeView.scss
index cbcc7c710..0a1946f09 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables';
+@import '../global/globalCssVariables.module.scss';
.treeView-label {
max-height: 1.5em;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 193c70add..be5737a25 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,9 +1,12 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+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 { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
@@ -11,32 +14,29 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
-import { Docs, DocUtils } from '../../documents/Documents';
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 { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
+import { UndoManager, undoBatch, undoable } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
-import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
-import { DocumentView, DocumentViewInternal, DocumentViewProps, OpenWhere, StyleProviderFunc } from '../nodes/DocumentView';
-import { FieldViewProps } from '../nodes/FieldView';
+import { ObservableReactComponent } from '../ObservableReactComponent';
+import { StyleProp } from '../StyleProvider';
+import { DocumentView, DocumentViewInternal, OpenWhere } from '../nodes/DocumentView';
+import { FieldViewProps, StyleProviderFuncType } from '../nodes/FieldView';
+import { KeyValueBox } from '../nodes/KeyValueBox';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { StyleProp } from '../StyleProvider';
import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
import { CollectionView } from './CollectionView';
-import './TreeView.scss';
-import React = require('react');
-import { IconButton, Size } from 'browndash-components';
import { TreeSort } from './TreeSort';
-import { SettingsManager } from '../../util/SettingsManager';
+import './TreeView.scss';
+const { default: { TREE_BULLET_WIDTH } } = require('../global/globalCssVariables.module.scss'); // prettier-ignore
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -44,7 +44,7 @@ export interface TreeViewProps {
observeHeight: (ref: any) => void;
unobserveHeight: (ref: any) => void;
prevSibling?: Doc;
- document: Doc;
+ Document: Doc;
dataDoc?: Doc;
treeViewParent: Doc;
renderDepth: number;
@@ -62,7 +62,7 @@ export interface TreeViewProps {
ScreenToLocalTransform: () => Transform;
contextMenuItems?: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
dontRegisterView?: boolean;
- styleProvider?: StyleProviderFunc | undefined;
+ styleProvider?: StyleProviderFuncType | undefined;
treeViewHideHeaderFields: () => boolean;
renderedIds: string[]; // list of document ids rendered used to avoid unending expansion of items in a cycle
onCheckedClick?: () => ScriptField;
@@ -87,70 +87,74 @@ const treeBulletWidth = function () {
* treeView_ExpandedView : name of field whose contents are being displayed as the document's subtree
*/
@observer
-export class TreeView extends React.Component<TreeViewProps> {
+export class TreeView extends ObservableReactComponent<TreeViewProps> {
static _editTitleOnLoad: Opt<{ id: string; parent: TreeView | CollectionTreeView | undefined }>;
static _openTitleScript: Opt<ScriptField | undefined>;
static _openLevelScript: Opt<ScriptField | undefined>;
private _header: React.RefObject<HTMLDivElement> = React.createRef();
private _tref = React.createRef<HTMLDivElement>();
- @observable _docRef: Opt<DocumentView>;
+ @observable _docRef: Opt<DocumentView> = undefined;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _editTitleScript: (() => ScriptField) | undefined;
private _openScript: (() => ScriptField) | undefined;
private _treedropDisposer?: DragManager.DragDropDisposer;
get treeViewOpenIsTransient() {
- return this.props.treeView.doc.treeView_OpenIsTransient || Doc.IsDataProto(this.doc);
+ return this.treeView.Document.treeView_OpenIsTransient || Doc.IsDataProto(this.Document);
}
set treeViewOpen(c: boolean) {
if (this.treeViewOpenIsTransient) this._transientOpenState = c;
else {
- this.doc.treeView_Open = c;
+ this.Document.treeView_Open = c;
this._transientOpenState = false;
}
}
@observable _transientOpenState = false; // override of the treeView_Open field allowing the display state to be independent of the document's state
@observable _editTitle: boolean = false;
- @observable _dref: DocumentView | undefined | null;
+ @observable _dref: DocumentView | undefined | null = undefined;
get displayName() {
- return 'TreeView(' + this.props.document.title + ')';
+ return 'TreeView(' + this.Document.title + ')';
} // this makes mobx trace() statements more descriptive
get defaultExpandedView() {
- return this.doc._type_collection === CollectionViewType.Docking
+ return this.Document._type_collection === CollectionViewType.Docking
? this.fieldKey
- : this.props.treeView.dashboardMode
- ? this.fieldKey
- : this.props.treeView.fileSysMode
- ? this.doc.isFolder
- ? this.fieldKey
- : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
- : this.props.treeView.outlineMode || this.childDocs
- ? this.fieldKey
- : Doc.noviceMode
- ? 'layout'
- : StrCast(this.props.treeView.doc.treeView_ExpandedView, 'fields');
+ : this.treeView.dashboardMode
+ ? this.fieldKey
+ : this.treeView.fileSysMode
+ ? this.Document.isFolder
+ ? this.fieldKey
+ : 'data' // file system folders display their contents (data). used to be they displayed their embeddings but now its a tree structure and not a flat list
+ : this.treeView.outlineMode || this.childDocs
+ ? this.fieldKey
+ : Doc.noviceMode
+ ? 'layout'
+ : StrCast(this.treeView.Document.treeView_ExpandedView, 'fields');
}
- @computed get doc() {
- return this.props.document;
+ @computed get treeView() {
+ return this._props.treeView;
+ }
+
+ @computed get Document() {
+ return this._props.Document;
}
@computed get treeViewOpen() {
- return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
+ return (!this.treeViewOpenIsTransient && Doc.GetT(this.Document, 'treeView_Open', 'boolean', true)) || this._transientOpenState;
}
@computed get treeViewExpandedView() {
- return this.validExpandViewTypes.includes(StrCast(this.doc.treeView_ExpandedView)) ? StrCast(this.doc.treeView_ExpandedView) : this.defaultExpandedView;
+ return this.validExpandViewTypes.includes(StrCast(this.Document.treeView_ExpandedView)) ? StrCast(this.Document.treeView_ExpandedView) : this.defaultExpandedView;
}
@computed get MAX_EMBED_HEIGHT() {
- return NumCast(this.props.treeViewParent.maxEmbedHeight, 200);
+ return NumCast(this._props.treeViewParent.maxEmbedHeight, 200);
}
@computed get dataDoc() {
- return this.props.document.treeView_ChildrenOnRoot ? this.doc : this.doc[DocData];
+ return this.Document[DocData];
}
@computed get layoutDoc() {
- return Doc.Layout(this.doc);
+ return Doc.Layout(this.Document);
}
@computed get fieldKey() {
- return StrCast(this.doc._treeView_FieldKey, Doc.LayoutFieldKey(this.doc));
+ return StrCast(this.Document._treeView_FieldKey, Doc.LayoutFieldKey(this.Document));
}
@computed get childDocs() {
return this.childDocList(this.fieldKey);
@@ -165,34 +169,36 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.childDocList(this.fieldKey + '_annotations');
}
@computed get selected() {
- return SelectionManager.IsSelected(this._docRef);
+ return this._docRef?.IsSelected;
}
+ ScreenToLocalTransform = () => this._props.ScreenToLocalTransform();
+
childDocList(field: string) {
- const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
- return DocListCast(this.props.dataDoc?.[field], DocListCast(layout?.[field], DocListCast(this.doc[field])));
+ const layout = Cast(Doc.LayoutField(this.Document), Doc, null);
+ return DocListCast(this._props.dataDoc?.[field], DocListCast(layout?.[field], DocListCast(this.Document[field])));
}
moving: boolean = false;
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- if (this.doc !== target && addDoc !== returnFalse) {
- const canAdd1 = (this.props.parentTreeView as any).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this.props.parentTreeView?.doc.data)) instanceof ComputedField);
+ if (this.Document !== target && addDoc !== returnFalse) {
+ const canAdd1 = (this._props.parentTreeView as any).dropping || !(ComputedField.WithoutComputed(() => FieldValue(this._props.parentTreeView?.Document.data)) instanceof ComputedField);
// bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
- if (canAdd1 && this.props.removeDoc?.(doc) === true) {
- this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.moving = true);
+ if (canAdd1 && this._props.removeDoc?.(doc) === true) {
+ this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.moving = true);
const res = addDoc(doc);
- this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.moving = false);
+ this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.moving = false);
return res;
}
}
return false;
};
- @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
- this.props.treeView.props.select(false);
+ @undoBatch remove = (doc: Doc | Doc[], key: string) => {
+ this.treeView._props.select(false);
const ind = DocListCast(this.dataDoc[key]).indexOf(doc instanceof Doc ? doc : doc.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.props.treeView.props.DocumentView?.())?.select(false);
+ res && ind > 0 && DocumentManager.Instance.getDocumentView(DocListCast(this.dataDoc[key])[ind - 1], this.treeView.DocumentView?.())?.select(false);
return res;
};
@@ -200,10 +206,10 @@ export class TreeView extends React.Component<TreeViewProps> {
this._disposers.selection?.();
if (!docView) {
this._editTitle = false;
- } else if (docView.isSelected()) {
+ } else if (docView.IsSelected) {
this._editTitle = true;
this._disposers.selection = reaction(
- () => docView.isSelected(),
+ () => docView.IsSelected,
isSel => !isSel && this.setEditTitle(undefined)
);
} else {
@@ -212,17 +218,16 @@ export class TreeView extends React.Component<TreeViewProps> {
};
@action
openLevel = (docView: DocumentView) => {
- if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
+ if (this.Document.isFolder || Doc.IsSystem(this.Document)) {
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.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.rootDoc : Doc.BestEmbedding(docView.rootDoc);
- this.props.addDocTab(bestEmbedding, OpenWhere.lightbox);
+ const bestEmbedding = docView.Document.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.Document) ? docView.Document : Doc.BestEmbedding(docView.Document);
+ this._props.addDocTab(bestEmbedding, OpenWhere.lightbox);
}
};
@undoBatch
- @action
recurToggle = (childList: Doc[]) => {
if (childList.length > 0) {
childList.forEach(child => {
@@ -233,7 +238,6 @@ export class TreeView extends React.Component<TreeViewProps> {
};
@undoBatch
- @action
getRunningChildren = (childList: Doc[]) => {
if (childList.length === 0) {
return [];
@@ -253,23 +257,24 @@ export class TreeView extends React.Component<TreeViewProps> {
static GetRunningChildren = new Map<Doc, any>();
static ToggleChildrenRun = new Map<Doc, () => void>();
- constructor(props: any) {
+ constructor(props: TreeViewProps) {
super(props);
+ makeObservable(this);
if (!TreeView._openLevelScript) {
TreeView._openTitleScript = ScriptField.MakeScript('scriptContext.setEditTitle(documentView)', { scriptContext: 'any', documentView: 'any' });
TreeView._openLevelScript = ScriptField.MakeScript(`scriptContext.openLevel(documentView)`, { scriptContext: 'any', documentView: 'any' });
}
- this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!;
- this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!;
+ this._openScript = Doc.IsSystem(this.Document) ? undefined : () => TreeView._openLevelScript!;
+ this._editTitleScript = Doc.IsSystem(this.Document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!;
// set for child processing highligting
this.dataDoc.hasChildren = this.childDocs.length > 0;
// this.dataDoc.children = this.childDocs;
- TreeView.ToggleChildrenRun.set(this.doc, () => {
+ TreeView.ToggleChildrenRun.set(this.Document, () => {
this.recurToggle(this.childDocs);
});
- TreeView.GetRunningChildren.set(this.doc, () => {
+ TreeView.GetRunningChildren.set(this.Document, () => {
return this.getRunningChildren(this.childDocs);
});
}
@@ -277,31 +282,33 @@ export class TreeView extends React.Component<TreeViewProps> {
_treeEle: any;
protected createTreeDropTarget = (ele: HTMLDivElement) => {
this._treedropDisposer?.();
- ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this))), this.doc);
- if (this._treeEle) this.props.unobserveHeight(this._treeEle);
- this.props.observeHeight((this._treeEle = ele));
+ ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), this.Document, this.preTreeDrop.bind(this))), this.Document);
+ if (this._treeEle) this._props.unobserveHeight(this._treeEle);
+ this._props.observeHeight((this._treeEle = ele));
};
componentWillUnmount() {
+ this._treedropDisposer?.();
this._renderTimer && clearTimeout(this._renderTimer);
Object.values(this._disposers).forEach(disposer => disposer?.());
- this._treeEle && this.props.unobserveHeight(this._treeEle);
+ this._treeEle && this._props.unobserveHeight(this._treeEle);
document.removeEventListener('pointermove', this.onDragMove, true);
document.removeEventListener('pointermove', this.onDragUp, true);
// TODO: [AL] add these
- this.props.hierarchyIndex !== undefined && this.props.RemFromMap?.(this.doc, this.props.hierarchyIndex);
+ this._props.hierarchyIndex !== undefined && this._props.RemFromMap?.(this.Document, this._props.hierarchyIndex);
}
- componentDidUpdate() {
+ componentDidUpdate(prevProps: Readonly<TreeViewProps>) {
+ super.componentDidUpdate(prevProps);
this._disposers.opening = reaction(
() => this.treeViewOpen,
open => !open && (this._renderCount = 20)
);
- this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
+ this._props.hierarchyIndex !== undefined && this._props.AddToMap?.(this.Document, this._props.hierarchyIndex);
}
componentDidMount() {
- this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
+ this._props.hierarchyIndex !== undefined && this._props.AddToMap?.(this.Document, this._props.hierarchyIndex);
}
onDragUp = (e: PointerEvent) => {
@@ -309,8 +316,8 @@ export class TreeView extends React.Component<TreeViewProps> {
document.removeEventListener('pointermove', this.onDragMove, true);
};
onPointerEnter = (e: React.PointerEvent): void => {
- this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
- if (e.buttons === 1 && SnappingManager.GetIsDragging() && this.props.isContentActive()) {
+ this._props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
+ if (e.buttons === 1 && SnappingManager.IsDragging && this._props.isContentActive()) {
this._header.current!.className = 'treeView-header';
document.removeEventListener('pointermove', this.onDragMove, true);
document.removeEventListener('pointerup', this.onDragUp, true);
@@ -333,11 +340,9 @@ export class TreeView extends React.Component<TreeViewProps> {
const before = pt[1] < rect.top + rect.height / 2;
const inside = pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length);
this._header.current!.className = 'treeView-header';
- if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
- if (inside) this._header.current!.className += ' treeView-header-inside';
- else if (before) this._header.current!.className += ' treeView-header-above';
- else if (!before) this._header.current!.className += ' treeView-header-below';
- }
+ if (inside) this._header.current!.className += ' treeView-header-inside';
+ else if (before) this._header.current!.className += ' treeView-header-above';
+ else if (!before) this._header.current!.className += ' treeView-header-below';
e.stopPropagation();
};
@@ -361,8 +366,9 @@ export class TreeView extends React.Component<TreeViewProps> {
_width: 1000,
_height: 10,
});
- Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
- Doc.GetProto(bullet).data = new List<Doc>([]);
+ const bulletData = bullet[DocData];
+ bulletData.title = ComputedField.MakeFunction('this.text?.Text');
+ bulletData.data = new List<Doc>([]);
DocumentManager.Instance.AddViewRenderedCb(bullet, dv => dv.ComponentView?.setFocus?.());
return bullet;
@@ -371,18 +377,17 @@ export class TreeView extends React.Component<TreeViewProps> {
makeTextCollection = () => {
const bullet = TreeView.makeTextBullet();
TreeView._editTitleOnLoad = { id: bullet[Id], parent: this };
- return this.props.addDocument(bullet);
+ return this._props.addDocument(bullet);
};
makeFolder = () => {
const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _dragOnlyWithinContainer: true, isFolder: true });
- TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
- return this.props.addDocument(folder);
+ TreeView._editTitleOnLoad = { id: folder[Id], parent: this._props.parentTreeView };
+ return this._props.addDocument(folder);
};
- preTreeDrop = (e: Event, de: DragManager.DropEvent) => {
- const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
+ preTreeDrop = (e: Event, de: DragManager.DropEvent, docDropAction: dropActionType) => {
+ // fall through and let the CollectionTreeView handle this since treeView items have no special properties of their own
};
@undoBatch
@@ -391,17 +396,17 @@ export class TreeView extends React.Component<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.props.treeView.fileSysMode && !this.doc.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 ? true : false);
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
- const destDoc = this.doc;
+ const destDoc = this.Document;
DocUtils.MakeLink(sourceDoc, destDoc, { link_relationship: 'tree link' });
e.stopPropagation();
return true;
}
const docDragData = de.complete.docDragData;
if (docDragData && pt[0] < rect.left + rect.width) {
- if (docDragData.draggedDocuments[0] === this.doc) return true;
+ if (docDragData.draggedDocuments[0] === this.Document) return true;
const added = this.dropDocuments(
docDragData.droppedDocuments, //
before,
@@ -409,7 +414,8 @@ export class TreeView extends React.Component<TreeViewProps> {
docDragData.dropAction,
docDragData.removeDocument,
docDragData.moveDocument,
- docDragData.treeViewDoc === this.props.treeView.props.Document
+ docDragData.treeViewDoc === this.treeView.Document,
+ de.embedKey
);
e.stopPropagation();
!added && e.preventDefault();
@@ -419,43 +425,52 @@ export class TreeView extends React.Component<TreeViewProps> {
};
dropping: boolean = false;
- dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, removeDocument: DragManager.RemoveFunction | undefined, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
- const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, undefined, before);
+ dropDocuments(
+ droppedDocuments: Doc[],
+ before: boolean,
+ inside: number | boolean,
+ dropAction: dropActionType,
+ removeDocument: DragManager.RemoveFunction | undefined,
+ moveDocument: DragManager.MoveFunction | undefined,
+ forceAdd: boolean,
+ canEmbed?: boolean
+ ) {
+ const parentAddDoc = (doc: Doc | Doc[]) => this._props.addDocument(doc, undefined, undefined, before);
const localAdd = (doc: Doc | Doc[]) => {
const innerAdd = (doc: Doc) => {
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.doc.embedContainer));
+ 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);
};
const addDoc = inside ? localAdd : parentAddDoc;
- const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
- const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
- if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this.props.parentTreeView?.doc))) {
- this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
+ const canAdd = !StrCast((inside ? this.Document : this._props.treeViewParent)?.treeView_FreezeChildren).includes('add') || forceAdd;
+ if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this._props.parentTreeView?.Document))) {
+ const move = (!dropAction || canEmbed || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
+ this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = true);
const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
- this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
+ this._props.parentTreeView instanceof TreeView && (this._props.parentTreeView.dropping = false);
return res;
}
return false;
}
refTransform = (ref: HTMLDivElement | undefined | null) => {
- if (!ref) return this.props.ScreenToLocalTransform();
+ if (!ref) return this.ScreenToLocalTransform();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ref);
- const outerXf = Utils.GetScreenTransform(this.props.treeView.MainEle());
- const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
+ const outerXf = Utils.GetScreenTransform(this.treeView.MainEle());
+ const offset = this.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ return this.ScreenToLocalTransform().translate(offset[0], offset[1]);
};
docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
getTransform = () => this.refTransform(this._tref.current);
- embeddedPanelWidth = () => this.props.panelWidth() / (this.props.treeView.props.NativeDimScaling?.() || 1);
+ embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1);
embeddedPanelHeight = () => {
- const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+ const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.Document))(this.treeView._props.childLayoutTemplate?.()) || this.layoutDoc;
return Math.min(
- layoutDoc[Height](),
+ NumCast(layoutDoc._height),
this.MAX_EMBED_HEIGHT,
(() => {
const aspect = Doc.NativeAspect(layoutDoc);
@@ -463,24 +478,24 @@ export class TreeView extends React.Component<TreeViewProps> {
return layoutDoc._layout_fitWidth
? !Doc.NativeHeight(layoutDoc)
? NumCast(layoutDoc._height)
- : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height)))
- : (this.embeddedPanelWidth() * layoutDoc[Height]()) / layoutDoc[Width]();
+ : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this._props.treeViewParent._height)))
+ : (this.embeddedPanelWidth() * NumCast(layoutDoc._height)) / NumCast(layoutDoc._width);
})()
);
};
@computed get expandedField() {
const ids: { [key: string]: string } = {};
const rows: JSX.Element[] = [];
- const doc = this.doc;
+ const doc = this.Document;
doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
for (const key of Object.keys(ids).slice().sort()) {
- if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeView_Open') 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 expandedWidth = () => this.props.panelWidth() - leftOffset.width;
+ 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);
@@ -488,44 +503,44 @@ export class TreeView extends React.Component<TreeViewProps> {
const innerAdd = (doc: 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.doc.embedContainer));
+ 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);
};
contentElement = TreeView.GetChildElements(
contents instanceof Doc ? [contents] : DocListCast(contents),
- this.props.treeView,
+ this.treeView,
this,
doc,
undefined,
- this.props.treeViewParent,
- this.props.prevSibling,
+ this._props.treeViewParent,
+ this._props.prevSibling,
addDoc,
remDoc,
moveDoc,
- this.props.dragAction,
- this.props.addDocTab,
+ this._props.dragAction,
+ this._props.addDocTab,
this.titleStyleProvider,
- this.props.ScreenToLocalTransform,
- this.props.isContentActive,
+ this.ScreenToLocalTransform,
+ this._props.isContentActive,
expandedWidth,
- this.props.renderDepth,
- this.props.treeViewHideHeaderFields,
- [...this.props.renderedIds, doc[Id]],
- this.props.onCheckedClick,
- this.props.onChildClick,
- this.props.skipFields,
+ this._props.renderDepth,
+ this._props.treeViewHideHeaderFields,
+ [...this._props.renderedIds, doc[Id]],
+ this._props.onCheckedClick,
+ this._props.onChildClick,
+ this._props.skipFields,
false,
- this.props.whenChildContentsActiveChanged,
- this.props.dontRegisterView,
+ this._props.whenChildContentsActiveChanged,
+ this._props.dontRegisterView,
emptyFunction,
emptyFunction,
this.childContextMenuItems(),
// TODO: [AL] Add these
- this.props.AddToMap,
- this.props.RemFromMap,
- this.props.hierarchyIndex
+ this._props.AddToMap,
+ this._props.RemFromMap,
+ this._props.hierarchyIndex
);
} else {
contentElement = (
@@ -573,9 +588,9 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderContent() {
TraceMobx();
const expandKey = this.treeViewExpandedView;
- const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {};
+ const sortings = (this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {};
if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) {
- const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.WhenAdded);
+ const sorting = StrCast(this.Document.treeView_SortCriterion, TreeSort.WhenAdded);
const sortKeys = Object.keys(sortings);
const curSortIndex = Math.max(
0,
@@ -587,7 +602,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
// if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
// then the modification would be done here
- const ordering = StrCast(this.doc.treeView_SortCriterion);
+ const ordering = StrCast(this.Document.treeView_SortCriterion);
if (ordering === TreeSort.Zindex) {
const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
@@ -595,8 +610,8 @@ export class TreeView extends React.Component<TreeViewProps> {
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, true);
- !dataIsComputed && added && Doc.SetContainer(doc, this.doc);
+ const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false);
+ !dataIsComputed && added && Doc.SetContainer(doc, this.Document);
return added;
};
@@ -614,12 +629,12 @@ export class TreeView extends React.Component<TreeViewProps> {
}
return (
<div>
- {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
+ {!docs?.length || this._props.AddToMap /* hack to identify pres box trees */ ? null : (
<div className="treeView-sorting">
<IconButton
color={sortings[sorting]?.color}
size={Size.XSMALL}
- tooltip={`Sorted by : ${this.doc.treeView_SortCriterion}. click to cycle`}
+ tooltip={`Sorted by : ${this.Document.treeView_SortCriterion}. click to cycle`}
icon={sortings[sorting]?.icon}
onPointerDown={e => {
downX = e.clientX;
@@ -627,8 +642,8 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}}
onClick={undoable(e => {
- if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
- !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+ if (this._props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+ !this.treeView.outlineMode && (this.Document.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
e.stopPropagation();
}
}, 'sort order')}
@@ -638,7 +653,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<ul
style={{ cursor: 'inherit' }}
key={expandKey + 'more'}
- title={`Sorted by : ${this.doc.treeView_SortCriterion}. click to cycle`}
+ title={`Sorted by : ${this.Document.treeView_SortCriterion}. click to cycle`}
className="" //this.doc.treeView_HideTitle ? 'no-indent' : ''}
onPointerDown={e => {
downX = e.clientX;
@@ -646,8 +661,8 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}}
onClick={undoable(e => {
- if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
- !this.props.treeView.outlineMode && (this.doc.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+ if (this._props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+ !this.treeView.outlineMode && (this.Document.treeView_SortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
e.stopPropagation();
}
}, 'sort order')}>
@@ -655,37 +670,37 @@ export class TreeView extends React.Component<TreeViewProps> {
? null
: TreeView.GetChildElements(
docs,
- this.props.treeView,
+ this.treeView,
this,
this.layoutDoc,
this.dataDoc,
- this.props.treeViewParent,
- this.props.prevSibling,
+ this._props.treeViewParent,
+ this._props.prevSibling,
addDoc,
remDoc,
moveDoc,
- StrCast(this.doc.childDragAction, this.props.dragAction) as dropActionType,
- this.props.addDocTab,
+ StrCast(this.Document.childDragAction, this._props.dragAction) as dropActionType,
+ this._props.addDocTab,
this.titleStyleProvider,
- this.props.ScreenToLocalTransform,
- this.props.isContentActive,
- this.props.panelWidth,
- this.props.renderDepth,
- this.props.treeViewHideHeaderFields,
- [...this.props.renderedIds, this.doc[Id]],
- this.props.onCheckedClick,
- this.props.onChildClick,
- this.props.skipFields,
+ this.ScreenToLocalTransform,
+ this._props.isContentActive,
+ this._props.panelWidth,
+ this._props.renderDepth,
+ this._props.treeViewHideHeaderFields,
+ [...this._props.renderedIds, this.Document[Id]],
+ this._props.onCheckedClick,
+ this._props.onChildClick,
+ this._props.skipFields,
false,
- this.props.whenChildContentsActiveChanged,
- this.props.dontRegisterView,
+ this._props.whenChildContentsActiveChanged,
+ this._props.dontRegisterView,
emptyFunction,
emptyFunction,
this.childContextMenuItems(),
// TODO: [AL] add these
- this.props.AddToMap,
- this.props.RemFromMap,
- this.props.hierarchyIndex,
+ this._props.AddToMap,
+ this._props.RemFromMap,
+ this._props.hierarchyIndex,
this._renderCount
)}
</ul>
@@ -693,7 +708,7 @@ export class TreeView extends React.Component<TreeViewProps> {
);
} else if (this.treeViewExpandedView === 'fields') {
return (
- <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit' }}>
+ <ul key={this.Document[Id] + this.Document.title} style={{ cursor: 'inherit' }}>
<div>{this.expandedField}</div>
</ul>
);
@@ -705,13 +720,13 @@ export class TreeView extends React.Component<TreeViewProps> {
e.preventDefault();
e.stopPropagation();
}}>
- {this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}
+ {this.renderEmbeddedDocument(false, this.treeView._props.childDocumentsActive ?? returnFalse)}
</ul>
); // "layout"
}
get onCheckedClick() {
- return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick);
+ return this.Document.type === DocumentType.COL ? undefined : this._props.onCheckedClick?.() ?? ScriptCast(this.Document.onCheckedClick);
}
@action
@@ -719,10 +734,10 @@ export class TreeView extends React.Component<TreeViewProps> {
if (this.onCheckedClick) {
this.onCheckedClick?.script.run(
{
- this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
- heading: this.props.treeViewParent.title,
- checked: this.doc.treeView_Checked === 'check' ? 'x' : this.doc.treeView_Checked === 'x' ? 'remove' : 'check',
- containingTreeView: this.props.treeView.props.Document,
+ this: this.Document.isTemplateForField && this._props.dataDoc ? this._props.dataDoc : this.Document,
+ heading: this._props.treeViewParent.title,
+ checked: this.Document.treeView_Checked === 'check' ? 'x' : this.Document.treeView_Checked === 'x' ? 'remove' : 'check',
+ containingTreeView: this.treeView.Document,
},
console.log
);
@@ -734,28 +749,28 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderBullet() {
TraceMobx();
- const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
+ const iconType = this.treeView._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
const color = SettingsManager.userColor;
- const checked = this.onCheckedClick ? this.doc.treeView_Checked ?? 'unchecked' : undefined;
+ const checked = this.onCheckedClick ? this.Document.treeView_Checked ?? 'unchecked' : undefined;
return (
<div
- className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
+ className={`bullet${this.treeView.outlineMode ? '-outline' : ''}`}
key="bullet"
- title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : 'view fields'}
+ title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : `view ${this.Document.type} content`}
onClick={this.bulletClick}
style={
- this.props.treeView.outlineMode
+ this.treeView.outlineMode
? {
- opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity),
+ opacity: this.titleStyleProvider?.(this.Document, this.treeView._props, StyleProp.Opacity),
}
: {
- pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+ pointerEvents: this._props.isContentActive() ? 'all' : undefined,
opacity: checked === 'unchecked' || typeof iconType !== 'string' ? undefined : 0.4,
color: checked === 'unchecked' ? SettingsManager.userColor : 'inherit',
}
}>
- {this.props.treeView.outlineMode ? (
- !(this.doc.text as RichTextField)?.Text ? null : (
+ {this.treeView.outlineMode ? (
+ !(this.Document.text as RichTextField)?.Text ? null : (
<IconButton color={color} icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} size={Size.XSMALL} />
)
) : (
@@ -775,28 +790,28 @@ export class TreeView extends React.Component<TreeViewProps> {
}
@computed get validExpandViewTypes() {
- const annos = () => (DocListCast(this.doc[this.fieldKey + '_annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
- const links = () => (LinkManager.Links(this.doc).length && !this.props.treeView.dashboardMode ? 'links' : '');
- const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
- const embeddings = () => (this.props.treeView.dashboardMode ? '' : 'embeddings');
+ 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 data = () => (this.childDocs || this.treeView.dashboardMode ? this.fieldKey : '');
+ const embeddings = () => (this.treeView.dashboardMode ? '' : 'embeddings');
const fields = () => (Doc.noviceMode ? '' : 'fields');
- const layout = Doc.noviceMode || this.doc._type_collection === CollectionViewType.Docking ? [] : ['layout'];
- return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m);
+ const layout = Doc.noviceMode || this.Document._type_collection === CollectionViewType.Docking ? [] : ['layout'];
+ return [data(), ...layout, ...(this.treeView.fileSysMode ? [embeddings(), links(), annos()] : []), fields()].filter(m => m);
}
@action
expandNextviewType = () => {
- if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeView_ExpandedViewLock) {
+ if (this.treeViewOpen && !this.Document.isFolder && !this.treeView.outlineMode && !this.Document.treeView_ExpandedViewLock) {
const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.treeViewExpandedView)) + 1) % modes.length];
- this.doc.treeView_ExpandedView = next(this.validExpandViewTypes);
+ this.Document.treeView_ExpandedView = next(this.validExpandViewTypes);
}
this.treeViewOpen = true;
};
@observable headerEleWidth = 0;
@computed get titleButtons() {
- const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations);
+ const customHeaderButtons = this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.Decorations);
const color = SettingsManager.userColor;
- return this.props.treeViewHideHeaderFields() || this.doc.treeView_HideHeaderFields ? null : (
+ return this._props.treeViewHideHeaderFields() || this.Document.treeView_HideHeaderFields ? null : (
<>
{customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
<IconButton
@@ -808,7 +823,7 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}}
/>
- {Doc.noviceMode ? null : this.doc.treeView_ExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
+ {Doc.noviceMode ? null : this.Document.treeView_ExpandedViewLock || Doc.IsSystem(this.Document) ? null : (
<span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
{this.treeViewExpandedView}
</span>
@@ -825,54 +840,54 @@ export class TreeView extends React.Component<TreeViewProps> {
contextMenuItems = () => {
const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
const folderOp = this.childDocs?.length ? [makeFolder] : [];
- const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' };
- const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
- const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Reopen' };
+ const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(this), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' };
+ const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Focus or Open' };
+ const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!, icon: 'eye', label: 'Reopen' };
return [
- ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
- ...(this.doc.isFolder
+ ...(this._props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.Document })?.result)),
+ ...(this.Document.isFolder
? folderOp
- : Doc.IsSystem(this.doc)
- ? []
- : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
- ? [openEmbedding, makeFolder]
- : this.doc._type_collection === CollectionViewType.Docking
- ? []
- : this.props.treeView.rootDoc === Doc.MyRecentlyClosed
- ? [reopenDoc]
- : [openEmbedding, focusDoc]),
+ : Doc.IsSystem(this.Document)
+ ? []
+ : this.treeView.fileSysMode && this.Document === this.Document[DocData]
+ ? [openEmbedding, makeFolder]
+ : this.Document._type_collection === CollectionViewType.Docking
+ ? []
+ : this.treeView.Document === Doc.MyRecentlyClosed
+ ? [reopenDoc]
+ : [openEmbedding, focusDoc]),
];
};
childContextMenuItems = () => {
- const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
- const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
- const icons = StrListCast(this.doc.childContextMenuIcons);
- return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
+ const customScripts = Cast(this.Document.childContextMenuScripts, listSpec(ScriptField), []);
+ const customFilters = Cast(this.Document.childContextMenuFilters, listSpec(ScriptField), []);
+ const icons = StrListCast(this.Document.childContextMenuIcons);
+ return StrListCast(this.Document.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
- onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
+ onChildClick = () => this._props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(this)`)!);
- onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeView_ChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
+ onChildDoubleClick = () => ScriptCast(this.treeView.Document.treeView_ChildDoubleClick, !this.treeView.outlineMode ? this._openScript?.() : null);
- refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document, {});
+ refocus = () => this.treeView._props.focus(this.treeView.Document, {});
ignoreEvent = (e: any) => {
- if (this.props.isContentActive(true)) {
+ if (this._props.isContentActive(true)) {
e.stopPropagation();
e.preventDefault();
}
};
- titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
- if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
+ 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.props.treeView;
+ const treeView = this.treeView;
// prettier-ignore
switch (property.split(':')[0]) {
- case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
+ case StyleProp.Opacity: return this.treeView.outlineMode ? undefined : 1;
case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
- case StyleProp.Highlighting: if (this.props.treeView.outlineMode) return undefined;
+ case StyleProp.Highlighting: if (this.treeView.outlineMode) return undefined;
case StyleProp.BoxShadow: return undefined;
case StyleProp.DocContents:
- const highlightIndex = this.props.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.isBrushedHighlightedDegree(doc);
+ 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 : (
<div
@@ -881,33 +896,33 @@ export class TreeView extends React.Component<TreeViewProps> {
// just render a title for a tree view label (identified by treeViewDoc being set in 'props')
maxWidth: props?.PanelWidth() || undefined,
background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
- outline: SnappingManager.GetIsDragging() ? undefined: `solid ${highlightColor} ${highlightIndex}px`,
- paddingLeft: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
- paddingRight: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
- paddingTop: treeView.props.childYPadding,
- paddingBottom: treeView.props.childYPadding,
+ outline: SnappingManager.IsDragging ? undefined: `solid ${highlightColor} ${highlightIndex}px`,
+ paddingLeft: NumCast(treeView.Document.childXPadding, NumCast(treeView._props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+ paddingRight: NumCast(treeView.Document.childXPadding, NumCast(treeView._props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+ paddingTop: treeView._props.childYPadding,
+ paddingBottom: treeView._props.childYPadding,
}}>
{StrCast(doc?.title)}
</div>
);
}
- return treeView.props.styleProvider?.(doc, props, property);
+ return treeView._props.styleProvider?.(doc, props, property);
};
- embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
+ embeddedStyleProvider = (doc: Doc | undefined, props: Opt<FieldViewProps>, property: string): any => {
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
+ 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) => {
- if (this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
+ if (this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode) {
switch (e.key) {
case 'Tab':
e.stopPropagation?.();
e.preventDefault?.();
setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
- UndoManager.RunInBatch(() => (e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true)), 'tab');
+ UndoManager.RunInBatch(() => (e.shiftKey ? this._props.outdentDocument?.(true) : this._props.indentDocument?.(true)), 'tab');
return true;
case 'Backspace':
- if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) {
+ if (!(this.Document.text as RichTextField)?.Text && this._props.removeDoc?.(this.Document)) {
e.stopPropagation?.();
e.preventDefault?.();
return true;
@@ -921,7 +936,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
return false;
};
- titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth())) / (this.props.treeView.props.NativeDimScaling?.() || 1) - this.headerEleWidth - treeBulletWidth();
+ titleWidth = () => Math.max(20, Math.min(this.treeView.truncateTitleWidth(), this._props.panelWidth())) / (this.treeView._props.NativeDimScaling?.() || 1) - this.headerEleWidth - treeBulletWidth();
/**
* Renders the EditableView title element for placement into the tree.
@@ -936,21 +951,21 @@ export class TreeView extends React.Component<TreeViewProps> {
display={'inline-block'}
editing={this._editTitle}
background={'#7089bb'}
- contents={StrCast(this.doc.title)}
+ contents={StrCast(this.Document.title)}
height={12}
sizeToContent={true}
fontSize={12}
isEditingCallback={action(e => (this._editTitle = e))}
- GetValue={() => StrCast(this.doc.title)}
+ GetValue={() => StrCast(this.Document.title)}
OnTab={undoBatch((shift?: boolean) => {
- if (!shift) this.props.indentDocument?.(true);
- else this.props.outdentDocument?.(true);
+ if (!shift) this._props.indentDocument?.(true);
+ else this._props.outdentDocument?.(true);
})}
- OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))}
- OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()}
+ OnEmpty={undoBatch(() => this.treeView.outlineMode && this._props.removeDoc?.(this.Document))}
+ OnFillDown={val => this.treeView.fileSysMode && this.makeFolder()}
SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
- Doc.SetInPlace(this.doc, 'title', value, false);
- this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
+ Doc.SetInPlace(this.Document, 'title', value, false);
+ this.treeView.outlineMode && enterKey && this.makeTextCollection();
})}
/>
) : (
@@ -958,49 +973,44 @@ export class TreeView extends React.Component<TreeViewProps> {
key="title"
ref={action((r: any) => {
this._docRef = r ? r : undefined;
- if (this._docRef && TreeView._editTitleOnLoad?.id === this.props.document[Id] && TreeView._editTitleOnLoad.parent === this.props.parentTreeView) {
+ if (this._docRef && TreeView._editTitleOnLoad?.id === this.Document[Id] && TreeView._editTitleOnLoad.parent === this._props.parentTreeView) {
this._docRef.select(false);
this.setEditTitle(this._docRef);
TreeView._editTitleOnLoad = undefined;
}
})}
- Document={this.doc}
- DataDoc={undefined} // or this.dataDoc?
+ Document={this.Document}
layout_fitWidth={returnTrue}
scriptContext={this}
- hideDecorationTitle={this.props.treeView.outlineMode}
- hideResizeHandles={this.props.treeView.outlineMode}
+ hideDecorations={true}
+ hideClickBehaviors={true}
styleProvider={this.titleStyleProvider}
onClickScriptDisable="never" // tree docViews have a script to show fields, etc.
- docViewPath={this.props.treeView.props.docViewPath}
- treeViewDoc={this.props.treeView.props.Document}
+ containerViewPath={this.treeView.childContainerViewPath}
addDocument={undefined}
- addDocTab={this.props.addDocTab}
- rootSelected={returnTrue}
- pinToPres={emptyFunction}
- onClick={this.onChildClick}
- onDoubleClick={this.onChildDoubleClick}
- dragAction={this.props.dragAction}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this.treeView._props.pinToPres}
+ onClickScript={this.onChildClick}
+ onDoubleClickScript={this.onChildDoubleClick}
+ dragAction={this._props.dragAction}
moveDocument={this.move}
- removeDocument={this.props.removeDoc}
+ removeDocument={this._props.removeDoc}
ScreenToLocalTransform={this.getTransform}
- NativeHeight={return18}
+ NativeHeight={returnZero}
NativeWidth={returnZero}
- shouldNotScale={returnTrue}
PanelWidth={this.titleWidth}
PanelHeight={return18}
contextMenuItems={this.contextMenuItems}
renderDepth={1}
- isContentActive={emptyFunction} //this.props.isContentActive}
- isDocumentActive={this.props.isContentActive}
+ isContentActive={emptyFunction} //this._props.isContentActive}
+ isDocumentActive={this._props.isContentActive}
focus={this.refocus}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- bringToFront={emptyFunction}
- disableBrushing={this.props.treeView.props.disableBrushing}
- hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
- dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
- xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)}
- yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ disableBrushing={this.treeView._props.disableBrushing}
+ hideLinkButton={BoolCast(this.treeView.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.treeView.Document.childDontRegisterViews, this._props.dontRegisterView)}
+ xPadding={NumCast(this.treeView.Document.childXPadding, this.treeView._props.childXPadding)}
+ yPadding={NumCast(this.treeView.Document.childYPadding, this.treeView._props.childYPadding)}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
@@ -1009,16 +1019,16 @@ export class TreeView extends React.Component<TreeViewProps> {
return (
<>
<div
- className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? '-system' : ''}`}
+ className={`docContainer${Doc.IsSystem(this.Document) || this.Document.isFolder ? '-system' : ''}`}
ref={this._tref}
title="click to edit title. Double Click or Drag to Open"
style={{
- backgroundColor: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? SettingsManager.userVariantColor : undefined,
- color: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? lightOrDark(SettingsManager.userVariantColor) : undefined,
- fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
- textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
- outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
- pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
+ backgroundColor: Doc.IsSystem(this.Document) || this.Document.isFolder ? SettingsManager.userVariantColor : undefined,
+ color: Doc.IsSystem(this.Document) || this.Document.isFolder ? lightOrDark(SettingsManager.userVariantColor) : undefined,
+ fontWeight: Doc.IsSearchMatch(this.Document) !== undefined ? 'bold' : undefined,
+ textDecoration: Doc.GetT(this.Document, 'title', 'string', true) ? 'underline' : undefined,
+ outline: this.Document === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
+ pointerEvents: !this._props.isContentActive() ? 'none' : undefined,
}}>
{view}
</div>
@@ -1037,7 +1047,19 @@ export class TreeView extends React.Component<TreeViewProps> {
key="titleheader"
ref={this._header}
onClick={this.ignoreEvent}
- onPointerDown={this.ignoreEvent}
+ onPointerDown={e => {
+ this.treeView.isContentActive() &&
+ setupMoveUpEvents(
+ this,
+ e,
+ () => {
+ this._dref?.startDragging(e.clientX, e.clientY, '' as any);
+ return true;
+ },
+ returnFalse,
+ emptyFunction
+ );
+ }}
onPointerEnter={this.onPointerEnter}
onPointerLeave={this.onPointerLeave}>
<div
@@ -1058,45 +1080,41 @@ export class TreeView extends React.Component<TreeViewProps> {
return (
<div style={{ height: this.embeddedPanelHeight(), width: this.embeddedPanelWidth() }}>
<DocumentView
- key={this.doc[Id]}
+ key={this.Document[Id]}
ref={action((r: DocumentView | null) => (this._dref = r))}
- Document={this.doc}
- DataDoc={undefined}
+ Document={this.Document}
layout_fitWidth={this.fitWidthFilter}
PanelWidth={this.embeddedPanelWidth}
PanelHeight={this.embeddedPanelHeight}
LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
- LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
+ LayoutTemplate={this.treeView._props.childLayoutTemplate}
isContentActive={isActive}
isDocumentActive={isActive}
styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ fitContentsToBox={returnTrue}
hideTitle={asText}
- //fitContentsToBox={returnTrue}
- hideDecorationTitle={this.props.treeView.outlineMode}
- hideResizeHandles={this.props.treeView.outlineMode}
- onClick={this.onChildClick}
- focus={this.refocus}
- onKey={this.onKeyDown}
- hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
- dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
+ 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}
- treeViewDoc={this.props.treeView?.props.Document}
- rootSelected={returnTrue}
- docViewPath={this.props.treeView.props.docViewPath}
+ renderDepth={this._props.renderDepth + 1}
+ onClickScript={this.onChildClick}
+ onKey={this.onKeyDown}
+ containerViewPath={this.treeView.childContainerViewPath}
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
- addDocument={this.props.addDocument}
+ focus={this.refocus}
+ addDocument={this._props.addDocument}
moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)}
- yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.treeView.props.pinToPres}
- disableBrushing={this.props.treeView.props.disableBrushing}
- bringToFront={returnFalse}
+ 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>
@@ -1105,7 +1123,7 @@ export class TreeView extends React.Component<TreeViewProps> {
// 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.props.treeView.Document.treeView_HideUnrendered && this.doc.layout_unrendered && !this.doc.treeView_FieldKey ? (
+ return this.treeView.Document.treeView_HideUnrendered && this.Document.layout_unrendered && !this.Document.treeView_FieldKey ? (
<div></div>
) : (
<>
@@ -1120,16 +1138,16 @@ export class TreeView extends React.Component<TreeViewProps> {
return (
<>
{this.renderBullet}
- {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
+ {this.renderEmbeddedDocument(asText, this._props.isContentActive)}
</>
);
};
@computed get renderBorder() {
- const sorting = StrCast(this.doc.treeView_SortCriterion, TreeSort.WhenAdded);
- const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } };
+ const sorting = StrCast(this.Document.treeView_SortCriterion, TreeSort.WhenAdded);
+ const sortings = (this._props.styleProvider?.(this.Document, this.treeView._props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } };
return (
- <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
+ <div className={`treeView-border${this.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
{!this.treeViewOpen ? null : this.renderContent}
</div>
);
@@ -1139,28 +1157,28 @@ export class TreeView extends React.Component<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.props.treeView.fileSysMode && !this.doc.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 ? true : false);
- const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false));
+ const docs = this.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false, false));
};
render() {
TraceMobx();
- const hideTitle = this.doc.treeView_HideHeader || (this.doc.treeView_HideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
- return this.props.renderedIds?.indexOf(this.doc[Id]) !== -1 ? (
- '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
+ const hideTitle = this.Document.treeView_HideHeader || (this.Document.treeView_HideHeaderIfTemplate && this.treeView._props.childLayoutTemplate?.()) || this.treeView.outlineMode;
+ return this._props.renderedIds?.indexOf(this.Document[Id]) !== -1 ? (
+ '<' + this.Document.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
) : (
<div
- className={`treeView-container${this.props.isContentActive() ? '-active' : ''}`}
+ 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) && SelectionManager.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">
- {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeView_RenderAsBulletHeader // should test for prop 'treeView_RenderDocWithBulletAsHeader"
+ {hideTitle && this.Document.type !== DocumentType.RTF && !this.Document.treeView_RenderAsBulletHeader // should test for prop 'treeView_RenderDocWithBulletAsHeader"
? this.renderEmbeddedDocument(false, returnFalse)
- : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeView_RenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+ : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.Document.treeView_RenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
</li>
</div>
);
@@ -1208,7 +1226,7 @@ export class TreeView extends React.Component<TreeViewProps> {
move: DragManager.MoveFunction,
dragAction: dropActionType,
addDocTab: (doc: Doc, where: OpenWhere) => boolean,
- styleProvider: undefined | StyleProviderFunc,
+ styleProvider: undefined | StyleProviderFuncType,
screenToLocalXf: () => Transform,
isContentActive: (outsideReaction?: boolean) => boolean,
panelWidth: () => number,
@@ -1236,7 +1254,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const docs = TreeView.sortDocs(childDocs, StrCast(treeView_Parent.treeView_SortCriterion, TreeSort.WhenAdded));
- const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1);
+ const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView._props.NativeDimScaling?.() || 1);
const treeView_Refs = new Map<Doc, TreeView | undefined>();
return docs
.filter(child => child instanceof Doc)
@@ -1248,11 +1266,11 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
- if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
+ if (parent instanceof TreeView && parent._props.treeView.fileSysMode && !newParent.isFolder) return;
const fieldKey = Doc.LayoutFieldKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
- FormattedTextBox.SelectOnLoad = child[Id];
+ FormattedTextBox.SetSelectOnLoad(child);
TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
newParent.treeView_Open = true;
@@ -1260,18 +1278,18 @@ export class TreeView extends React.Component<TreeViewProps> {
}
};
const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeView_Refs.get(docs[i - 1]));
- const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
+ const outdent = !parentCollectionDoc ? undefined : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView._props.parentTreeView : undefined);
const addDocument = (doc: Doc | Doc[], annotationKey?: string, relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
const childLayout = Doc.Layout(pair.layout);
const rowHeight = () => {
const aspect = Doc.NativeAspect(childLayout);
- return aspect ? Math.min(childLayout[Width](), rowWidth()) / aspect : childLayout[Height]();
+ return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height);
};
return (
<TreeView
key={child[Id]}
ref={r => treeView_Refs.set(child, r ? r : undefined)}
- document={pair.layout}
+ Document={pair.layout}
dataDoc={pair.data}
treeViewParent={treeView_Parent}
prevSibling={docs[i]}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
new file mode 100644
index 000000000..08dfb32ad
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -0,0 +1,75 @@
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast } from '../../../../fields/Types';
+import './CollectionFreeFormView.scss';
+
+export interface CollectionFreeFormViewBackgroundGridProps {
+ panX: () => number;
+ panY: () => number;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ color: () => string;
+ isAnnotationOverlay?: boolean;
+ nativeDimScaling: () => number;
+ zoomScaling: () => number;
+ layoutDoc: Doc;
+ cachedCenteringShiftX: number;
+ cachedCenteringShiftY: number;
+}
+@observer
+export class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
+ chooseGridSpace = (gridSpace: number): number => {
+ if (!this.props.zoomScaling()) return gridSpace;
+ const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace;
+ return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2);
+ };
+ render() {
+ const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
+ const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling();
+ const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling();
+ const renderGridSpace = gridSpace * this.props.zoomScaling();
+ const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace;
+ const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace;
+ const strokeStyle = this.props.color();
+ return !this.props.nativeDimScaling() ? null : (
+ <canvas
+ className="collectionFreeFormView-grid"
+ width={w}
+ height={h}
+ style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={el => {
+ const ctx = el?.getContext('2d');
+ if (ctx) {
+ const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
+ const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
+ ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
+ ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+ ctx.clearRect(0, 0, w, h);
+ if (ctx) {
+ ctx.strokeStyle = strokeStyle;
+ ctx.fillStyle = strokeStyle;
+ ctx.beginPath();
+ if (this.props.zoomScaling() > 1) {
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+ ctx.moveTo(x, Cy - h);
+ ctx.lineTo(x, Cy + h);
+ }
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.moveTo(Cx - w, y);
+ ctx.lineTo(Cx + w, y);
+ }
+ } else {
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace)
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
+ }
+ }
+ ctx.stroke();
+ }
+ }
+ }}
+ />
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
new file mode 100644
index 000000000..58f6b1593
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -0,0 +1,114 @@
+import { IconButton, Size, Type } from 'browndash-components';
+import { IReactionDisposer, action, makeObservable, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import './CollectionFreeFormView.scss';
+// import assets from './assets/link.png';
+
+/**
+ * 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
+ */
+export type infoArc = [() => any, (res?: any) => infoState];
+
+export const StateMessage = Symbol('StateMessage');
+export const StateEntryFunc = Symbol('StateEntryFunc');
+export const StateMessageGIF = Symbol('StateMessageGIF');
+export class infoState {
+ [StateMessage]: string = '';
+ [StateEntryFunc]?: () => any;
+ [StateMessageGIF]?: string = '';
+ [key: string]: infoArc;
+ constructor(message: string, arcs: { [key: string]: infoArc }, entryFunc?: () => any, messageGif?: string) {
+ this[StateMessage] = message;
+ Object.assign(this, arcs);
+ this[StateEntryFunc] = entryFunc;
+ this[StateMessageGIF] = messageGif;
+ }
+}
+
+/**
+ * Create an FSA state.
+ * @param msg the message displayed when in this state
+ * @param arcs an object with fields containing @infoArcs (an object with field names indicating the arc transition and
+ * field values being a tuple of an arc transition trigger function (that returns a truthy value when the arc should fire),
+ * and an arc transition action function (that sets the next state)
+ * @param entryFunc a function to call when entering the state
+ * @returns an FSA state
+ */
+export function InfoState(
+ msg: string, //
+ arcs: { [key: string]: infoArc },
+ entryFunc?: () => any,
+ gif?: string
+) {
+ return new infoState(msg, arcs, entryFunc, gif);
+}
+
+export interface CollectionFreeFormInfoStateProps {
+ infoState: infoState;
+ next: (state: infoState) => any;
+ close: () => void;
+}
+
+@observer
+export class CollectionFreeFormInfoState extends ObservableReactComponent<CollectionFreeFormInfoStateProps> {
+ _disposers: IReactionDisposer[] = [];
+ @observable _hide = false;
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ get State() {
+ return this._props.infoState;
+ }
+ get Arcs() {
+ return Object.keys(this.State ?? []).map(key => this.State?.[key]);
+ }
+
+ clearState = () => this._disposers.map(disposer => disposer());
+ initState = () =>
+ (this._disposers = this.Arcs.map(arc => ({ test: arc[0], act: arc[1] })).map(arc => {
+ return reaction(
+ //
+ arc.test,
+ res => {
+ if (res) {
+ const next = arc.act(res);
+ this._props.next(next);
+ }
+ },
+ { fireImmediately: true }
+ );
+ }));
+
+ componentDidMount(): void {
+ this.initState();
+ }
+ componentDidUpdate(prevProps: Readonly<CollectionFreeFormInfoStateProps>) {
+ super.componentDidUpdate(prevProps);
+ this.clearState();
+ this.initState();
+ }
+ componentWillUnmount(): void {
+ this.clearState();
+ }
+ render() {
+ return (
+ <div className="collectionFreeform-infoUI" style={{ display: this._hide ? 'none' : undefined }}>
+ <div className="msg">{this.State?.[StateMessage]}</div>
+ <div className="gif-container" style={{ display: this.State?.[StateMessageGIF] ? undefined : 'none' }}>
+ <img className="gif" src={this.State?.[StateMessageGIF]} alt="state message gif"></img>
+ </div>
+ <div style={{ position: 'absolute', top: -10, left: -10 }}>
+ <IconButton icon="x" color={SettingsManager.userColor} size={Size.XSMALL} type={Type.TERT} background={SettingsManager.userBackgroundColor} onClick={action(e => this.props.close())} />
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
new file mode 100644
index 000000000..8628ca3c3
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoUI.tsx
@@ -0,0 +1,255 @@
+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 { 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 { CollectionFreeFormInfoState, InfoState, StateEntryFunc, infoState } from './CollectionFreeFormInfoState';
+import { CollectionFreeFormView } from './CollectionFreeFormView';
+import './CollectionFreeFormView.scss';
+
+export interface CollectionFreeFormInfoUIProps {
+ Document: Doc;
+ Freeform: CollectionFreeFormView;
+ close: () => boolean;
+}
+
+@observer
+export class CollectionFreeFormInfoUI extends ObservableReactComponent<CollectionFreeFormInfoUIProps> {
+ _firstDocPos = { x: 0, y: 0 };
+
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ this.currState = this.setupStates();
+ }
+ _originalbackground: string | undefined;
+
+ @observable _currState: infoState | undefined = undefined;
+ get currState() { return this._currState!; } // prettier-ignore
+ set currState(val) { runInAction(() => (this._currState = val)); } // prettier-ignore
+
+ componentWillUnmount(): void {
+ this._props.Freeform.layoutDoc.backgroundColor = this._originalbackground;
+ }
+
+ setCurrState = (state: infoState) => {
+ if (state) {
+ this.currState = state;
+ this.currState[StateEntryFunc]?.();
+ }
+ };
+
+ setupStates = () => {
+ this._originalbackground = StrCast(this._props.Freeform.layoutDoc.backgroundColor);
+ // state entry functions
+ const setBackground = (colour: string) => () => (this._props.Freeform.layoutDoc.backgroundColor = colour);
+ const setOpacity = (opacity: number) => () => (this._props.Freeform.layoutDoc.opacity = opacity);
+ // arc transition trigger conditions
+ const firstDoc = () => (this._props.Freeform.childDocs.length ? this._props.Freeform.childDocs[0] : undefined);
+ const numDocs = () => this._props.Freeform.childDocs.length;
+
+ let docX: FieldResult<Field>;
+ let docY: FieldResult<Field>;
+
+ const docNewX = () => firstDoc()?.x;
+ const docNewY = () => firstDoc()?.y;
+
+ const linkStart = () => DocumentLinksButton.StartLink;
+
+ const numDocLinks = () => LinkManager.Instance.getAllDirectLinks(firstDoc())?.length;
+ const linkMenuOpen = () => DocButtonState.Instance.LinkEditorDocView;
+
+ const activeTool = () => Doc.ActiveTool;
+
+ const pin = () => DocListCast(Doc.ActivePresentation?.data);
+
+ let trail: number;
+
+ const trailView = () => DocumentManager.Instance.DocumentViews.find(view => view.Document === Doc.MyTrails);
+ const presentationMode = () => Doc.ActivePresentation?.presentation_status;
+
+ // set of states
+ const start = InfoState('Click anywhere and begin typing to create your first text document.', {
+ docCreated: [() => numDocs(), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return oneDoc;
+ }],
+ }, setBackground("blue")); // prettier-ignore
+
+ const oneDoc = InfoState('Hello world! You can drag and drop to move your document around.', {
+ // docCreated: [() => numDocs() > 1, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc1;
+ }],
+ }, setBackground("red")); // prettier-ignore
+
+ const movedDoc1 = InfoState('Great moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc2;
+ }],
+ }, setBackground("yellow")); // prettier-ignore
+
+ const movedDoc2 = InfoState('Slick moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc3;
+ }],
+ }, setBackground("pink")); // prettier-ignore
+
+ const movedDoc3 = InfoState('Groovy moves. Try creating a second document.', {
+ docCreated: [() => numDocs() == 2, () => multipleDocs],
+ docDeleted: [() => numDocs() < 1, () => start],
+ docMoved: [() => (docX && docX != docNewX()) || (docY && docY != docNewY()), () => {
+ docX = firstDoc()?.x;
+ docY = firstDoc()?.y;
+ return movedDoc1;
+ }],
+ }, setBackground("green")); // prettier-ignore
+
+ const multipleDocs = InfoState('Let\'s create a new link. Click the link icon on one of your documents.', {
+ linkStarted: [() => linkStart(), () => startedLink],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ }, setBackground("purple")); // prettier-ignore
+
+ const startedLink = InfoState('Now click the highlighted link icon on your other document.', {
+ linkCreated: [() => numDocLinks(), () => madeLink],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ }, setBackground("orange")); // prettier-ignore
+
+ const madeLink = InfoState('You made your first link! You can view your links by selecting the blue dot.', {
+ linkCreated: [() => !numDocLinks(), () => multipleDocs],
+ linkViewed: [() => linkMenuOpen(), () => {
+ alert(numDocLinks() + " cheer for " + numDocLinks() + " link!");
+ return viewedLink;
+ }],
+ }, setBackground("blue")); // prettier-ignore
+
+ const viewedLink = InfoState('Great work. You are now ready to create your own hypermedia world.', {
+ linkDeleted: [() => !numDocLinks(), () => multipleDocs],
+ docRemoved: [() => numDocs() < 2, () => oneDoc],
+ docCreated: [() => numDocs() == 3, () => {
+ trail = pin().length;
+ return presentDocs;
+ }],
+ activePen: [() => activeTool() === InkTool.Pen, () => penMode],
+ }, setBackground("black")); // prettier-ignore
+
+ const presentDocs = InfoState(
+ 'Another document! You could make a presentation. Click the pin icon in the top left corner.',
+ {
+ docPinned: [
+ () => pin().length > trail,
+ () => {
+ trail = pin().length;
+ return pinnedDoc1;
+ },
+ ],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ },
+ setBackground('black'),
+ '/assets/dash-pin-with-view.gif'
+ );
+
+ const penMode = InfoState('You\'re in pen mode. Click and drag to draw your first masterpiece.', {
+ // activePen: [() => activeTool() === InkTool.Eraser, () => eraserMode],
+ activePen: [() => activeTool() !== InkTool.Pen, () => viewedLink],
+ }); // prettier-ignore
+
+ // const eraserMode = InfoState('You\'re in eraser mode. Say goodbye to your first masterpiece.', {
+ // docsRemoved: [() => numDocs() == 3, () => demos],
+ // }); // prettier-ignore
+
+ const pinnedDoc1 = InfoState('You just pinned your doc.', {
+ docPinned: [
+ () => pin().length > trail,
+ () => {
+ trail = pin().length;
+ return pinnedDoc2;
+ },
+ ],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ const pinnedDoc2 = InfoState(`You pinned another doc.`, {
+ docPinned: [
+ () => pin().length > trail,
+ () => {
+ trail = pin().length;
+ return pinnedDoc3;
+ },
+ ],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ const pinnedDoc3 = InfoState(`You pinned yet another doc.`, {
+ docPinned: [
+ () => pin().length > trail,
+ () => {
+ trail = pin().length;
+ return pinnedDoc2;
+ },
+ ],
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ });
+
+ // const openedTrail = InfoState('This is your trails tab.', {
+ // trailView: [() => presentationMode() === 'edit', () => editPresentationMode],
+ // });
+
+ // const editPresentationMode = InfoState('You are editing your presentation.', {
+ // manualPresentation: [() => presentationMode() === 'manual', () => manualPresentationMode],
+ // autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ // docRemoved: [() => numDocs() < 3, () => demos],
+ // docCreated: [() => numDocs() == 4, () => completed],
+ // });
+
+ const manualPresentationMode = InfoState("You're in manual presentation mode.", {
+ // editPresentation: [() => presentationMode() === 'edit', () => editPresentationMode],
+ autoPresentation: [() => presentationMode() === 'auto', () => autoPresentationMode],
+ docRemoved: [() => numDocs() < 3, () => viewedLink],
+ 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],
+ });
+
+ const completed = InfoState('Eager to learn more? Click the ? icon in the top right corner to read our full documentation.', {
+ docRemoved: [() => numDocs() == 1, () => oneDoc],
+ }, setBackground("white")); // prettier-ignore
+
+ return start;
+ };
+
+ render() {
+ return <CollectionFreeFormInfoState next={this.setCurrState} close={this._props.close} infoState={this.currState} />;
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d93e44ab7..b8c0967c1 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,12 +1,11 @@
import { Doc, Field, FieldResult } from '../../../../fields/Doc';
-import { Height, Width } from '../../../../fields/DocSymbols';
import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
-import React = require('react');
+import * as React from 'react';
export interface ViewDefBounds {
type: string;
@@ -29,6 +28,8 @@ export interface ViewDefBounds {
}
export interface PoolData {
+ pair: { layout: Doc; data?: Doc };
+ replica: string;
x: number;
y: number;
z?: number;
@@ -36,14 +37,13 @@ export interface PoolData {
zIndex?: number;
width?: number;
height?: number;
+ autoDim?: number; // 1 for set to Panel dims, 0 for use width/height as entered
backgroundColor?: string;
color?: string;
opacity?: number;
transition?: string;
highlight?: boolean;
- replica: string;
- pointerEvents?: string; // without this, toggling lockPosition of a group/collection in a freeform view won't update until something else invalidates the freeform view's documents forcing -- this is a problem with doLayoutComputation which makes a performance test to insure somethingChanged
- pair: { layout: Doc; data?: Doc };
+ pointerEvents?: string;
}
export interface ViewDefResult {
@@ -91,8 +91,8 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
docMap.set(layout[Id], {
x: NumCast(layout.x),
y: NumCast(layout.y),
- width: layout[Width](),
- height: layout[Height](),
+ width: NumCast(layout._width),
+ height: NumCast(layout._height),
pair: { layout, data },
transition: 'all .3s',
replica: '',
@@ -106,8 +106,8 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
const burstScale = NumCast(pivotDoc._starburstDocScale, 1);
childPairs.forEach(({ layout, data }, i) => {
- const aspect = layout[Height]() / layout[Width]();
- const docSize = Math.min(Math.min(400, layout[Width]()), Math.min(400, layout[Width]()) / aspect) * burstScale;
+ const aspect = NumCast(layout._height) / NumCast(layout._width);
+ const docSize = Math.min(Math.min(400, NumCast(layout._width)), Math.min(400, NumCast(layout._width)) / aspect) * burstScale;
const deg = (i / childPairs.length) * Math.PI * 2;
docMap.set(layout[Id], {
x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)),
@@ -156,7 +156,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
x: 0,
y: 0,
zIndex: 0,
- width: 0, // should make doc hidden in CollectionFreefromDocumentView
+ width: 0, // should make doc hidden in CollectionFreeFormDocumentView
height: 0,
pair,
replica: '',
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 470ff9527..1b9627bb6 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,7 +1,8 @@
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Doc, Field } from '../../../../fields/Doc';
-import { DocCss } from '../../../../fields/DocSymbols';
+import { Brushed, DocCss } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
@@ -12,8 +13,8 @@ import { SettingsManager } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../../nodes/DocumentView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import './CollectionFreeFormLinkView.scss';
-import React = require('react');
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -21,27 +22,30 @@ export interface CollectionFreeFormLinkViewProps {
LinkDocs: Doc[];
}
-// props.screentolocatransform
-
@observer
-export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFormLinkViewProps> {
+export class CollectionFreeFormLinkView extends ObservableReactComponent<CollectionFreeFormLinkViewProps> {
@observable _opacity: number = 0;
@observable _start = 0;
_anchorDisposer: IReactionDisposer | undefined;
_timeout: NodeJS.Timeout | undefined;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentWillUnmount() {
this._anchorDisposer?.();
}
- @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
+ @action timeout: any = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
componentDidMount() {
this._anchorDisposer = reaction(
() => [
- this.props.A.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
- this.props.B.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+ this._props.A.screenToViewTransform(),
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_1, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
+ this._props.B.screenToViewTransform(),
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.layout_scrollTop,
+ Cast(Cast(Cast(this._props.A.Document, Doc, null)?.link_anchor_2, Doc, null)?.annotationOn, Doc, null)?.[DocCss],
],
action(() => {
this._start = Date.now();
@@ -53,9 +57,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
);
}
placeAnchors = () => {
- const { A, B, LinkDocs } = this.props;
+ const { A, B, LinkDocs } = this._props;
const linkDoc = LinkDocs[0];
- if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
+ if (SnappingManager.IsDragging || !A.ContentDiv || !B.ContentDiv) return;
setTimeout(
action(() => (this._opacity = 0.75)),
0
@@ -85,9 +89,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
} else {
const m = targetAhyperlink.getBoundingClientRect();
- const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / A.props.PanelWidth();
- const mpy = mp[1] / A.props.PanelHeight();
+ const mp = A.screenToViewTransform().transformPoint(m.right, m.top + 5);
+ const mpx = mp[0] / A._props.PanelWidth();
+ const mpy = mp[1] / A._props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_1_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_1_y = mpy * 100;
if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
@@ -100,9 +104,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
} else {
const m = targetBhyperlink.getBoundingClientRect();
- const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- const mpx = mp[0] / B.props.PanelWidth();
- const mpy = mp[1] / B.props.PanelHeight();
+ const mp = B.screenToViewTransform().transformPoint(m.right, m.top + 5);
+ const mpx = mp[0] / B._props.PanelWidth();
+ const mpy = mp[1] / B._props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.link_anchor_2_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.link_anchor_2_y = mpy * 100;
if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
@@ -115,18 +119,18 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
this,
e,
(e, down, delta) => {
- this.props.LinkDocs[0].link_relationship_OffsetX = NumCast(this.props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
- this.props.LinkDocs[0].link_relationship_OffsetY = NumCast(this.props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
+ this._props.LinkDocs[0].link_relationship_OffsetX = NumCast(this._props.LinkDocs[0].link_relationship_OffsetX) + delta[0];
+ this._props.LinkDocs[0].link_relationship_OffsetY = NumCast(this._props.LinkDocs[0].link_relationship_OffsetY) + delta[1];
return false;
},
emptyFunction,
action(() => {
SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
- LinkManager.currentLink = this.props.LinkDocs[0];
+ SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
+ LinkManager.currentLink = this._props.LinkDocs[0];
this.toggleProperties();
// OverlayView.Instance.addElement(
- // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+ // <LinkEditor sourceDoc={this._props.A.Document} linkDoc={this._props.LinkDocs[0]}
// showLinks={action(() => { })}
// />, { x: 300, y: 300 });
})
@@ -171,23 +175,23 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
@action
toggleProperties = () => {
- if ((SettingsManager.propertiesWidth ?? 0) < 100) {
- SettingsManager.propertiesWidth = 250;
+ if ((SettingsManager.Instance.propertiesWidth ?? 0) < 100) {
+ SettingsManager.Instance.propertiesWidth = 250;
}
};
@action
onClickLine = () => {
SelectionManager.DeselectAll();
- SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
- LinkManager.currentLink = this.props.LinkDocs[0];
+ SelectionManager.SelectSchemaViewDoc(this._props.LinkDocs[0], true);
+ LinkManager.currentLink = this._props.LinkDocs[0];
this.toggleProperties();
};
@computed.struct get renderData() {
this._start;
- SnappingManager.GetIsDragging();
- const { A, B, LinkDocs } = this.props;
+ SnappingManager.IsDragging;
+ const { A, B, LinkDocs } = this._props;
if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
@@ -200,8 +204,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const atop = this.visibleY(adiv);
const btop = this.visibleY(bdiv);
if (!a.width || !b.width) return undefined;
- const aDocBounds = (A.props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
- const bDocBounds = (B.props as any).DocumentView?.().getBounds() || { left: 0, right: 0, top: 0, bottom: 0 };
+ const aDocBounds = (A._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
+ const bDocBounds = (B._props as any).DocumentView?.().getBounds || { left: 0, right: 0, top: 0, bottom: 0 };
const aleft = this.visibleX(adiv);
const bleft = this.visibleX(bdiv);
const aclipped = aleft !== a.left || atop !== a.top;
@@ -223,12 +227,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
- const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
- const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
+ const aActive = A.IsSelected || A.Document[Brushed];
+ const bActive = B.IsSelected || B.Document[Brushed];
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
- const link = this.props.LinkDocs[0];
+ const link = this._props.LinkDocs[0];
return {
a,
b,
@@ -250,7 +254,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
render() {
if (!this.renderData) return null;
- const link = this.props.LinkDocs[0];
+ const link = this._props.LinkDocs[0];
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
const linkRelationship = Field.toString(link?.link_relationship as any as Field); //get string representing relationship
const linkRelationshipList = Doc.UserDoc().link_relationshipList as List<string>;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 420e6a318..e5b6c366f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,17 +1,17 @@
import { computed } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { Id } from '../../../../fields/FieldSymbols';
import { DocumentManager } from '../../../util/DocumentManager';
import { LightboxView } from '../../LightboxView';
-import './CollectionFreeFormLinksView.scss';
import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
-import React = require('react');
+import './CollectionFreeFormLinksView.scss';
@observer
export class CollectionFreeFormLinksView extends React.Component {
@computed get uniqueConnections() {
return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews))
- .filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath)))
+ .filter(c => !LightboxView.LightboxDoc || (LightboxView.Contains(c.a) && LightboxView.Contains(c.b)))
.map(c => <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
new file mode 100644
index 000000000..69cbae86f
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -0,0 +1,63 @@
+import { computed, makeObservable } from 'mobx';
+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 './CollectionFreeFormView.scss';
+export interface CollectionFreeFormPannableContentsProps {
+ Document: Doc;
+ viewDefDivClick?: ScriptField;
+ children?: React.ReactNode | undefined;
+ transition?: string;
+ isAnnotationOverlay: boolean | undefined;
+ transform: () => string;
+ brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
+}
+
+@observer
+export class CollectionFreeFormPannableContents extends React.Component<CollectionFreeFormPannableContentsProps> {
+ constructor(props: CollectionFreeFormPannableContentsProps) {
+ super(props);
+ makeObservable(this);
+ }
+ @computed get presPaths() {
+ return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(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) =>
+ !viewport ? null : (
+ <div
+ className="collectionFreeFormView-brushView"
+ style={{
+ transform: `translate(${viewport.panX}px, ${viewport.panY}px)`,
+ width: viewport.width,
+ height: viewport.height,
+ border: `orange solid ${viewport.width * 0.005}px`,
+ }}
+ />
+ );
+
+ render() {
+ return (
+ <div
+ className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target)?.overflow === 'visible') {
+ target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
+ }
+ }}
+ 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
+ }}>
+ {this.props.children}
+ {this.presPaths}
+ {this.showViewport(this.props.brushedView())}
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
index 5fa01b102..7951aff65 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -1,14 +1,12 @@
-@import "global/globalCssVariables";
+@import 'global/globalCssVariables.module.scss';
.collectionFreeFormRemoteCursors-cont {
-
- position:absolute;
+ position: absolute;
z-index: $remoteCursors-zindex;
transform-origin: 'center center';
}
.collectionFreeFormRemoteCursors-canvas {
-
- position:absolute;
+ position: absolute;
width: 20px;
height: 20px;
opacity: 0.5;
@@ -21,4 +19,4 @@
// fontStyle: "italic",
margin-left: -12;
margin-top: 4;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9e8d92d7d..fa8218bdd 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,6 +1,8 @@
import { computed } from 'mobx';
import { observer } from 'mobx-react';
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';
@@ -9,8 +11,6 @@ import { listSpec } from '../../../../fields/Schema';
import { Cast } from '../../../../fields/Types';
import { CollectionViewProps } from '../CollectionView';
import './CollectionFreeFormView.scss';
-import React = require('react');
-import v5 = require('uuid/v5');
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
@@ -42,7 +42,7 @@ export class CollectionFreeFormRemoteCursors extends React.Component<CollectionV
if (el) {
const ctx = el.getContext('2d');
if (ctx) {
- ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillStyle = '#' + uuid.v5(metadata.id, uuid.v5.URL).substring(0, 6).toUpperCase() + '22';
ctx.fillRect(0, 0, 20, 20);
ctx.fillStyle = 'black';
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 250760bd5..7d3acaea7 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
.collectionfreeformview-none {
position: inherit;
@@ -255,3 +255,49 @@
background-color: rgba($color: #000000, $alpha: 0.4);
position: absolute;
}
+
+.collectionFreeform-infoUI {
+ position: absolute;
+ display: block;
+ top: 0;
+
+ color: white;
+ background-color: #5075ef;
+ border-radius: 5px;
+ box-shadow: 2px 2px 5px black;
+
+ margin: 15px;
+ padding: 10px;
+
+ .msg {
+ position: relative;
+ // display: block;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+
+ }
+
+ .gif-container {
+ position: relative;
+ margin-top: 5px;
+ // display: block;
+
+ justify-content: center;
+ align-items: center;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+
+
+ .gif {
+ background-color: transparent;
+ height: 300px;
+ }
+ }
+
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 2ac8f6291..818c754c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,28 +1,27 @@
import { Bezier } from 'bezier-js';
import { Colors } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+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 { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+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 { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
-import { InteractionUtils } from '../../../util/InteractionUtils';
import { FollowLinkScript } from '../../../util/LinkFollower';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
@@ -31,34 +30,31 @@ import { freeformScrollMode, SettingsManager } from '../../../util/SettingsManag
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
-import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { GestureOverlay } from '../../GestureOverlay';
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { CtrlKey } from '../../GlobalKeyHandler';
+import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
-import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
-import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
-import { FieldViewProps } from '../../nodes/FieldView';
+import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
+import { SchemaCSVPopUp } from '../../nodes/DataVizBox/SchemaCSVPopUp';
+import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { PinProps, PresBox } from '../../nodes/trails/PresBox';
import { CreateImage } from '../../nodes/WebBoxRenderer';
import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
-import { TabDocView } from '../TabDocView';
+import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
+import { CollectionFreeFormInfoUI } from './CollectionFreeFormInfoUI';
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';
-import React = require('react');
-import { DocumentWithColor, GeneratedResponse, generatePalette, StyleInput, StyleInputDocument } from '../../../apis/gpt/customization';
-import { PropertiesView } from '../../PropertiesView';
-import { MainView } from '../../MainView';
-import { ExtractColors } from '../../ExtractColors';
-import { extname } from 'path';
-
-export type collectionFreeformViewProps = {
+
+export interface collectionFreeformViewProps {
NativeWidth?: () => number;
NativeHeight?: () => number;
originTopLeft?: boolean;
@@ -69,16 +65,18 @@ export type collectionFreeformViewProps = {
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
getScrollHeight?: () => number | undefined;
- dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are transparent or not.
- // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
-};
+}
@observer
export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
public get displayName() {
- return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
+ return 'CollectionFreeFormView(' + this.Document.title?.toString() + ')';
} // this makes mobx trace() statements more descriptive
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
@observable
public static ShowPresPaths = false;
@@ -88,37 +86,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _downX: number = 0;
private _downY: number = 0;
private _downTime = 0;
- private _inkToTextStartX: number | undefined;
- private _inkToTextStartY: number | undefined;
- private _wordPalette: Map<string, string> = new Map<string, string>();
private _clusterDistance: number = 75;
private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
- private _layoutPoolData = observable.map<string, PoolData>();
- private _layoutSizeData = observable.map<string, { width?: number; height?: number }>();
- private _cachedPool: Map<string, PoolData> = new Map();
private _batch: UndoManager.Batch | undefined = undefined;
private _brushtimer: any;
private _brushtimer1: any;
public get isAnnotationOverlay() {
- return this.props.isAnnotationOverlay;
+ return this._props.isAnnotationOverlay;
}
public get scaleFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_scale';
+ return (this._props.viewField ?? '') + '_freeform_scale';
}
private get panXFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_panX';
+ return (this._props.viewField ?? '') + '_freeform_panX';
}
private get panYFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_panY';
+ return (this._props.viewField ?? '') + '_freeform_panY';
}
private get autoResetFieldKey() {
- return (this.props.viewField ?? '') + '_freeform_autoReset';
- }
- private get borderWidth() {
- return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
+ return (this._props.viewField ?? '') + '_freeform_autoReset';
}
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
@@ -128,31 +117,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
- @observable _marqueeRef: HTMLDivElement | null = null;
@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 _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region
- @computed get views() {
+ @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);
if (viewsMask.length) renderableEles.push(<div className={`collectionfreeformview-mask${this._layoutElements.some(ele => (ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}</div>);
return renderableEles;
}
@computed get fitToContentVals() {
+ const hgt = this.contentBounds.b - this.contentBounds.y;
+ const wid = this.contentBounds.r - this.contentBounds.x;
return {
- bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
+ bounds: { ...this.contentBounds, cx: this.contentBounds.x + wid / 2, cy: this.contentBounds.y + hgt / 2 },
scale:
- !this.childDocs.length || !Number.isFinite(this.contentBounds.b - this.contentBounds.y) || !Number.isFinite(this.contentBounds.r - this.contentBounds.x)
- ? 1
- : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)),
+ (!this.childDocs.length || !Number.isFinite(hgt) || !Number.isFinite(wid)
+ ? 1 //
+ : Math.min(this._props.PanelHeight() / hgt, this._props.PanelWidth() / wid)) / (this._props.NativeDimScaling?.() || 1),
};
}
@computed get fitContentsToBox() {
- return (this.props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
+ return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
}
@computed get contentBounds() {
- const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
+ const cb = Cast(this.dataDoc.contentBounds, listSpec('number'));
return cb
? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
: aggregateBounds(
@@ -162,56 +152,50 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
@computed get nativeWidth() {
- return this.props.NativeWidth?.() || (this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
+ return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
@computed get nativeHeight() {
- return this.props.NativeHeight?.() || (this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)));
+ return this._props.NativeHeight?.() || Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
@computed get cachedCenteringShiftX(): number {
- const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
- return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling;
+ return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : this._props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
- const dv = this.props.DocumentView?.();
- const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
+ const dv = this.DocumentView?.();
+ const fitWidth = this._props.layout_fitWidth?.(this.Document) ?? dv?.layoutDoc.layout_fitWidth;
+ const scaling = !this.nativeDimScaling ? 1 : this.nativeDimScaling;
// if freeform has a native aspect, then the panel height needs to be adjusted to match it
- const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
- return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
- }
- @computed get cachedGetLocalTransform(): Transform {
- return Transform.Identity()
- .scale(1 / this.zoomScaling())
- .translate(this.panX(), this.panY());
+ const aspect = dv?.nativeWidth && dv?.nativeHeight && !fitWidth ? dv.nativeHeight / dv.nativeWidth : this._props.PanelHeight() / this._props.PanelWidth();
+ return this._props.isAnnotationOverlay || this._props.originTopLeft ? 0 : (aspect * this._props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
- @computed get cachedGetContainerTransform(): Transform {
- return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
+ @computed get panZoomXf() {
+ return new Transform(this.panX(), this.panY(), 1 / this.zoomScaling());
}
- @computed get cachedGetTransform(): Transform {
- return this.getContainerTransform()
- .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+ @computed get screenToFreeformContentsXf() {
+ return this._props
+ .ScreenToLocalTransform() //
.translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
- .transform(this.cachedGetLocalTransform);
+ .transform(this.panZoomXf);
}
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
- if (timer) clearTimeout(timer);
- return DocumentView.SetViewTransition(docs, 'all', duration, undefined, true);
+ return DocumentView.SetViewTransition(docs, 'all', duration, timer, undefined, true);
}
public static updateKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], time: number) {
- if (timer) clearTimeout(timer);
- const newTimer = DocumentView.SetViewTransition(docs, 'all', 1000, undefined, true);
+ 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);
+ 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);
+ 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);
+ const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
findexed?.length <= timecode + 1 && findexed.push(undefined as any);
});
});
@@ -237,48 +221,44 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _keyframeEditing = false;
@action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
getKeyFrameEditing = () => this._keyframeEditing;
- onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
- onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
- onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+ 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;
fitContentOnce = () => {
- if (this.props.DocumentView?.().nativeWidth) return;
const vals = this.fitToContentVals;
this.layoutDoc._freeform_panX = vals.bounds.cx;
this.layoutDoc._freeform_panY = vals.bounds.cy;
this.layoutDoc._freeform_scale = vals.scale;
};
freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
- reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
// freeform_panx, freeform_pany, freeform_scale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
// this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image
panX = () => this.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], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
- contentTransform = () =>
- this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
- getTransform = () => this.cachedGetTransform.copy();
- getLocalTransform = () => this.cachedGetLocalTransform.copy();
- getContainerTransform = () => this.cachedGetContainerTransform.copy();
+ PanZoomCenterXf = () =>
+ this._props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}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 = (newBox: Doc) => {
- FormattedTextBox.SelectOnLoad = newBox[Id]; // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
- this.addDocument(newBox);
+ 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
+ this.addDocument(newDoc);
};
selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
- docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).map(dv => dv && SelectionManager.SelectView(dv, true));
+ docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
};
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
if (newBox instanceof Doc) {
- if ((retVal = this.props.addDocument?.(newBox) || false)) {
+ if ((retVal = this._props.addDocument?.(newBox) || false)) {
this.bringToFront(newBox);
this.updateCluster(newBox);
}
} else {
- retVal = this.props.addDocument?.(newBox) || false;
+ retVal = this._props.addDocument?.(newBox) || false;
// bcz: deal with clusters
}
if (retVal) {
@@ -286,13 +266,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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}_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]));
}
}
- if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
+ if (this.Document._currentFrame !== undefined && !this._props.isAnnotationOverlay) {
CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
}
}
@@ -306,16 +286,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return dispTime === -1 || curTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
}
- groupFocus = (anchor: Doc, options: DocFocusOptions) => {
- options.docTransform = new Transform(-NumCast(this.rootDoc[this.panXFieldKey]) + NumCast(anchor.x), -NumCast(this.rootDoc[this.panYFieldKey]) + NumCast(anchor.y), 1);
- const res = this.props.focus(this.rootDoc, options);
+ 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);
+ const res = this._props.focus(this.Document, options);
options.docTransform = undefined;
return res;
};
- focus = (anchor: Doc, options: DocFocusOptions) => {
+ focus = (anchor: Doc, options: FocusViewOptions) => {
if (this._lightboxDoc) return;
- if (anchor === this.rootDoc) {
+ if (anchor === this.Document) {
if (options.willZoomCentered && options.zoomScale) {
this.fitContentOnce();
options.didMove = true;
@@ -324,7 +304,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).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.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
+ const cantTransform = this.fitContentsToBox || ((this.Document.isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? options?.zoomScale ?? 0.75 : undefined);
// focus on the document in the collection
@@ -339,22 +319,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- getView = async (doc: Doc): Promise<Opt<DocumentView>> => {
- return new Promise<Opt<DocumentView>>(res => {
- if (doc.hidden && this._lightboxDoc !== doc) doc.hidden = false;
+ 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);
const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
findDoc(dv => res(dv));
});
- };
- @action
- internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
+ internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) {
if (!super.onInternalDrop(e, de)) return false;
const refDoc = docDragData.droppedDocuments[0];
- const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
- const z = NumCast(refDoc.z);
- const x = (z ? xpo : xp) - docDragData.offset[0];
- const y = (z ? ypo : yp) - docDragData.offset[1];
+ const fromScreenXf = NumCast(refDoc.z) ? this.ScreenToLocalBoxXf() : this.screenToFreeformContentsXf;
+ const [xpo, ypo] = fromScreenXf.transformPoint(de.x, de.y);
+ const x = xpo - docDragData.offset[0];
+ const y = ypo - docDragData.offset[1];
const zsorted = this.childLayoutPairs
.map(pair => pair.layout)
.slice()
@@ -366,16 +344,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
+ const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], fromScreenXf.Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000), false); // get non-default values for everything else
- vals.x = x + NumCast(pvals.x) - dropPos[0];
- vals.y = y + NumCast(pvals.y) - dropPos[1];
+ vals.x = NumCast(pvals.x) + delta.x;
+ vals.y = NumCast(pvals.y) + delta.y;
CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
} else {
- d.x = x + NumCast(d.x) - dropPos[0];
- d.y = y + NumCast(d.y) - dropPos[1];
+ d.x = NumCast(d.x) + delta.x;
+ d.y = NumCast(d.y) + delta.y;
}
d._layout_modificationDate = new DateField();
const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
@@ -411,8 +390,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@undoBatch
- internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData, xp: number, yp: number) {
+ internalAnchorAnnoDrop(e: Event, de: DragManager.DropEvent, annoDragData: DragManager.AnchorAnnoDragData) {
const dropCreator = annoDragData.dropDocCreator;
+ const [xp, yp] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
const dropDoc = dropCreator(annotationOn);
if (dropDoc) {
@@ -420,26 +400,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
dropDoc.y = yp - annoDragData.offset[1];
this.bringToFront(dropDoc);
}
- return dropDoc || this.rootDoc;
+ return dropDoc || this.Document;
};
return true;
}
@undoBatch
- internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
- if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
+ internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData) {
+ if (this.DocumentView?.() && linkDragData.linkDragView.containerViewPath?.().includes(this.DocumentView())) {
+ const [x, y] = this.screenToFreeformContentsXf.transformPoint(de.x, de.y);
let added = false;
// do nothing if link is dropped into any freeform view parent of dragged document
const source =
- !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc
- ? Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' })
+ !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.Document
+ ? Docs.Create.TextDocument('', { _width: 200, _height: 75, x, y, title: 'dropped annotation' })
: Docs.Create.FontIconDocument({
title: 'anchor',
icon_label: '',
followLinkToggle: true,
icon: 'map-pin',
- x: xp,
- y: yp,
+ x,
+ y,
backgroundColor: '#ACCEF7',
layout_hideAllLinks: true,
layout_hideLinkButton: true,
@@ -448,7 +429,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_xPadding: 0,
onClick: FollowLinkScript(),
});
- added = this.props.addDocument?.(source) ? true : false;
+ added = this._props.addDocument?.(source) ? true : false;
de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
e.stopPropagation();
@@ -459,20 +440,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
- const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
- if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp);
- else if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
- else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
+ 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);
return false;
};
- onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.getTransform().transformPoint(e.pageX, e.pageY));
-
+ 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.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster, -1) : NumCast(cd.group, -1);
+ 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;
@@ -485,16 +478,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}, -1);
}
- tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) {
+ tryDragCluster(e: PointerEvent, cluster: number) {
if (cluster !== -1) {
- const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+ const ptsParent = e;
if (ptsParent) {
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
- const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
- const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
+ 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 ? 'embed' : undefined);
- de.moveDocument = this.props.moveDocument;
- de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
+ de.moveDocument = this._props.moveDocument;
+ de.offset = this.screenToFreeformContentsXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
DragManager.StartDocumentDrag(
clusterDocs.map(v => v.ContentDiv!),
de,
@@ -511,7 +504,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
updateClusters(_freeform_useClusters: boolean) {
- this.props.Document._freeform_useClusters = _freeform_useClusters;
+ this.Document._freeform_useClusters = _freeform_useClusters;
this._clusterSets.length = 0;
this.childLayoutPairs.map(pair => pair.layout).map(c => this.updateCluster(c));
}
@@ -519,7 +512,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
updateClusterDocs(docs: Doc[]) {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document._freeform_useClusters) {
+ 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);
@@ -527,7 +520,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
docs.map(doc =>
this._clusterSets.map((set, i) =>
set.map(member => {
- if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
docFirst.layout_cluster = i;
}
})
@@ -560,15 +553,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@action
- updateCluster(doc: Doc) {
+ updateCluster = (doc: Doc) => {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
- if (this.props.Document._freeform_useClusters) {
+ 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 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
doc.layout_cluster = i;
}
})
@@ -589,38 +582,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._clusterSets[doc.layout_cluster ?? 0].push(doc);
}
}
- }
+ };
- getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
- let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
- switch (property) {
- 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)));
+ 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) {
+ 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;
- }
- }
+ 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 (this._hitCluster !== -1) {
!addToSel && SelectionManager.DeselectAll();
- const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === this._hitCluster);
+ 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;
}
@@ -628,40 +622,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- onPenUp = (e: PointerEvent): void => {
- if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener('pointerup', this.onPenUp);
- const currentCol = DocListCast(this.rootDoc.currentInkDoc);
- const rootDocList = DocListCast(this.rootDoc.data);
- currentCol.push(rootDocList[rootDocList.length - 1]);
-
- this._batch?.end();
- }
- };
-
- @action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
- if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- if (
- !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
- !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
- !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
- ) {
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+ if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive()) {
+ if (!this.Document.isGroup) {
+ // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
switch (Doc.ActiveTool) {
- case InkTool.Highlighter: break;
+ case InkTool.Highlighter: break;
case InkTool.Write: break;
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.Eraser:
this._batch = UndoManager.StartBatch('collectionErase');
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
break;
case InkTool.None:
- if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
+ 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);
}
break;
@@ -670,29 +650,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- @action
- handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
- // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
- const pt = me.changedTouches[0];
- if (pt) {
- this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY));
- if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- this.removeMoveListeners();
- this.addMoveListeners();
- this.removeEndListeners();
- this.addEndListeners();
- if (Doc.ActiveTool === InkTool.None) {
- this._lastX = pt.pageX;
- this._lastY = pt.pageY;
- e.preventDefault();
- e.stopPropagation();
- } else {
- e.preventDefault();
- }
- }
- }
- };
-
public unprocessedDocs: Doc[] = [];
public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@undoBatch
@@ -705,25 +662,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.Triangle:
case GestureUtils.Gestures.Stroke:
const points = ge.points;
- const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- console.log(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
+ 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(
- ActiveInkColor(),
- ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
points,
- ActiveIsInkMask(),
- {
- title: ge.gesture.toString(),
- x: B.x - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
- y: B.y - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
- _width: B.width + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- _height: B.height + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- }
+ { title: ge.gesture.toString(),
+ x: B.x - inkWidth / 2,
+ y: B.y - inkWidth / 2,
+ _width: B.width + inkWidth,
+ _height: B.height + inkWidth }, // prettier-ignore
+ inkWidth
);
if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
@@ -733,69 +681,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
break;
case GestureUtils.Gestures.Rectangle:
- if (this._inkToTextStartX && this._inkToTextStartY) {
- const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const setDocs = this.getActiveDocuments().filter(s => DocCast(s.proto)?.type === DocumentType.RTF && s.color);
- const sets = setDocs.map(sd => Cast(sd.text, RichTextField)?.Text as string);
- if (sets.length && sets[0]) {
- this._wordPalette.clear();
- const colors = setDocs.map(sd => FieldValue(sd.color) as string);
- sets.forEach((st: string, i: number) => st.split(',').forEach(word => this._wordPalette.set(word, colors[i])));
- }
- const inks = this.getActiveDocuments().filter(doc => {
- if (doc.type === 'ink') {
- const l = NumCast(doc.x);
- const r = l + doc[Width]();
- const t = NumCast(doc.y);
- const b = t + doc[Height]();
- const pass = !(this._inkToTextStartX! > r || end[0] < l || this._inkToTextStartY! > b || end[1] < t);
- return pass;
- }
- return false;
- });
- // const inkFields = inks.map(i => Cast(i.data, InkField));
- const strokes: InkData[] = [];
- inks.forEach(i => {
- const d = Cast(i.data, InkField);
- const x = NumCast(i.x);
- const y = NumCast(i.y);
- const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
- const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
- if (d) {
- strokes.push(d.inkData.map(pd => ({ X: pd.X + x - left, Y: pd.Y + y - top })));
- }
+ 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 => {
- const wordResults = results.filter((r: any) => r.category === 'inkWord');
- for (const word of wordResults) {
- const indices: number[] = word.strokeIds;
- indices.forEach(i => {
- const otherInks: Doc[] = [];
- indices.forEach(i2 => i2 !== i && otherInks.push(inks[i2]));
- inks[i].relatedInks = new List<Doc>(otherInks);
- const uniqueColors: string[] = [];
- Array.from(this._wordPalette.values()).forEach(c => uniqueColors.indexOf(c) === -1 && uniqueColors.push(c));
- inks[i].alternativeColors = new List<string>(uniqueColors);
- if (this._wordPalette.has(word.recognizedText.toLowerCase())) {
- inks[i].color = this._wordPalette.get(word.recognizedText.toLowerCase());
- } else if (word.alternates) {
- for (const alt of word.alternates) {
- if (this._wordPalette.has(alt.recognizedString.toLowerCase())) {
- inks[i].color = this._wordPalette.get(alt.recognizedString.toLowerCase());
- break;
- }
- }
- }
- });
- }
- });
- this._inkToTextStartX = end[0];
- }
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {});
break;
case GestureUtils.Gestures.Text:
if (ge.text) {
- const B = this.getTransform().transformPoint(ge.points[0].X, ge.points[0].Y);
+ 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();
}
@@ -803,7 +702,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
onEraserUp = (e: PointerEvent): void => {
- this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
+ this._deleteList.forEach(ink => ink._props.removeDocument?.(ink.Document));
this._deleteList = [];
this._batch?.end();
};
@@ -813,12 +712,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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.props.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
+ this.onBrowseClickHandler().script.run({ documentView: this.DocumentView?.(), clientX: e.clientX, clientY: e.clientY });
e.stopPropagation();
e.preventDefault();
} else if (this.isContentActive() && e.shiftKey) {
// reset zoom of freeform view to 1-to-1 on a shift + double click
- this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
+ this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY), 1);
e.stopPropagation();
e.preventDefault();
}
@@ -832,11 +731,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
+ pan = (e: PointerEvent): void => {
+ const ctrlKey = e.ctrlKey && !e.shiftKey;
+ const shiftKey = e.shiftKey && !e.ctrlKey;
PresBox.Instance?.pauseAutoPres();
- this.props.DocumentView?.().clearViewTransition();
- const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true);
+ 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);
+ this.setPan(NumCast(this.Document[this.panXFieldKey]) - (ctrlKey ? 0 : dx), NumCast(this.Document[this.panYFieldKey]) - (shiftKey ? 0 : dy), 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
};
@@ -855,8 +757,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
if (!this._deleteList.includes(intersect.inkView)) {
this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
+ SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
if (!e.shiftKey) {
this._eraserLock++;
@@ -882,20 +784,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- onPointerMove = (e: PointerEvent): boolean => {
- if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false;
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- Doc.ActiveTool = InkTool.None;
- } else {
- if (this.tryDragCluster(e, this._hitCluster)) {
- 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;
- }
- // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
- if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.getLocalTransform().inverse().Scale) {
- this.pan(e);
- e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
- }
+ onPointerMove = (e: PointerEvent) => {
+ if (this.tryDragCluster(e, this._hitCluster)) {
+ 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;
+ }
+ // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
+ if (!this._props.isAnnotationOverlay || 1 - NumCast(this.layoutDoc._freeform_scale_min, 1) / this.zoomScaling()) {
+ this.pan(e);
+ e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
}
return false;
};
@@ -909,9 +806,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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.props.DocumentView?.()))
+ .map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.()))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
- .map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
+ .map(inkView => ({ inkViewBounds: inkView!.getBounds, inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
.filter(
({ inkViewBounds }) =>
inkViewBounds && // bounding box of eraser segment and ink stroke overlap
@@ -1003,14 +900,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.props.DocumentView?.())?.ComponentView as InkingStroke;
+ const otherInk = DocumentManager.Instance.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) {
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.
- if (ink?.Document === otherInk.props.Document && neighboringSegment) 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 })));
const c0 = otherCurve.get(0);
@@ -1033,60 +930,61 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
- cleanUpInteractions = () => {
- this.removeMoveListeners();
- this.removeEndListeners();
- };
-
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
- if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
+ if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
- const [x, y] = this.getTransform().transformPoint(pointX, pointY);
- const invTransform = this.getLocalTransform().inverse();
+ const [x, y] = this.screenToFreeformContentsXf.transformPoint(pointX, pointY);
+ const invTransform = this.panZoomXf.inverse();
if (deltaScale * invTransform.Scale > 20) {
deltaScale = 20 / invTransform.Scale;
}
- if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc[this.scaleFieldKey + '_min'])) {
+ if (deltaScale < 1 && invTransform.Scale <= NumCast(this.Document[this.scaleFieldKey + '_min'])) {
this.setPan(0, 0);
return;
}
- if (deltaScale * invTransform.Scale > NumCast(this.rootDoc[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
- deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
+ if (deltaScale * invTransform.Scale > NumCast(this.Document[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
+ deltaScale = NumCast(this.Document[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
}
- if (deltaScale * invTransform.Scale < NumCast(this.rootDoc[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
- deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
+ if (deltaScale * invTransform.Scale < NumCast(this.Document[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
+ deltaScale = NumCast(this.Document[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
}
const localTransform = invTransform.scaleAbout(deltaScale, x, y);
if (localTransform.Scale >= 0.05 || localTransform.Scale > this.zoomScaling()) {
const safeScale = Math.min(Math.max(0.05, localTransform.Scale), 20);
- this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
- this.setPan(-localTransform.TranslateX / safeScale, (this.props.originTopLeft ? undefined : NumCast(this.props.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
+ this.Document[this.scaleFieldKey] = Math.abs(safeScale);
+ this.setPan(-localTransform.TranslateX / safeScale, (this._props.originTopLeft ? undefined : NumCast(this.Document.layout_scrollTop) * safeScale) || -localTransform.TranslateY / safeScale);
}
};
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.Document._isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
+ if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
PresBox.Instance?.pauseAutoPres();
- if (this.layoutDoc._Transform || this.props.Document.treeView_OutlineMode === TreeViewType.outline) return;
+ if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
- const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
- const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
- switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) {
+ const docHeight = NumCast(this.Document[Doc.LayoutFieldKey(this.Document) + '_nativeHeight'], this.nativeHeight);
+ const scrollable = this.isAnnotationOverlay && NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this._props.PanelHeight() / this.nativeDimScaling + 1e-4;
+ switch (
+ !e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
+ Doc.UserDoc().freeformScrollMode : // no modifiers, do assigned mode
+ e.ctrlKey && !CtrlKey? // otherwise, if ctrl key (pinch gesture) try to zoom else pan
+ freeformScrollMode.Zoom : freeformScrollMode.Pan // prettier-ignore
+ ) {
case freeformScrollMode.Pan:
- // if ctrl is selected then zoom
- if (!e.ctrlKey && this.props.isContentActive(true)) {
- this.scrollPan({ deltaX: -e.deltaX * this.getTransform().Scale, deltaY: e.shiftKey ? 0 : -e.deltaY * this.getTransform().Scale });
+ if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this._props.isContentActive()) {
+ const deltaX = e.shiftKey ? e.deltaX : e.ctrlKey ? 0 : e.deltaX;
+ const deltaY = e.shiftKey ? 0 : e.ctrlKey ? e.deltaY : e.deltaY;
+ this.scrollPan({ deltaX: -deltaX * this.screenToFreeformContentsXf.Scale, deltaY: e.shiftKey ? 0 : -deltaY * this.screenToFreeformContentsXf.Scale });
break;
}
default:
case freeformScrollMode.Zoom:
- if ((e.ctrlKey || !scrollable) && this.props.isContentActive(true)) {
- 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();
+ 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();
}
break;
}
@@ -1101,7 +999,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
const measuredDocs = docs
- .map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ''), size: this.childSizeProviderUnmemoized(doc, '') }))
+ .map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc._width), height: NumCast(doc._height) } }))
.filter(({ pos, size }) => pos && size)
.map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
@@ -1114,45 +1012,45 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) },
}),
{
- xrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
- yrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ xrange: { min: this._props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ yrange: { min: this._props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
}
);
-
- const panelWidMax = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelWidMin = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1);
- const panelHgtMax = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelHgtMin = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1);
- if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this.props.originTopLeft ? 0 : panelWidMax / 2);
- else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this.props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2);
- if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this.props.originTopLeft ? 0 : panelHgtMax / 2);
- else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this.props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2);
+ const scaling = this.zoomScaling() * (this._props.NativeDimScaling?.() || 1);
+ const panelWidMax = (this._props.PanelWidth() / scaling) * (this._props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelWidMin = (this._props.PanelWidth() / scaling) * (this._props.originTopLeft ? 0 : 1);
+ const panelHgtMax = (this._props.PanelHeight() / scaling) * (this._props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelHgtMin = (this._props.PanelHeight() / scaling) * (this._props.originTopLeft ? 0 : 1);
+ if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this._props.originTopLeft ? 0 : panelWidMax / 2);
+ else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this._props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2);
+ if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this._props.originTopLeft ? 0 : panelHgtMax / 2);
+ else if (ranges.yrange.max <= panY - panelHgtMin / 2) panY = ranges.yrange.min - (this._props.originTopLeft ? panelHgtMax / 2 : panelHgtMin / 2);
}
}
if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc) {
this.setPanZoomTransition(panTime);
- const minScale = NumCast(this.rootDoc._freeform_scale_min, 1);
- const scale = 1 - minScale / this.getLocalTransform().inverse().Scale;
- const minPanX = NumCast(this.rootDoc._freeform_panX_min, 0);
- const minPanY = NumCast(this.rootDoc._freeform_panY_min, 0);
- const maxPanX = NumCast(this.rootDoc._freeform_panX_max, this.nativeWidth);
+ const minScale = NumCast(this.dataDoc._freeform_scale_min, 1);
+ const scale = 1 - minScale / this.zoomScaling();
+ const minPanX = NumCast(this.dataDoc._freeform_panX_min, 0);
+ const minPanY = NumCast(this.dataDoc._freeform_panY_min, 0);
+ const maxPanX = NumCast(this.dataDoc._freeform_panX_max, this.nativeWidth);
const newPanX = Math.min(minPanX + scale * maxPanX, Math.max(minPanX, panX));
- const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this.props.PanelWidth() - this.props.PanelHeight()) * this.props.ScreenToLocalTransform().Scale) / minScale;
- const nativeHeight = (this.props.PanelHeight() / this.props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
- const maxScrollTop = this.nativeHeight / this.props.ScreenToLocalTransform().Scale - this.props.PanelHeight();
+ const fitYscroll = (((this.nativeHeight / this.nativeWidth) * this._props.PanelWidth() - this._props.PanelHeight()) * this.ScreenToLocalBoxXf().Scale) / minScale;
+ const nativeHeight = (this._props.PanelHeight() / this._props.PanelWidth() / (this.nativeHeight / this.nativeWidth)) * this.nativeHeight;
+ const maxScrollTop = this.nativeHeight / this.ScreenToLocalBoxXf().Scale - this._props.PanelHeight();
const maxPanY =
minPanY + // minPanY + scrolling introduced by view scaling + scrolling introduced by layout_fitWidth
- scale * NumCast(this.rootDoc._panY_max, nativeHeight) +
- (!this.props.getScrollHeight?.() ? fitYscroll : 0); // when not zoomed, scrolling is handled via a scrollbar, not panning
+ 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.rootDoc.layout_scrollTop) && NumCast(this.rootDoc._freeform_scale, minScale) !== minScale) {
- const relTop = NumCast(this.rootDoc.layout_scrollTop) / maxScrollTop;
- this.rootDoc.layout_scrollTop = undefined;
+ 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.rootDoc.layout_scrollTop === undefined && NumCast(this.rootDoc._freeform_scale, minScale) === minScale) {
+ } 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.rootDoc.layout_scrollTop = relTop * maxScrollTop), 10);
+ setTimeout(() => (this.layoutDoc.layout_scrollTop = relTop * maxScrollTop), 10);
newPanY = minPanY;
}
!this.Document._verticalScroll && (this.Document[this.panXFieldKey] = this.isAnnotationOverlay ? newPanX : panX);
@@ -1162,11 +1060,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
nudge = (x: number, y: number, nudgeTime: number = 500) => {
- const collectionDoc = this.props.docViewPath().lastElement().rootDoc;
- if (collectionDoc?._type_collection !== CollectionViewType.Freeform || collectionDoc._freeform_ !== undefined) {
+ const collectionDoc = this.Document;
+ if (collectionDoc?._type_collection !== CollectionViewType.Freeform) {
this.setPan(
- NumCast(this.layoutDoc[this.panXFieldKey]) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
- NumCast(this.layoutDoc[this.panYFieldKey]) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(),
+ NumCast(this.layoutDoc[this.panXFieldKey]) + ((this._props.PanelWidth() / 2) * x) / this.zoomScaling(), // nudge x,y as a function of panel dimension and scale
+ NumCast(this.layoutDoc[this.panYFieldKey]) + ((this._props.PanelHeight() / 2) * -y) / this.zoomScaling(),
nudgeTime,
true
);
@@ -1211,13 +1109,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
- if (this.Document._isGroup) return;
+ if (this.Document.isGroup) return;
this.setPanZoomTransition(transitionTime);
- const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
+ const screenXY = this.screenToFreeformContentsXf.inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
- const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
+ const newScreenXY = this.screenToFreeformContentsXf.inverse().transformPoint(docpt[0], docpt[1]);
const scrDelta = { x: screenXY[0] - newScreenXY[0], y: screenXY[1] - newScreenXY[1] };
- const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y);
+ const newpan = this.screenToFreeformContentsXf.transformDirection(scrDelta.x, scrDelta.y);
this.layoutDoc[this.panXFieldKey] = NumCast(this.layoutDoc[this.panXFieldKey]) - newpan[0];
this.layoutDoc[this.panYFieldKey] = NumCast(this.layoutDoc[this.panYFieldKey]) - newpan[1];
}
@@ -1225,7 +1123,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
const layoutdoc = Doc.Layout(doc);
const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
- const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[Width](), NumCast(doc.y) + layoutdoc[Height]());
+ const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height));
const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
if (scale !== undefined) {
@@ -1233,20 +1131,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newScale =
scale === 0
? NumCast(this.layoutDoc[this.scaleFieldKey])
- : Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+ : Math.min(maxZoom, (1 / (this.nativeDimScaling || 1)) * scale * Math.min(this._props.PanelWidth() / Math.abs(bounds.width), this._props.PanelHeight() / Math.abs(bounds.height)));
return {
- panX: this.props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
- panY: this.props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
+ panX: this._props.isAnnotationOverlay ? bounds.left - (Doc.NativeWidth(this.layoutDoc) / newScale - bounds.width) / 2 : (bounds.left + bounds.right) / 2,
+ panY: this._props.isAnnotationOverlay ? bounds.top - (Doc.NativeHeight(this.layoutDoc) / newScale - bounds.height) / 2 : (bounds.top + bounds.bot) / 2,
scale: newScale,
};
}
- const panelWidth = this.props.isAnnotationOverlay ? this.nativeWidth : this.props.PanelWidth();
- const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight();
+ const panelWidth = this._props.isAnnotationOverlay ? this.nativeWidth : this._props.PanelWidth();
+ const panelHeight = this._props.isAnnotationOverlay ? this.nativeHeight : this._props.PanelHeight();
const pw = panelWidth / NumCast(this.layoutDoc._freeform_scale, 1);
const ph = panelHeight / NumCast(this.layoutDoc._freeform_scale, 1);
- const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
- const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
+ const cx = NumCast(this.layoutDoc[this.panXFieldKey]) + (this._props.isAnnotationOverlay ? pw / 2 : 0);
+ const cy = NumCast(this.layoutDoc[this.panYFieldKey]) + (this._props.isAnnotationOverlay ? ph / 2 : 0);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
const maxYShift = Math.max(0, screen.bot - screen.top - (bounds.bot - bounds.top));
const phborder = bounds.top < screen.top || bounds.bot > screen.bot ? Math.min(ph / 10, maxYShift / 2) : 0;
@@ -1254,115 +1152,117 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return {
panX: (bounds.left + bounds.right) / 2,
panY: (bounds.top + bounds.bot) / 2,
- scale: Math.min(this.props.PanelHeight() / (bounds.bot - bounds.top), this.props.PanelWidth() / (bounds.right - bounds.left)) / 1.1,
+ scale: Math.min(this._props.PanelHeight() / (bounds.bot - bounds.top), this._props.PanelWidth() / (bounds.right - bounds.left)) / 1.1,
};
}
return {
- panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panXFieldKey]) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
- panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panYFieldKey]) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
+ panX: (this._props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panXFieldKey]) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
+ panY: (this._props.isAnnotationOverlay ? NumCast(this.layoutDoc[this.panYFieldKey]) : cy) + Math.min(0, bounds.top - phborder - screen.top) + Math.max(0, bounds.bot + phborder - screen.bot),
};
};
- isContentActive = () => this.props.isContentActive();
+ isContentActive = () => this._props.isContentActive();
@undoBatch
- @action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- const docView = fieldProps.DocumentView?.();
- if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
+ if ((e.metaKey || e.ctrlKey || e.altKey || fieldProps.Document._createDocOnCR) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
const below = !e.altKey && e.key !== 'Tab';
- const layout_fieldKey = StrCast(docView.LayoutFieldKey);
- const newDoc = Doc.MakeCopy(docView.rootDoc, true);
- const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+ const layout_fieldKey = 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 (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
- else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
- if (layout_fieldKey !== 'layout' && docView.rootDoc[layout_fieldKey] instanceof Doc) {
- newDoc[layout_fieldKey] = docView.rootDoc[layout_fieldKey];
+ if (below) newDoc.y = NumCast(fieldProps.Document.y) + NumCast(fieldProps.Document._height) + 10;
+ else newDoc.x = NumCast(fieldProps.Document.x) + NumCast(fieldProps.Document._width) + 10;
+ if (layout_fieldKey !== 'layout' && fieldProps.Document[layout_fieldKey] instanceof Doc) {
+ newDoc[layout_fieldKey] = fieldProps.Document[layout_fieldKey];
}
- Doc.GetProto(newDoc).text = undefined;
- FormattedTextBox.SelectOnLoad = newDoc[Id];
+ newDoc[DocData].text = undefined;
+ FormattedTextBox.SetSelectOnLoad(newDoc);
return this.addDocument?.(newDoc);
}
};
@computed get childPointerEvents() {
- const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
- const pointerEvents = DocumentView.Interacting
+ 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(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.());
- return pointerEvents;
+ : this._props.childPointerEvents?.() ??
+ (this._props.viewDefDivClick || //
+ (engine === computePassLayout.name && !this._props.isSelected()) ||
+ this.isContentActive() === false
+ ? 'none'
+ : this._props.pointerEvents?.());
}
- childPointerEventsFunc = () => this.childPointerEvents;
- childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
+
+ @observable _childPointerEvents: 'none' | 'all' | 'visiblepainted' | undefined = undefined;
+ childPointerEventsFunc = () => this._childPointerEvents;
+ childContentsActive = () => (this._props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)();
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
return (
- <CollectionFreeFormDocumentView
+ <CollectionFreeFormDocumentViewWrapper
+ {...OmitKeys(entry, ['replica', 'pair']).omit}
key={childLayout[Id] + (entry.replica || '')}
- DataDoc={childData}
Document={childLayout}
- isGroupActive={this.props.isGroupActive}
- renderDepth={this.props.renderDepth + 1}
- replica={entry.replica}
+ containerViewPath={this.DocumentView?.().docViewPath}
+ styleProvider={this.clusterStyleProvider}
+ TemplateDataDocument={childData}
+ dragStarting={this.dragStarting}
+ dragEnding={this.dragEnding}
+ isGroupActive={this._props.isGroupActive}
+ renderDepth={this._props.renderDepth + 1}
hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
- renderCutoffProvider={this.renderCutoffProvider}
+ RenderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
- LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
- LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
+ LayoutTemplate={childLayout.z ? undefined : this._props.childLayoutTemplate}
+ LayoutTemplateString={childLayout.z ? undefined : this._props.childLayoutString}
rootSelected={childData ? this.rootSelected : returnFalse}
- waitForDoubleClickToClick={this.props.waitForDoubleClickToClick}
- onClick={this.onChildClickHandler}
+ waitForDoubleClickToClick={this._props.waitForDoubleClickToClick}
+ onClickScript={this.onChildClickHandler}
onKey={this.onKeyDown}
- onDoubleClick={this.onChildDoubleClickHandler}
- onBrowseClick={this.onBrowseClickHandler}
- ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
+ onBrowseClickScript={this.onBrowseClickHandler}
+ ScreenToLocalTransform={childLayout.z ? this.ScreenToLocalBoxXf : this.ScreenToContentsXf}
PanelWidth={childLayout[Width]}
PanelHeight={childLayout[Height]}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+ isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive}
isContentActive={this.childContentsActive}
- focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus}
+ focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus}
addDocTab={this.addDocTab}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- moveDocument={this.props.moveDocument}
- pinToPres={this.props.pinToPres}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- docViewPath={this.props.docViewPath}
- styleProvider={this.getClusterColor}
- dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
- dataProvider={this.childDataProvider}
- sizeProvider={this.childSizeProvider}
- bringToFront={this.bringToFront}
- layout_showTitle={this.props.childlayout_showTitle}
- dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
+ addDocument={this._props.addDocument}
+ removeDocument={this._props.removeDocument}
+ moveDocument={this._props.moveDocument}
+ pinToPres={this._props.pinToPres}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
+ layout_showTitle={this._props.childlayout_showTitle}
+ dontRegisterView={this._props.dontRegisterView}
pointerEvents={this.childPointerEventsFunc}
- //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.treeView_FreezeChildDimensions)} // bcz: check this
/>
);
}
addDocTab = action((doc: Doc, where: OpenWhere) => {
- if (this.props.isAnnotationOverlay) return this.props.addDocTab(doc, where);
+ if (this._props.isAnnotationOverlay) return this._props.addDocTab(doc, where);
switch (where) {
case OpenWhere.inParent:
- return this.props.addDocument?.(doc) || false;
+ return this._props.addDocument?.(doc) || false;
case OpenWhere.inParentFromScreen:
const docContext = DocCast((doc instanceof Doc ? doc : doc?.[0])?.embedContainer);
return (
(this.addDocument?.(
(doc instanceof Doc ? [doc] : doc).map(doc => {
- const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
+ const pt = this.screenToFreeformContentsXf.transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = pt[0];
doc.y = pt[1];
return doc;
})
) &&
- (!docContext || this.props.removeDocument?.(docContext))) ||
+ (!docContext || this._props.removeDocument?.(docContext))) ||
false
);
case undefined:
@@ -1376,21 +1276,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
}
- return this.props.addDocTab(doc, where);
+ return this._props.addDocTab(doc, where);
});
- @observable _lightboxDoc: Opt<Doc>;
+ @observable _lightboxDoc: Opt<Doc> = undefined;
- getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
+ getCalculatedPositions(pair: { layout: Doc; data?: Doc }): PoolData {
const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min);
- const childDoc = params.pair.layout;
+ const childDoc = pair.layout;
const childDocLayout = Doc.Layout(childDoc);
const layoutFrameNumber = Cast(this.Document._currentFrame, 'number'); // frame number that container is at which determines layout frame values
const contentFrameNumber = Cast(childDocLayout._currentFrame, 'number', layoutFrameNumber ?? null); // frame number that content is at which determines what content is displayed
const { z, zIndex } = childDoc;
const { backgroundColor, color } = contentFrameNumber === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, contentFrameNumber);
- const { x, y, _width, _height, opacity, _rotation } =
- layoutFrameNumber === undefined
- ? { _width: Cast(childDocLayout._width, 'number'), _height: Cast(childDocLayout._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() }
+ const { x, y, autoDim, _width, _height, opacity, _rotation } =
+ layoutFrameNumber === undefined // -1 for width/height means width/height should be PanelWidth/PanelHeight (prevents collectionfreeformdocumentview width/height from getting out of synch with panelWIdth/Height which causes detailView to re-render and lose focus because HTMLtag scaling gets set to a bad intermediate value)
+ ? { autoDim: 1, _width: Cast(childDoc._width, 'number'), _height: Cast(childDoc._height, 'number'), _rotation: Cast(childDocLayout._rotation, 'number'), x: childDoc.x, y: childDoc.y, opacity: this._props.childOpacity?.() }
: CollectionFreeFormDocumentView.getValues(childDoc, layoutFrameNumber);
// prettier-ignore
const rotation = Cast(_rotation,'number',
@@ -1400,22 +1300,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
- rotation: rotation,
- color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
- backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
- opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
+ autoDim,
+ rotation,
+ color: Cast(color, 'string') ? StrCast(color) : this._props.styleProvider?.(childDoc, this._props, StyleProp.Color),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, this._props, StyleProp.BackgroundColor),
+ opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this._props.styleProvider?.(childDoc, this._props, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: _width,
height: _height,
transition: StrCast(childDocLayout.dataTransition),
- pair: params.pair,
pointerEvents: Cast(childDoc.pointerEvents, 'string', null),
+ pair,
replica: '',
};
}
onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
- (this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
+ (this._props.viewDefDivClick || ScriptCast(this.Document.onViewDefDivClick))?.script.run({ this: this.Document, payload });
e.stopPropagation();
};
@@ -1466,34 +1367,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}.bind(this)
);
- childPositionProviderUnmemoized = (doc: Doc, replica: string) => this._layoutPoolData.get(doc[Id] + (replica || ''));
- childDataProvider = computedFn(
- function childDataProvider(this: any, doc: Doc, replica: string) {
- return this.childPositionProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
- childSizeProviderUnmemoized = (doc: Doc, replica: string) => this._layoutSizeData.get(doc[Id] + (replica || ''));
- childSizeProvider = computedFn(
- function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this.childSizeProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
doEngineLayout(
poolData: Map<string, PoolData>,
engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
) {
- return engine(poolData, this.props.Document, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX, this.props.engineProps);
+ return engine(poolData, this.Document, this.childLayoutPairs, [this._props.PanelWidth(), this._props.PanelHeight()], this.viewDefsToJSX, this._props.engineProps);
}
doFreeformLayout(poolData: Map<string, PoolData>) {
- this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions({ pair, index: i, collection: this.Document })));
+ this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map((pair, i) => poolData.set(pair.layout[Id], this.getCalculatedPositions(pair)));
return [] as ViewDefResult[];
}
@computed get layoutEngine() {
- return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
}
@computed get doInternalLayoutComputation() {
TraceMobx();
@@ -1508,94 +1395,58 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
- @observable _numLoaded = 1;
- _lastPoolSize = 0;
- get doLayoutComputation() {
- const { newPool, computedElementData } = this.doInternalLayoutComputation;
- const array = Array.from(newPool.entries());
- let somethingChanged = array.length !== this._lastPoolSize;
- this._lastPoolSize = array.length;
- runInAction(() => {
- for (const entry of array) {
- const lastPos = this._cachedPool.get(entry[0]); // last computed pos
- const newPos = entry[1];
- if (
- !lastPos ||
- newPos.color !== lastPos.color ||
- newPos.backgroundColor !== lastPos.backgroundColor ||
- newPos.opacity !== lastPos.opacity ||
- newPos.x !== lastPos.x ||
- newPos.y !== lastPos.y ||
- newPos.z !== lastPos.z ||
- newPos.rotation !== lastPos.rotation ||
- newPos.zIndex !== lastPos.zIndex ||
- newPos.transition !== lastPos.transition ||
- newPos.pointerEvents !== lastPos.pointerEvents
- ) {
- this._layoutPoolData.set(entry[0], newPos);
- somethingChanged = true;
- }
- if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
- this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
- somethingChanged = true;
- }
- }
- });
- if (!somethingChanged) return undefined;
- this._cachedPool.clear();
- Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
+ @action
+ doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
const elements = computedElementData.slice();
Array.from(newPool.entries())
.filter(entry => this.isCurrent(entry[1].pair.layout))
- .forEach((entry, i) => {
- const childData: ViewDefBounds = this.childDataProvider(entry[1].pair.layout, entry[1].replica);
- const childSize = this.childSizeProvider(entry[1].pair.layout, entry[1].replica);
+ .forEach(entry =>
elements.push({
ele: this.getChildDocView(entry[1]),
- bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height },
+ bounds: (entry[1].opacity === 0 ? { ...entry[1], width: 0, height: 0 } : { ...entry[1] }) as any,
inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
- });
- });
+ })
+ );
this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
return elements;
- }
+ };
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
// create an anchor that saves information about the current state of the freeform view (pan, zoom, view type)
- const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.rootDoc });
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document._isGroup, type_collection: true, filters: true } }, this.rootDoc);
+ const anchor = Docs.Create.ConfigDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._type_collection), layout_unrendered: true, presentation_transition: 500, annotationOn: this.Document });
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), pannable: !this.Document.isGroup, type_collection: true, filters: true } }, this.Document);
if (addAsAnnotation) {
- if (Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this.props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
+ if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
} else {
- this.dataDoc[this.props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
+ this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
}
}
return anchor;
};
- @action
+ @action closeInfo = () => (this.Document._hideInfo = true);
+ infoUI = () => (this.Document._hideInfo || this.Document.annotationOn || this._props.renderDepth ? null : <CollectionFreeFormInfoUI Document={this.Document} Freeform={this} close={this.closeInfo} />);
+
componentDidMount() {
- this.printDoc(this.rootDoc);
- this.props.setContentView?.(this);
+ this._props.setContentViewBox?.(this);
super.componentDidMount?.();
- this.props.setBrushViewer?.(this.brushView);
setTimeout(
action(() => {
this._firstRender = false;
this._disposers.groupBounds = reaction(
() => {
- if (this.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[Width](), height: cd[Height]() }));
+ if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) {
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
}
return undefined;
},
cbounds => {
if (cbounds) {
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[Width]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[Height]() / 2];
+ const c = [NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) / 2, NumCast(this.layoutDoc.y) + NumCast(this.layoutDoc._height) / 2];
const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])];
const pbounds = {
x: cbounds.x - p[0] + c[0],
@@ -1616,20 +1467,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{ fireImmediately: true }
);
- this._disposers.layoutComputation = reaction(
- () => this.doLayoutComputation,
- elements => {
- if (elements !== undefined) this._layoutElements = elements || [];
- },
- { fireImmediately: true, name: 'doLayout' }
+ this._disposers.pointerevents = reaction(
+ () => this.childPointerEvents,
+ pointerevents => (this._childPointerEvents = pointerevents as any),
+ { fireImmediately: true }
);
this._disposers.active = reaction(
() => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
- active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
+ active => !SnappingManager.IsDragging && this.dataDoc[this.autoResetFieldKey] && active && this.resetView()
);
})
);
+ 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)),
+ { fireImmediately: true }
+ );
}
static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
@@ -1667,19 +1522,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
updateIcon = () =>
CollectionFreeFormView.UpdateIcon(
this.layoutDoc[Id] + '-icon' + new Date().getTime(),
- this.props.docViewPath().lastElement().ContentDiv!,
- this.layoutDoc[Width](),
- this.layoutDoc[Height](),
- this.props.PanelWidth(),
- this.props.PanelHeight(),
+ this.DocumentView?.().ContentDiv!,
+ NumCast(this.layoutDoc._width),
+ NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(),
+ this._props.PanelHeight(),
0,
1,
false,
'',
(iconFile, nativeWidth, nativeHeight) => {
this.dataDoc.icon = new ImageField(iconFile);
- this.dataDoc['icon_nativeWidth'] = nativeWidth;
- this.dataDoc['icon_nativeHeight'] = nativeHeight;
+ this.dataDoc.icon_nativeWidth = nativeWidth;
+ this.dataDoc.icon_nativeHeight = nativeHeight;
}
);
@@ -1714,8 +1569,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
componentWillUnmount() {
+ this.dataDoc[this.autoResetFieldKey] && this.resetView();
Object.values(this._disposers).forEach(disposer => disposer?.());
- this._marqueeRef?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
}
@action
@@ -1723,35 +1578,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
- @action
- onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
- if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
- (e as any).handlePan = true;
-
- if (!this.layoutDoc._freeform_noAutoPan && !this.props.renderDepth && this._marqueeRef) {
- const dragX = e.detail.clientX;
- const dragY = e.detail.clientY;
- const bounds = this._marqueeRef?.getBoundingClientRect();
-
- const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0;
- const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0;
- if (deltaX !== 0 || deltaY !== 0) {
- this.Document[this.panYFieldKey] = NumCast(this.Document[this.panYFieldKey]) + deltaY / 2;
- this.Document[this.panXFieldKey] = NumCast(this.Document[this.panXFieldKey]) + deltaX / 2;
- }
- }
- e.stopPropagation();
- };
-
@undoBatch
promoteCollection = () => {
const childDocs = this.childDocs.slice();
childDocs.forEach(doc => {
- const scr = this.getTransform().inverse().transformPoint(NumCast(doc.x), NumCast(doc.y));
+ 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 as any as Doc, OpenWhere.inParentFromScreen);
};
@undoBatch
@@ -1775,9 +1610,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
///
@undoBatch
resetView = () => {
- this.rootDoc[this.panXFieldKey] = NumCast(this.rootDoc[this.panXFieldKey + '_reset']);
- this.props.Document[this.panYFieldKey] = NumCast(this.rootDoc[this.panYFieldKey + '_reset']);
- this.rootDoc[this.scaleFieldKey] = NumCast(this.rootDoc[this.scaleFieldKey + '_reset'], 1);
+ this.layoutDoc[this.panXFieldKey] = NumCast(this.dataDoc[this.panXFieldKey + '_reset']);
+ this.layoutDoc[this.panYFieldKey] = NumCast(this.dataDoc[this.panYFieldKey + '_reset']);
+ this.layoutDoc[this.scaleFieldKey] = NumCast(this.dataDoc[this.scaleFieldKey + '_reset'], 1);
};
///
/// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
@@ -1785,11 +1620,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
///
@undoBatch
toggleResetView = () => {
- this.rootDoc[this.autoResetFieldKey] = !this.rootDoc[this.autoResetFieldKey];
- if (this.rootDoc[this.autoResetFieldKey]) {
- this.rootDoc[this.panXFieldKey + '_reset'] = this.rootDoc[this.panXFieldKey];
- this.rootDoc[this.panYFieldKey + '_reset'] = this.rootDoc[this.panYFieldKey];
- this.rootDoc[this.scaleFieldKey + '_reset'] = this.rootDoc[this.scaleFieldKey];
+ this.dataDoc[this.autoResetFieldKey] = !this.dataDoc[this.autoResetFieldKey];
+ if (this.dataDoc[this.autoResetFieldKey]) {
+ this.dataDoc[this.panXFieldKey + '_reset'] = this.dataDoc[this.panXFieldKey];
+ this.dataDoc[this.panYFieldKey + '_reset'] = this.dataDoc[this.panYFieldKey];
+ this.dataDoc[this.scaleFieldKey + '_reset'] = this.dataDoc[this.scaleFieldKey];
}
};
@@ -1863,46 +1698,40 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
onContextMenu = (e: React.MouseEvent) => {
- if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
+ if (this._props.isAnnotationOverlay || !ContextMenu.Instance) return;
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
- !this.props.Document._isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
+ !this.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+ !this.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Toggle auto arrange',
event: () => (this.layoutDoc._autoArrange = !this.layoutDoc._autoArrange),
icon: 'compress-arrows-alt',
});
- if (this.props.setContentView === emptyFunction) {
+ if (this._props.setContentViewBox === emptyFunction) {
!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' });
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
+ 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' });
+ this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
- this.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' });
+ 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;
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
- const viewctrls = ContextMenu.Instance.findByDescription('UI Controls...');
- const viewCtrlItems = viewctrls && 'subitems' in viewctrls ? viewctrls.subitems : [];
- !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
- !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
-
const options = ContextMenu.Instance.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
- !this.props.isAnnotationOverlay &&
+ !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' });
- this.props.renderDepth && optionItems.push({ description: 'Fit Content Once', event: this.fitContentOnce, icon: 'object-group' });
- // gpt styling
- this.props.renderDepth && optionItems.push({ description: 'Style with AI', event: this.gptStyling, icon: 'paint-brush' });
+ 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' });
}
@@ -1913,17 +1742,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@undoBatch
- @action
- transcribeStrokes = (math: boolean) => {
- if (this.props.Document._isGroup && this.props.Document.transcription) {
- if (!math) {
- const text = StrCast(this.props.Document.transcription);
-
- const lines = text.split('\n');
- const height = 30 + 15 * lines.length;
+ transcribeStrokes = () => {
+ if (this.Document.isGroup && this.Document.transcription) {
+ const text = StrCast(this.Document.transcription);
+ const lines = text.split('\n');
+ const height = 30 + 15 * lines.length;
- this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
- }
+ this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
}
};
@@ -1933,33 +1758,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.clearSnapLines();
};
@action
- dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean, visited = new Set<Doc>()) => {
- if (visited.has(this.rootDoc)) return;
- visited.add(this.rootDoc);
- showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
- if (this.rootDoc._isGroup && this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
- this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.dragStarting(snapToDraggedDoc, false, visited);
- }
+ dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
+ if (visited.has(this.Document)) return;
+ visited.add(this.Document);
+ showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.isGroup));
const activeDocs = this.getActiveDocuments();
- const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+ const size = this.screenToFreeformContentsXf.transformDirection(this._props.PanelWidth(), this._props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
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.forEach(
- doc =>
- doc._isGroup &&
- SnappingManager.GetIsResizing() !== doc &&
- !DragManager.docsBeingDragged.includes(doc) &&
- (DocumentManager.Instance.getDocumentView(doc)?.ComponentView as CollectionFreeFormView)?.dragStarting(snapToDraggedDoc, false, visited)
- );
+ activeDocs
+ .filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))
+ .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
- const invXf = this.getTransform().inverse();
+ const invXf = this.screenToFreeformContentsXf.inverse();
snappableDocs
- .filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
+ .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = invXf.transformPoint(left, top);
@@ -1974,7 +1792,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
incrementalRendering = () => this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id])).length !== 0;
incrementalRender = action(() => {
- if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+ if (!LightboxView.LightboxDoc || LightboxView.Contains(this.DocumentView?.())) {
const layout_unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = 5;
for (var i = 0; i < Math.min(layout_unrendered.length, loadIncrement); i++) {
@@ -1984,35 +1802,61 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
- get children() {
- this.incrementalRender();
- const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : [];
- return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
- }
-
@computed get placeholder() {
return (
- <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.props.Document.annotationOn ? '' : this.props.Document.title?.toString()}</span>
+ <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>
);
}
brushedView = () => this._brushedView;
gridColor = () =>
- DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)))
- .fade(0.6)
+ DashColor(lightOrDark(this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor)))
+ .fade(0.5)
.toString();
- @computed get marqueeView() {
- TraceMobx();
- return this._firstRender ? (
- this.placeholder
- ) : (
+ @computed 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!!?
+ PanelWidth={this._props.PanelWidth}
+ PanelHeight={this._props.PanelHeight}
+ panX={this.panX}
+ panY={this.panY}
+ color={this.gridColor}
+ nativeDimScaling={this.nativeDim}
+ zoomScaling={this.zoomScaling}
+ layoutDoc={this.layoutDoc}
+ isAnnotationOverlay={this.isAnnotationOverlay}
+ cachedCenteringShiftX={this.cachedCenteringShiftX}
+ cachedCenteringShiftY={this.cachedCenteringShiftY}
+ />
+ </div>
+ );
+ }
+ get pannableContents() {
+ this.incrementalRender();
+ return (
+ <CollectionFreeFormPannableContents
+ Document={this.Document}
+ 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))}
+ 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}
+ <CollectionFreeFormRemoteCursors {...this._props} key="remoteCursors" />
+ </CollectionFreeFormPannableContents>
+ );
+ }
+ get marqueeView() {
+ return (
<MarqueeView
- {...this.props}
+ {...this._props}
ref={this._marqueeViewRef}
- ungroup={this.rootDoc._isGroup ? this.promoteCollection : undefined}
- nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
+ ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
+ nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
trySelectCluster={this.trySelectCluster}
@@ -2020,81 +1864,48 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform}
- getTransform={this.getTransform}
+ getContainerTransform={this.ScreenToLocalBoxXf}
+ getTransform={this.ScreenToContentsXf}
+ panXFieldKey={this.panXFieldKey}
+ panYFieldKey={this.panYFieldKey}
isAnnotationOverlay={this.isAnnotationOverlay}>
- <div
- className="marqueeView-div"
- ref={r => {
- this._marqueeRef = r;
- r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
- }}
- style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
- {this.layoutDoc._freeform_backgroundGrid ? (
- <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!!?
- PanelWidth={this.props.PanelWidth}
- PanelHeight={this.props.PanelHeight}
- panX={this.panX}
- panY={this.panY}
- color={this.gridColor}
- nativeDimScaling={this.nativeDim}
- zoomScaling={this.zoomScaling}
- layoutDoc={this.layoutDoc}
- isAnnotationOverlay={this.isAnnotationOverlay}
- cachedCenteringShiftX={this.cachedCenteringShiftX}
- cachedCenteringShiftY={this.cachedCenteringShiftY}
- />
- </div>
- ) : null}
- <CollectionFreeFormViewPannableContents
- rootDoc={this.rootDoc}
- brushedView={this.brushedView}
- isAnnotationOverlay={this.isAnnotationOverlay}
- transform={this.contentTransform}
- transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
- viewDefDivClick={this.props.viewDefDivClick}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
- </div>
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : null}
+ {this.layoutDoc._freeform_backgroundGrid ? this.backgroundGrid : null}
+ {this.pannableContents}
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this._props} /> : null}
</MarqueeView>
);
}
@computed get nativeDimScaling() {
- if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
+ if (this._firstRender || (this._props.isAnnotationOverlay && !this._props.annotationLayerHostsContent)) return 0;
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.layoutDoc.layout_fitWidth ? wscale : hscale;
+ 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) => {
+ 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)),
- 2500
- );
+ 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.props.ScreenToLocalTransform().translate(-15, -15);
+ 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.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
- const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling;
- this.props.isSelected() && !scrollable && e.preventDefault();
+ 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() {
@@ -2117,31 +1928,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onDragOver={e => e.preventDefault()}
onContextMenu={this.onContextMenu}
style={{
- pointerEvents: this.props.isContentActive() && SnappingManager.GetIsDragging() ? 'all' : (this.props.pointerEvents?.() as any),
+ pointerEvents: this._props.isContentActive() && SnappingManager.IsDragging ? 'all' : (this._props.pointerEvents?.() as any),
textAlign: this.isAnnotationOverlay ? 'initial' : undefined,
transform: `scale(${this.nativeDimScaling || 1})`,
width: `${100 / (this.nativeDimScaling || 1)}%`,
- height: this.props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
+ height: this._props.getScrollHeight?.() ?? `${100 / (this.nativeDimScaling || 1)}%`,
}}>
{this._lightboxDoc ? (
<div style={{ padding: 15, width: '100%', height: '100%' }}>
<DocumentView
- {...this.props}
+ {...this._props}
Document={this._lightboxDoc}
- DataDoc={undefined}
+ containerViewPath={this.DocumentView?.().docViewPath}
+ TemplateDataDocument={undefined}
PanelWidth={this.lightboxPanelWidth}
PanelHeight={this.lightboxPanelHeight}
NativeWidth={returnZero}
NativeHeight={returnZero}
- onClick={this.onChildClickHandler}
+ onClickScript={this.onChildClickHandler}
onKey={this.onKeyDown}
- onDoubleClick={this.onChildDoubleClickHandler}
- onBrowseClick={this.onBrowseClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
+ onBrowseClickScript={this.onBrowseClickHandler}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
- isContentActive={this.props.childContentsActive ?? emptyFunction}
+ isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive}
+ isContentActive={this._props.childContentsActive ?? emptyFunction}
addDocTab={this.addDocTab}
ScreenToLocalTransform={this.lightboxScreenToLocal}
fitContentsToBox={undefined}
@@ -2150,8 +1962,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
) : (
<>
- {this.marqueeView}
- {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
+ {this._firstRender ? this.placeholder : this.marqueeView}
+ {this._props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
{!this.GroupChildDrag ? null : <div className="collectionFreeForm-groupDropper" />}
</>
)}
@@ -2160,139 +1972,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
-interface CollectionFreeFormOverlayViewProps {
- elements: () => ViewDefResult[];
-}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps> {
- render() {
- return this.props
- .elements()
- .filter(ele => ele.bounds?.z)
- .map(ele => ele.ele);
- }
-}
-
-interface CollectionFreeFormViewPannableContentsProps {
- rootDoc: Doc;
- viewDefDivClick?: ScriptField;
- children?: React.ReactNode | undefined;
- transition?: string;
- isAnnotationOverlay: boolean | undefined;
- transform: () => string;
- brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
-}
-
-@observer
-class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps> {
- @computed get presPaths() {
- return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : 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) =>
- !viewport ? null : (
- <div
- className="collectionFreeFormView-brushView"
- style={{
- transform: `translate(${viewport.panX}px, ${viewport.panY}px)`,
- width: viewport.width,
- height: viewport.height,
- border: `orange solid ${viewport.width * 0.005}px`,
- }}
- />
- );
-
- render() {
- return (
- <div
- className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
- onScroll={e => {
- const target = e.target as any;
- if (getComputedStyle(target)?.overflow === 'visible') {
- target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
- }
- }}
- 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
- }}>
- {this.props.children}
- {this.presPaths}
- {this.showViewport(this.props.brushedView())}
- </div>
- );
- }
-}
-
-interface CollectionFreeFormViewBackgroundGridProps {
- panX: () => number;
- panY: () => number;
- PanelWidth: () => number;
- PanelHeight: () => number;
- color: () => string;
- isAnnotationOverlay?: boolean;
- nativeDimScaling: () => number;
- zoomScaling: () => number;
- layoutDoc: Doc;
- cachedCenteringShiftX: number;
- cachedCenteringShiftY: number;
-}
@observer
-class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
- chooseGridSpace = (gridSpace: number): number => {
- if (!this.props.zoomScaling()) return gridSpace;
- const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace;
- return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2);
- };
+class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
render() {
- const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
- const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling();
- const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling();
- const renderGridSpace = gridSpace * this.props.zoomScaling();
- const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace;
- const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace;
- const strokeStyle = this.props.color();
- return !this.props.nativeDimScaling() ? null : (
- <canvas
- className="collectionFreeFormView-grid"
- width={w}
- height={h}
- style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
- ref={el => {
- const ctx = el?.getContext('2d');
- if (ctx) {
- const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
- const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
- ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
- ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
- ctx.clearRect(0, 0, w, h);
- if (ctx) {
- ctx.strokeStyle = strokeStyle;
- ctx.fillStyle = strokeStyle;
- ctx.beginPath();
- if (this.props.zoomScaling() > 1) {
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
- ctx.moveTo(x, Cy - h);
- ctx.lineTo(x, Cy + h);
- }
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.moveTo(Cx - w, y);
- ctx.lineTo(Cx + w, y);
- }
- } else {
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace)
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
- }
- }
- ctx.stroke();
- }
- }
- }}
- />
- );
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
}
}
@@ -2300,48 +1983,85 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
const browseTransitionTime = 500;
SelectionManager.DeselectAll();
dv &&
- DocumentManager.Instance.showDocument(dv.rootDoc, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
+ DocumentManager.Instance.showDocument(dv.Document, { zoomScale: 0.8, willZoomCentered: true }, (focused: boolean) => {
if (!focused) {
- const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
- let containers = dv.props.docViewPath();
- let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ 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.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ parFfview = parFfview ?? cont.CollectionFreeFormView;
}
- while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.rootDoc[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.getTransform().transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
- Doc.linkFollowHighlight(dv?.props.Document, false);
+ 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);
ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+ !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
});
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
- !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+ !readOnly && (SelectionManager.Views[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
ScriptingGlobals.add(function curKeyFrame(readOnly: boolean) {
- const selView = SelectionManager.Views();
+ const selView = SelectionManager.Views;
if (readOnly) return selView[0].ComponentView?.getKeyFrameEditing?.() ? Colors.MEDIUM_BLUE : 'transparent';
runInAction(() => selView[0].ComponentView?.setKeyFrameEditing?.(!selView[0].ComponentView?.getKeyFrameEditing?.()));
});
ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
- SelectionManager.Views().forEach(view =>
- view.props.pinToPres(view.rootDoc, {
- currentFrame: Cast(view.rootDoc.currentFrame, 'number', null),
+ SelectionManager.Views.forEach(view =>
+ view._props.pinToPres(view.Document, {
+ currentFrame: Cast(view.Document.currentFrame, 'number', null),
pinData: {
poslayoutview: pinContent,
dataview: pinContent,
},
- pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()),
+ pinViewport: MarqueeView.CurViewBounds(view.Document, view._props.PanelWidth(), view._props.PanelHeight()),
})
);
});
ScriptingGlobals.add(function bringToFront() {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc));
+ SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document));
});
ScriptingGlobals.add(function sendToBack(doc: Doc) {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
+ SelectionManager.Views.forEach(view => view.CollectionFreeFormView?.bringToFront(view.Document, true));
+});
+ScriptingGlobals.add(function datavizFromSchema(doc: Doc) {
+ SelectionManager.Views.forEach(view => {
+ 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');
+ if (!keys) return;
+
+ const children = DocListCast(view.Document[Doc.LayoutFieldKey(view.Document)]);
+ let csvRows = [];
+ csvRows.push(keys.join(','));
+ for (let i = 0; i < children.length; i++) {
+ let eachRow = [];
+ for (let j = 0; j < keys.length; j++) {
+ var cell = children[i][keys[j]];
+ if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, '');
+ eachRow.push(cell);
+ }
+ csvRows.push(eachRow);
+ }
+ const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' });
+ const options = { x: 0, y: -300, title: 'schemaTable', _width: 300, _height: 100, type: 'text/csv' };
+ const file = new File([blob], 'schemaTable', options);
+ const loading = Docs.Create.LoadingDocument(file, options);
+ loading.presentation_openInLightbox = true;
+ DocUtils.uploadFileToDoc(file, {}, loading);
+
+ if (view.ComponentView?.addDocument) {
+ // loading.dataViz_fromSchema = true;
+ loading.dataViz_asSchema = view.layoutDoc;
+ SchemaCSVPopUp.Instance.setView(view);
+ SchemaCSVPopUp.Instance.setTarget(view.layoutDoc);
+ SchemaCSVPopUp.Instance.setDataVizDoc(loading);
+ SchemaCSVPopUp.Instance.setVisible(true);
+ }
+ });
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 607f9fb95..79cc534dc 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,14 +1,11 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import { IconButton } from 'browndash-components';
+import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { unimplementedFunction } from '../../../../Utils';
-import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
-import { IconButton } from 'browndash-components';
-import { StrCast } from '../../../../fields/Types';
-import { Doc } from '../../../../fields/Doc';
-import { computed } from 'mobx';
import { SettingsManager } from '../../../util/SettingsManager';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@observer
export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -21,9 +18,9 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public hideMarquee: () => void = unimplementedFunction;
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public isShown = () => this._opacity > 0;
- constructor(props: Readonly<{}>) {
+ constructor(props: any) {
super(props);
-
+ makeObservable(this);
MarqueeOptionsMenu.Instance = this;
}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index a30ec5302..c2f8232c6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,5 +1,7 @@
-import { action, computed, observable } from 'mobx';
+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 { Doc, Opt } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
@@ -9,25 +11,23 @@ import { RichTextField } from '../../../../fields/RichTextField';
import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
-import { intersectRect, lightOrDark, returnFalse, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
-import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
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 { Transform } from '../../../util/Transform';
-import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { UndoManager, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
import { PreviewCursor } from '../../PreviewCursor';
+import { OpenWhere } from '../../nodes/DocumentView';
+import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { SubCollectionViewProps } from '../CollectionSubView';
-import { TabDocView } from '../TabDocView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
-import React = require('react');
-import { freeformScrollMode } from '../../../util/SettingsManager';
-
interface MarqueeViewProps {
getContainerTransform: () => Transform;
getTransform: () => Transform;
@@ -35,6 +35,8 @@ interface MarqueeViewProps {
selectDocuments: (docs: Doc[]) => void;
addLiveTextDocument: (doc: Doc) => void;
isSelected: () => boolean;
+ panXFieldKey: string;
+ panYFieldKey: string;
trySelectCluster: (addToSel: boolean) => boolean;
nudge?: (x: number, y: number, nudgeTime?: number) => boolean;
ungroup?: () => void;
@@ -50,12 +52,17 @@ export interface MarqueeViewBounds {
}
@observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
+export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps & MarqueeViewProps> {
public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
const ps = NumCast(pinDoc._freeform_scale, 1);
return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
}
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
private _commandExecuted = false;
@observable _lastX: number = 0;
@observable _lastY: number = 0;
@@ -66,7 +73,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@observable _lassoFreehand: boolean = false;
@computed get Transform() {
- return this.props.getTransform();
+ return this._props.getTransform();
}
@computed get Bounds() {
// nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
@@ -76,18 +83,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const bounds: MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
return bounds;
}
- get inkDoc() {
- return this.props.Document;
- }
- get ink() {
- return Cast(this.props.Document.ink, InkField);
- }
- set ink(value: Opt<InkField>) {
- this.props.Document.ink = value;
- }
componentDidMount() {
- this.props.setPreviewCursor?.(this.setPreviewCursor);
+ this._props.setPreviewCursor?.(this.setPreviewCursor);
}
@action
@@ -109,20 +107,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
if (e.key === '?') {
- cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
+ cm.setDefaultItem('?', (str: string) => this._props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', data_useCors: true }), OpenWhere.addRight));
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
- } else if (e.key === 'u' && this.props.ungroup) {
+ } else if (e.key === 'u' && this._props.ungroup) {
e.stopPropagation();
- this.props.ungroup();
+ this._props.ungroup();
} else if (e.key === ':') {
- DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+ DocUtils.addDocumentCreatorMenuItems(this._props.addLiveTextDocument, this._props.addDocument || returnFalse, x, y);
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
} else if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
- this.props.selectDocuments(this.props.activeDocuments());
+ this._props.selectDocuments(this._props.activeDocuments());
e.stopPropagation();
} else if (e.key === 'q' && e.ctrlKey) {
e.preventDefault();
@@ -144,7 +142,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
ns.map(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);
+ this._props.addDocument?.(newBox);
ypos += 40 * this.Transform.Scale;
});
})();
@@ -155,8 +153,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
pasteImageBitmap((data: any, error: any) => {
error && console.log(error);
data &&
- Utils.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
- this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
+ Utils.convertDataUri(data, this._props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+ this._props.Document['thumb-frozen'] = new ImageField(returnedfilename);
});
})
);
@@ -167,7 +165,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
slide.y = y;
FormattedTextBox.SelectOnLoad = slide[Id];
TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
- this.props.addDocument?.(slide);
+ this._props.addDocument?.(slide);
e.stopPropagation();
}*/ else if (e.key === 'p' && e.ctrlKey) {
e.preventDefault();
@@ -177,10 +175,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.pasteTable(ns, x, y);
})();
e.stopPropagation();
- } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
- FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+ } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views.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, this.props.xPadding === 0));
+ this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this._props.xPadding === 0));
e.stopPropagation();
}
};
@@ -211,27 +209,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const file = new File([blob], 'droppedTable', options);
const loading = Docs.Create.LoadingDocument(file, options);
DocUtils.uploadFileToDoc(file, {}, loading);
- this.props.addDocument?.(loading);
+ this._props.addDocument?.(loading);
}
@action
onPointerDown = (e: React.PointerEvent): void => {
- // if (this.props.pointerEvents?.() === 'none') return;
this._downX = this._lastX = e.clientX;
this._downY = this._lastY = e.clientY;
- if (!(e.nativeEvent as any).marqueeHit) {
- (e.nativeEvent as any).marqueeHit = true;
- // allow marquee if right click OR alt+left click OR in adding presentation slide & left key drag mode
- if (e.button === 2 || (e.button === 0 && (e.altKey || (this.props.isContentActive?.(true) && Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan)))) {
- // if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
- this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
- // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
- e.preventDefault();
- // }
- // bcz: do we need this? it kills the context menu on the main collection if !altKey
- // e.stopPropagation();
- } else PreviewCursor.Visible = false;
- }
+
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+ // allow marquee if right drag/meta drag, or pan mode
+ if (e.button === 2 || e.metaKey || scrollMode === freeformScrollMode.Pan) {
+ this.setPreviewCursor(e.clientX, e.clientY, true, false, this._props.Document);
+ e.preventDefault();
+ } else PreviewCursor.Instance.Visible = false;
};
@action
@@ -240,7 +231,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._lastY = e.pageY;
this._lassoPts.push([e.clientX, e.clientY]);
if (!e.cancelBubble) {
- if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
+ if (!Utils.isClick(this._lastX, this._lastY, this._downX, this._downY, Date.now())) {
if (!this._commandExecuted) {
this.showMarquee();
}
@@ -258,12 +249,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (this._visible) {
const mselect = this.marqueeSelect();
if (!e.shiftKey) {
- SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
+ SelectionManager.DeselectAll(mselect.length ? undefined : this._props.Document);
}
- // let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
- // let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
- const docs = mselect.length ? mselect : [this.props.Document];
- this.props.selectDocuments(docs);
+ const docs = mselect.length ? mselect : [this._props.Document];
+ this._props.selectDocuments(docs);
}
const hideMarquee = () => {
this.hideMarquee();
@@ -302,14 +291,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._downX = this._lastX = x;
this._downY = this._lastY = y;
this._commandExecuted = false;
- PreviewCursor.Visible = false;
- PreviewCursor.Doc = undefined;
+ PreviewCursor.Instance.Visible = false;
+ PreviewCursor.Instance.Doc = undefined;
} else if (drag) {
this._downX = this._lastX = x;
this._downY = this._lastY = y;
this._commandExecuted = false;
- PreviewCursor.Visible = false;
- PreviewCursor.Doc = undefined;
+ PreviewCursor.Instance.Visible = false;
+ PreviewCursor.Instance.Doc = undefined;
this.cleanupInteractions(true);
document.addEventListener('pointermove', this.onPointerMove, true);
document.addEventListener('pointerup', this.onPointerUp, true);
@@ -317,10 +306,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
} else {
this._downX = x;
this._downY = y;
- const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
+ const effectiveAcl = GetEffectiveAcl(this._props.Document[DocData]);
if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) {
- PreviewCursor.Doc = doc;
- PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge, this.props.slowLoadDocuments);
+ PreviewCursor.Instance.Doc = doc;
+ PreviewCursor.Show(x, y, this.onKeyPress, this._props.addLiveTextDocument, this._props.getTransform, this._props.addDocument, this._props.nudge, this._props.slowLoadDocuments);
}
this.clearSelection();
}
@@ -328,15 +317,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onClick = (e: React.MouseEvent): void => {
- if (this.props.pointerEvents?.() === 'none') return;
+ if (this._props.pointerEvents?.() === 'none') return;
if (Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
if (Doc.ActiveTool === InkTool.None) {
- if (!(e.nativeEvent as any).marqueeHit) {
- (e.nativeEvent as any).marqueeHit = true;
- if (!this.props.trySelectCluster(e.shiftKey)) {
- !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
- } else e.stopPropagation();
- }
+ if (!this._props.trySelectCluster(e.shiftKey)) {
+ !SnappingManager.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this._props.Document);
+ } else e.stopPropagation();
}
// let the DocumentView stopPropagation of this event when it selects this document
} else {
@@ -352,30 +338,30 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
hideMarquee = () => (this._visible = false);
@undoBatch
- @action
- delete = (e?: React.PointerEvent<Element> | KeyboardEvent | undefined, hide?: boolean) => {
+ 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)));
+ selected.forEach(doc => (hide ? (doc.hidden = true) : this._props.removeDocument?.(doc)));
this.cleanupInteractions(false);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
- Doc.GetProto(doc).data = new List<Doc>(selected);
- Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
+ const docData = doc[DocData];
+ docData.data = new List<Doc>(selected);
+ docData.isGroup = makeGroup;
+ docData.title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.isSystem = undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
- newCollection._isGroup = makeGroup;
newCollection._dragWhenActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
@@ -386,17 +372,16 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
});
@undoBatch
- @action
- pileup = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ pileup = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
SelectionManager.DeselectAll();
- selected.forEach(d => this.props.removeDocument?.(d));
+ 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);
- this.props.selectDocuments([newCollection]);
+ this._props.addDocument?.(newCollection);
+ this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
/**
* This triggers the TabDocView.PinDoc method which is the universal method
@@ -405,35 +390,32 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
* This one is unique in that it includes the bounds associated with marquee view.
*/
@undoBatch
- @action
- pinWithView = () => {
- TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds });
+ pinWithView = action(() => {
+ this._props.pinToPres(this._props.Document, { pinViewport: this.Bounds });
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
@undoBatch
- @action
- collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => {
+ collection = action((e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => {
const selected = selection ?? this.marqueeSelect(false);
const activeFrame = selected.reduce((v, d) => v ?? Cast(d._activeFrame, 'number', null), undefined as number | undefined);
if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
- this.props.removeDocument?.(selected);
+ this._props.removeDocument?.(selected);
}
const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
newCollection._freeform_panY = this.Bounds.top + this.Bounds.height / 2;
newCollection._currentFrame = activeFrame;
- this.props.addDocument?.(newCollection);
- this.props.selectDocuments([newCollection]);
+ this._props.addDocument?.(newCollection);
+ this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- };
+ });
@undoBatch
- @action
- syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
+ syntaxHighlight = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
const inks = selected.filter(s => s.type === DocumentType.INK);
@@ -492,15 +474,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
const lines = results.filter((r: any) => r.category === 'line');
const text = lines.map((l: any) => l.recognizedText).join('\r\n');
- this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
+ this._props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
});
}
- };
+ });
@undoBatch
summary = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false).map(d => {
- this.props.removeDocument?.(d);
+ this._props.removeDocument?.(d);
d.x = NumCast(d.x) - this.Bounds.left;
d.y = NumCast(d.y) - this.Bounds.top;
return d;
@@ -520,8 +502,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
portal.hidden = true;
- this.props.addDocument?.(portal);
- this.props.addLiveTextDocument(summary);
+ this._props.addDocument?.(portal);
+ this._props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
});
@@ -602,17 +584,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
(this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
}
};
- this.props
+ this._props
.activeDocuments()
.filter(doc => !doc.z && !doc._lockedPosition)
.map(selectFunc);
if (!selection.length && selectBackgrounds)
- this.props
+ this._props
.activeDocuments()
.filter(doc => doc.z === undefined)
.map(selectFunc);
if (!selection.length)
- this.props
+ this._props
.activeDocuments()
.filter(doc => doc.z !== undefined)
.map(selectFunc);
@@ -621,8 +603,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@computed get marqueeDiv() {
const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
- const p = this.props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
- const v = this._lassoFreehand ? [0, 0] : this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
+ const p = this._props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
+ const v = this._lassoFreehand ? [0, 0] : this._props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
return (
<div
className="marquee"
@@ -630,8 +612,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
transform: `translate(${p[0]}px, ${p[1]}px)`,
width: Math.abs(v[0]),
height: Math.abs(v[1]),
- color: lightOrDark(this.props.Document?.backgroundColor ?? 'white'),
- borderColor: lightOrDark(this.props.Document?.backgroundColor ?? 'white'),
+ color: lightOrDark(this._props.Document?.backgroundColor ?? 'white'),
+ borderColor: lightOrDark(this._props.Document?.backgroundColor ?? 'white'),
zIndex: 2000,
}}>
{' '}
@@ -640,7 +622,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
<polyline //
points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')}
fill="none"
- stroke={lightOrDark(this.props.Document?.backgroundColor ?? 'white')}
+ stroke={lightOrDark(this._props.Document?.backgroundColor ?? 'white')}
strokeWidth="1"
strokeDasharray="3"
/>
@@ -651,13 +633,37 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
</div>
);
}
+ MarqueeRef: HTMLDivElement | null = null;
+ @action
+ onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
+ if ((e as any).handlePan || this._props.isAnnotationOverlay) return;
+ (e as any).handlePan = true;
+
+ const bounds = this.MarqueeRef?.getBoundingClientRect();
+ if (!this._props.Document._freeform_noAutoPan && !this._props.renderDepth && bounds) {
+ const dragX = e.detail.clientX;
+ const dragY = e.detail.clientY;
+
+ const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0;
+ const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0;
+ if (deltaX !== 0 || deltaY !== 0) {
+ this._props.Document[this._props.panYFieldKey] = NumCast(this._props.Document[this._props.panYFieldKey]) + deltaY / 2;
+ this._props.Document[this._props.panXFieldKey] = NumCast(this._props.Document[this._props.panXFieldKey]) + deltaX / 2;
+ }
+ }
+ e.stopPropagation();
+ };
render() {
return (
<div
className="marqueeView"
+ ref={r => {
+ r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+ this.MarqueeRef = r;
+ }}
style={{
- overflow: StrCast(this.props.Document._overflow),
+ overflow: StrCast(this._props.Document._overflow),
cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible ? 'crosshair' : 'pointer',
}}
onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index cd8b7a0cc..f25872c2b 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -1,4 +1,4 @@
-import { action, computed, Lambda, observable, reaction } from 'mobx';
+import { action, computed, Lambda, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Opt } from '../../../../fields/Doc';
@@ -7,7 +7,6 @@ import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types
import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents } from '../../../../Utils';
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 { ContextMenu } from '../../ContextMenu';
@@ -17,48 +16,52 @@ 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();
private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
- @observable private _rowHeight: Opt<number>; // temporary store of row height to make change undoable
+ @observable private _rowHeight: Opt<number> = undefined; // temporary store of row height to make change undoable
@observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
private dropLocation: object = {}; // sets the drop location for external drops
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
@computed get numCols() {
- return NumCast(this.props.Document.gridNumCols, 10);
+ return NumCast(this.Document.gridNumCols, 10);
}
@computed get rowHeight() {
- return this._rowHeight === undefined ? NumCast(this.props.Document.gridRowHeight, 100) : this._rowHeight;
+ return this._rowHeight === undefined ? NumCast(this.Document.gridRowHeight, 100) : this._rowHeight;
}
// sets the default width and height of the grid nodes
@computed get defaultW() {
- return NumCast(this.props.Document.gridDefaultW, 2);
+ return NumCast(this.Document.gridDefaultW, 2);
}
@computed get defaultH() {
- return NumCast(this.props.Document.gridDefaultH, 2);
+ return NumCast(this.Document.gridDefaultH, 2);
}
@computed get colWidthPlusGap() {
- return (this.props.PanelWidth() - this.margin) / this.numCols;
+ return (this._props.PanelWidth() - this.margin) / this.numCols;
}
@computed get rowHeightPlusGap() {
return this.rowHeight + this.margin;
}
@computed get margin() {
- return NumCast(this.props.Document.margin, 10);
+ return NumCast(this.Document.margin, 10);
} // sets the margin between grid nodes
@computed get flexGrid() {
- return BoolCast(this.props.Document.gridFlex, true);
+ return BoolCast(this.Document.gridFlex, true);
} // is grid static/flexible i.e. whether nodes be moved around and resized
@computed get compaction() {
- return StrCast(this.props.Document.gridStartCompaction, StrCast(this.props.Document.gridCompaction, 'vertical'));
+ return StrCast(this.Document.gridStartCompaction, StrCast(this.Document.gridCompaction, 'vertical'));
} // is grid static/flexible i.e. whether nodes be moved around and resized
/**
@@ -91,12 +94,12 @@ export class CollectionGridView extends CollectionSubView() {
// updates the layouts if the reset button has been clicked
this._resetListenerDisposer = reaction(
- () => this.props.Document.gridResetLayout,
+ () => this.Document.gridResetLayout,
reset => {
if (reset && this.flexGrid) {
this.setLayout(this.childLayoutPairs.map((pair, index) => this.makeLayoutItem(pair.layout, this.unflexedPosition(index))));
}
- this.props.Document.gridResetLayout = false;
+ this.Document.gridResetLayout = false;
}
);
}
@@ -127,7 +130,7 @@ export class CollectionGridView extends CollectionSubView() {
* Maps the x- and y- coordinates of the event to a grid cell.
*/
screenToCell(sx: number, sy: number) {
- const pt = this.props.ScreenToLocalTransform().transformPoint(sx, sy);
+ const pt = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
const x = Math.floor(pt[0] / this.colWidthPlusGap);
const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
return { x, y };
@@ -156,25 +159,25 @@ export class CollectionGridView extends CollectionSubView() {
const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
- return this.props.ScreenToLocalTransform().translate(-pos.x, -pos.y);
+ return this.ScreenToLocalBoxXf().translate(-pos.x, -pos.y);
};
/**
* @returns the layout list converted from JSON
*/
get savedLayoutList() {
- return (this.props.Document.gridLayoutString ? JSON.parse(StrCast(this.props.Document.gridLayoutString)) : []) as Layout[];
+ return (this.Document.gridLayoutString ? JSON.parse(StrCast(this.Document.gridLayoutString)) : []) as Layout[];
}
/**
* Stores the layout list on the Document as JSON
*/
setLayoutList(layouts: Layout[]) {
- this.props.Document.gridLayoutString = JSON.stringify(layouts);
+ this.Document.gridLayoutString = JSON.stringify(layouts);
}
- isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive();
+ isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
/**
*
* @param layout
@@ -186,20 +189,20 @@ export class CollectionGridView extends CollectionSubView() {
getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
return (
<DocumentView
- {...this.props}
+ {...this._props}
NativeWidth={returnZero}
NativeHeight={returnZero}
- setContentView={emptyFunction}
+ setContentViewBox={emptyFunction}
Document={layout}
- DataDoc={layout.resolvedDataDoc as Doc}
+ TemplateDataDocument={layout.resolvedDataDoc as Doc}
isContentActive={this.isChildContentActive}
PanelWidth={width}
PanelHeight={height}
ScreenToLocalTransform={dxf}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- onClick={this.onChildClickHandler}
- renderDepth={this.props.renderDepth + 1}
- dontCenter={this.props.Document.centerY ? undefined : 'y'}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ onClickScript={this.onChildClickHandler}
+ renderDepth={this._props.renderDepth + 1}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
/>
);
}
@@ -219,12 +222,12 @@ export class CollectionGridView extends CollectionSubView() {
if (gridLayout) Object.assign(gridLayout, layoutArray.find(layout => layout.i === doc[Id]) || gridLayout);
});
- if (this.props.Document.gridStartCompaction) {
+ if (this.Document.gridStartCompaction) {
undoBatch(() => {
- this.props.Document.gridCompaction = this.props.Document.gridStartCompaction;
+ this.Document.gridCompaction = this.Document.gridStartCompaction;
this.setLayoutList(savedLayouts);
})();
- this.props.Document.gridStartCompaction = undefined;
+ this.Document.gridStartCompaction = undefined;
} else {
undoBatch(() => this.setLayoutList(savedLayouts))();
}
@@ -246,7 +249,7 @@ export class CollectionGridView extends CollectionSubView() {
const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
child &&
collector.push(
- <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this.props.isSelected() ? '' : ' static')}>
+ <div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this._props.isSelected() ? '' : ' static')}>
{this.getDisplayDoc(child.layout, dxf, width, height)}
</div>
);
@@ -277,7 +280,6 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Handles internal drop of Dash documents.
*/
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
const savedLayouts = this.savedLayoutList;
const dropped = de.complete.docDragData?.droppedDocuments;
@@ -292,7 +294,6 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Handles external drop of images/PDFs etc from outside Dash.
*/
- @action
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
this.dropLocation = this.screenToCell(e.clientX, e.clientY);
super.onExternalDrop(e, {});
@@ -316,7 +317,7 @@ export class CollectionGridView extends CollectionSubView() {
e,
returnFalse,
action(() => {
- undoBatch(() => (this.props.Document.gridRowHeight = this._rowHeight))();
+ undoBatch(() => (this.Document.gridRowHeight = this._rowHeight))();
this._rowHeight = undefined;
}),
emptyFunction,
@@ -330,8 +331,8 @@ export class CollectionGridView extends CollectionSubView() {
*/
onContextMenu = () => {
const displayOptionsMenu: ContextMenuProps[] = [];
- displayOptionsMenu.push({ description: 'Toggle Content Display Style', event: () => (this.props.Document.display = this.props.Document.display ? undefined : 'contents'), icon: 'copy' });
- displayOptionsMenu.push({ description: 'Toggle Vertical Centering', event: () => (this.props.Document.centerY = !this.props.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' });
};
@@ -339,7 +340,7 @@ export class CollectionGridView extends CollectionSubView() {
* Handles text document creation on double click.
*/
onPointerDown = (e: React.PointerEvent) => {
- if (this.props.isContentActive(true)) {
+ if (this._props.isContentActive()) {
setupMoveUpEvents(
this,
e,
@@ -350,8 +351,8 @@ export class CollectionGridView extends CollectionSubView() {
undoBatch(
action(() => {
const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
- FormattedTextBox.SelectOnLoad = text[Id]; // 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.props.Document, this.props.fieldKey, text);
+ 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.AddDocToList(this.Document, this._props.fieldKey, text);
this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(e.clientX, e.clientY))));
})
)();
@@ -359,7 +360,7 @@ export class CollectionGridView extends CollectionSubView() {
},
false
);
- if (this.props.isSelected(true)) e.stopPropagation();
+ if (this._props.isSelected()) e.stopPropagation();
}
};
@@ -368,7 +369,7 @@ export class CollectionGridView extends CollectionSubView() {
<div
className="collectionGridView-contents"
ref={this.createDashEventsTarget}
- style={{ pointerEvents: !this.props.isContentActive() ? 'none' : undefined }}
+ style={{ pointerEvents: !this._props.isContentActive() ? 'none' : undefined }}
onContextMenu={this.onContextMenu}
onPointerDown={this.onPointerDown}
onDrop={this.onExternalDrop}>
@@ -378,29 +379,29 @@ export class CollectionGridView extends CollectionSubView() {
style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
onWheel={e => e.stopPropagation()}
onScroll={action(e => {
- if (!this.props.isSelected()) e.currentTarget.scrollTop = this._scroll;
+ if (!this._props.isSelected()) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}>
<Grid
- width={this.props.PanelWidth()}
+ 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() ? true : false}
numCols={this.numCols}
rowHeight={this.rowHeight}
setLayout={this.setLayout}
- transformScale={this.props.ScreenToLocalTransform().Scale}
+ transformScale={this.ScreenToLocalBoxXf().Scale}
compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
- preventCollision={BoolCast(this.props.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
+ preventCollision={BoolCast(this.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
margin={this.margin}
/>
<input
className="rowHeightSlider"
type="range"
- style={{ width: this.props.PanelHeight() - 30 }}
+ style={{ width: this._props.PanelHeight() - 30 }}
min={1}
value={this.rowHeight}
- max={this.props.PanelHeight() - 30}
+ max={this._props.PanelHeight() - 30}
onPointerDown={this.onSliderDown}
onChange={this.onSliderChange}
/>
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 3d1d87aa0..9145d7ef1 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,5 +1,5 @@
-import * as React from 'react';
import { observer } from 'mobx-react';
+import * as React from 'react';
import '../../../../../node_modules/react-grid-layout/css/styles.css';
import '../../../../../node_modules/react-resizable/css/styles.css';
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index d0c14a21d..b8ceec139 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables';
+@import '../../global/globalCssVariables.module.scss';
@import '../../_nodeModuleOverrides';
.collectionLinearView {
@@ -18,9 +18,9 @@
user-select: none;
}
- > input:not(:checked) ~ &.true {
- background-color: transparent;
- }
+ // > input:not(:checked) ~ &.true {
+ // background-color: transparent;
+ // }
.collectionLinearView {
display: flex;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 9a2c79a22..228af78aa 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,24 +1,24 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Tooltip } from '@material-ui/core';
+import { Tooltip } from '@mui/material';
import { Toggle, ToggleType, Type } from 'browndash-components';
-import { action, IReactionDisposer, observable, reaction } from 'mobx';
+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 { 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 { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
import { CollectionViewType } from '../../../documents/DocumentTypes';
import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
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 { UndoStack } from '../../UndoStack';
import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionLinearView.scss';
@@ -33,12 +33,15 @@ import './CollectionLinearView.scss';
*/
@observer
export class CollectionLinearView extends CollectionSubView() {
- @observable public addMenuToggle = React.createRef<HTMLInputElement>();
- @observable private _selectedIndex = -1;
private _dropDisposer?: DragManager.DragDropDisposer;
private _widthDisposer?: IReactionDisposer;
private _selectedDisposer?: IReactionDisposer;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
componentWillUnmount() {
this._dropDisposer?.();
this._widthDisposer?.();
@@ -48,7 +51,7 @@ export class CollectionLinearView extends CollectionSubView() {
componentDidMount() {
this._widthDisposer = reaction(
- () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
+ () => 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),
{ fireImmediately: true }
);
@@ -58,7 +61,7 @@ export class CollectionLinearView extends CollectionSubView() {
if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
};
- dimension = () => NumCast(this.rootDoc._height);
+ dimension = () => NumCast(this.layoutDoc._height);
getTransform = (ele: Opt<HTMLDivElement>) => {
if (!ele) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
@@ -76,16 +79,16 @@ export class CollectionLinearView extends CollectionSubView() {
@action
changeDescriptionSetting = () => {
- if (LinkDescriptionPopup.showDescriptions) {
- if (LinkDescriptionPopup.showDescriptions === 'ON') {
- LinkDescriptionPopup.showDescriptions = 'OFF';
- LinkDescriptionPopup.descriptionPopup = false;
+ if (LinkDescriptionPopup.Instance.showDescriptions) {
+ if (LinkDescriptionPopup.Instance.showDescriptions === 'ON') {
+ LinkDescriptionPopup.Instance.showDescriptions = 'OFF';
+ LinkDescriptionPopup.Instance.display = false;
} else {
- LinkDescriptionPopup.showDescriptions = 'ON';
+ LinkDescriptionPopup.Instance.showDescriptions = 'ON';
}
} else {
- LinkDescriptionPopup.showDescriptions = 'OFF';
- LinkDescriptionPopup.descriptionPopup = false;
+ LinkDescriptionPopup.Instance.showDescriptions = 'OFF';
+ LinkDescriptionPopup.Instance.display = false;
}
};
@@ -107,7 +110,7 @@ export class CollectionLinearView extends CollectionSubView() {
<Tooltip title={<div className="dash-tooltip">{'Toggle description pop-up'} </div>} placement="top">
<span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
- Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : 'ON'}
+ Labels: {LinkDescriptionPopup.Instance.showDescriptions ? LinkDescriptionPopup.Instance.showDescriptions : 'ON'}
</span>
</Tooltip>
@@ -126,8 +129,8 @@ export class CollectionLinearView extends CollectionSubView() {
Currently playing:
{CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
<>
- <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.rootDoc, { willZoomCentered: true })}>
- {clip.rootDoc.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? ' ' : ',')}
+ <span className="audio-title" onPointerDown={() => DocumentManager.Instance.showDocument(clip.Document, { willZoomCentered: true })}>
+ {clip.Document.title + (i === CollectionStackedTimeline.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?.()} />{' '}
@@ -144,8 +147,8 @@ export class CollectionLinearView extends CollectionSubView() {
switch (doc.layout) {
case '<LinkingUI>': return this.getLinkUI();
case '<CurrentlyPlayingUI>': return this.getCurrentlyPlayingUI();
- case '<UndoStack>': return <UndoStack key={doc[Id]} width={200} height={40} inline={true} />;
- case '<Branching>': return Doc.UserDoc().isBranchingMode ? <BranchingTrailManager /> : null;
+ case '<UndoStack>': return <UndoStack key={doc[Id]}/>;
+ case '<Branching>': return Doc.UserDoc().isBranchingMode ? <BranchingTrailManager key={doc[Id]} /> : null;
}
const nested = doc._type_collection === CollectionViewType.Linear;
@@ -170,28 +173,27 @@ export class CollectionLinearView extends CollectionSubView() {
}}>
<DocumentView
Document={doc}
- isContentActive={this.props.isContentActive}
+ isContentActive={this._props.isContentActive}
isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ addDocTab={this._props.addDocTab}
pinToPres={emptyFunction}
- dragAction={(this.layoutDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
+ dragAction={(this.layoutDoc.childDragAction ?? this._props.childDragAction) as dropActionType}
+ rootSelected={this.rootSelected}
+ removeDocument={this._props.removeDocument}
ScreenToLocalTransform={docXf}
PanelWidth={doc[Width]}
PanelHeight={nested || doc._height ? doc[Height] : this.dimension}
- renderDepth={this.props.renderDepth + 1}
- dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
+ renderDepth={this._props.renderDepth + 1}
+ dontRegisterView={BoolCast(this.Document.childDontRegisterViews)}
focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- docViewPath={returnEmptyDoclist}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={this.childContainerViewPath}
whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- childFilters={this.props.childFilters}
- childFiltersByRanges={this.props.childFiltersByRanges}
- searchFilterDocs={this.props.searchFilterDocs}
+ childFilters={this._props.childFilters}
+ childFiltersByRanges={this._props.childFiltersByRanges}
+ searchFilterDocs={this._props.searchFilterDocs}
hideResizeHandles={true}
/>
</div>
@@ -205,8 +207,8 @@ export class CollectionLinearView extends CollectionSubView() {
const menuOpener = (
<Toggle
- text={Cast(this.props.Document.icon, 'string', null)}
- icon={Cast(this.props.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon color={SettingsManager.userColor} icon={isExpanded ? 'minus' : 'plus'} />}
+ text={Cast(this.Document.icon, 'string', null)}
+ icon={Cast(this.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon color={SettingsManager.userColor} icon={isExpanded ? 'minus' : 'plus'} />}
color={SettingsManager.userColor}
background={SettingsManager.userVariantColor}
type={Type.TERT}
@@ -215,7 +217,7 @@ export class CollectionLinearView extends CollectionSubView() {
toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)}
onClick={() => {
this.layoutDoc.linearView_IsOpen = !isExpanded;
- ScriptCast(this.rootDoc.onClick)?.script.run({ this: this.rootDoc }, console.log);
+ ScriptCast(this.Document.onClick)?.script.run({ this: this.Document }, console.log);
}}
tooltip={isExpanded ? 'Close' : 'Open'}
fillWidth={true}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index cb0d5e03f..f983fd815 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -1,6 +1,6 @@
.collectionMulticolumnView_contents {
display: flex;
- overflow: hidden;
+ //overflow: hidden; // bcz: turned of to allow highlighting to appear when there is no border (e.g, for a component of the slide template)
width: 100%;
height: 100%;
@@ -17,7 +17,7 @@
}
.contentFittingDocumentView {
- width: unset;
+ margin: auto;
}
.label-wrapper {
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 81453c0b8..b181b59ce 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,16 +1,16 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { Button } from 'browndash-components';
-import { action, computed } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { emptyFunction, returnFalse } from '../../../../Utils';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, undoable } from '../../../util/UndoManager';
import { DocumentView } from '../../nodes/DocumentView';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionMulticolumnView.scss';
@@ -37,6 +37,11 @@ const resizerWidth = 8;
@observer
export class CollectionMulticolumnView extends CollectionSubView() {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
/**
* @returns the list of layout documents whose width unit is
* *, denoting that it will be displayed with a ratio, not fixed pixel, value
@@ -122,7 +127,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
private get totalRatioAllocation(): number | undefined {
const layoutInfoLen = this.resolvedLayoutInformation.widthSpecifiers.length;
if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
- return this.props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._xMargin);
+ return this._props.PanelWidth() - (this.totalFixedAllocation + resizerWidth * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._xMargin);
}
}
@@ -184,7 +189,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
let offset = 0;
for (const { layout: candidate } of this.childLayoutPairs) {
if (candidate === layout) {
- return this.props.ScreenToLocalTransform().translate(-offset / (this.props.NativeDimScaling?.() || 1), 0);
+ return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0);
}
offset += this.lookupPixels(candidate) + resizerWidth;
}
@@ -192,7 +197,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
};
@undoBatch
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
let dropInd = -1;
if (de.complete.docDragData && this._mainCont) {
@@ -219,8 +223,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
if (this.childDocs.includes(d)) {
if (dropInd > this.childDocs.indexOf(d)) dropInd--;
}
- Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
- Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+ Doc.RemoveDocFromList(this.dataDoc, this._props.fieldKey, d);
+ Doc.AddDocToList(this.dataDoc, this._props.fieldKey, d, DocListCast(this.dataDoc[this._props.fieldKey])[dropInd], undefined, dropInd === -1);
}
})
);
@@ -233,51 +237,57 @@ export class CollectionMulticolumnView extends CollectionSubView() {
onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
- isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
isChildContentActive = () => {
- const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
- return this.props.isContentActive?.() === false || childDocsActive === false
+ const childDocsActive = this._props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
+ return this._props.isContentActive?.() === false || childDocsActive === false
? false //
- : this.props.isDocumentActive?.() && childDocsActive
- ? true
- : undefined;
+ : this._props.isDocumentActive?.() && childDocsActive
+ ? true
+ : undefined;
};
- getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
+ getDisplayDoc = (childLayout: Doc) => {
+ const width = () => this.lookupPixels(childLayout);
+ const height = () => this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+ const dxf = () =>
+ this.lookupIndividualTransform(childLayout)
+ .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={layout}
- DataDoc={layout.resolvedDataDoc as Doc}
- styleProvider={this.props.styleProvider}
- docViewPath={this.props.docViewPath}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- renderDepth={this.props.renderDepth + 1}
+ Document={childLayout}
+ TemplateDataDocument={childLayout.resolvedDataDoc as Doc}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={this.childContainerViewPath}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ renderDepth={this._props.renderDepth + 1}
PanelWidth={width}
PanelHeight={height}
- shouldNotScale={shouldNotScale}
rootSelected={this.rootSelected}
- dragAction={(this.props.Document.childDragAction ?? this.props.childDragAction) as dropActionType}
- onClick={this.onChildClickHandler}
- onDoubleClick={this.onChildDoubleClickHandler}
+ dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+ onClickScript={this.onChildClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
suppressSetHeight={true}
ScreenToLocalTransform={dxf}
isContentActive={this.isChildContentActive}
- isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
- hideResizeHandles={this.props.childHideResizeHandles?.()}
- hideDecorationTitle={this.props.childHideDecorationTitle?.()}
- fitContentsToBox={this.props.fitContentsToBox}
- focus={this.props.focus}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+ hideResizeHandles={childLayout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ fitContentsToBox={this._props.fitContentsToBox}
+ focus={this._props.focus}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- dontRegisterView={this.props.dontRegisterView}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
+ dontRegisterView={this._props.dontRegisterView}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
/>
);
};
@@ -288,34 +298,23 @@ export class CollectionMulticolumnView extends CollectionSubView() {
@computed
private get contents(): JSX.Element[] | null {
const { childLayoutPairs } = this;
- const { Document, PanelHeight } = this.props;
const collector: JSX.Element[] = [];
for (let i = 0; i < childLayoutPairs.length; i++) {
const { layout } = childLayoutPairs[i];
- const aspect = Doc.NativeAspect(layout, undefined, true);
- const width = () => this.lookupPixels(layout);
- const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
- const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(height() * aspect, width()));
- const docheight = () => Math.min(docwidth() / aspect, height());
- const dxf = () =>
- this.lookupIndividualTransform(layout)
- .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin))
- .scale(this.props.NativeDimScaling?.() || 1);
- const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
collector.push(
- <Tooltip title={'Tab: ' + StrCast(layout.title)}>
- <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}>
- {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
- <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} />
- <WidthLabel layout={layout} collectionDoc={Document} />
+ <Tooltip title={'Tab: ' + StrCast(layout.title)} key={'wrapper' + i}>
+ <div className="document-wrapper" style={{ flexDirection: 'column', width: this.lookupPixels(layout) }}>
+ {this.getDisplayDoc(layout)}
+ <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} />
+ <WidthLabel layout={layout} collectionDoc={this.Document} />
</div>
</Tooltip>,
<ResizeBar
width={resizerWidth}
key={'resizer' + i}
- styleProvider={this.props.styleProvider}
- isContentActive={this.props.isContentActive}
- select={this.props.select}
+ styleProvider={this._props.styleProvider}
+ isContentActive={this._props.isContentActive}
+ select={this._props.select}
columnUnitLength={this.getColumnUnitLength}
toLeft={layout}
toRight={childLayoutPairs[i + 1]?.layout}
@@ -326,18 +325,18 @@ export class CollectionMulticolumnView extends CollectionSubView() {
return collector;
}
- render(): JSX.Element {
+ render() {
return (
<div
- className={'collectionMulticolumnView_contents'}
+ className="collectionMulticolumnView_contents"
ref={this.createDashEventsTarget}
style={{
- width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
- height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
- marginLeft: NumCast(this.props.Document._xMargin),
- marginRight: NumCast(this.props.Document._xMargin),
- marginTop: NumCast(this.props.Document._yMargin),
- marginBottom: NumCast(this.props.Document._yMargin),
+ width: `calc(100% - ${2 * NumCast(this.Document._xMargin)}px)`,
+ height: `calc(100% - ${2 * NumCast(this.Document._yMargin)}px)`,
+ marginLeft: NumCast(this.Document._xMargin),
+ marginRight: NumCast(this.Document._xMargin),
+ marginTop: NumCast(this.Document._yMargin),
+ marginBottom: NumCast(this.Document._yMargin),
}}>
{this.contents}
</div>
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index ec7200a03..f44eacb2a 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -1,6 +1,6 @@
.collectionMultirowView_contents {
display: flex;
- overflow: hidden;
+ //overflow: hidden; // bcz: turned of to allow highlighting to appear when there is no border (e.g, for a component of the slide template)
width: 100%;
height: 100%;
flex-direction: column;
@@ -10,11 +10,6 @@
flex-direction: row;
height: 100%;
align-items: center;
- margin: auto;
-
- .contentFittingDocumentView {
- height: unset;
- }
.label-wrapper {
display: flex;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 04cfc5456..659f7ccdc 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -1,9 +1,9 @@
-import { action, computed } from 'mobx';
+import { action, computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { emptyFunction, returnFalse } from '../../../../Utils';
import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { undoBatch } from '../../../util/UndoManager';
@@ -12,7 +12,6 @@ import { CollectionSubView } from '../CollectionSubView';
import './CollectionMultirowView.scss';
import HeightLabel from './MultirowHeightLabel';
import ResizeBar from './MultirowResizer';
-
interface HeightSpecifier {
magnitude: number;
unit: string;
@@ -33,6 +32,11 @@ const resizerHeight = 8;
@observer
export class CollectionMultirowView extends CollectionSubView() {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
/**
* @returns the list of layout documents whose width unit is
* *, denoting that it will be displayed with a ratio, not fixed pixel, value
@@ -118,7 +122,7 @@ export class CollectionMultirowView extends CollectionSubView() {
private get totalRatioAllocation(): number | undefined {
const layoutInfoLen = this.resolvedLayoutInformation.heightSpecifiers.length;
if (layoutInfoLen > 0 && this.totalFixedAllocation !== undefined) {
- return this.props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.props.Document._yMargin);
+ return this._props.PanelHeight() - (this.totalFixedAllocation + resizerHeight * (layoutInfoLen - 1)) - 2 * NumCast(this.Document._yMargin);
}
}
@@ -180,7 +184,7 @@ export class CollectionMultirowView extends CollectionSubView() {
let offset = 0;
for (const { layout: candidate } of this.childLayoutPairs) {
if (candidate === layout) {
- return this.props.ScreenToLocalTransform().translate(0, -offset / (this.props.NativeDimScaling?.() || 1));
+ return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1));
}
offset += this.lookupPixels(candidate) + resizerHeight;
}
@@ -188,7 +192,6 @@ export class CollectionMultirowView extends CollectionSubView() {
};
@undoBatch
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
let dropInd = -1;
if (de.complete.docDragData && this._mainCont) {
@@ -215,8 +218,8 @@ export class CollectionMultirowView extends CollectionSubView() {
if (this.childDocs.includes(d)) {
if (dropInd > this.childDocs.indexOf(d)) dropInd--;
}
- Doc.RemoveDocFromList(this.rootDoc, this.props.fieldKey, d);
- Doc.AddDocToList(this.rootDoc, this.props.fieldKey, d, DocListCast(this.rootDoc[this.props.fieldKey])[dropInd], undefined, dropInd === -1);
+ Doc.RemoveDocFromList(this.dataDoc, this._props.fieldKey, d);
+ Doc.AddDocToList(this.dataDoc, this._props.fieldKey, d, DocListCast(this.dataDoc[this._props.fieldKey])[dropInd], undefined, dropInd === -1);
}
})
);
@@ -229,51 +232,56 @@ export class CollectionMultirowView extends CollectionSubView() {
onChildClickHandler = () => ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => ScriptCast(this.Document.onChildDoubleClick);
- isContentActive = () => this.props.isSelected() || this.props.isContentActive() || this.props.isAnyChildContentActive();
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
isChildContentActive = () => {
- const childDocsActive = this.props.childDocumentsActive?.() ?? this.rootDoc.childDocumentsActive;
- return this.props.isContentActive?.() === false || childDocsActive === false
+ const childDocsActive = this._props.childDocumentsActive?.() ?? this.Document.childDocumentsActive;
+ return this._props.isContentActive?.() === false || childDocsActive === false
? false //
- : this.props.isDocumentActive?.() && childDocsActive
- ? true
- : undefined;
+ : this._props.isDocumentActive?.() && childDocsActive
+ ? true
+ : undefined;
};
- getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number, shouldNotScale: () => boolean) => {
+ getDisplayDoc = (layout: Doc) => {
+ const height = () => this.lookupPixels(layout);
+ const width = () => this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xMargin) - (BoolCast(this.layoutDoc.showWidthLabels) ? 20 : 0);
+ const dxf = () =>
+ this.lookupIndividualTransform(layout)
+ .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}
- DataDoc={layout.resolvedDataDoc as Doc}
- styleProvider={this.props.styleProvider}
- docViewPath={this.props.docViewPath}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- renderDepth={this.props.renderDepth + 1}
+ TemplateDataDocument={layout.resolvedDataDoc as Doc}
+ styleProvider={this._props.styleProvider}
+ containerViewPath={this.childContainerViewPath}
+ LayoutTemplate={this._props.childLayoutTemplate}
+ LayoutTemplateString={this._props.childLayoutString}
+ renderDepth={this._props.renderDepth + 1}
PanelWidth={width}
PanelHeight={height}
- shouldNotScale={shouldNotScale}
rootSelected={this.rootSelected}
- dropAction={StrCast(this.rootDoc.childDragAction) as dropActionType}
- onClick={this.onChildClickHandler}
- onDoubleClick={this.onChildDoubleClickHandler}
+ dragAction={StrCast(this.Document.childDragAction, this._props.childDragAction) as dropActionType}
+ onClickScript={this.onChildClickHandler}
+ onDoubleClickScript={this.onChildDoubleClickHandler}
ScreenToLocalTransform={dxf}
isContentActive={this.isChildContentActive}
- isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
- hideResizeHandles={this.props.childHideResizeHandles?.()}
- hideDecorationTitle={this.props.childHideDecorationTitle?.()}
- fitContentsToBox={this.props.fitContentsToBox}
- dragAction={this.props.childDragAction}
- focus={this.props.focus}
+ isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
+ hideResizeHandles={layout.layout_fitWidth || this._props.childHideResizeHandles ? true : false}
+ hideDecorationTitle={this._props.childHideDecorationTitle}
+ fitContentsToBox={this._props.fitContentsToBox}
+ focus={this._props.focus}
childFilters={this.childDocFilters}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- dontRegisterView={this.props.dontRegisterView}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
+ dontRegisterView={this._props.dontRegisterView}
+ addDocument={this._props.addDocument}
+ moveDocument={this._props.moveDocument}
+ removeDocument={this._props.removeDocument}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy'
/>
);
};
@@ -284,29 +292,18 @@ export class CollectionMultirowView extends CollectionSubView() {
@computed
private get contents(): JSX.Element[] | null {
const { childLayoutPairs } = this;
- const { Document, PanelWidth } = this.props;
const collector: JSX.Element[] = [];
for (let i = 0; i < childLayoutPairs.length; i++) {
const { layout } = childLayoutPairs[i];
- const aspect = Doc.NativeAspect(layout, undefined, true);
- const height = () => this.lookupPixels(layout);
- const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
- const docheight = () => Math.min(width() / aspect, height());
- const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(width(), docheight() * aspect));
- const dxf = () =>
- this.lookupIndividualTransform(layout)
- .translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2)
- .scale(this.props.NativeDimScaling?.() || 1);
- const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
collector.push(
- <div className="document-wrapper" style={{ height: height() }} key={'wrapper' + i}>
- {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
- <HeightLabel layout={layout} collectionDoc={Document} />
+ <div className="document-wrapper" style={{ flexDirection: 'row', height: this.lookupPixels(layout) }} key={'wrapper' + i}>
+ {this.getDisplayDoc(layout)}
+ <HeightLabel layout={layout} collectionDoc={this.Document} />
</div>,
<ResizeBar
height={resizerHeight}
- styleProvider={this.props.styleProvider}
- isContentActive={this.props.isContentActive}
+ styleProvider={this._props.styleProvider}
+ isContentActive={this._props.isContentActive}
key={'resizer' + i}
columnUnitLength={this.getRowUnitLength}
toTop={layout}
@@ -318,17 +315,17 @@ export class CollectionMultirowView extends CollectionSubView() {
return collector;
}
- render(): JSX.Element {
+ render() {
return (
<div
- className={'collectionMultirowView_contents'}
+ className="collectionMultirowView_contents"
style={{
- width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
- height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
- marginLeft: NumCast(this.props.Document._xMargin),
- marginRight: NumCast(this.props.Document._xMargin),
- marginTop: NumCast(this.props.Document._yMargin),
- marginBottom: NumCast(this.props.Document._yMargin),
+ width: `calc(100% - ${2 * NumCast(this.Document._xMargin)}px)`,
+ height: `calc(100% - ${2 * NumCast(this.Document._yMargin)}px)`,
+ marginLeft: NumCast(this.Document._xMargin),
+ marginRight: NumCast(this.Document._xMargin),
+ marginTop: NumCast(this.Document._yMargin),
+ marginBottom: NumCast(this.Document._yMargin),
}}
ref={this.createDashEventsTarget}>
{this.contents}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 868b1140d..d580d9c52 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,16 +1,16 @@
-import * as React from 'react';
+import { action } from 'mobx';
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { DimUnit } from './CollectionMulticolumnView';
import { UndoManager } from '../../../util/UndoManager';
-import { StyleProviderFunc } from '../../nodes/DocumentView';
import { StyleProp } from '../../StyleProvider';
+import { StyleProviderFuncType } from '../../nodes/FieldView';
+import { DimUnit } from './CollectionMulticolumnView';
interface ResizerProps {
width: number;
- styleProvider?: StyleProviderFunc;
+ styleProvider?: StyleProviderFuncType;
isContentActive?: () => boolean | undefined;
columnUnitLength(): number | undefined;
toLeft?: Doc;
@@ -18,12 +18,8 @@ interface ResizerProps {
select: (isCtrlPressed: boolean) => void;
}
-const resizerOpacity = 1;
-
@observer
export default class ResizeBar extends React.Component<ResizerProps> {
- @observable private isHoverActive = false;
- @observable private isResizingActive = false;
private _resizeUndo?: UndoManager.Batch;
@action
@@ -35,7 +31,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
window.removeEventListener('pointerup', this.onPointerUp);
window.addEventListener('pointermove', this.onPointerMove);
window.addEventListener('pointerup', this.onPointerUp);
- this.isResizingActive = true;
this._resizeUndo = UndoManager.StartBatch('multcol resizing');
};
@@ -80,8 +75,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
@action
private onPointerUp = () => {
- this.isResizingActive = false;
- this.isHoverActive = false;
window.removeEventListener('pointermove', this.onPointerMove);
window.removeEventListener('pointerup', this.onPointerUp);
this._resizeUndo?.end();
@@ -96,9 +89,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
width: this.props.width,
backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
- }}
- onPointerEnter={action(() => (this.isHoverActive = true))}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+ }}>
<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 9985a9fba..a9579d931 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnWidthLabel.tsx
@@ -1,27 +1,25 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { computed } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
-import { EditableView } from "../../EditableView";
-import { DimUnit } from "./CollectionMulticolumnView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { EditableView } from '../../EditableView';
+import { DimUnit } from './CollectionMulticolumnView';
interface WidthLabelProps {
layout: Doc;
collectionDoc: Doc;
- decimals?: number;
}
@observer
export default class WidthLabel extends React.Component<WidthLabelProps> {
-
@computed
private get contents() {
- const { layout, decimals } = this.props;
+ const { layout } = this.props;
const getUnit = () => StrCast(layout.dimUnit);
- const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(decimals ?? 3));
+ const getMagnitude = () => String(+NumCast(layout.dimMagnitude).toFixed(3));
return (
- <div className={"label-wrapper"}>
+ <div className={'label-wrapper'}>
<EditableView
GetValue={getMagnitude}
SetValue={value => {
@@ -50,7 +48,6 @@ export default class WidthLabel extends React.Component<WidthLabelProps> {
}
render() {
- return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : (null);
+ return BoolCast(this.props.collectionDoc.showWidthLabels) ? this.contents : null;
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index aa5439fa4..878c7ff3c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,10 +1,10 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { computed } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast, BoolCast } from "../../../../fields/Types";
-import { EditableView } from "../../EditableView";
-import { DimUnit } from "./CollectionMultirowView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { EditableView } from '../../EditableView';
+import { DimUnit } from './CollectionMultirowView';
interface HeightLabelProps {
layout: Doc;
@@ -14,14 +14,13 @@ interface HeightLabelProps {
@observer
export default class HeightLabel extends React.Component<HeightLabelProps> {
-
@computed
private get contents() {
const { layout, decimals } = this.props;
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 => {
@@ -50,7 +49,6 @@ export default class HeightLabel extends React.Component<HeightLabelProps> {
}
render() {
- return BoolCast(this.props.collectionDoc.showHeightLabels) ? this.contents : (null);
+ return BoolCast(this.props.collectionDoc.showHeightLabels) ? this.contents : null;
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 5a9d6a82c..73d08d5ef 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,28 +1,24 @@
-import * as React from 'react';
+import { action } from 'mobx';
import { observer } from 'mobx-react';
-import { observable, action } from 'mobx';
+import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
-import { DimUnit } from './CollectionMultirowView';
import { UndoManager } from '../../../util/UndoManager';
import { StyleProp } from '../../StyleProvider';
-import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { StyleProviderFuncType } from '../../nodes/FieldView';
+import { DimUnit } from './CollectionMultirowView';
interface ResizerProps {
height: number;
- styleProvider?: StyleProviderFunc;
+ styleProvider?: StyleProviderFuncType;
isContentActive?: () => boolean | undefined;
columnUnitLength(): number | undefined;
toTop?: Doc;
toBottom?: Doc;
}
-const resizerOpacity = 1;
-
@observer
export default class ResizeBar extends React.Component<ResizerProps> {
- @observable private isHoverActive = false;
- @observable private isResizingActive = false;
private _resizeUndo?: UndoManager.Batch;
@action
@@ -33,7 +29,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
window.removeEventListener('pointerup', this.onPointerUp);
window.addEventListener('pointermove', this.onPointerMove);
window.addEventListener('pointerup', this.onPointerUp);
- this.isResizingActive = true;
this._resizeUndo = UndoManager.StartBatch('multcol resizing');
};
@@ -78,8 +73,6 @@ export default class ResizeBar extends React.Component<ResizerProps> {
@action
private onPointerUp = () => {
- this.isResizingActive = false;
- this.isHoverActive = false;
window.removeEventListener('pointermove', this.onPointerMove);
window.removeEventListener('pointerup', this.onPointerUp);
this._resizeUndo?.end();
@@ -94,9 +87,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
height: this.props.height,
backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
- }}
- onPointerEnter={action(() => (this.isHoverActive = true))}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+ }}>
<div className={'multiRowResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
</div>
);
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 76bd392a5..29d121974 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.scss';
+@import '../../global/globalCssVariables.module.scss';
.collectionSchemaView {
cursor: default;
@@ -210,8 +210,9 @@
border: 1px solid $medium-gray;
overflow-x: hidden;
overflow-y: auto;
- padding: 5px;
display: inline-flex;
+ padding: 0;
+ align-items: center;
}
.schema-row {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f73c037f4..581425d77 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,9 +1,8 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, ObservableMap, observe, trace } from 'mobx';
+import { action, computed, makeObservable, observable, ObservableMap, observe } from 'mobx';
import { observer } from 'mobx-react';
-import { computedFn } from 'mobx-utils';
-import { Doc, DocListCast, Field, NumListCast, StrListCast } from '../../../../fields/Doc';
+import * as React from 'react';
+import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
@@ -17,9 +16,11 @@ import { undoable, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocumentView } from '../../nodes/DocumentView';
+import { FocusViewOptions, FieldViewProps } from '../../nodes/FieldView';
import { KeyValueBox } from '../../nodes/KeyValueBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
@@ -58,6 +59,11 @@ export class CollectionSchemaView extends CollectionSubView() {
private _tableContentRef: HTMLDivElement | null = null;
private _menuTarget = React.createRef<HTMLDivElement>();
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
static _rowHeight: number = 50;
static _rowSingleLineHeight: number = 32;
public static _minColWidth: number = 25;
@@ -69,16 +75,16 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _menuKeys: string[] = [];
@observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
@observable _colEles: HTMLDivElement[] = [];
- @observable _displayColumnWidths: number[] | undefined;
- @observable _columnMenuIndex: number | undefined;
+ @observable _displayColumnWidths: number[] | undefined = undefined;
+ @observable _columnMenuIndex: number | undefined = undefined;
@observable _newFieldWarning: string = '';
@observable _makeNewField: boolean = false;
@observable _newFieldDefault: any = 0;
@observable _newFieldType: ColumnType = ColumnType.Number;
@observable _menuValue: string = '';
- @observable _filterColumnIndex: number | undefined;
+ @observable _filterColumnIndex: number | undefined = undefined;
@observable _filterSearchValue: string = '';
- @observable _selectedCell: [Doc, number] | undefined;
+ @observable _selectedCell: [Doc, number] | undefined = undefined;
// target HTMLelement portal for showing a popup menu to edit cell values.
public get MenuTarget() {
@@ -86,7 +92,17 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get _selectedDocs() {
- return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+ const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
+ 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]] : [];
+ }
+ }
+ }
+ return selected;
}
@computed get documentKeys() {
@@ -98,7 +114,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get tableWidth() {
- return this.props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth);
+ return this._props.PanelWidth() - this.previewWidth - (this.previewWidth === 0 ? 0 : CollectionSchemaView._previewDividerWidth);
}
@computed get columnKeys() {
@@ -130,14 +146,13 @@ export class CollectionSchemaView extends CollectionSubView() {
return BoolCast(this.layoutDoc.sortDesc);
}
- @action
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentViewBox?.(this);
document.addEventListener('keydown', this.onKeyDown);
Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
this._keysDisposer = observe(
- this.rootDoc[this.fieldKey ?? 'data'] as List<Doc>,
+ this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
change => {
switch (change.type as any) {
case 'splice':
@@ -145,7 +160,7 @@ export class CollectionSchemaView extends CollectionSubView() {
(change as any).added.forEach((doc: Doc) => // for each document added
Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself)
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(''))))));
+ !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo(key, 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
}
@@ -230,19 +245,14 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@undoBatch
- @action
setColumnSort = (field: string | undefined, desc: boolean = false) => {
this.layoutDoc.sortField = field;
this.layoutDoc.sortDesc = desc;
};
- addRow = (doc: Doc | Doc[]) => {
- const result: boolean = this.addDocument(doc);
- return result;
- };
+ addRow = (doc: Doc | Doc[]) => this.addDocument(doc);
@undoBatch
- @action
changeColumnKey = (index: number, newKey: string, defaultVal?: any) => {
if (!this.documentKeys.includes(newKey)) {
this.addNewKey(newKey, defaultVal);
@@ -254,7 +264,6 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@undoBatch
- @action
addColumn = (key: string, defaultVal?: any) => {
if (!this.documentKeys.includes(key)) {
this.addNewKey(key, defaultVal);
@@ -275,7 +284,6 @@ export class CollectionSchemaView extends CollectionSubView() {
addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[key] = defaultVal));
@undoBatch
- @action
removeColumn = (index: number) => {
if (this.columnKeys.length === 1) return;
const currWidths = this.storedColumnWidths.slice();
@@ -314,8 +322,8 @@ export class CollectionSchemaView extends CollectionSubView() {
change = this._displayColumnWidths[shrinking] - CollectionSchemaView._minColWidth;
}
- this._displayColumnWidths[shrinking] -= change * this.props.ScreenToLocalTransform().Scale;
- this._displayColumnWidths[growing] += change * this.props.ScreenToLocalTransform().Scale;
+ this._displayColumnWidths[shrinking] -= change * this.ScreenToLocalBoxXf().Scale;
+ this._displayColumnWidths[growing] += change * this.ScreenToLocalBoxXf().Scale;
return false;
}
@@ -329,7 +337,6 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@undoBatch
- @action
moveColumn = (fromIndex: number, toIndex: number) => {
let currKeys = this.columnKeys.slice();
currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
@@ -373,7 +380,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
highlightDropColumn = (e: PointerEvent) => {
e.stopPropagation();
- const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ const mouseX = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY)[0];
const index = this.findDropIndex(mouseX);
this._colEles.forEach((colRef, i) => {
let leftStyle = '';
@@ -410,8 +417,8 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
clearSelection = () => SelectionManager.DeselectAll();
- selectRows = (rootDoc: Doc, lastSelected: Doc) => {
- const index = this.rowIndex(rootDoc);
+ selectRows = (doc: Doc, lastSelected: Doc) => {
+ const index = this.rowIndex(doc);
const lastSelectedRow = this.rowIndex(lastSelected);
const startRow = Math.min(lastSelectedRow, index);
const endRow = Math.max(lastSelectedRow, index);
@@ -431,10 +438,9 @@ export class CollectionSchemaView extends CollectionSubView() {
setDropIndex = (index: number) => (this._closestDropIndex = index);
- @action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.columnDragData) {
- const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
+ const mouseX = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y)[0];
const index = this.findDropIndex(mouseX);
this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex);
@@ -467,7 +473,6 @@ export class CollectionSchemaView extends CollectionSubView() {
return false;
};
- @action
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
};
@@ -479,7 +484,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const nativeWidth = this._previewRef!.getBoundingClientRect();
const minWidth = 40;
const maxWidth = 1000;
- const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+ const movedWidth = this.ScreenToLocalBoxXf().transformDirection(nativeWidth.right - e.clientX, 0)[0];
const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
this.layoutDoc.schema_previewWidth = width;
return false;
@@ -493,18 +498,18 @@ export class CollectionSchemaView extends CollectionSubView() {
ContextMenu.Instance.displayMenu(x, y, undefined, true);
};
- focusDocument = (doc: Doc, options: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: FocusViewOptions) => {
Doc.BrushDoc(doc);
this.scrollToDoc(doc, options);
return undefined;
};
- scrollToDoc = (doc: Doc, options: DocFocusOptions) => {
+ scrollToDoc = (doc: Doc, options: FocusViewOptions) => {
const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
if (found) {
const rect = found.getBoundingClientRect();
- const localRect = this.props.ScreenToLocalTransform().transformBounds(rect.left, rect.top, rect.width, rect.height);
- if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this.props.PanelHeight()) {
+ 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;
smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
return focusSpeed;
@@ -550,9 +555,8 @@ export class CollectionSchemaView extends CollectionSubView() {
};
setColumnValues = (key: string, value: string) => {
- let success: boolean = true;
- this.childDocs.forEach(doc => success && KeyValueBox.SetField(doc, key, value));
- return success;
+ this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value));
+ return true;
};
@action
@@ -577,9 +581,7 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeFilterMenu = () => {
- this._filterColumnIndex = undefined;
- };
+ closeFilterMenu = () => (this._filterColumnIndex = undefined);
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
@@ -754,7 +756,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@computed get renderFilterOptions() {
const keyOptions: string[] = [];
const columnKey = this.columnKeys[this._filterColumnIndex!];
- const allDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
+ const allDocs = DocListCast(this.dataDoc[this._props.fieldKey]);
allDocs.forEach(doc => {
const value = StrCast(doc[columnKey]);
if (!keyOptions.includes(value) && value !== '' && (this._filterSearchValue === '' || value.includes(this._filterSearchValue))) {
@@ -778,9 +780,9 @@ export class CollectionSchemaView extends CollectionSubView() {
onClick={e => e.stopPropagation()}
onChange={action(e => {
if (e.target.checked) {
- Doc.setDocFilter(this.props.Document, columnKey, key, 'check');
+ Doc.setDocFilter(this.Document, columnKey, key, 'check');
} else {
- Doc.setDocFilter(this.props.Document, columnKey, key, 'remove');
+ Doc.setDocFilter(this.Document, columnKey, key, 'remove');
}
})}
checked={bool}
@@ -827,8 +829,8 @@ export class CollectionSchemaView extends CollectionSubView() {
}
rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
sortedDocsFunc = () => this.sortedDocs;
- isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive();
+ screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0);
previewWidthFunc = () => this.previewWidth;
render() {
return (
@@ -836,8 +838,8 @@ export class CollectionSchemaView extends CollectionSubView() {
<div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
<div
className="schema-table"
- style={{ width: `calc(100% - ${this.previewWidth + 4}px)` }}
- onWheel={e => this.props.isContentActive() && e.stopPropagation()}
+ style={{ width: `calc(100% - ${this.previewWidth}px)` }}
+ onWheel={e => this._props.isContentActive() && e.stopPropagation()}
ref={r => {
// prevent wheel events from passively propagating up through containers
r?.addEventListener('wheel', (e: WheelEvent) => {}, { passive: false });
@@ -863,7 +865,7 @@ export class CollectionSchemaView extends CollectionSubView() {
openContextMenu={this.openContextMenu}
dragColumn={this.dragColumn}
setColRef={this.setColRef}
- isContentActive={this.props.isContentActive}
+ isContentActive={this._props.isContentActive}
/>
))}
</div>
@@ -889,16 +891,15 @@ export class CollectionSchemaView extends CollectionSubView() {
{Array.from(this._selectedDocs).lastElement() && (
<DocumentView
Document={Array.from(this._selectedDocs).lastElement()}
- DataDoc={undefined}
fitContentsToBox={returnTrue}
dontCenter={'y'}
onClickScriptDisable="always"
focus={emptyFunction}
defaultDoubleClick={returnIgnore}
- renderDepth={this.props.renderDepth + 1}
+ renderDepth={this._props.renderDepth + 1}
rootSelected={this.rootSelected}
PanelWidth={this.previewWidthFunc}
- PanelHeight={this.props.PanelHeight}
+ PanelHeight={this._props.PanelHeight}
isContentActive={returnTrue}
isDocumentActive={returnFalse}
ScreenToLocalTransform={this.screenToLocal}
@@ -906,14 +907,13 @@ export class CollectionSchemaView extends CollectionSubView() {
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
styleProvider={DefaultStyleProvider}
- docViewPath={returnEmptyDoclist}
- moveDocument={this.props.moveDocument}
+ containerViewPath={returnEmptyDoclist}
+ moveDocument={this._props.moveDocument}
addDocument={this.addRow}
- removeDocument={this.props.removeDocument}
+ removeDocument={this._props.removeDocument}
whenChildContentsActiveChanged={returnFalse}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
+ addDocTab={this._props.addDocTab}
+ pinToPres={this._props.pinToPres}
/>
)}
</div>
@@ -932,51 +932,72 @@ interface CollectionSchemaViewDocsProps {
@observer
class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
- tableWidthFunc = () => this.props.schema.tableWidth;
- childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - index * this.props.rowHeight()));
render() {
return (
<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) => {
- const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
- return (
- <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
- <DocumentView
- key={doc[Id]}
- {...this.props.schema.props}
- LayoutTemplate={this.props.schema.props.childLayoutTemplate}
- LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
- Document={doc}
- DataDoc={dataDoc}
- yPadding={index}
- renderDepth={this.props.schema.props.renderDepth + 1}
- PanelWidth={this.tableWidthFunc}
- PanelHeight={this.props.rowHeight}
- styleProvider={DefaultStyleProvider}
- waitForDoubleClickToClick={returnNever}
- defaultDoubleClick={returnIgnore}
- dragAction="move"
- onClickScriptDisable="always"
- focus={this.props.schema.focusDocument}
- childFilters={this.props.schema.childDocFilters}
- childFiltersByRanges={this.props.schema.childDocRangeFilters}
- searchFilterDocs={this.props.schema.searchFilterDocs}
- rootSelected={this.props.schema.rootSelected}
- ScreenToLocalTransform={this.childScreenToLocal(index)}
- bringToFront={emptyFunction}
- isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
- isContentActive={emptyFunction}
- whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
- layout_fitWidth={returnTrue}
- />
- </div>
- );
- })}
+ {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} />
+ </div>
+ ))}
</div>
);
}
}
+
+interface CollectionSchemaViewDocProps {
+ schema: CollectionSchemaView;
+ index: number;
+ doc: Doc;
+ rowHeight: () => number;
+}
+
+@observer
+class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaViewDocProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ tableWidthFunc = () => this._props.schema.tableWidth;
+ screenToLocalXf = () => this._props.schema.ScreenToLocalBoxXf().translate(0, -this._props.rowHeight() - this._props.index * this._props.rowHeight());
+ noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps>, property: string) => {
+ if (property === StyleProp.Opacity) return 1;
+ return DefaultStyleProvider(doc, props, property);
+ };
+ render() {
+ return (
+ <DocumentView
+ key={this._props.doc[Id]}
+ {...this._props.schema._props}
+ containerViewPath={this._props.schema.childContainerViewPath}
+ LayoutTemplate={this._props.schema._props.childLayoutTemplate}
+ LayoutTemplateString={SchemaRowBox.LayoutString(this._props.schema._props.fieldKey, this._props.index)}
+ Document={this._props.doc}
+ renderDepth={this._props.schema._props.renderDepth + 1}
+ PanelWidth={this.tableWidthFunc}
+ PanelHeight={this._props.rowHeight}
+ styleProvider={this.noOpacityStyleProvider}
+ waitForDoubleClickToClick={returnNever}
+ defaultDoubleClick={returnIgnore}
+ dragAction="move"
+ onClickScriptDisable="always"
+ focus={this._props.schema.focusDocument}
+ childFilters={this._props.schema.childDocFilters}
+ childFiltersByRanges={this._props.schema.childDocRangeFilters}
+ searchFilterDocs={this._props.schema.searchFilterDocs}
+ rootSelected={this._props.schema.rootSelected}
+ ScreenToLocalTransform={this.screenToLocalXf}
+ dragWhenActive={true}
+ isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive}
+ isContentActive={emptyFunction}
+ whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged}
+ hideDecorations={true}
+ hideTitle={true}
+ hideDocumentButtonBar={true}
+ hideLinkAnchors={true}
+ layout_fitWidth={returnTrue}
+ />
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index 65e47f441..5f8b412be 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,7 +1,7 @@
-import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, observable } from 'mobx';
import { observer } from 'mobx-react';
+import * as React from 'react';
import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
import { Colors } from '../../global/globalEnums';
import './CollectionSchemaView.scss';
@@ -26,7 +26,7 @@ export interface SchemaColumnHeaderProps {
export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
@observable _ref: HTMLDivElement | null = null;
- @computed get fieldKey() {
+ get fieldKey() {
return this.props.columnKeys[this.props.columnIndex];
}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 4e418728f..f2fe0dde7 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,12 +1,16 @@
-import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { computed } from 'mobx';
+import { IconButton, Size } from 'browndash-components';
+import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
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 { Doc } from '../../../../fields/Doc';
import { BoolCast } from '../../../../fields/Types';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
import { undoable } from '../../../util/UndoManager';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
@@ -15,49 +19,51 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
import { CollectionSchemaView } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaTableCell } from './SchemaTableCell';
-import { Transform } from '../../../util/Transform';
-import { IconButton, Size } from 'browndash-components';
-import { CgClose } from 'react-icons/cg';
-import { FaExternalLinkAlt } from 'react-icons/fa';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+interface SchemaRowBoxProps extends FieldViewProps {
+ rowIndex: number;
+}
@observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(SchemaRowBox, fieldKey);
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+ public static LayoutString(fieldKey: string, rowIndex: number) {
+ return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`);
}
-
private _ref: HTMLDivElement | null = null;
+ constructor(props: SchemaRowBoxProps) {
+ super(props);
+ makeObservable(this);
+ }
+
bounds = () => this._ref?.getBoundingClientRect();
@computed get schemaView() {
- return this.props.DocumentView?.().props.docViewPath().lastElement()?.ComponentView as CollectionSchemaView;
+ return this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionSchemaView;
}
@computed get schemaDoc() {
- return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc;
+ return this.DocumentView?.().containerViewPath?.().lastElement()?.Document;
}
@computed get rowIndex() {
- return this.schemaView?.rowIndex(this.rootDoc) ?? -1;
+ return this.schemaView?.rowIndex(this.Document) ?? -1;
}
componentDidMount(): void {
- this.props.setContentView?.(this);
+ this._props.setContentViewBox?.(this);
}
select = (ctrlKey: boolean, shiftKey: boolean) => {
if (!this.schemaView) return;
const lastSelected = Array.from(this.schemaView._selectedDocs).lastElement();
- if (shiftKey && lastSelected) this.schemaView.selectRows(this.rootDoc, lastSelected);
+ if (shiftKey && lastSelected) this.schemaView.selectRows(this.Document, lastSelected);
else {
- this.props.select?.(ctrlKey);
+ this._props.select?.(ctrlKey);
}
};
onPointerEnter = (e: any) => {
- if (SnappingManager.GetIsDragging() && this.props.isContentActive()) {
+ if (SnappingManager.IsDragging && this._props.isContentActive()) {
document.removeEventListener('pointermove', this.onPointerMove);
document.addEventListener('pointermove', this.onPointerMove);
}
@@ -101,20 +107,36 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
return (
<div
className="schema-row"
- style={{ height: this.props.PanelHeight(), backgroundColor: this.props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
+ style={{ height: this._props.PanelHeight(), backgroundColor: this._props.isSelected() ? Colors.LIGHT_BLUE : undefined }}
onPointerEnter={this.onPointerEnter}
onPointerLeave={this.onPointerLeave}
ref={(row: HTMLDivElement | null) => {
- row && this.schemaView?.addRowRef?.(this.rootDoc, row);
+ row && this.schemaView?.addRowRef?.(this.Document, row);
this._ref = row;
}}>
<div
className="row-menu"
style={{
width: CollectionSchemaView._rowMenuWidth,
- pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
+ pointerEvents: !this._props.isContentActive() ? 'none' : undefined,
}}>
<IconButton
+ tooltip="whether document interations are enabled"
+ icon={this.Document._lockedPosition ? <CgLockUnlock size="12px" /> : <CgLock size="12px" />}
+ size={Size.XSMALL}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(e => {
+ e.stopPropagation();
+ Doc.toggleLockedPosition(this.Document);
+ }, 'Delete Row')
+ )
+ }></IconButton>
+ <IconButton
tooltip="close"
icon={<CgClose size={'16px'} />}
size={Size.XSMALL}
@@ -126,7 +148,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
emptyFunction,
undoable(e => {
e.stopPropagation();
- this.props.removeDocument?.(this.rootDoc);
+ this._props.removeDocument?.(this.Document);
}, 'Delete Row')
)
}
@@ -143,7 +165,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
emptyFunction,
undoable(e => {
e.stopPropagation();
- this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
+ this._props.addDocTab(this.Document, OpenWhere.addRight);
}, 'Open schema Doc preview')
)
}
@@ -153,13 +175,13 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
{this.schemaView?.columnKeys?.map((key, index) => (
<SchemaTableCell
key={key}
- Document={this.rootDoc}
+ Document={this.Document}
col={index}
fieldKey={key}
allowCRs={false} // to enter text with new lines, must use \n
columnWidth={this.columnWidth(index)}
rowHeight={this.schemaView.rowHeightFunc}
- isRowActive={this.props.isContentActive}
+ isRowActive={this._props.isContentActive}
getFinfo={this.getFinfo}
selectCell={this.selectCell}
deselectCell={this.deselectCell}
@@ -170,7 +192,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
transform={() => {
const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
const x = this.schemaView?.displayColumnWidths.reduce((p, c, i) => (i <= ind ? p + c : p), 0);
- const y = (this.props.yPadding ?? 0) * this.props.PanelHeight();
+ const y = (this._props.rowIndex ?? 0) * this._props.PanelHeight();
return new Transform(x + CollectionSchemaView._rowMenuWidth, y, 1);
}}
/>
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 9d5b533d1..dbaa6e110 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -1,27 +1,28 @@
-import * as React from 'react';
-import Select, { MenuPlacement } from 'react-select';
-import { action, computed, observable } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import { extname } from 'path';
+import * as React from 'react';
import DatePicker from 'react-datepicker';
+import Select from 'react-select';
+import { Utils, emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } 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 { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero, Utils } from '../../../../Utils';
import { FInfo } from '../../../documents/Documents';
import { DocFocusOrOpen } from '../../../util/DocumentManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, undoable } from '../../../util/UndoManager';
import { EditableView } from '../../EditableView';
+import { ObservableReactComponent } from '../../ObservableReactComponent';
+import { DefaultStyleProvider } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
-import { OpenWhere } from '../../nodes/DocumentView';
-import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { OpenWhere, returnEmptyDocViewList } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
import { KeyValueBox } from '../../nodes/KeyValueBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { CollectionSchemaView, ColumnType, FInfotoColType } from './CollectionSchemaView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { ColumnType, FInfotoColType } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
export interface SchemaTableCellProps {
@@ -47,7 +48,12 @@ export interface SchemaTableCellProps {
}
@observer
-export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
+export class SchemaTableCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: SchemaTableCellProps) {
+ super(props);
+ makeObservable(this);
+ }
+
static addFieldDoc = (doc: Doc, where: OpenWhere) => {
DocFocusOrOpen(doc);
return true;
@@ -69,15 +75,13 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
const fieldProps: FieldViewProps = {
childFilters: returnEmptyFilter,
childFiltersByRanges: returnEmptyFilter,
+ docViewPath: returnEmptyDocViewList,
searchFilterDocs: returnEmptyDoclist,
styleProvider: DefaultStyleProvider,
- docViewPath: returnEmptyDoclist,
- rootSelected: returnFalse,
isSelected: returnFalse,
setHeight: returnFalse,
select: emptyFunction,
dragAction: 'move',
- bringToFront: emptyFunction,
renderDepth: 1,
isContentActive: returnFalse,
whenChildContentsActiveChanged: emptyFunction,
@@ -97,12 +101,12 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
}
@computed get selected() {
- const selected: [Doc, number] | undefined = this.props.selectedCell();
- return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+ const selected: [Doc, number] | undefined = this._props.selectedCell();
+ return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
}
@computed get defaultCellContent() {
- const { color, textDecoration, fieldProps } = SchemaTableCell.renderProps(this.props);
+ const { color, textDecoration, fieldProps, pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<div
@@ -111,19 +115,21 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
color,
textDecoration,
width: '100%',
+ pointerEvents,
}}>
<EditableView
- oneLine={this.props.oneLine}
- allowCRs={this.props.allowCRs}
- contents={<FieldView {...fieldProps} />}
+ oneLine={this._props.oneLine}
+ allowCRs={this._props.allowCRs}
+ contents={undefined}
+ fieldContents={fieldProps}
editing={this.selected ? undefined : false}
- GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
+ GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
- this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
+ this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
}
- const ret = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
- this.props.finishEdit?.();
+ const ret = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value);
+ this._props.finishEdit?.();
return ret;
}, 'edit schema cell')}
/>
@@ -132,8 +138,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
}
get getCellType() {
- const columnTypeStr = this.props.getFinfo(this.props.fieldKey)?.fieldType;
- const cellValue = this.props.Document[this.props.fieldKey];
+ const columnTypeStr = this._props.getFinfo(this._props.fieldKey)?.fieldType;
+ const cellValue = this._props.Document[this._props.fieldKey];
if (cellValue instanceof ImageField) return ColumnType.Image;
if (cellValue instanceof DateField) return ColumnType.Date;
if (cellValue instanceof RichTextField) return ColumnType.RTF;
@@ -152,11 +158,11 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
const cellType: ColumnType = this.getCellType;
// prettier-ignore
switch (cellType) {
- 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.Date: // return <SchemaDateCell {...this.props} />;
+ 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.Date: // return <SchemaDateCell {...this._props} />;
default: return this.defaultCellContent;
}
}
@@ -165,8 +171,8 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
return (
<div
className="schema-table-cell"
- onPointerDown={action(e => !this.selected && this.props.selectCell(this.props.Document, this.props.col))}
- style={{ padding: this.props.padding, maxWidth: this.props.maxWidth?.(), width: this.props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
+ onPointerDown={action(e => !this.selected && this._props.selectCell(this._props.Document, this._props.col))}
+ style={{ padding: this._props.padding, maxWidth: this._props.maxWidth?.(), width: this._props.columnWidth() || undefined, border: this.selected ? `solid 2px ${Colors.MEDIUM_BLUE}` : undefined }}>
{this.content}
</div>
);
@@ -175,8 +181,13 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
// mj: most of this is adapted from old schema code so I'm not sure what it does tbh
@observer
-export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
- @observable _previewRef: HTMLImageElement | undefined;
+export class SchemaImageCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
+ @observable _previewRef: HTMLImageElement | undefined = undefined;
choosePath(url: URL) {
if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
@@ -188,8 +199,8 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
}
get url() {
- const field = Cast(this.props.Document[this.props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this.props.Document[this.props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+ const field = Cast(this._props.Document[this._props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(this._props.Document[this._props.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
const altpaths = alts
.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
.filter(url => url)
@@ -226,10 +237,10 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
};
render() {
- const aspect = Doc.NativeAspect(this.props.Document); // aspect ratio
- // let width = Math.max(75, this.props.columnWidth); // get a with that is no smaller than 75px
+ const aspect = Doc.NativeAspect(this._props.Document); // aspect ratio
+ // let width = Math.max(75, this._props.columnWidth); // get a with that is no smaller than 75px
// const height = Math.max(75, width / aspect); // get a height either proportional to that or 75 px
- const height = this.props.rowHeight() ? this.props.rowHeight() - (this.props.padding || 6) * 2 : undefined;
+ 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} />;
@@ -237,22 +248,26 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
}
@observer
-export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
- @observable _pickingDate: boolean = false;
+export class SchemaDateCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+ @observable _pickingDate: boolean = false;
@computed get date(): DateField {
// if the cell is a date field, cast then contents to a date. Otherrwwise, make the contents undefined.
- return DateCast(this.props.Document[this.props.fieldKey]);
+ return DateCast(this._props.Document[this._props.fieldKey]);
}
@action
handleChange = (date: any) => {
// const script = CompileScript(date.toString(), { requiredType: "Date", addReturn: true, params: { this: Doc.name } });
// if (script.compiled) {
- // this.applyToDoc(this._document, this.props.row, this.props.col, script.run);
+ // this.applyToDoc(this._document, this._props.row, this._props.col, script.run);
// } else {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
- this.props.Document[this.props.fieldKey] = new DateField(date as Date);
+ this._props.Document[this._props.fieldKey] = new DateField(date as Date);
//}
};
@@ -261,53 +276,64 @@ export class SchemaDateCell extends React.Component<SchemaTableCellProps> {
}
}
@observer
-export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
+export class SchemaRTFCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get selected() {
- const selected: [Doc, number] | undefined = this.props.selectedCell();
- return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+ const selected: [Doc, number] | undefined = this._props.selectedCell();
+ return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
}
selectedFunc = () => this.selected;
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
+ const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
fieldProps.isContentActive = this.selectedFunc;
return (
<div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ {this.selected ? <FormattedTextBox {...fieldProps} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
}
@observer
-export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
+export class SchemaBoolCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get selected() {
- const selected: [Doc, number] | undefined = this.props.selectedCell();
- return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+ const selected: [Doc, number] | undefined = this._props.selectedCell();
+ return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
}
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
+ const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this._props);
return (
<div className="schemaBoolCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
<input
style={{ marginRight: 4 }}
type="checkbox"
- checked={BoolCast(this.props.Document[this.props.fieldKey])}
+ 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());
+ this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
}
- KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
+ KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + value?.target?.checked.toString());
})}
/>
<EditableView
- contents={<FieldView {...fieldProps} />}
+ contents={undefined}
+ fieldContents={fieldProps}
editing={this.selected ? undefined : false}
- GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}
+ GetValue={() => Field.toKeyValueString(this._props.Document, this._props.fieldKey)}
SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => {
if (shiftDown && enterKey) {
- this.props.setColumnValues(this.props.fieldKey.replace(/^_/, ''), value);
+ this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value);
}
- const set = KeyValueBox.SetField(this.props.Document, this.props.fieldKey.replace(/^_/, ''), value);
- this.props.finishEdit?.();
+ const set = KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), value);
+ this._props.finishEdit?.();
return set;
})}
/>
@@ -316,14 +342,19 @@ export class SchemaBoolCell extends React.Component<SchemaTableCellProps> {
}
}
@observer
-export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps> {
+export class SchemaEnumerationCell extends ObservableReactComponent<SchemaTableCellProps> {
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get selected() {
- const selected: [Doc, number] | undefined = this.props.selectedCell();
- return this.props.isRowActive() && selected?.[0] === this.props.Document && selected[1] === this.props.col;
+ const selected: [Doc, number] | undefined = this._props.selectedCell();
+ return this._props.isRowActive() && selected?.[0] === this._props.Document && selected[1] === this._props.col;
}
render() {
- const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
- const options = this.props.options?.map(facet => ({ value: facet, label: facet }));
+ const { color, textDecoration, fieldProps, 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 }}>
<div style={{ width: '100%' }}>
@@ -357,17 +388,17 @@ export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps>
...base,
left: 0,
top: 0,
- transform: `translate(${this.props.transform().TranslateX}px, ${this.props.transform().TranslateY}px)`,
- width: Number(base.width) * this.props.transform().Scale,
+ transform: `translate(${this._props.transform().TranslateX}px, ${this._props.transform().TranslateY}px)`,
+ width: Number(base.width) * this._props.transform().Scale,
zIndex: 9999,
}),
}}
- menuPortalTarget={this.props.menuTarget}
+ menuPortalTarget={this._props.menuTarget}
menuPosition={'absolute'}
- placeholder={StrCast(this.props.Document[this.props.fieldKey], 'select...')}
+ 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 => KeyValueBox.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), `"${val?.value ?? ''}"`)}
/>
</div>
</div>
diff --git a/src/client/views/collections/goldenLayoutTheme.css b/src/client/views/collections/goldenLayoutTheme.css
new file mode 100644
index 000000000..cf577d6b1
--- /dev/null
+++ b/src/client/views/collections/goldenLayoutTheme.css
@@ -0,0 +1,132 @@
+.lm_goldenlayout {
+ background: #000000;
+}
+.lm_content {
+ background: #222222;
+}
+.lm_dragProxy .lm_content {
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
+}
+.lm_dropTargetIndicator {
+ box-shadow: inset 0 0 30px #000000;
+ outline: 1px dashed #cccccc;
+ transition: all 200ms ease;
+}
+.lm_dropTargetIndicator .lm_inner {
+ background: #000000;
+ opacity: 0.2;
+}
+.lm_splitter {
+ background: #000000;
+ opacity: 0.001;
+ transition: opacity 200ms ease;
+}
+.lm_splitter:hover,
+.lm_splitter.lm_dragging {
+ background: #444444;
+ opacity: 1;
+}
+.lm_header {
+ height: 20px;
+ user-select: none;
+}
+.lm_header.lm_selectable {
+ cursor: pointer;
+}
+.lm_header .lm_tab {
+ font-family: Arial, sans-serif;
+ font-size: 12px;
+ color: #999999;
+ background: #111111;
+ box-shadow: 2px -2px 2px rgba(0, 0, 0, 0.3);
+ margin-right: 2px;
+ padding-bottom: 2px;
+ padding-top: 2px;
+}
+.lm_header .lm_tab .lm_close_tab {
+ width: 11px;
+ height: 11px;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAATElEQVR4nG3OwQ0DMQwDwZGRBtR/j1YJzMc5+IDoR+yCVO29g+pu981MFgqZmRdAfU7+CYWcbF11LwALjpBL0N0qybNx/RPU+gOeiS/+XCRwDlTgkQAAAABJRU5ErkJggg==);
+ background-position: center center;
+ background-repeat: no-repeat;
+ top: 4px;
+ right: 6px;
+ opacity: 0.4;
+}
+.lm_header .lm_tab .lm_close_tab:hover {
+ opacity: 1;
+}
+.lm_header .lm_tab.lm_active {
+ border-bottom: none;
+ box-shadow: 0 -2px 2px #000000;
+ padding-bottom: 3px;
+}
+.lm_header .lm_tab.lm_active .lm_close_tab {
+ opacity: 1;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tab,
+.lm_stack.lm_bottom .lm_header .lm_tab {
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3);
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tab.lm_active,
+.lm_stack.lm_bottom .lm_header .lm_tab.lm_active {
+ box-shadow: 0 2px 2px #000000;
+}
+.lm_selected .lm_header {
+ background-color: #452500;
+}
+.lm_tab:hover,
+.lm_tab.lm_active {
+ background: #222222;
+ color: #dddddd;
+}
+.lm_header .lm_controls .lm_tabdropdown:before {
+ color: #ffffff;
+}
+.lm_controls > li {
+ position: relative;
+ background-position: center center;
+ background-repeat: no-repeat;
+ opacity: 0.4;
+ transition: opacity 300ms ease;
+}
+.lm_controls > li:hover {
+ opacity: 1;
+}
+.lm_controls .lm_popout {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAPklEQVR4nI2Q0QoAIAwCNfr/X7aXCpGN8snBdgejJOzckpkxs9jR6K6T5JpU0nWl5pSXTk7qwh8SnNT+CAAWCgkKFpuSWsUAAAAASUVORK5CYII=);
+}
+.lm_controls .lm_maximise {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKElEQVR4nGP8////fwYCgImQAgYGBgYWKM2IR81/okwajIpgvsMbVgAwgQYRVakEKQAAAABJRU5ErkJggg==);
+}
+.lm_controls .lm_close {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAQUlEQVR4nHXOQQ4AMAgCQeT/f6aXpsGK3jSTuCVJAAr7iBdoAwCKd0nwfaAdHbYERw5b44+E8JoBjEYGMBq5gAYP3usUDu2IvoUAAAAASUVORK5CYII=);
+}
+.lm_maximised .lm_header {
+ background-color: #000000;
+}
+.lm_maximised .lm_controls .lm_maximise {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAJ0lEQVR4nGP8//8/AzGAiShVI1YhCwMDA8OsWbPwBmZaWhoj0SYCAN1lBxMAX4n0AAAAAElFTkSuQmCC);
+}
+.lm_transition_indicator {
+ background-color: #000000;
+ border: 1px dashed #555555;
+}
+.lm_popin {
+ cursor: pointer;
+}
+.lm_popin .lm_bg {
+ background: #ffffff;
+ opacity: 0.3;
+}
+.lm_popin .lm_icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAJCAYAAADpeqZqAAAAWklEQVR4nJWOyw3AIAxDHcQC7L8jbwT3AlJBfNp3SiI7dtRaLSlKKeoA1oEsKSQZCEluexw8Tm3ohk+E7bnOUHUGcNh+HwbBygw4AZ7FN/Lt84p0l+yTflV8AKQyLdcCRJi/AAAAAElFTkSuQmCC);
+ background-position: center center;
+ background-repeat: no-repeat;
+ border-left: 1px solid #eeeeee;
+ border-top: 1px solid #eeeeee;
+ opacity: 0.7;
+}
+.lm_popin:hover .lm_icon {
+ opacity: 1;
+} /*# sourceMappingURL=goldenlayout-dark-theme.css.map */