aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorNaafiyan Ahmed <naafiyan@gmail.com>2022-07-12 12:08:49 -0400
committerNaafiyan Ahmed <naafiyan@gmail.com>2022-07-12 12:08:49 -0400
commit9ad978eac113cf3559d885c62a9368a68f6333ec (patch)
tree8daa3a5663379b29d8121aaa39e80cfd5e7fd9ed /src/client/views/collections
parent31041d7a5b2c3699518ebb33ccab016af0acd579 (diff)
parent5628b585fa6356d66cf2e7454be20e3b847ad22e (diff)
merged master
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.scss39
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx352
-rw-r--r--src/client/views/collections/CollectionMenu.tsx1246
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx716
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx659
-rw-r--r--src/client/views/collections/CollectionSubView.tsx286
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx320
-rw-r--r--src/client/views/collections/CollectionView.tsx221
-rw-r--r--src/client/views/collections/TabDocView.tsx623
-rw-r--r--src/client/views/collections/TreeView.tsx1175
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx248
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx201
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx34
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx105
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx1682
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx517
-rw-r--r--src/client/views/collections/collectionGrid/Grid.tsx17
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx296
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx80
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx102
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss27
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx610
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx660
23 files changed, 5538 insertions, 4678 deletions
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 21045a20e..091ba8e74 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,5 +1,6 @@
-@import "../global/globalCssVariables.scss";
-
+@import '../global/globalCssVariables.scss';
+@import '../../../../node_modules/golden-layout/src/css/goldenlayout-base.css';
+@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css';
.lm_title {
-webkit-appearance: none;
@@ -36,11 +37,11 @@
}
.lm_header .lm_tab {
- padding: 0px;
- opacity: 0.7;
- box-shadow: none;
- height: 25px;
- border-bottom: black solid;
+ // padding: 0px; // moved to MainView.scss, othwerise they get overridden by default stylings
+ // opacity: 0.7;
+ // box-shadow: none;
+ // height: 25px;
+ // border-bottom: black solid;
.collectionDockingView-gear {
display: none;
@@ -153,7 +154,7 @@
background: $white;
}
- .lm_controls>li {
+ .lm_controls > li {
opacity: 1;
transform: scale(1);
}
@@ -196,7 +197,7 @@
position: absolute;
cursor: move;
border: 2px solid #cfe8ff;
- box-shadow: inset 0 0 60px rgba(0, 0, 0, .2);
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2);
border-radius: 5px;
z-index: 1000;
box-sizing: border-box;
@@ -205,7 +206,7 @@
.flexlayout__outline_rect_edge {
cursor: move;
border: 2px solid #b7d1b5;
- box-shadow: inset 0 0 60px rgba(0, 0, 0, .2);
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2);
border-radius: 5px;
z-index: 1000;
box-sizing: border-box;
@@ -214,7 +215,7 @@
.flexlayout__edge_rect {
position: absolute;
z-index: 1000;
- box-shadow: inset 0 0 5px rgba(0, 0, 0, .2);
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: lightgray;
}
@@ -222,7 +223,7 @@
position: absolute;
cursor: move;
border: 2px solid #aaaaaa;
- box-shadow: inset 0 0 60px rgba(0, 0, 0, .3);
+ box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.3);
border-radius: 5px;
z-index: 1000;
box-sizing: border-box;
@@ -301,7 +302,7 @@
.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;
+ background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center;
}
.flexlayout__tab_button_overflow {
@@ -314,7 +315,7 @@
font-size: 10px;
color: lightgray;
font-family: Arial, sans-serif;
- background: transparent url("../../../../node_modules/flexlayout-react/images/more.png") no-repeat left;
+ background: transparent url('../../../../node_modules/flexlayout-react/images/more.png') no-repeat left;
}
.flexlayout__tabset_header {
@@ -369,7 +370,7 @@
height: 20px;
border: none;
outline-width: 0;
- background: transparent url("../../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center;
+ background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center;
}
.flexlayout__tab_toolbar_button-max {
@@ -377,7 +378,7 @@
height: 20px;
border: none;
outline-width: 0;
- background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center;
+ background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center;
}
.flexlayout__popup_menu_item {
@@ -390,7 +391,7 @@
}
.flexlayout__popup_menu_container {
- box-shadow: inset 0 0 5px rgba(0, 0, 0, .15);
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.15);
border: 1px solid #555;
background: #222;
border-radius: 3px;
@@ -497,7 +498,7 @@
.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;
+ background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center;
}
.flexlayout__border_toolbar_left {
@@ -539,4 +540,4 @@
bottom: 0;
right: 0;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 0830b6fdf..42f9bb981 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -1,34 +1,31 @@
-import 'golden-layout/src/css/goldenlayout-base.css';
-import 'golden-layout/src/css/goldenlayout-dark-theme.css';
-import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom';
-import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
+import * as GoldenLayout from '../../../client/goldenLayout';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { inheritParentAcls } from '../../../fields/util';
import { emptyFunction, incrementTitleCopy } from '../../../Utils';
-import { DocServer } from "../../DocServer";
+import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DragManager } from "../../util/DragManager";
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+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 { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
import { LightboxView } from '../LightboxView';
-import "./CollectionDockingView.scss";
+import './CollectionDockingView.scss';
import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
-import { CollectionViewType } from './CollectionView';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { TabDocView } from './TabDocView';
-import React = require("react");
-const _global = (window /* browser */ || global /* node */) as any;
+import React = require('react');
+const _global = (window /* browser */ || global) /* node */ as any;
@observer
export class CollectionDockingView extends CollectionSubView() {
@@ -41,8 +38,8 @@ export class CollectionDockingView extends CollectionSubView() {
width: width,
props: {
documentId: document[Id],
- panelName // name of tab that can be used to close or replace its contents
- }
+ panelName, // name of tab that can be used to close or replace its contents
+ },
};
}
@@ -50,14 +47,16 @@ export class CollectionDockingView extends CollectionSubView() {
private _lightboxReactionDisposer?: IReactionDisposer;
private _containerRef = React.createRef<HTMLDivElement>();
public _flush: UndoManager.Batch | undefined;
- private _ignoreStateChange = "";
+ private _ignoreStateChange = '';
public tabMap: Set<any> = new Set();
- public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
+ public get HasFullScreen() {
+ return this._goldenLayout._maximisedItem !== null;
+ }
private _goldenLayout: any = null;
constructor(props: SubCollectionViewProps) {
super(props);
- runInAction(() => CollectionDockingView.Instance = this);
+ runInAction(() => (CollectionDockingView.Instance = this));
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
@@ -66,19 +65,18 @@ export class CollectionDockingView extends CollectionSubView() {
/**
* Switches from dragging a document around a freeform canvas to dragging it as a tab to be docked.
- *
+ *
* @param e fake mouse down event position data containing pageX and pageY coordinates
* @param dragDocs the documents to be dragged
* @param batch optionally an undo batch that has been started to use instead of starting a new batch
- */
- public StartOtherDrag = (e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => {
- this._flush = this._flush ?? UndoManager.StartBatch("golden layout drag");
- const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) :
- { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) };
- const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config);
+ */
+ public StartOtherDrag = (e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => {
+ this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag');
+ const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) };
+ const dragSource = this._goldenLayout.createDragSource(document.createElement('div'), config);
this.tabDragStart(dragSource, finishDrag);
dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 });
- }
+ };
tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => {
@@ -88,7 +86,7 @@ export class CollectionDockingView extends CollectionSubView() {
if (aborted) {
proxy._dragListener.AbortDrag();
if (this._flush) {
- this._flush.cancel(); // cancel the undo change being logged
+ this._flush.cancel(); // cancel the undo change being logged
this._flush = undefined;
this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
}
@@ -96,16 +94,16 @@ export class CollectionDockingView extends CollectionSubView() {
}
finishDrag?.(aborted);
};
- }
+ };
@undoBatch
public CloseFullScreen = () => {
this._goldenLayout._maximisedItem?.toggleMaximise();
this.stateChanged();
- }
+ };
@undoBatch
public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
- const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document));
if (tab) {
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
if (j !== -1) {
@@ -121,12 +119,12 @@ export class CollectionDockingView extends CollectionSubView() {
public static OpenFullScreen(doc: Doc) {
SelectionManager.DeselectAll();
const instance = CollectionDockingView.Instance;
- if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
- return CurrentUserUtils.openDashboard(doc);
+ if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
+ return DashboardView.openDashboard(doc);
}
const newItemStackConfig = {
type: 'stack',
- content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))]
+ content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))],
};
const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
instance._goldenLayout.root.contentItems[0].addChild(docconfig);
@@ -150,7 +148,7 @@ export class CollectionDockingView extends CollectionSubView() {
stack.contentItems[activeContentItemIndex].remove();
return CollectionDockingView.Instance.layoutChanged();
}
- const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName);
+ const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName);
if (tab) {
tab.header.parent.addChild(newConfig, undefined);
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
@@ -160,11 +158,9 @@ export class CollectionDockingView extends CollectionSubView() {
return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
}
-
@undoBatch
public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) {
- return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ?
- CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
+ return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1 ? CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
}
//
@@ -173,7 +169,7 @@ export class CollectionDockingView extends CollectionSubView() {
@undoBatch
@action
public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
- if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document);
+ if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
if (tab) {
@@ -193,35 +189,37 @@ export class CollectionDockingView extends CollectionSubView() {
const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout);
newItem.callDownwards('_$init');
return newItem;
- }
- if (glayRoot.contentItems.length === 0) { // if no rows / columns
+ };
+ if (glayRoot.contentItems.length === 0) {
+ // if no rows / columns
glayRoot.addChild(newContentItem());
} else if (glayRoot.contentItems[0].isStack) {
glayRoot.contentItems[0].addChild(docContentConfig);
- } else if (
- glayRoot.contentItems.length === 1 &&
- glayRoot.contentItems[0].contentItems.length === 1 &&
- glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) {
+ } else if (glayRoot.contentItems.length === 1 && glayRoot.contentItems[0].contentItems.length === 1 && glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) {
glayRoot.contentItems[0].contentItems[0].addChild(docContentConfig);
- }
- else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
+ } else if (instance._goldenLayout.root.contentItems[0].isRow) {
+ // if row
switch (pullSide) {
default:
- case "right": glayRoot.contentItems[0].addChild(newContentItem()); break;
- case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
- case "top":
- case "bottom":
+ case 'right':
+ glayRoot.contentItems[0].addChild(newContentItem());
+ break;
+ case 'left':
+ glayRoot.contentItems[0].addChild(newContentItem(), 0);
+ break;
+ case 'top':
+ case 'bottom':
// if not going in a row layout, must add already existing content into column
const rowlayout = glayRoot.contentItems[0];
- const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
+ const newColumn = rowlayout.layoutManager.createContentItem({ type: 'column' }, instance._goldenLayout);
const newItem = newContentItem();
instance._goldenLayout.saveScrollTops(rowlayout.element);
rowlayout.parent.replaceChild(rowlayout, newColumn);
- if (pullSide === "top") {
+ if (pullSide === 'top') {
newColumn.addChild(rowlayout, undefined, true);
newColumn.addChild(newItem, 0, true);
- } else if (pullSide === "bottom") {
+ } else if (pullSide === 'bottom') {
newColumn.addChild(newItem, undefined, true);
newColumn.addChild(rowlayout, 0, true);
}
@@ -230,21 +228,26 @@ export class CollectionDockingView extends CollectionSubView() {
rowlayout.config.height = 50;
newItem.config.height = 50;
}
- } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
+ } else {
+ // if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
switch (pullSide) {
- case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
- case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break;
- case "left":
- case "right":
+ case 'top':
+ glayRoot.contentItems[0].addChild(newContentItem(), 0);
+ break;
+ case 'bottom':
+ glayRoot.contentItems[0].addChild(newContentItem());
+ break;
+ case 'left':
+ case 'right':
default:
// if not going in a row layout, must add already existing content into column
const collayout = glayRoot.contentItems[0];
- const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
+ const newRow = collayout.layoutManager.createContentItem({ type: 'row' }, instance._goldenLayout);
const newItem = newContentItem();
instance._goldenLayout.saveScrollTops(collayout.element);
collayout.parent.replaceChild(collayout, newRow);
- if (pullSide === "left") {
+ if (pullSide === 'left') {
newRow.addChild(collayout, undefined, true);
newRow.addChild(newItem, 0, true);
} else {
@@ -276,7 +279,7 @@ export class CollectionDockingView extends CollectionSubView() {
const config = StrCast(this.props.Document.dockingConfig);
if (config) {
const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
- const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? [];
+ const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
await Promise.all(docids.map(id => DocServer.GetRefField(id)));
if (this._goldenLayout) {
@@ -287,12 +290,12 @@ export class CollectionDockingView extends CollectionSubView() {
this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
- } catch (e) { }
+ } catch (e) {}
}
this.tabMap.clear();
this._goldenLayout.destroy();
}
- const glay = this._goldenLayout = new GoldenLayout(JSON.parse(config));
+ const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config)));
glay.on('tabCreated', this.tabCreated);
glay.on('tabDestroyed', this.tabDestroyed);
glay.on('stackCreated', this.stackCreated);
@@ -303,46 +306,52 @@ export class CollectionDockingView extends CollectionSubView() {
glay.root.layoutManager.on('dragStart', this.tabDragStart);
glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
}
- }
+ };
componentDidMount: () => void = () => {
if (this._containerRef.current) {
- this._lightboxReactionDisposer = reaction(() => LightboxView.LightboxDoc, doc => setTimeout(() => !doc && this.onResize(undefined)));
+ this._lightboxReactionDisposer = reaction(
+ () => LightboxView.LightboxDoc,
+ doc => setTimeout(() => !doc && this.onResize(undefined))
+ );
new _global.ResizeObserver(this.onResize).observe(this._containerRef.current);
- this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig),
+ this._reactionDisposer = reaction(
+ () => StrCast(this.props.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.
+ 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.
this.setupGoldenLayout();
}
- this._ignoreStateChange = "";
- });
+ this._ignoreStateChange = '';
+ }
+ );
setTimeout(this.setupGoldenLayout);
//window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
}
- }
+ };
componentWillUnmount: () => void = () => {
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- } catch (e) { }
+ } catch (e) {}
this._goldenLayout?.destroy();
window.removeEventListener('resize', this.onResize);
this._reactionDisposer?.();
this._lightboxReactionDisposer?.();
- }
+ };
@action
onResize = (event: any) => {
const cur = this._containerRef.current;
// bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed
!LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height);
- }
+ };
@action
onPointerUp = (e: MouseEvent): void => {
- window.removeEventListener("pointerup", this.onPointerUp);
+ window.removeEventListener('pointerup', this.onPointerUp);
const flush = this._flush;
this._flush = undefined;
if (flush) {
@@ -350,75 +359,71 @@ export class CollectionDockingView extends CollectionSubView() {
if (!this.stateChanged()) flush.cancel();
else flush.end();
}
- }
+ };
@action
onPointerDown = (e: React.PointerEvent): void => {
let hitFlyout = false;
for (let par = e.target as any; !hitFlyout && par; par = par.parentElement) {
- hitFlyout = (par.className === "dockingViewButtonSelector");
+ hitFlyout = par.className === 'dockingViewButtonSelector';
}
if (!hitFlyout) {
const htmlTarget = e.target as HTMLElement;
- 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_close") && !className.includes("lm_maximise")) {
- this._flush = UndoManager.StartBatch("golden layout edit");
+ 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_close') && !className.includes('lm_maximise')) {
+ this._flush = UndoManager.StartBatch('golden layout edit');
}
}
}
- if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
- ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+ if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
e.stopPropagation();
}
- }
+ };
public CaptureThumbnail() {
const content = this.props.DocumentView?.()?.ContentDiv;
if (content) {
- const _width = Number(getComputedStyle(content).width.replace("px",""));
- const _height = Number(getComputedStyle(content).height.replace("px",""));
- return CollectionFreeFormView.UpdateIcon(
- this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
- content,
- _width, _height,
- _width, _height, 0, 1, true, this.layoutDoc[Id] + "-icon",
- (iconFile, _nativeWidth, _nativeHeight) => {
- const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight});
- const proto = Cast(img.proto, Doc, null)!;
- proto["data-nativeWidth"] = _width;
- proto["data-nativeHeight"] = _height;
- this.dataDoc.thumb = img;
- });
+ const _width = Number(getComputedStyle(content).width.replace('px', ''));
+ const _height = Number(getComputedStyle(content).height.replace('px', ''));
+ return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
+ const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title + '-icon', _width, _height, _nativeWidth, _nativeHeight });
+ const proto = Cast(img.proto, Doc, null)!;
+ proto['data-nativeWidth'] = _width;
+ proto['data-nativeHeight'] = _height;
+ this.dataDoc.thumb = img;
+ });
}
-
}
- public static async TakeSnapshot(doc: Doc|undefined, clone = false) {
+ public static async TakeSnapshot(doc: Doc | undefined, clone = false) {
if (!doc) return undefined;
let json = StrCast(doc.dockingConfig);
if (clone) {
const cloned = await Doc.MakeClone(doc);
- Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id]));
+ Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
Doc.GetProto(cloned.clone).dockingConfig = json;
- return CurrentUserUtils.openDashboard(cloned.clone);
+ return DashboardView.openDashboard(cloned.clone);
}
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
- const origtabids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || [];
- const origtabs = origtabids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
+ const origtabids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) || [];
+ const origtabs = origtabids
+ .map(id => DocServer.GetCachedRefField(id))
+ .filter(f => f)
+ .map(f => f as Doc);
const newtabs = origtabs.map(origtab => {
const origtabdocs = DocListCast(origtab.data);
const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeAlias(origtab);
const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeAlias(origtabdoc));
if (newtabdocs.length) {
Doc.GetProto(newtab).data = new List<Doc>(newtabdocs);
- newtabdocs.forEach(ntab => ntab.context = newtab);
+ newtabdocs.forEach(ntab => (ntab.context = newtab));
}
json = json.replace(origtab[Id], newtab[Id]);
return newtab;
});
const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
- return CurrentUserUtils.openDashboard(await copy);
+ return DashboardView.openDashboard(await copy);
}
@action
@@ -426,22 +431,27 @@ export class CollectionDockingView extends CollectionSubView() {
this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig());
const json = JSON.stringify(this._goldenLayout.toConfig());
const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
- const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", ""));
- const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
+ const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', ''));
+ const docs = !docids
+ ? []
+ : docids
+ .map(id => DocServer.GetCachedRefField(id))
+ .filter(f => f)
+ .map(f => f as Doc);
const changesMade = this.props.Document.dockcingConfig !== json;
if (changesMade && !this._flush) {
UndoManager.RunInBatch(() => {
this.props.Document.dockingConfig = json;
this.props.Document.data = new List<Doc>(docs);
- }, "state changed");
+ }, 'state changed');
}
return changesMade;
- }
+ };
tabDestroyed = (tab: any) => {
- if(tab.DashDoc?.type !== DocumentType.KVP) {
- Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc);
- Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+ if (tab.DashDoc?.type !== DocumentType.KVP) {
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
+ Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
}
const dview = CollectionDockingView.Instance.props.Document;
const fieldKey = CollectionDockingView.Instance.props.fieldKey;
@@ -450,63 +460,95 @@ export class CollectionDockingView extends CollectionSubView() {
tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
this.stateChanged();
- }
+ };
tabCreated = (tab: any) => {
this.tabMap.add(tab);
- tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
- }
+ tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
+ };
stackCreated = (stack: any) => {
stack.header?.element.on('mousedown', (e: any) => {
- const dashboard = CurrentUserUtils.ActiveDashboard;
+ const dashboard = Doc.ActiveDashboard;
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
- dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+ dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
+ _width: this.props.PanelWidth(),
+ _height: this.props.PanelHeight(),
+ _backgroundGridShow: true,
+ _fitWidth: true,
+ title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
});
this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
- CollectionDockingView.AddSplit(docToAdd, "", stack);
+ CollectionDockingView.AddSplit(docToAdd, '', stack);
}
});
- stack.header?.controlsContainer.find('.lm_close') //get the close icon
+ stack.header?.controlsContainer
+ .find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
- .click(action(() => {
- //if (confirm('really close this?')) {
- if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
- stack.remove();
- } else {
- alert('cant delete the last stack');
- }
- }));
+ .click(
+ action(() => {
+ //if (confirm('really close this?')) {
+ if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
+ stack.remove();
+ } else {
+ alert('cant delete the last stack');
+ }
+ })
+ );
- stack.header?.controlsContainer.find('.lm_maximise') //get the close icon
+ stack.header?.controlsContainer
+ .find('.lm_maximise') //get the close icon
.click(() => setTimeout(this.stateChanged));
- stack.header?.controlsContainer.find('.lm_popout') //get the popout icon
+ stack.header?.controlsContainer
+ .find('.lm_popout') //get the popout icon
.off('click') //unbind the current click handler
- .click(action(() => {
- // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size
- const dashboard = CurrentUserUtils.ActiveDashboard;
- if (dashboard) {
- dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
- const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
- });
- this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
- CollectionDockingView.AddSplit(docToAdd, "", stack);
- }
- }));
- }
+ .click(
+ action(() => {
+ // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size
+ 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(),
+ _fitWidth: true,
+ _backgroundGridShow: true,
+ title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
+ });
+ this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+ CollectionDockingView.AddSplit(docToAdd, '', stack);
+ }
+ })
+ );
+ };
render() {
return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />;
}
}
-ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
- "opens up document in a lightbox", "(doc: any)");
-ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
- "opens up document in tab on right side of the screen", "(doc: any)");
-ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); },
- "opens up document in screen overlay layer", "(doc: any)");
-ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); }); \ No newline at end of file
+ScriptingGlobals.add(
+ function openInLightbox(doc: any) {
+ LightboxView.AddDocTab(doc, 'lightbox');
+ },
+ 'opens up document in a lightbox',
+ '(doc: any)'
+);
+ScriptingGlobals.add(
+ function openOnRight(doc: any) {
+ return CollectionDockingView.AddSplit(doc, 'right');
+ },
+ 'opens up document in tab on right side of the screen',
+ '(doc: any)'
+);
+ScriptingGlobals.add(
+ function openInOverlay(doc: any) {
+ return Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+ },
+ 'opens up document in screen overlay layer',
+ '(doc: any)'
+);
+ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) {
+ CollectionDockingView.ReplaceTab(doc, 'right', undefined, shiftKey);
+});
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 668d82387..2c0e44bc3 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,46 +1,46 @@
-import React = require("react");
+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 { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState } from "react-color";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { Document } from "../../../fields/documentSchemas";
-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 { ScriptField } from "../../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { AntimodeMenu, AntimodeMenuProps } 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 { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocumentView } from "../nodes/DocumentView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
-import { PresBox } from "../nodes/trails/PresBox";
-import { DefaultStyleProvider } from "../StyleProvider";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionLinearView } from "./collectionLinear";
-import "./CollectionMenu.scss";
-import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { TabDocView } from "./TabDocView";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState } from 'react-color';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Document } from '../../../fields/documentSchemas';
+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 { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+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 { Colors } from '../global/globalEnums';
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { PresBox } from '../nodes/trails/PresBox';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionLinearView } from './collectionLinear';
+import './CollectionMenu.scss';
+import { COLLECTION_BORDER_WIDTH } from './CollectionView';
+import { TabDocView } from './TabDocView';
interface CollectionMenuProps {
panelHeight: () => number;
@@ -48,7 +48,7 @@ interface CollectionMenuProps {
}
@observer
-export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
+export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
@observable static Instance: CollectionMenu;
@observable SelectedCollection: DocumentView | undefined;
@@ -58,16 +58,18 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
constructor(props: any) {
super(props);
- this.FieldKey = "";
- runInAction(() => CollectionMenu.Instance = this);
+ this.FieldKey = '';
+ runInAction(() => (CollectionMenu.Instance = this));
this._canFade = false; // don't let the inking menu fade away
- runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true));
+ runInAction(() => (this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true)));
this.jumpTo(300, 300);
}
componentDidMount() {
- reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
- view => view && this.SetSelection(view));
+ reaction(
+ () => SelectionManager.Views().length && SelectionManager.Views()[0],
+ view => view && this.SetSelection(view)
+ );
}
@action
@@ -77,84 +79,87 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
@action
toggleMenuPin = (e: React.MouseEvent) => {
- Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned;
+ Doc.UserDoc()['menuCollections-pinned'] = this.Pinned = !this.Pinned;
if (!this.Pinned && this._left < 0) {
this.jumpTo(300, 300);
}
- }
+ };
@action
toggleTopBar = () => {
- if (CurrentUserUtils.headerBarHeight > 0) {
- CurrentUserUtils.headerBarHeight = 0;
+ if (SettingsManager.headerBarHeight > 0) {
+ SettingsManager.headerBarHeight = 0;
} else {
- CurrentUserUtils.headerBarHeight = 60;
+ SettingsManager.headerBarHeight = 60;
}
- }
+ };
buttonBarXf = () => {
if (!this._docBtnRef.current) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
return new Transform(-translateX, -translateY, 1 / scale);
- }
+ };
@computed get contMenuButtons() {
- const selDoc = CurrentUserUtils.MyContextMenuBtns;
- return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
- <CollectionLinearView
- Document={selDoc}
- DataDoc={undefined}
- fieldKey={"data"}
- dropAction={"alias"}
- setHeight={returnFalse}
- styleProvider={DefaultStyleProvider}
- rootSelected={returnTrue}
- bringToFront={emptyFunction}
- select={emptyFunction}
- isContentActive={returnTrue}
- isAnyChildContentActive={returnFalse}
- isSelected={returnFalse}
- docViewPath={returnEmptyDoclist}
- moveDocument={returnFalse}
- CollectionView={undefined}
- addDocument={returnFalse}
- addDocTab={returnFalse}
- pinToPres={emptyFunction}
- removeDocument={returnFalse}
- ScreenToLocalTransform={this.buttonBarXf}
- PanelWidth={this.props.panelWidth}
- PanelHeight={this.props.panelHeight}
- renderDepth={0}
- focus={emptyFunction}
- whenChildContentsActiveChanged={emptyFunction}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- </div>;
+ const selDoc = Doc.MyContextMenuBtns;
+ return !(selDoc instanceof Doc) ? null : (
+ <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }}>
+ <CollectionLinearView
+ Document={selDoc}
+ DataDoc={undefined}
+ fieldKey={'data'}
+ dropAction={'alias'}
+ setHeight={returnFalse}
+ styleProvider={DefaultStyleProvider}
+ rootSelected={returnTrue}
+ bringToFront={emptyFunction}
+ select={emptyFunction}
+ isContentActive={returnTrue}
+ isAnyChildContentActive={returnFalse}
+ isSelected={returnFalse}
+ docViewPath={returnEmptyDoclist}
+ moveDocument={returnFalse}
+ CollectionView={undefined}
+ addDocument={returnFalse}
+ addDocTab={returnFalse}
+ pinToPres={emptyFunction}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={this.buttonBarXf}
+ PanelWidth={this.props.panelWidth}
+ PanelHeight={this.props.panelHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ whenChildContentsActiveChanged={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />
+ </div>
+ );
}
render() {
+ const propIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
+ const propTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
- const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down";
- const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar";
-
- const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
- <div className="collectionMenu-hardCodedButton"
- style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
- onPointerDown={this.toggleTopBar}>
- <FontAwesomeIcon icon={propIcon} size="lg" />
- </div>
- </Tooltip>;
+ const prop = (
+ <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
+ <div className="collectionMenu-hardCodedButton" style={{ backgroundColor: SettingsManager.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }} onPointerDown={this.toggleTopBar}>
+ <FontAwesomeIcon icon={propIcon} size="lg" />
+ </div>
+ </Tooltip>
+ );
// NEW BUTTONS
//dash col linear view buttons
- const contMenuButtons =
+ const contMenuButtons = (
<div className="collectionMenu-container">
{this.contMenuButtons}
{prop}
- </div>;
+ </div>
+ );
return contMenuButtons;
@@ -187,49 +192,62 @@ const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
- get document() { return this.props.docView?.props.Document; }
- get target() { return this.document; }
+ get document() {
+ return this.props.docView?.props.Document;
+ }
+ get target() {
+ return this.document;
+ }
_templateCommand = {
- params: ["target", "source"], title: "item view",
- script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])",
+ params: ['target', 'source'],
+ title: 'item view',
+ script: 'self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])',
immediate: undoBatch((source: Doc[]) => {
let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
- try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; }
+ try {
+ formatStr && JSON.parse(formatStr);
+ } catch (e) {
+ formatStr = '';
+ }
if (source.length === 1 && formatStr) {
- Doc.SetInPlace(this.target, "childLayoutString", formatStr, false);
+ Doc.SetInPlace(this.target, 'childLayoutString', formatStr, false);
} else if (source.length) {
this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]);
} else {
- Doc.SetInPlace(this.target, "childLayoutString", undefined, true);
- Doc.SetInPlace(this.target, "childLayoutTemplate", undefined, true);
+ Doc.SetInPlace(this.target, 'childLayoutString', undefined, true);
+ Doc.SetInPlace(this.target, 'childLayoutTemplate', undefined, true);
}
}),
initialize: emptyFunction,
};
_narrativeCommand = {
- params: ["target", "source"], title: "child click view",
- script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])",
+ params: ['target', 'source'],
+ title: 'child click view',
+ script: 'self.target.childClickedOpenTemplateView = getDocTemplate(self.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)),
+ 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))),
initialize: emptyFunction,
};
_onClickCommand = {
- params: ["target", "proxy"], title: "copy onClick",
+ 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);
}}`,
- immediate: undoBatch((source: Doc[]) => { }),
+ immediate: undoBatch((source: Doc[]) => {}),
initialize: emptyFunction,
};
_openLinkInCommand = {
- params: ["target", "container"], title: "link follow target",
+ params: ['target', 'container'],
+ title: 'link follow target',
script: `{ if (self.container?.length) {
getProto(self.target).linkContainer = self.container[0];
getProto(self.target).isLinkButton = true;
@@ -239,126 +257,180 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
if (container.length) {
Doc.GetProto(this.target).linkContainer = container[0];
Doc.GetProto(this.target).isLinkButton = true;
- Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
+ Doc.GetProto(this.target).onClick = ScriptField.MakeScript('getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])');
}
}),
initialize: emptyFunction,
};
_viewCommand = {
- params: ["target"], title: "bookmark view",
+ params: ['target'],
+ title: 'bookmark view',
script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
- immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }),
- initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; },
+ immediate: undoBatch((source: Doc[]) => {
+ this.target._panX = 0;
+ this.target._panY = 0;
+ this.target._viewScale = 1;
+ this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
+ }),
+ initialize: (button: Doc) => {
+ button['target-panX'] = this.target._panX;
+ button['target-panY'] = this.target._panY;
+ button['target-viewScale'] = this.target._viewScale;
+ button['target-currentFrame'] = this.target._currentFrame;
+ },
};
_clusterCommand = {
- params: ["target"], title: "fit content",
- script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
- immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
- initialize: emptyFunction
+ params: ['target'],
+ title: 'fit content',
+ script: 'self.target._fitContentsToBox = !self.target._fitContentsToBox;',
+ immediate: undoBatch((source: Doc[]) => (this.target._fitContentsToBox = !this.target._fitContentsToBox)),
+ initialize: emptyFunction,
};
_fitContentCommand = {
- params: ["target"], title: "toggle clusters",
- script: "self.target._useClusters = !self.target._useClusters;",
- immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters),
- initialize: emptyFunction
+ params: ['target'],
+ title: 'toggle clusters',
+ script: 'self.target._useClusters = !self.target._useClusters;',
+ immediate: undoBatch((source: Doc[]) => (this.target._useClusters = !this.target._useClusters)),
+ initialize: emptyFunction,
};
_saveFilterCommand = {
- params: ["target"], title: "save filter",
+ params: ['target'],
+ title: 'save filter',
script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']);
self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
- immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
+ immediate: undoBatch((source: Doc[]) => {
+ this.target._docFilters = undefined;
+ this.target._searchFilterDocs = undefined;
+ }),
initialize: (button: Doc) => {
- const activeDash = CurrentUserUtils.ActiveDashboard;
+ const activeDash = Doc.ActiveDashboard;
if (activeDash) {
- button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ?
- ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+ button['target-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
}
},
};
- @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
- @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
- @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
- @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
- @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
- @computed get _tree_commands() { return undefined; }
+ @computed get _freeform_commands() {
+ return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand];
+ }
+ @computed get _stacking_commands() {
+ return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand];
+ }
+ @computed get _masonry_commands() {
+ return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand];
+ }
+ @computed get _schema_commands() {
+ return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand];
+ }
+ @computed get _doc_commands() {
+ return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand];
+ }
+ @computed get _tree_commands() {
+ return undefined;
+ }
private get _buttonizableCommands() {
switch (this.props.type) {
- default: return this._doc_commands;
- case CollectionViewType.Freeform: return this._freeform_commands;
- case CollectionViewType.Tree: return this._tree_commands;
- case CollectionViewType.Schema: return this._schema_commands;
- case CollectionViewType.Stacking: return this._stacking_commands;
- case CollectionViewType.Masonry: return this._stacking_commands;
- case CollectionViewType.Time: return this._freeform_commands;
- case CollectionViewType.Carousel: return this._freeform_commands;
- case CollectionViewType.Carousel3D: return this._freeform_commands;
+ default:
+ return this._doc_commands;
+ case CollectionViewType.Freeform:
+ return this._freeform_commands;
+ case CollectionViewType.Tree:
+ return this._tree_commands;
+ case CollectionViewType.Schema:
+ return this._schema_commands;
+ case CollectionViewType.Stacking:
+ return this._stacking_commands;
+ case CollectionViewType.Masonry:
+ return this._stacking_commands;
+ case CollectionViewType.Time:
+ return this._freeform_commands;
+ case CollectionViewType.Carousel:
+ return this._freeform_commands;
+ case CollectionViewType.Carousel3D:
+ return this._freeform_commands;
}
}
private _commandRef = React.createRef<HTMLInputElement>();
private _viewRef = React.createRef<HTMLInputElement>();
- @observable private _currentKey: string = "";
+ @observable private _currentKey: string = '';
componentDidMount = action(() => {
- this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : "");
+ this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : '');
});
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
- const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
+ const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : (this.document.proto as Doc);
//@ts-ignore
target._viewType = e.target.selectedOptions[0].value;
- }
+ };
commandChanged = (e: React.ChangeEvent) => {
//@ts-ignore
- runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
- }
-
+ runInAction(() => (this._currentKey = e.target.selectedOptions[0].value));
+ };
@action closeViewSpecs = () => {
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;
+ 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.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.Freeform:
+ return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />;
+ case CollectionViewType.Stacking:
+ return <CollectionStackingViewChrome 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} />);
+ 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} />);
+ 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?.();
if (ele) {
this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document);
}
- }
+ };
@undoBatch
@action
@@ -372,120 +444,139 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
}
dragViewDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e, down, delta) => {
- const vtype = this.props.type;
- const c = {
- params: ["target"], title: vtype,
- script: `this.target._viewType = '${StrCast(this.props.type)}'`,
- immediate: (source: Doc[]) => this.document._viewType = Doc.getDocTemplate(source?.[0]),
- initialize: emptyFunction,
- };
- DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title),
- { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
- return true;
- }, emptyFunction, emptyFunction);
- }
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ const vtype = this.props.type;
+ const c = {
+ params: ['target'],
+ title: vtype,
+ script: `this.target._viewType = '${StrCast(this.props.type)}'`,
+ immediate: (source: Doc[]) => (this.document._viewType = Doc.getDocTemplate(source?.[0])),
+ initialize: emptyFunction,
+ };
+ DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
+ return true;
+ },
+ emptyFunction,
+ emptyFunction
+ );
+ };
dragCommandDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e, down, delta) => {
- this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c =>
- DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
- { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
- return true;
- }, emptyFunction, () => {
- this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
- });
- }
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
+ return true;
+ },
+ emptyFunction,
+ () => {
+ this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
+ }
+ );
+ };
@computed get templateChrome() {
- return <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
- <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
- <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
- <button className={"antimodeMenu-button"} >
- <FontAwesomeIcon icon="bullseye" size="lg" />
- </button>
- <select
- className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
- {this._buttonizableCommands?.map(cmd =>
- <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
- )}
- </select>
- </div>
- </Tooltip>
- </div>;
+ return (
+ <div className="collectionViewBaseChrome-template" ref={this.createDropTarget}>
+ <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
+ <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
+ <button className={'antimodeMenu-button'}>
+ <FontAwesomeIcon icon="bullseye" size="lg" />
+ </button>
+ <select className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={'empty'} value={''} />
+ {this._buttonizableCommands?.map(cmd => (
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>
+ {cmd.title}
+ </option>
+ ))}
+ </select>
+ </div>
+ </Tooltip>
+ </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>);
+ 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 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;
+ 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>;
+ 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", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
- //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
- CollectionDockingView.AddSplit(doc, "right");
- }
+ const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+ //Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+ CollectionDockingView.AddSplit(doc, '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={{}}>
+ 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>
- </div>
- </button>
- </Tooltip>;
+ </button>
+ </Tooltip>
+ );
}
@undoBatch
@@ -517,21 +608,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
activeDoc.presPinView = true;
}
}
- }
+ };
@computed
get pinWithViewButton() {
- const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: "auto", width: 19 }} />;
- return !this.selectedDoc ? (null) :
- <Tooltip title={<div className="dash-tooltip">{"Pin with current view"}</div>} placement="top">
- <button className="antimodeMenu-button" style={{ justifyContent: 'center' }}
- onClick={() => this.pinWithView(this.selectedDoc)}>
+ const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />;
+ return !this.selectedDoc ? null : (
+ <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top">
+ <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}>
{presPinWithViewIcon}
</button>
- </Tooltip>;
+ </Tooltip>
+ );
}
-
@undoBatch
onAlias = () => {
if (this.selectedDoc && this.selectedDocumentView) {
@@ -544,10 +634,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
alias.y = NumCast(this.selectedDoc.y) + 30;
this.selectedDocumentView.props.addDocument?.(alias);
}
- }
+ };
onAliasButtonDown = (e: React.PointerEvent): void => {
setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
- }
+ };
@undoBatch
onAliasButtonMoved = (e: PointerEvent) => {
@@ -555,62 +645,72 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
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 = "alias";
+ dragData.defaultDropAction = 'alias';
dragData.canEmbed = true;
DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
offsetX: offset[0],
offsetY: offset[1],
- hideSource: false
+ hideSource: false,
});
return true;
}
return false;
- }
+ };
@computed
get aliasButton() {
const targetDoc = this.selectedDoc;
- return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
- <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}>
- <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
- </button>
- </Tooltip>;
+ return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
+ <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an alias'}</div>} placement="top">
+ <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} 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>;
+ 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.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
- color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
- }}
- onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
- <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
- </button>
- </Tooltip>
- </>;
+ 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.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'none' : undefined,
+ color: this.props.docView.props.ContainingCollectionDoc?._viewType !== 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-cont">
<div className="collectionMenu">
<div className="collectionViewBaseChrome">
- {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
+ {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
<div className="collectionMenu-divider" key="divider1"></div>
{this.aliasButton}
{/* {this.pinButton} */}
@@ -621,7 +721,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
<div className="collectionMenu-divider" key="divider3"></div>
{this.lightboxButton}
{this.recordButton}
- {!this._buttonizableCommands ? (null) : this.templateChrome}
+ {!this._buttonizableCommands ? null : this.templateChrome}
</div>
</div>
</div>
@@ -632,30 +732,40 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@observer
export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
render() {
- return (null);
+ return null;
}
}
@observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
+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; }
+ get document() {
+ return this.props.docView.props.Document;
+ }
@computed get dataField() {
- return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
+ 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;
}
- @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; }
@undoBatch
@action
nextKeyframe = (): void => {
- const currentFrame = Cast(this.document._currentFrame, "number", null);
+ const currentFrame = Cast(this.document._currentFrame, 'number', null);
if (currentFrame === undefined) {
this.document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
@@ -663,72 +773,88 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, 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);
+ const currentFrame = Cast(this.document._currentFrame, 'number', null);
if (currentFrame === undefined) {
this.document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
}
CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
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 _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"];
- 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"];
+ private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
+ private _head = ['', '', '', '', 'arrow', '', ''];
+ private _end = ['', '', '', 'arrow', 'arrow', '', ''];
+ private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'];
+ 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 clearKeepPrimitiveMode() {
+ this._selectedPrimitive = this._shapePrims.length;
+ }
@action primCreated() {
- if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
- if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
- CurrentUserUtils.ActiveTool = InkTool.None;
+ 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");
+ 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: "",
+ 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") {
+ if (type === 'color') {
SetActiveInkColor(Utils.colorString(col));
- } else if (type === "fill") {
+ } 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.strokeWidth = Number(value); break;
- case "color": doc.color = String(value); break;
- case "fill": doc.fillColor = String(value); break;
- case "dash": doc.strokeDash = value;
+ SelectionManager.Views().forEach(
+ action((element: DocumentView) => {
+ const doc = Document(element.rootDoc);
+ if (doc.type === DocumentType.INK) {
+ switch (field) {
+ case 'width':
+ doc.strokeWidth = Number(value);
+ break;
+ case 'color':
+ doc.color = String(value);
+ break;
+ case 'fill':
+ doc.fillColor = String(value);
+ break;
+ case 'dash':
+ doc.strokeDash = value;
+ }
}
- }
- }));
- }
+ })
+ );
+ };
@computed get drawButtons() {
const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
@@ -736,147 +862,184 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
// these are for shapes
if (this._selectedPrimitive !== i) {
this._selectedPrimitive = i;
- if (this._title[i] === "highlighter") {
- CurrentUserUtils.ActiveTool = InkTool.Highlighter;
+ if (this._title[i] === 'highlighter') {
+ Doc.ActiveTool = InkTool.Highlighter;
GestureOverlay.Instance.SavedColor = ActiveInkColor();
- SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+ SetActiveInkColor('rgba(245, 230, 95, 0.75)');
} else {
- CurrentUserUtils.ActiveTool = InkTool.Pen;
+ Doc.ActiveTool = InkTool.Pen;
}
SetActiveArrowStart(this._head[i]);
SetActiveArrowEnd(this._end[i]);
- SetActiveBezierApprox("300");
+ SetActiveBezierApprox('300');
GestureOverlay.Instance.InkShape = this._shapePrims[i];
} else {
this._selectedPrimitive = this._shapePrims.length;
- CurrentUserUtils.ActiveTool = InkTool.None;
- SetActiveArrowStart("");
- SetActiveArrowEnd("");
- GestureOverlay.Instance.InkShape = "";
- SetActiveBezierApprox("0");
+ Doc.ActiveTool = InkTool.None;
+ SetActiveArrowStart('');
+ SetActiveArrowEnd('');
+ GestureOverlay.Instance.InkShape = '';
+ 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>;
+ 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>;
- }
+ 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 :
+ 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) =>
+ {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
+ 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>;
+ </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 :
+ 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 }}>
+ {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> : ""}
+ {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
</div>
- </button >)}
- </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" >
+ 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 }}>
+ {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> : ""}
+ {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
</div>
- </button>)}
-
- </div>;
+ </button>
+ ))}
+ </div>
+ );
}
render() {
- return !this.props.docView.layoutDoc ? (null) :
+ return !this.props.docView.layoutDoc ? null : (
<div className="collectionFreeFormMenu-cont">
<RichTextMenu key="rich" />
- {!this.isText ?
+ {!this.isText ? (
<>
{this.drawButtons}
{this.widthPicker}
{this.colorPicker}
{this.fillPicker}
- {Doc.noviceMode || this.props.isDoc ? (null) :
+ {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"} />
+ <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
</div>
</Tooltip>
<Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</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?.()))} >
+ <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"} />
+ <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
</div>
</Tooltip>
- </>}
- </> : (null)
- }
- {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()}
- </div>;
+ </>
+ )}
+ </>
+ ) : 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 _currentKey: string = '';
@observable private suggestions: string[] = [];
- get document() { return this.props.docView.props.Document; }
+ 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); }
+ @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();
@@ -884,16 +1047,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
if (Doc.noviceMode) {
if (docs instanceof Doc) {
- const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
- key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0].toUpperCase() === key[0] && key[0] !== "_"));
+ const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
} else {
const keys = new Set<string>();
docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
- const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
- key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
- (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
+ const noviceKeys = Array.from(keys).filter(
+ key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+ );
return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
}
}
@@ -905,81 +1066,77 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
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; };
+ 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">
+ 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)` }}>
+ <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
- }
- }}
+ 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"}
+ contents={this.pivotField ? this.pivotField : 'N/A'}
/>
</div>
</div>
@@ -988,11 +1145,12 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
}
}
-
@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; }
+ get document() {
+ return this.props.docView.props.Document;
+ }
@undoBatch
togglePreview = () => {
@@ -1002,12 +1160,12 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
const previewWidth = NumCast(this.document.schemaPreviewWidth);
const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
- }
+ };
@undoBatch
@action
toggleTextwrap = async () => {
- const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []);
+ const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []);
if (textwrappedRows.length) {
this.document.textwrappedSchemaRows = new List<string>([]);
} else {
@@ -1015,56 +1173,52 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
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.schemaPreviewWidth);
- const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
+ 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 className={'collectionSchemaViewChrome-togglerButton' + (previewWidth !== 0 ? ' on' : ' off')}>{previewWidth !== 0 ? 'on' : 'off'}</div>
</div>
</div>
- </div >
+ </div>
);
}
}
@observer
export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
-
- get document() { return this.props.docView.props.Document; }
+ get document() {
+ return this.props.docView.props.Document;
+ }
get sortAscending() {
- return this.document[this.props.fieldKey + "-sortAscending"];
+ return this.document[this.props.fieldKey + '-sortAscending'];
}
set sortAscending(value) {
- this.document[this.props.fieldKey + "-sortAscending"] = value;
+ this.document[this.props.fieldKey + '-sortAscending'] = value;
}
@computed private get ascending() {
- return Cast(this.sortAscending, "boolean", null);
+ 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)` }}>
+ <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>
@@ -1073,10 +1227,12 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewMenu
}
}
-// Enter scroll speed for 3D Carousel
+// Enter scroll speed for 3D Carousel
@observer
export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
- get document() { return this.props.docView.props.Document; }
+ get document() {
+ return this.props.docView.props.Document;
+ }
@computed get scrollSpeed() {
return this.document._autoScrollSpeed;
}
@@ -1089,22 +1245,16 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
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>
+ {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} />
+ <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
</div>
</div>
</div>
@@ -1117,21 +1267,21 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
*/
@observer
export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {
-
private clicked: boolean = false;
private entered: boolean = false;
private decrementLimitReached: boolean = false;
@observable private resize = false;
private resizeListenerDisposer: Opt<Lambda>;
- get document() { return this.props.docView.props.Document; }
+ get document() {
+ return this.props.docView.props.Document;
+ }
componentDidMount() {
-
- runInAction(() => this.resize = this.props.docView.props.PanelWidth() < 700);
+ runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
// listener to reduce text on chrome resize (panel resize)
this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => {
- runInAction(() => this.resize = newValue < 700);
+ runInAction(() => (this.resize = newValue < 700));
});
}
@@ -1139,14 +1289,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
this.resizeListenerDisposer?.();
}
- get numCols() { return NumCast(this.document.gridNumCols, 10); }
+ get numCols() {
+ return NumCast(this.document.gridNumCols, 10);
+ }
/**
- * Sets the value of `numCols` on the grid's Document to the value entered.
- */
+ * Sets the value of `numCols` on the grid's Document to the value entered.
+ */
onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
- if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
- }
+ if (e.currentTarget.valueAsNumber > 0) undoBatch(() => (this.document.gridNumCols = e.currentTarget.valueAsNumber))();
+ };
/**
* Sets the value of `rowHeight` on the grid's Document to the value entered.
@@ -1166,7 +1318,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
@undoBatch
toggleFlex = () => {
this.document.gridFlex = !BoolCast(this.document.gridFlex, true);
- }
+ };
/**
* Increments the value of numCols on button click
@@ -1174,9 +1326,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
onIncrementButtonClick = () => {
this.clicked = true;
this.entered && (this.document.gridNumCols as number)--;
- undoBatch(() => this.document.gridNumCols = this.numCols + 1)();
+ undoBatch(() => (this.document.gridNumCols = this.numCols + 1))();
this.entered = false;
- }
+ };
/**
* Decrements the value of numCols on button click
@@ -1185,11 +1337,11 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
this.clicked = true;
if (this.numCols > 1 && !this.decrementLimitReached) {
this.entered && (this.document.gridNumCols as number)++;
- undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
+ undoBatch(() => (this.document.gridNumCols = this.numCols - 1))();
if (this.numCols === 1) this.decrementLimitReached = true;
}
this.entered = false;
- }
+ };
/**
* Increments the value of numCols on button hover
@@ -1201,7 +1353,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
}
this.decrementLimitReached = false;
this.clicked = false;
- }
+ };
/**
* Decrements the value of numCols on button hover
@@ -1211,21 +1363,20 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
if (!this.clicked) {
if (this.numCols > 1) {
this.document.gridNumCols = this.numCols - 1;
- }
- else {
+ } else {
this.decrementLimitReached = true;
}
}
this.clicked = false;
- }
+ };
/**
* Toggles the value of preventCollision
*/
toggleCollisions = () => {
this.document.gridPreventCollision = !this.document.gridPreventCollision;
- }
+ };
/**
* Changes the value of the compactType
@@ -1233,16 +1384,26 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
// need to change startCompaction so that this operation will be undoable.
this.document.gridStartCompaction = e.target.selectedOptions[0].value;
- }
+ };
render() {
return (
- <div className="collectionGridViewChrome-cont" >
- <span className="grid-control" style={{ width: this.resize ? "25%" : "30%" }}>
+ <div className="collectionGridViewChrome-cont">
+ <span className="grid-control" style={{ width: this.resize ? '25%' : '30%' }}>
<span className="grid-icon">
<FontAwesomeIcon icon="columns" size="1x" />
</span>
- <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+ <input
+ className="collectionGridViewChrome-entryBox"
+ type="number"
+ value={this.numCols}
+ onChange={this.onNumColsChange}
+ onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
+ e.stopPropagation();
+ e.preventDefault();
+ e.currentTarget.focus();
+ }}
+ />
<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>
@@ -1252,36 +1413,30 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
</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%" }}>
+ <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>
+ <label className="flexLabel">{this.resize ? 'Coll' : 'Collisions'}</label>
</span>
- <select className="collectionGridViewChrome-viewPicker"
+ <select
+ className="collectionGridViewChrome-viewPicker"
style={{ marginRight: 5 }}
onPointerDown={stopPropagation}
onChange={this.changeCompactType}
value={StrCast(this.document.gridStartCompaction, StrCast(this.document.gridCompaction))}>
- {["vertical", "horizontal", "none"].map(type =>
- <option className="collectionGridViewChrome-viewOption"
- onPointerDown={stopPropagation}
- value={type}>
- {this.resize ? type[0].toUpperCase() + type.substring(1) : "Compact: " + type}
+ {['vertical', 'horizontal', 'none'].map(type => (
+ <option className="collectionGridViewChrome-viewOption" onPointerDown={stopPropagation} value={type}>
+ {this.resize ? type[0].toUpperCase() + type.substring(1) : 'Compact: ' + type}
</option>
- )}
+ ))}
</select>
- <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
- <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex}
- checked={BoolCast(this.document.gridFlex, true)} />
- <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label>
+ <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
+ <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex} checked={BoolCast(this.document.gridFlex, true)} />
+ <label className="flexLabel">{this.resize ? 'Flex' : 'Flexible'}</label>
</span>
- <button onClick={() => this.document.gridResetLayout = true}>
- {!this.resize ? "Reset" :
- <FontAwesomeIcon icon="redo-alt" size="1x" />}
- </button>
-
+ <button onClick={() => (this.document.gridResetLayout = true)}>{!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}</button>
</div>
);
}
@@ -1289,7 +1444,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
const dataField = doc[Doc.LayoutFieldKey(doc)];
const childDocs = DocListCast(dataField);
- const currentFrame = Cast(doc._currentFrame, "number", null);
+ const currentFrame = Cast(doc._currentFrame, 'number', null);
if (currentFrame === undefined) {
doc._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
@@ -1297,4 +1452,3 @@ ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
});
-
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b00017453..dcf3f7c51 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,50 +1,33 @@
-import React = require("react");
-import {
- action,
- computed,
- IReactionDisposer,
- observable,
- reaction
-} from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import {
- emptyFunction,
- formatTime,
- OmitKeys,
- returnFalse,
- returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent
-} from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager } from "../../util/DragManager";
-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 { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import {
- DocAfterFocusFunc,
- DocFocusFunc,
- DocumentView,
- DocumentViewProps
-} from "../nodes/DocumentView";
-import { LabelBox } from "../nodes/LabelBox";
-import "./CollectionStackedTimeline.scss";
-import { VideoBox } from "../nodes/VideoBox";
-import { ImageField } from "../../../fields/URLField";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+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 { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { LabelBox } from '../nodes/LabelBox';
+import './CollectionStackedTimeline.scss';
+import { VideoBox } from '../nodes/VideoBox';
+import { ImageField } from '../../../fields/URLField';
export type CollectionStackedTimelineProps = {
Play: () => void;
@@ -68,7 +51,6 @@ export enum TrimScope {
None = 0,
}
-
@observer
export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
@observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
@@ -94,21 +76,38 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@observable _thumbnail: string | 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 trimStart() { return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart; }
- @computed get trimDuration() { return this.trimEnd - this.trimStart; }
- @computed get trimEnd() { return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd; }
+ // 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 clipStart() { return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart); }
- @computed get clipDuration() { return this.clipEnd - this.clipStart; }
- @computed get clipEnd() { return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration); }
+ @computed get trimStart() {
+ return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
+ }
+ @computed get trimDuration() {
+ return this.trimEnd - this.trimStart;
+ }
+ @computed get trimEnd() {
+ return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd;
+ }
- @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
+ @computed get clipStart() {
+ return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart);
+ }
+ @computed get clipDuration() {
+ return this.clipEnd - this.clipStart;
+ }
+ @computed get clipEnd() {
+ return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration);
+ }
- @computed get zoomFactor() { return this._zoomFactor; }
+ @computed get currentTime() {
+ return NumCast(this.layoutDoc._currentTimecode);
+ }
+ @computed get zoomFactor() {
+ return this._zoomFactor;
+ }
constructor(props: any) {
super(props);
@@ -117,32 +116,33 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
CollectionStackedTimeline.RangeScript ||
ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
self: Doc.name,
- scriptContext: "any",
- clientX: "number",
+ scriptContext: 'any',
+ clientX: 'number',
})!;
CollectionStackedTimeline.RangePlayScript =
CollectionStackedTimeline.RangePlayScript ||
ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
self: Doc.name,
- scriptContext: "any",
- clientX: "number",
+ scriptContext: 'any',
+ clientX: 'number',
})!;
}
componentDidMount() {
- document.addEventListener("keydown", this.keyEvents, true);
+ document.addEventListener('keydown', this.keyEvents, true);
}
@action
componentWillUnmount() {
- document.removeEventListener("keydown", this.keyEvents, true);
+ document.removeEventListener('keydown', this.keyEvents, true);
if (CollectionStackedTimeline.SelectingRegion === this) {
CollectionStackedTimeline.SelectingRegion = undefined;
}
}
-
- public get IsTrimming() { return this._trimming; }
+ public get IsTrimming() {
+ return this._trimming;
+ }
@action
public StartTrimming(scope: TrimScope) {
@@ -162,81 +162,63 @@ 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);
-
// converts screen pixel offset to time
toTimeline = (screen_delta: number, width: number) => {
- return Math.max(
- this.clipStart,
- Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
- }
-
+ return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
+ };
rangeClickScript = () => CollectionStackedTimeline.RangeScript;
rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
-
// handles key events for for creating key anchors, scrubbing, exiting trim
@action
keyEvents = (e: KeyboardEvent) => {
if (
// need to include range inputs because after dragging video time slider it becomes target element
- !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+ !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
this.props.isSelected(true)
) {
// if shift pressed scrub 1 second otherwise 1/10th
const jump = e.shiftKey ? 1 : 0.1;
switch (e.key) {
- case " ":
+ case ' ':
if (!CollectionStackedTimeline.SelectingRegion) {
this._markerStart = this._markerEnd = this.currentTime;
CollectionStackedTimeline.SelectingRegion = this;
} else {
this._markerEnd = this.currentTime;
- CollectionStackedTimeline.createAnchor(
- this.rootDoc,
- this.dataDoc,
- this.props.fieldKey,
- this.props.startTag,
- this.props.endTag,
- this._markerStart,
- this._markerEnd
- );
+ CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
this._markerEnd = undefined;
CollectionStackedTimeline.SelectingRegion = undefined;
}
e.stopPropagation();
break;
- case "Escape":
+ case 'Escape':
// abandons current trim
this._trimStart = this.clipStart;
this._trimStart = this.clipEnd;
this._trimming = TrimScope.None;
e.stopPropagation();
break;
- case "ArrowLeft":
+ case 'ArrowLeft':
this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
e.stopPropagation();
break;
- case "ArrowRight":
+ case 'ArrowRight':
this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
e.stopPropagation();
break;
}
}
- }
-
+ };
getLinkData(l: Doc) {
let la1 = l.anchor1 as Doc;
let la2 = l.anchor2 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.anchor2 as Doc;
la2 = l.anchor1 as Doc;
@@ -244,7 +226,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
return { la1, la2, linkTime };
}
-
// handles dragging selection to create markers
@action
onPointerDownTimeline = (e: React.PointerEvent): void => {
@@ -259,7 +240,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setupMoveUpEvents(
this,
e,
- action((e) => {
+ action(e => {
if (!wasSelecting) {
this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
wasSelecting = true;
@@ -274,24 +255,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this._markerStart = this._markerEnd;
this._markerEnd = tmp;
}
- if (
- !isClick &&
- Math.abs(movement[0]) > 15 &&
- !this.IsTrimming
- ) {
- const anchor = CollectionStackedTimeline.createAnchor(
- this.rootDoc,
- this.dataDoc,
- this.props.fieldKey,
- this.props.startTag,
- this.props.endTag,
- this._markerStart,
- this._markerEnd
- );
+ if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
+ const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
}
- (!isClick || !wasSelecting) &&
- (this._markerEnd = undefined);
+ (!isClick || !wasSelecting) && (this._markerEnd = undefined);
}),
(e, doubleTap) => {
if (e.button !== 2) {
@@ -303,23 +271,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
undefined,
() => {
if (shiftKey) {
- CollectionStackedTimeline.createAnchor(
- this.rootDoc,
- this.dataDoc,
- this.props.fieldKey,
- this.props.startTag,
- this.props.endTag,
- this.currentTime
- );
+ CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
} else {
!wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
}
);
}
-
- }
-
+ };
@action
onHover = (e: React.MouseEvent): void => {
@@ -329,15 +288,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
if (rect) {
this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
if (this.dataDoc.thumbnails) {
- const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails);
- const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []);
- const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField("");
- const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : "";
+ const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
+ const thumbnails = Cast(this.dataDoc.thumbnails, listSpec('string'), []);
+ const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField('');
+ const src = imgField && imgField.url.href ? imgField.url.href.replace('.png', '_s.png') : '';
this._thumbnail = src ? src : undefined;
}
}
- }
-
+ };
// for dragging trim start handle
@action
@@ -348,13 +306,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
e,
action((e, [], []) => {
if (rect && this.props.isContentActive()) {
- this._trimStart = Math.min(
- Math.max(
- this.trimStart + (e.movementX / rect.width) * this.clipDuration,
- this.clipStart
- ),
- this.trimEnd - this.minTrimLength
- );
+ this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
}
return false;
}),
@@ -363,7 +315,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
doubleTap && (this._trimStart = this.clipStart);
})
);
- }
+ };
// for dragging trim end handle
@action
@@ -374,13 +326,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
e,
action((e, [], []) => {
if (rect && this.props.isContentActive()) {
- this._trimEnd = Math.max(
- Math.min(
- this.trimEnd + (e.movementX / rect.width) * this.clipDuration,
- this.clipEnd
- ),
- this.trimStart + this.minTrimLength
- );
+ this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
}
return false;
}),
@@ -389,15 +335,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
doubleTap && (this._trimEnd = this.clipEnd);
})
);
- }
-
+ };
// for rendering scrolling when timeline zoomed
@action
setScroll = (e: React.UIEvent) => {
e.stopPropagation();
this._scroll = this._timelineWrapper!.scrollLeft;
- }
+ };
// smooth scrolls to time like when following links overflowed due to zoom
@action
@@ -406,14 +351,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
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;
+ } else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
+ this._scroll = (time / this.timelineContentWidth) * this.clipDuration;
smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
}
}
- }
-
+ };
// handles dragging and dropping markers in timeline
@action
@@ -428,9 +371,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
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;
@@ -439,38 +382,28 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
return false;
- }
-
+ };
// creates marker on timeline
@undoBatch
@action
- static createAnchor(
- rootDoc: Doc,
- dataDoc: Doc,
- fieldKey: string,
- startTag: string,
- endTag: string,
- anchorStartTime?: number,
- anchorEndTime?: number,
- docAnchor?: Doc
- ) {
+ static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
if (anchorStartTime === undefined) return rootDoc;
- const anchor = docAnchor ?? Docs.Create.LabelDocument({
- title: ComputedField.MakeFunction(
- `self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`
- ) as any,
- _minFontSize: 12,
- _maxFontSize: 24,
- _singleLine: false,
- _stayInCollection: true,
- useLinkSmallAnchor: true,
- hideLinkButton: true,
- _isLinkButton: true,
- annotationOn: rootDoc,
- _timelineLabel: true,
- borderRounding: anchorEndTime === undefined ? "100%" : undefined
- });
+ const anchor =
+ docAnchor ??
+ Docs.Create.LabelDocument({
+ title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
+ _minFontSize: 12,
+ _maxFontSize: 24,
+ _singleLine: false,
+ _stayInCollection: true,
+ useLinkSmallAnchor: true,
+ hideLinkButton: true,
+ _isLinkButton: true,
+ annotationOn: rootDoc,
+ _timelineLabel: true,
+ borderRounding: anchorEndTime === undefined ? '100%' : undefined,
+ });
Doc.GetProto(anchor)[startTag] = anchorStartTime;
Doc.GetProto(anchor)[endTag] = anchorEndTime;
if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
@@ -481,7 +414,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
return anchor;
}
-
@action
playOnClick = (anchorDoc: Doc, clientX: number) => {
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
@@ -493,10 +425,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this.scrollToTime(seekTimeInSeconds);
}
} else {
- if (
- seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) &&
- endTime > NumCast(this.layoutDoc._currentTimecode)
- ) {
+ if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
this.props.Pause();
} else {
@@ -508,59 +437,43 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
}
}
return { select: true };
- }
+ };
@action
clickAnchor = (anchorDoc: Doc, clientX: number) => {
if (anchorDoc.isLinkButton) {
- LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+ LinkFollower.FollowLink(undefined, anchorDoc, this.props, false);
}
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
const endTime = this.anchorEnd(anchorDoc);
- if (
- seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 &&
- endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4
- ) {
+ if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
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);
- }
- else {
+ } else {
this.props.setTime(seekTimeInSeconds);
}
}
return { select: true };
- }
+ };
// makes sure no anchors overlaps each other by setting the correct position and width
- getLevel = (
- m: Doc,
- placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]
- ) => {
+ getLevel = (m: Doc, placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]) => {
const timelineContentWidth = this.timelineContentWidth;
const x1 = this.anchorStart(m);
- const x2 = this.anchorEnd(
- m,
- x1 + (10 / timelineContentWidth) * this.clipDuration
- );
+ const x2 = this.anchorEnd(m, x1 + (10 / timelineContentWidth) * this.clipDuration);
let max = 0;
const overlappedLevels = new Set(
- placed.map((p) => {
+ placed.map(p => {
const y1 = p.anchorStartTime;
const y2 = p.anchorEndTime;
- if (
- (x1 >= y1 && x1 <= y2) ||
- (x2 >= y1 && x2 <= y2) ||
- (y1 >= x1 && y1 <= x2) ||
- (y2 >= x1 && y2 <= x2)
- ) {
+ if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
max = Math.max(max, p.level);
return p.level;
}
@@ -571,14 +484,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
placed.push({ anchorStartTime: x1, anchorEndTime: x2, level });
return level;
- }
-
+ };
dictationHeightPercent = 50;
dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
- @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; }
- @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; } // subtract size of container border
+ @computed get timelineContentHeight() {
+ return (this.props.PanelHeight() * this.dictationHeightPercent) / 100;
+ }
+ @computed get timelineContentWidth() {
+ return this.props.PanelWidth() * this.zoomFactor;
+ } // subtract size of container border
dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight);
@@ -586,24 +502,18 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
currentTimecode = () => this.currentTime;
-
@computed get renderDictation() {
const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
return !dictation ? null : (
<div
style={{
- position: "absolute",
- height: "100%",
+ position: 'absolute',
+ height: '100%',
top: this.timelineContentHeight,
background: Colors.LIGHT_BLUE,
- }}
- >
+ }}>
<DocumentView
- {...OmitKeys(this.props, [
- "NativeWidth",
- "NativeHeight",
- "setContentView",
- ]).omit}
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
Document={dictation}
PanelHeight={this.dictationHeight}
isAnnotationOverlay={true}
@@ -618,8 +528,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
moveDocument={returnFalse}
addDocument={returnFalse}
CollectionView={undefined}
- renderDepth={this.props.renderDepth + 1}
- ></DocumentView>
+ renderDepth={this.props.renderDepth + 1}></DocumentView>
</div>
);
}
@@ -644,145 +553,131 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
anchorEndTime: number;
level: number;
}[] = [];
- const drawAnchors = this.childDocs.map((anchor) => ({
+ const drawAnchors = this.childDocs.map(anchor => ({
level: this.getLevel(anchor, overlaps),
anchor,
}));
const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
- return (<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined }}>
- <div className="timeline-container"
- style={{ width: this.props.PanelWidth() }}
- onWheel={e => e.stopPropagation()}
- onScroll={this.setScroll}
- onMouseMove={(e) => this.isContentActive() && this.onHover(e)}
- ref={wrapper => this._timelineWrapper = wrapper}>
+ return (
+ <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
<div
- className="collectionStackedTimeline"
- ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
- onClick={(e) => this.isContentActive() && StopEvent(e)}
- onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)}
- style={{ width: this.timelineContentWidth }}>
-
- {drawAnchors.map((d) => {
- const start = this.anchorStart(d.anchor);
- 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 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 : (
- <div
- className={"collectionStackedTimeline-marker-timeline"}
- key={d.anchor[Id]}
- style={{
- left,
- top,
- width: `${width}px`,
- height: `${height}px`,
- }}
- onClick={(e) => {
- this.props.playFrom(start, this.anchorEnd(d.anchor));
- e.stopPropagation();
- }}
- >
- <StackedTimelineAnchor
- {...this.props}
- mark={d.anchor}
- rangeClickScript={this.rangeClickScript}
- rangePlayScript={this.rangePlayScript}
- left={left - this._scroll}
- top={top}
- width={width}
- height={height}
- toTimeline={this.toTimeline}
- layoutDoc={this.layoutDoc}
- // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
- currentTimecode={this.currentTimecode}
- _timeline={this._timeline}
- stackedTimeline={this}
- trimStart={this.trimStart}
- trimEnd={this.trimEnd}
- />
- </div>
- );
- })}
- {!this.IsTrimming && this.selectionContainer}
- <AudioWaveform
- rawDuration={this.props.rawDuration}
- duration={this.clipDuration}
- mediaPath={this.props.mediaPath}
- layoutDoc={this.layoutDoc}
- clipStart={this.clipStart}
- clipEnd={this.clipEnd}
- zoomFactor={this.zoomFactor}
- PanelHeight={this.timelineContentHeight}
- PanelWidth={this.timelineContentWidth}
- />
- {/* {this.renderDictation} */}
-
+ className="timeline-container"
+ style={{ width: this.props.PanelWidth() }}
+ onWheel={e => e.stopPropagation()}
+ onScroll={this.setScroll}
+ onMouseMove={e => this.isContentActive() && this.onHover(e)}
+ ref={wrapper => (this._timelineWrapper = wrapper)}>
<div
- className="collectionStackedTimeline-hover"
- style={{
- left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
- }}
- />
+ className="collectionStackedTimeline"
+ ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+ onClick={e => this.isContentActive() && StopEvent(e)}
+ onPointerDown={e => this.isContentActive() && this.onPointerDownTimeline(e)}
+ style={{ width: this.timelineContentWidth }}>
+ {drawAnchors.map(d => {
+ const start = this.anchorStart(d.anchor);
+ 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 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 : (
+ <div
+ className={'collectionStackedTimeline-marker-timeline'}
+ key={d.anchor[Id]}
+ style={{
+ left,
+ top,
+ width: `${width}px`,
+ height: `${height}px`,
+ }}
+ onClick={e => {
+ this.props.playFrom(start, this.anchorEnd(d.anchor));
+ e.stopPropagation();
+ }}>
+ <StackedTimelineAnchor
+ {...this.props}
+ mark={d.anchor}
+ rangeClickScript={this.rangeClickScript}
+ rangePlayScript={this.rangePlayScript}
+ left={left - this._scroll}
+ top={top}
+ width={width}
+ height={height}
+ toTimeline={this.toTimeline}
+ layoutDoc={this.layoutDoc}
+ // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+ currentTimecode={this.currentTimecode}
+ _timeline={this._timeline}
+ stackedTimeline={this}
+ trimStart={this.trimStart}
+ trimEnd={this.trimEnd}
+ />
+ </div>
+ );
+ })}
+ {!this.IsTrimming && this.selectionContainer}
+ <AudioWaveform
+ rawDuration={this.props.rawDuration}
+ duration={this.clipDuration}
+ mediaPath={this.props.mediaPath}
+ layoutDoc={this.layoutDoc}
+ clipStart={this.clipStart}
+ clipEnd={this.clipEnd}
+ zoomFactor={this.zoomFactor}
+ PanelHeight={this.timelineContentHeight}
+ PanelWidth={this.timelineContentWidth}
+ />
+ {/* {this.renderDictation} */}
+
+ <div
+ className="collectionStackedTimeline-hover"
+ style={{
+ left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
+ }}
+ />
+
+ <div
+ className="collectionStackedTimeline-current"
+ style={{
+ left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
+ }}
+ />
+
+ {this.IsTrimming !== TrimScope.None && (
+ <>
+ <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}></div>
- <div
- className="collectionStackedTimeline-current"
- style={{
- left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
- }}
- />
-
- {this.IsTrimming !== TrimScope.None && (
- <>
- <div
- className="collectionStackedTimeline-trim-shade"
- style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}
- ></div>
-
- <div
- className="collectionStackedTimeline-trim-controls"
- style={{
- left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
- width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
- }}
- >
<div
- className="collectionStackedTimeline-trim-handle"
- onPointerDown={this.trimLeft}
- ></div>
+ className="collectionStackedTimeline-trim-controls"
+ style={{
+ left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
+ width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
+ }}>
+ <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft}></div>
+ <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight}></div>
+ </div>
+
<div
- className="collectionStackedTimeline-trim-handle"
- onPointerDown={this.trimRight}
- ></div>
- </div>
-
- <div
- className="collectionStackedTimeline-trim-shade"
- style={{
- left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
- width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
- }}
- ></div>
- </>
- )}
+ className="collectionStackedTimeline-trim-shade"
+ style={{
+ left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
+ width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
+ }}></div>
+ </>
+ )}
+ </div>
+ </div>
+ <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
+ <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
+ {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
</div>
</div>
- <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
- <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
- {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
- </div>
- </div >);
+ );
}
}
-
/**
* StackedTimelineAnchor
* creates the anchors to display markers, links, and embedded documents on timeline
@@ -814,7 +709,6 @@ interface StackedTimelineAnchorProps {
trimEnd: number;
}
-
@observer
class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
_lastTimecode: number;
@@ -831,23 +725,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
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(),
- (time) => {
- const dictationDoc = Cast(
- this.props.layoutDoc["data-dictation"],
- Doc,
- null
- );
- const isDictation =
- dictationDoc &&
- DocListCast(this.props.mark.links).some(
- (link) =>
- Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc
- );
+ time => {
+ const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
+ const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, 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.
@@ -859,13 +744,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
time < NumCast(this.props.mark[this.props.endTag]) &&
this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
) {
- LinkManager.FollowLink(
- undefined,
- this.props.mark,
- this.props as any as DocumentViewProps,
- false,
- true
- );
+ LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
}
this._lastTimecode = time;
}
@@ -876,7 +755,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
this._disposer?.();
}
-
// starting the drag event for anchor resizing
onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
this.props._timeline?.setPointerCapture(e.pointerId);
@@ -885,19 +763,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
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) Doc.SetInPlace(anchor, "borderRounding", time !== undefined ? undefined : "100%", true);
- }
- else {
- anchor[left ? "_timecodeToShow" : "_timecodeToHide"] = time;
+ Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
+ if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true);
+ } else {
+ anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
}
return false;
};
@@ -906,46 +778,34 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
setupMoveUpEvents(
this,
e,
- (e) => {
- if (!undo) undo = UndoManager.StartBatch("drag anchor");
+ e => {
+ if (!undo) undo = UndoManager.StartBatch('drag anchor');
this.props.setTime(newTime(e));
return changeAnchor(anchor, left, newTime(e));
},
- (e) => {
+ e => {
this.props.setTime(newTime(e));
this.props._timeline?.releasePointerCapture(e.pointerId);
undo?.end();
},
emptyFunction
);
- }
-
+ };
// 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}"])`)!, icon: "folder-plus", label: "Reset Title" };
+ 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}"])`)!,
+ icon: 'folder-plus',
+ label: 'Reset Title',
+ };
return [resetTitle];
- }
-
+ };
// renders anchor LabelBox
- renderInner = computedFn(function (
- this: StackedTimelineAnchor,
- mark: Doc,
- script: undefined | (() => ScriptField),
- doublescript: undefined | (() => ScriptField),
- screenXf: () => Transform,
- width: () => number,
- height: () => number
- ) {
+ 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,
- willZoom?: boolean,
- scale?: number,
- afterFocus?: DocAfterFocusFunc,
- docTransform?: Transform
- ) => {
+ const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
this.props.playLink(mark);
this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
};
@@ -954,13 +814,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
view: (
<DocumentView
key="view"
- {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- ref={action((r: DocumentView | null) => anchor.view = r)}
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+ ref={action((r: DocumentView | null) => (anchor.view = r))}
Document={mark}
DataDoc={undefined}
renderDepth={this.props.renderDepth + 1}
LayoutTemplate={undefined}
- LayoutTemplateString={LabelBox.LayoutStringWithTitle("data", this.computeTitle())}
+ LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
isDocumentActive={this.props.isDocumentActive}
PanelWidth={width}
PanelHeight={height}
@@ -985,32 +845,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
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 (
<>
{inner.view}
- {!inner.anchor.view ||
- !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+ {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
<>
- <div
- key="left"
- className="collectionStackedTimeline-left-resizer"
- onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
- />
- <div
- key="right"
- className="collectionStackedTimeline-resizer"
- onPointerDown={(e) =>
- this.onAnchorDown(e, this.props.mark, false)
- }
- />
+ <div key="left" className="collectionStackedTimeline-left-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
+ <div key="right" className="collectionStackedTimeline-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
</>
)}
</>
@@ -1025,4 +867,4 @@ ScriptingGlobals.add(function min(num1: number, num2: number): number {
});
ScriptingGlobals.add(function max(num1: number, num2: number): number {
return Math.max(num1, num2);
-}); \ No newline at end of file
+});
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 44abbdb1c..6850fb23a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,37 +1,36 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-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 { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { EditableView } from "../EditableView";
-import { LightboxView } from "../LightboxView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
-import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
-import "./CollectionStackingView.scss";
-import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
-import { CollectionSubView } from "./CollectionSubView";
-import { CollectionViewType } from "./CollectionView";
-import { FieldViewProps } from "../nodes/FieldView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-const _global = (window /* browser */ || global /* node */) as any;
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { CursorProperty } from 'csstype';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+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 { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { CollectionViewType } from '../../documents/DocumentTypes';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { EditableView } from '../EditableView';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
+import { 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';
+const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
chromeHidden?: boolean;
@@ -46,27 +45,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
_draggerRef = React.createRef<HTMLDivElement>();
_pivotFieldDisposer?: IReactionDisposer;
_autoHeightDisposer?: IReactionDisposer;
- _docXfs: { height: () => number, width: () => number, stackedDocTransform: () => Transform }[] = [];
+ _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
_columnStart: number = 0;
@observable _heightMap = new Map<string, number>();
- @observable _cursor: CursorProperty = "grab";
+ @observable _cursor: CursorProperty = 'grab';
@observable _scroll = 0; // used to force the document decoration to update when scrolling
- @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
- @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
- @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
- @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
- @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); }
- @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
- @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
- @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
- @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; }
- @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
- @computed get showAddAGroup() { return this.pivotField && !this.chromeHidden; }
+ @computed get chromeHidden() {
+ return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+ }
+ @computed get columnHeaders() {
+ return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null);
+ }
+ @computed get pivotField() {
+ return StrCast(this.layoutDoc._pivotField);
+ }
+ @computed get filteredChildren() {
+ return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
+ }
+ @computed get headerMargin() {
+ return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
+ }
+ @computed get xMargin() {
+ return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
+ }
+ @computed get yMargin() {
+ return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
+ } // 2 * this.gridGap)); }
+ @computed get gridGap() {
+ return NumCast(this.layoutDoc._gridGap, 10);
+ }
+ @computed get isStackingView() {
+ return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking;
+ }
+ @computed get numGroupColumns() {
+ return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1;
+ }
+ @computed get showAddAGroup() {
+ return this.pivotField && !this.chromeHidden;
+ }
@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;
}
- @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
constructor(props: any) {
super(props);
@@ -84,21 +106,23 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const width = () => this.getDocWidth(d);
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
- return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} style={style} >
- {this.getDisplayDoc(d, width)}
- </div>;
+ return (
+ <div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
+ {this.getDisplayDoc(d, width)}
+ </div>
+ );
});
- }
+ };
@action
setDocHeight = (key: string, sectionHeight: number) => {
this._heightMap.set(key, sectionHeight);
- }
+ };
get Sections() {
if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
if (this.columnHeaders === undefined) {
- setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
+ setTimeout(() => (this.layoutDoc._columnHeaders = new List<SchemaHeaderField>()), 0);
return new Map<SchemaHeaderField, Doc[]>();
}
const columnHeaders = Array.from(this.columnHeaders);
@@ -114,8 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
if (existingHeader) {
fields.get(existingHeader)!.push(d);
- }
- else {
+ } else {
const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
fields.set(newSchemaHeader, [d]);
columnHeaders.push(newSchemaHeader);
@@ -124,13 +147,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
});
// remove all empty columns if hideHeadings is set
if (this.layoutDoc._columnsHideIfEmpty) {
- Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
- fields.delete(header);
- columnHeaders.splice(columnHeaders.indexOf(header), 1);
- changed = true;
- });
+ Array.from(fields.keys())
+ .filter(key => !fields.get(key)!.length)
+ .map(header => {
+ fields.delete(header);
+ columnHeaders.splice(columnHeaders.indexOf(header), 1);
+ changed = true;
+ });
}
- changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
+ changed &&
+ setTimeout(
+ action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)),
+ 0
+ );
return fields;
}
@@ -140,13 +169,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// reset section headers when a new filter is inputted
this._pivotFieldDisposer = reaction(
() => this.pivotField,
- () => this.layoutDoc._columnHeaders = new List()
+ () => (this.layoutDoc._columnHeaders = new List())
+ );
+ this._autoHeightDisposer = reaction(
+ () => this.layoutDoc._autoHeight,
+ autoHeight =>
+ autoHeight &&
+ 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))
+ )
+ )
);
- this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
- autoHeight => autoHeight && 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)))));
}
componentWillUnmount() {
@@ -158,45 +193,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@action
moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
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?
- }
+ };
- @computed get onChildClickHandler() { return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
- @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
+ @computed get onChildClickHandler() {
+ return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+ }
+ @computed get onChildDoubleClickHandler() {
+ return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+ }
addDocTab = (doc: Doc, where: string) => {
- if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+ if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
return true;
}
return this.props.addDocTab(doc, where);
- }
+ };
scrollToBottom = () => {
smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
- }
+ };
focusDocument = (doc: Doc, options?: DocFocusOptions) => {
Doc.BrushDoc(doc);
let focusSpeed = 0;
- const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]);
+ 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) {
- smoothScroll(focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500, this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+ smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
}
}
- const endFocus = async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing;
+ const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
this.props.focus(this.rootDoc, {
- willZoom: options?.willZoom, scale: options?.scale, afterFocus: (didFocus: boolean) =>
- new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed))
+ willZoom: options?.willZoom,
+ scale: options?.scale,
+ afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
});
- }
+ };
styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
if (property === StyleProp.Opacity && doc) {
@@ -208,85 +248,88 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
}
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 (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
e.stopPropagation?.();
- const below = !e.altKey && e.key !== "Tab";
+ const below = !e.altKey && e.key !== 'Tab';
const layoutKey = StrCast(docView.LayoutFieldKey);
const newDoc = Doc.MakeCopy(docView.rootDoc, true);
const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
- if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+ if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = docView.rootDoc[layoutKey];
}
Doc.GetProto(newDoc).text = undefined;
FormattedTextBox.SelectOnLoad = newDoc[Id];
return this.addDocument?.(newDoc);
}
- }
+ };
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined;
+ isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
getDisplayDoc(doc: Doc, width: () => number) {
- const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
+ const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
const height = () => this.getDocHeight(doc);
let dref: Opt<DocumentView>;
const stackedDocTransform = () => this.getDocTransform(doc, dref);
this._docXfs.push({ stackedDocTransform, width, height });
- return <DocumentView ref={r => dref = r || undefined}
- Document={doc}
- DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
- renderDepth={this.props.renderDepth + 1}
- PanelWidth={width}
- PanelHeight={height}
- styleProvider={this.styleProvider}
- docViewPath={this.props.docViewPath}
- fitWidth={this.props.childFitWidth}
- isContentActive={this.isChildContentActive}
- onKey={this.onKeyDown}
- isDocumentActive={this.isContentActive}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={this.props.childLayoutString}
- NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
- NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._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}
- showTitle={this.props.childShowTitle}
- dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
- onClick={this.onChildClickHandler}
- onDoubleClick={this.onChildDoubleClickHandler}
- ScreenToLocalTransform={stackedDocTransform}
- focus={this.focusDocument}
- docFilters={this.childDocFilters}
- hideDecorationTitle={this.props.childHideDecorationTitle?.()}
- hideResizeHandles={this.props.childHideResizeHandles?.()}
- hideTitle={this.props.childHideTitle?.()}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- ContainingCollectionDoc={this.props.CollectionView?.props.Document}
- ContainingCollectionView={this.props.CollectionView}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- removeDocument={this.props.removeDocument}
- contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.addDocTab}
- bringToFront={returnFalse}
- scriptContext={this.props.scriptContext}
- pinToPres={this.props.pinToPres}
- />;
+ return (
+ <DocumentView
+ ref={r => (dref = r || undefined)}
+ Document={doc}
+ DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+ renderDepth={this.props.renderDepth + 1}
+ PanelWidth={width}
+ PanelHeight={height}
+ styleProvider={this.styleProvider}
+ docViewPath={this.props.docViewPath}
+ fitWidth={this.props.childFitWidth}
+ isContentActive={this.isChildContentActive}
+ onKey={this.onKeyDown}
+ isDocumentActive={this.isContentActive}
+ LayoutTemplate={this.props.childLayoutTemplate}
+ LayoutTemplateString={this.props.childLayoutString}
+ NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+ NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._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}
+ showTitle={this.props.childShowTitle}
+ dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+ onClick={this.onChildClickHandler}
+ onDoubleClick={this.onChildDoubleClickHandler}
+ ScreenToLocalTransform={stackedDocTransform}
+ focus={this.focusDocument}
+ docFilters={this.childDocFilters}
+ hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+ hideResizeHandles={this.props.childHideResizeHandles?.()}
+ hideTitle={this.props.childHideTitle?.()}
+ docRangeFilters={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+ ContainingCollectionView={this.props.CollectionView}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ removeDocument={this.props.removeDocument}
+ contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ addDocTab={this.addDocTab}
+ bringToFront={returnFalse}
+ scriptContext={this.props.scriptContext}
+ pinToPres={this.props.pinToPres}
+ />
+ );
}
getDocTransform(doc: Doc, dref?: DocumentView) {
const y = this._scroll; // required for document decorations to update when the text box container is scrolled
const { scale, 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);
+ // 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);
}
getDocWidth(d?: Doc) {
if (!d) return 0;
@@ -300,37 +343,42 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocHeight(d?: Doc) {
if (!d || d.hidden) return 0;
const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
- const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
- const maxHeight = (lim => lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim)(NumCast(this.layoutDoc.childLimitHeight, -1));
+ const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? 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._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 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);
+ return Math.min(maxHeight, (docWid * nh) / nw);
}
const childHeight = NumCast(childLayoutDoc._height);
- const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+ const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
return Math.min(childHeight, maxHeight, panelHeight);
}
columnDividerDown = (e: React.PointerEvent) => {
- runInAction(() => this._cursor = "grabbing");
- setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
- }
+ runInAction(() => (this._cursor = 'grabbing'));
+ setupMoveUpEvents(
+ this,
+ e,
+ this.onDividerMove,
+ action(() => (this._cursor = 'grab')),
+ emptyFunction
+ );
+ };
@action
onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
return false;
- }
+ };
@computed get columnDragger() {
- return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
- style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
- <FontAwesomeIcon icon={"arrows-alt-h"} />
- </div>;
+ return (
+ <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+ <FontAwesomeIcon icon={'arrows-alt-h'} />
+ </div>
+ );
}
@undoBatch
@@ -341,7 +389,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
let dropAfter = 0;
if (de.complete.docDragData) {
this._docXfs.map((cd, i) => {
- const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ const pos = cd
+ .stackedDocTransform()
+ .inverse()
+ .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) {
dropInd = i;
@@ -358,21 +409,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
DragManager.docsBeingDragged = [];
if (docs && newDocs.length) {
const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter;
- const offset = newDocs.reduce((off, ndoc) => this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off, 0);
+ const offset = newDocs.reduce((off, ndoc) => (this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off), 0);
newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
docs.splice(insertInd - offset, 0, ...newDocs);
}
}
- }
- else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
- const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, _fitWidth: true, title: "dropped annotation" });
+ } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
this.props.addDocument?.(source);
- de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
+ de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
e.stopPropagation();
- }
- else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+ } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
return false;
- }
+ };
@undoBatch
internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
@@ -390,7 +439,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const where = [e.clientX, e.clientY];
let targInd = -1;
this._docXfs.map((cd, i) => {
- const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+ const pos = cd
+ .stackedDocTransform()
+ .inverse()
+ .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
targInd = i;
@@ -406,98 +458,103 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
}
});
- }
+ };
headings = () => Array.from(this.Sections);
refList: any[] = [];
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
const key = this.pivotField;
- let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+ let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
if (this.pivotField) {
const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
type = types[0];
}
}
- return <CollectionStackingViewFieldColumn
- 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._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
- 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);
- }
- }
- }));
- this.observer.observe(ref);
- }
- }}
- addDocument={this.addDocument}
- chromeHidden={this.chromeHidden}
- columnHeaders={this.columnHeaders}
- Document={this.props.Document}
- DataDoc={this.props.DataDoc}
- renderChildren={this.children}
- columnWidth={this.columnWidth}
- numGroupColumns={this.numGroupColumns}
- gridGap={this.gridGap}
- pivotField={this.pivotField}
- key={heading?.heading ?? ""}
- headings={this.headings}
- heading={heading?.heading ?? ""}
- headingObject={heading}
- docList={docList}
- yMargin={this.yMargin}
- type={type}
- createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.props.ScreenToLocalTransform}
- />;
- }
+ return (
+ <CollectionStackingViewFieldColumn
+ 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._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ 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);
+ }
+ }
+ })
+ );
+ this.observer.observe(ref);
+ }
+ }}
+ addDocument={this.addDocument}
+ chromeHidden={this.chromeHidden}
+ columnHeaders={this.columnHeaders}
+ Document={this.props.Document}
+ DataDoc={this.props.DataDoc}
+ renderChildren={this.children}
+ columnWidth={this.columnWidth}
+ numGroupColumns={this.numGroupColumns}
+ gridGap={this.gridGap}
+ pivotField={this.pivotField}
+ key={heading?.heading ?? ''}
+ headings={this.headings}
+ heading={heading?.heading ?? ''}
+ headingObject={heading}
+ docList={docList}
+ yMargin={this.yMargin}
+ type={type}
+ createDropTarget={this.createDashEventsTarget}
+ screenToLocalTransform={this.props.ScreenToLocalTransform}
+ />
+ );
+ };
sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
const key = this.pivotField;
- let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+ let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
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))));
- return <CollectionMasonryViewFieldRow
- showHandle={first}
- Document={this.props.Document}
- chromeHidden={this.chromeHidden}
- pivotField={this.pivotField}
- 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._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
- const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
- this.props.setHeight?.(this.headerMargin + height);
- }
- }));
- this.observer.observe(ref);
- }
- }}
- key={heading ? heading.heading : ""}
- rows={rows}
- headings={this.headings}
- heading={heading ? heading.heading : ""}
- headingObject={heading}
- docList={docList}
- parent={this}
- type={type}
- createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.props.ScreenToLocalTransform}
- setDocHeight={this.setDocHeight}
- />;
- }
+ 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}
+ chromeHidden={this.chromeHidden}
+ pivotField={this.pivotField}
+ 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._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
+ this.props.setHeight?.(this.headerMargin + height);
+ }
+ })
+ );
+ this.observer.observe(ref);
+ }
+ }}
+ key={heading ? heading.heading : ''}
+ rows={rows}
+ headings={this.headings}
+ heading={heading ? heading.heading : ''}
+ headingObject={heading}
+ docList={docList}
+ parent={this}
+ type={type}
+ createDropTarget={this.createDashEventsTarget}
+ screenToLocalTransform={this.props.ScreenToLocalTransform}
+ setDocHeight={this.setDocHeight}
+ />
+ );
+ };
@action
addGroup = (value: string) => {
@@ -507,25 +564,25 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return true;
}
return false;
- }
+ };
sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
- const descending = StrCast(this.layoutDoc._columnsSort) === "descending";
+ const descending = StrCast(this.layoutDoc._columnsSort) === 'descending';
const firstEntry = descending ? b : a;
const secondEntry = descending ? a : b;
return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
- }
+ };
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 (!e.isPropagationStopped()) {
const subItems: ContextMenuProps[] = [];
- subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
- subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
- subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" });
- ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
+ subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+ subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+ subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
+ ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
}
- }
+ };
@computed get renderedSections() {
TraceMobx();
@@ -534,7 +591,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const entries = Array.from(this.Sections.entries());
sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
}
- return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
+ return sections.map((section, i) => (this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0)));
}
@computed get buttonMenu() {
@@ -544,85 +601,90 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const width: number = NumCast(menuDoc._width, 30);
const height: number = NumCast(menuDoc._height, 30);
console.log(menuDoc.title, width, height);
- return (<div className="buttonMenu-docBtn"
- style={{ width: width, height: height }}>
- <DocumentView
- Document={menuDoc}
- DataDoc={menuDoc}
- isContentActive={this.props.isContentActive}
- isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={Transform.Identity}
- PanelWidth={() => 35}
- PanelHeight={() => 35}
- renderDepth={this.props.renderDepth}
- focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- />
- </div>
+ return (
+ <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
+ <DocumentView
+ Document={menuDoc}
+ DataDoc={menuDoc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={Transform.Identity}
+ PanelWidth={() => 35}
+ PanelHeight={() => 35}
+ renderDepth={this.props.renderDepth}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />
+ </div>
);
}
}
+ @computed get nativeWidth() {
+ return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+ }
+ @computed get nativeHeight() {
+ return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+ }
- @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
- @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
-
- @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
+ @computed get scaling() {
+ return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight;
+ }
- @computed get backgroundEvents() { return SnappingManager.GetIsDragging(); }
+ @computed get backgroundEvents() {
+ return SnappingManager.GetIsDragging();
+ }
observer: any;
render() {
TraceMobx();
const editableViewProps = {
- GetValue: () => "",
+ GetValue: () => '',
SetValue: this.addGroup,
- contents: "+ ADD A GROUP"
+ contents: '+ ADD A GROUP',
};
const buttonMenu = this.rootDoc.buttonMenu;
const noviceExplainer = this.rootDoc.explainer;
return (
<>
- {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
- {buttonMenu ? this.buttonMenu : null}
- {Doc.noviceMode && noviceExplainer ?
- <div className="documentExplanation">
- {noviceExplainer}
- </div>
- : null
- }
- </div> : null}
- <div className="collectionStackingMasonry-cont" >
- <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+ {buttonMenu || noviceExplainer ? (
+ <div className="documentButtonMenu">
+ {buttonMenu ? this.buttonMenu : null}
+ {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null}
+ </div>
+ ) : null}
+ <div className="collectionStackingMasonry-cont">
+ <div
+ className={this.isStackingView ? 'collectionStackingView' : 'collectionMasonryView'}
ref={this.createRef}
style={{
- overflowY: this.props.isContentActive() ? "auto" : "hidden",
+ overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
- pointerEvents: this.backgroundEvents ? "all" : undefined
+ pointerEvents: this.backgroundEvents ? 'all' : undefined,
}}
- onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+ onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
- onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+ onWheel={e => this.props.isContentActive(true) && 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 }}>
+ {!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 }}>
<EditableView {...editableViewProps} />
- </div>}
+ </div>
+ )}
{/* {this.chromeHidden || !this.props.isSelected() ? (null) :
<Switch
onChange={this.onToggle}
@@ -634,7 +696,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
</div>
</div>
</>
-
);
}
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 03450b798..5479929bd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,24 +1,23 @@
-import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
-import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { WebField } from "../../../fields/URLField";
-import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
-import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-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 { action, computed, observable } from 'mobx';
import ReactLoading from 'react-loading';
import * as rp from 'request-promise';
-import { Networking } from "../../Network";
-
+import CursorField from '../../../fields/CursorField';
+import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { returnFalse, Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
+import { Networking } from '../../Network';
+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');
export interface SubCollectionViewProps extends CollectionViewProps {
CollectionView: Opt<CollectionView>;
@@ -33,7 +32,8 @@ export function CollectionSubView<X>(moreProps?: X) {
protected _mainCont?: HTMLDivElement;
@observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
@observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+ //used for stacking and masonry view
this.dropDisposer?.();
this.gestureDisposer?.();
this._multiTouchDisposer?.();
@@ -43,8 +43,9 @@ export function CollectionSubView<X>(moreProps?: X) {
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
}
- }
- protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
+ };
+ protected CreateDropTarget(ele: HTMLDivElement) {
+ //used in schema view
this.createDashEventsTarget(ele);
}
@@ -54,13 +55,12 @@ export function CollectionSubView<X>(moreProps?: X) {
}
@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
+ 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
}
rootSelected = (outsideReaction?: boolean) => {
return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction));
- }
+ };
// 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
@@ -73,10 +73,12 @@ export function CollectionSubView<X>(moreProps?: X) {
return this.dataDoc[this.props.fieldKey];
}
- get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
+ get childLayoutPairs(): { layout: Doc; data: Doc }[] {
const { Document, DataDoc } = this.props;
- const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)).
- filter(pair => { // filter out any documents that have a proto that we don't have permissions to
+ const validPairs = this.childDocs
+ .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
+ .filter(pair => {
+ // filter out any documents that have a proto that we don't have permissions to
return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
});
return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
@@ -85,21 +87,24 @@ export function CollectionSubView<X>(moreProps?: X) {
return Cast(this.dataField, listSpec(Doc));
}
collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
- collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+ collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []);
childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
- IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
- this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+ IsFiltered = () =>
+ this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
@computed.struct get childDocs() {
TraceMobx();
let rawdocs: (Doc | Promise<Doc>)[] = [];
- if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
+ if (this.dataField instanceof Doc) {
+ // if collection data is just a document, then promote it to a singleton list;
rawdocs = [this.dataField];
- } else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it.
+ } 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 { // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
+ } else {
+ // 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);
@@ -117,19 +122,19 @@ export function CollectionSubView<X>(moreProps?: X) {
return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
}
- // console.log(CurrentUserUtils.ActiveDashboard._docFilters);
+ // console.log(Doc.ActiveDashboard._docFilters);
// if (!this.props.Document._docFilters && this.props.Document.currentFilter) {
// (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean;
// }
const docsforFilter: Doc[] = [];
- childDocs.forEach((d) => {
+ childDocs.forEach(d => {
// if (DocUtils.Excluded(d, docFilters)) return;
- let notFiltered = d.z || Doc.IsSystem(d) || (DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0);
+ let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0;
if (notFiltered) {
- notFiltered = ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
+ notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0;
const fieldKey = Doc.LayoutFieldKey(d);
- const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
- const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+ const data = d[annos ? fieldKey + '-annotations' : fieldKey];
if (data !== undefined) {
let subDocs = DocListCast(data);
if (subDocs.length > 0) {
@@ -137,11 +142,12 @@ export function CollectionSubView<X>(moreProps?: X) {
notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, viewSpecScript, d).length);
while (subDocs.length > 0 && !notFiltered) {
newarray = [];
- subDocs.forEach((t) => {
+ subDocs.forEach(t => {
const fieldKey = Doc.LayoutFieldKey(t);
- const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
- notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
- DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+ const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+ notFiltered =
+ notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
+ DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
});
subDocs = newarray;
}
@@ -157,7 +163,7 @@ export function CollectionSubView<X>(moreProps?: X) {
protected async setCursorPosition(position: [number, number]) {
let ind;
const doc = this.props.Document;
- const id = CurrentUserUtils.id;
+ const id = Doc.UserDoc()[Id];
const email = Doc.CurrentUserEmail;
const pos = { x: position[0], y: position[1] };
if (id && email) {
@@ -167,7 +173,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
// The following conditional detects a recurring bug we've seen on the server
if (proto[Id] === Docs.Prototypes.get(DocumentType.COL)[Id]) {
- alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...");
+ alert('COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...');
console.log(doc);
console.log(proto);
throw new Error(`AHA! You were trying to set a cursor on a collection's proto, which is the original collection proto! Look at the two previously printed lines for document values!`);
@@ -186,8 +192,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
@undoBatch
- protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
- }
+ protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
if (de.complete.docDragData) {
@@ -210,12 +215,16 @@ export function CollectionSubView<X>(moreProps?: X) {
const dropAction = docDragData.dropAction || docDragData.userDropAction;
const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
const someMoved = !docDragData.userDropAction && 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 === "same" || dropAction === "move" || someMoved) && docDragData.moveDocument) {
+ if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
+ if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
if (movedDocs.length) {
- const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || (!this.props.isAnnotationOverlay || this.props.Document.allowOverlayDrop) ||
+ const canAdd =
+ this.props.Document._viewType === CollectionViewType.Pile ||
+ de.embedKey ||
+ !this.props.isAnnotationOverlay ||
+ this.props.Document.allowOverlayDrop ||
Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
} else {
@@ -228,11 +237,10 @@ export function CollectionSubView<X>(moreProps?: X) {
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
added = this.addDocument(docDragData.droppedDocuments);
}
- !added && alert("You cannot perform this move");
+ !added && alert('You cannot perform this move');
e.stopPropagation();
return added;
- }
- else if (de.complete.annoDragData) {
+ } else if (de.complete.annoDragData) {
const dropCreator = de.complete.annoDragData.dropDocCreator;
de.complete.annoDragData.dropDocCreator = () => {
const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
@@ -253,11 +261,11 @@ export function CollectionSubView<X>(moreProps?: X) {
}
const { dataTransfer } = e;
- const html = dataTransfer.getData("text/html");
- const text = dataTransfer.getData("text/plain");
- const uriList = dataTransfer.getData("text/uri-list");
+ const html = dataTransfer.getData('text/html');
+ const text = dataTransfer.getData('text/plain');
+ const uriList = dataTransfer.getData('text/uri-list');
- if (text && text.startsWith("<div")) {
+ if (text && text.startsWith('<div')) {
return;
}
@@ -271,11 +279,15 @@ export function CollectionSubView<X>(moreProps?: X) {
const href = FormattedTextBox.GetHref(html);
if (href) {
const docid = FormattedTextBox.GetDocFromUrl(href);
- if (docid) { // prosemirror text containing link to dash document
+ if (docid) {
+ // prosemirror text containing link to dash document
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
- if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView
- (f instanceof Doc) && addDocument(f);
+ if (options.x || options.y) {
+ f.x = options.x as number;
+ f.y = options.y as number;
+ } // should be in CollectionFreeFormView
+ f instanceof Doc && addDocument(f);
}
});
} else {
@@ -286,47 +298,50 @@ export function CollectionSubView<X>(moreProps?: X) {
}
return;
}
- if (!html.startsWith("<a")) {
- const tags = html.split("<");
- if (tags[0] === "") tags.splice(0, 1);
- let img = tags[0].startsWith("img") ? tags[0] : tags.length > 1 && tags[1].startsWith("img") ? tags[1] : "";
- const cors = img.includes("corsProxy") ? img.match(/http.*corsProxy\//)![0] : "";
- img = cors ? img.replace(cors, "") : img;
+ if (!html.startsWith('<a')) {
+ const tags = html.split('<');
+ if (tags[0] === '') tags.splice(0, 1);
+ let img = tags[0].startsWith('img') ? tags[0] : tags.length > 1 && tags[1].startsWith('img') ? tags[1] : '';
+ 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];
+ 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 (split.startsWith('data:image') && split.includes('base64')) {
+ const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
source = Utils.prepend(accessPaths.agnostic.client);
}
- if (source.startsWith("http")) {
+ if (source.startsWith('http')) {
const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
ImageUtils.ExtractExif(doc);
addDocument(doc);
}
return;
} else {
- const path = window.location.origin + "/doc/";
+ const path = window.location.origin + '/doc/';
if (text.startsWith(path)) {
- const docid = text.replace(Doc.globalServerPath(), "").split("?")[0];
+ const docid = text.replace(Doc.globalServerPath(), '').split('?')[0];
DocServer.GetRefField(docid).then(f => {
if (f instanceof Doc) {
- if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView
- (f instanceof Doc) && addDocument(f);
+ if (options.x || options.y) {
+ f.x = options.x as number;
+ f.y = options.y as number;
+ } // should be in CollectionFreeFormView
+ f instanceof Doc && addDocument(f);
}
});
} else {
const srcWeb = SelectionManager.Views().lastElement();
const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0];
- const reg = new RegExp(Utils.prepend(""), "g");
+ const reg = new RegExp(Utils.prepend(''), 'g');
const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
- const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, "$1")).filter(t => t)?.[0];
- const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? "from:" + srcUrl : "-web clip-", _width: 300, _height: 300, backgroundColor });
- Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text;
+ const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, '$1')).filter(t => t)?.[0];
+ const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? 'from:' + srcUrl : '-web clip-', _width: 300, _height: 300, backgroundColor });
+ Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
addDocument(htmlDoc);
if (srcWeb) {
- const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName("iframe")?.[0];
- const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any);
+ const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+ const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
if (focusNode) {
const anchor = srcWeb?.ComponentView?.getAnchor?.();
anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
@@ -339,11 +354,10 @@ export function CollectionSubView<X>(moreProps?: X) {
}
if (uriList || text) {
- if ((uriList || text).includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
-
- const batch = UndoManager.StartBatch("youtube upload");
+ if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed')) {
+ const batch = UndoManager.StartBatch('youtube upload');
const generatedDocuments: Doc[] = [];
- this.slowLoadDocuments((uriList || text).split("v=")[1].split("&")[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
+ this.slowLoadDocuments((uriList || text).split('v=')[1].split('&')[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
return;
}
@@ -374,15 +388,16 @@ export function CollectionSubView<X>(moreProps?: X) {
// alias._height = 512;
// alias._width = 400;
// addDocument(alias);
- // } else
+ // } else
{
- const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
+ const newDoc = Docs.Create.WebDocument(uriList.split('#annotations:')[0], {
+ // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
...options,
- title: uriList.split("#annotations:")[0],
+ title: uriList.split('#annotations:')[0],
_width: 400,
_height: 512,
_nativeWidth: 850,
- useCors: true
+ useCors: true,
});
addDocument(newDoc);
}
@@ -394,82 +409,99 @@ export function CollectionSubView<X>(moreProps?: X) {
const files: File[] = [];
const generatedDocuments: Doc[] = [];
if (!length) {
- alert("No uploadable content found.");
+ alert('No uploadable content found.');
return;
}
- const batch = UndoManager.StartBatch("collection view drop");
+ const batch = UndoManager.StartBatch('collection view drop');
for (let i = 0; i < length; i++) {
const item = e.dataTransfer.items[i];
- if (item.kind === "string" && item.type.includes("uri")) {
+ if (item.kind === 'string' && item.type.includes('uri')) {
const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
- const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+ const type = (await rp.head(Utils.CorsProxy(stringContents)))['content-type'];
if (type) {
const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
doc && generatedDocuments.push(doc);
}
}
- if (item.kind === "file") {
+ if (item.kind === 'file') {
const file = item.getAsFile();
file?.type && files.push(file);
- file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => {
- const json = JSON.parse(result as string);
- addDocument(Docs.Create.TreeDocument(
- json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => {
- const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 });
- const proto = Doc.GetProto(label);
- proto._width = 120;
- proto._height = 20;
- return proto;
- }
- ), { _width: 150, _height: 600, title: "across", backgroundColor: "white", _singleLine: true }));
- });
+ file?.type === 'application/json' &&
+ Utils.readUploadedFileAsText(file).then(result => {
+ const json = JSON.parse(result as string);
+ addDocument(
+ Docs.Create.TreeDocument(
+ json['rectangular-puzzle'].crossword.clues[0].clue.map((c: any) => {
+ const label = Docs.Create.LabelDocument({ title: c['#text'], _width: 120, _height: 20 });
+ const proto = Doc.GetProto(label);
+ proto._width = 120;
+ proto._height = 20;
+ return proto;
+ }),
+ { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true }
+ )
+ );
+ });
}
}
this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
}
- slowLoadDocuments = async (files: (File[] | string), options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => {
- const disposer = OverlayView.Instance.addElement(
- <ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
- if (typeof files === "string") {
- generatedDocuments.push(...await DocUtils.uploadYoutubeVideo(files, options));
+ slowLoadDocuments = async (
+ files: File[] | string,
+ options: DocumentOptions,
+ generatedDocuments: Doc[],
+ text: string,
+ completed: ((doc: Doc[]) => void) | undefined,
+ clientX: number,
+ clientY: number,
+ addDocument: (doc: Doc | Doc[]) => boolean
+ ) => {
+ const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
+ if (typeof files === 'string') {
+ generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
} else {
- generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
+ generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options)));
}
if (generatedDocuments.length) {
// Creating a dash document
const isFreeformView = this.props.Document._viewType === CollectionViewType.Freeform;
- const set = !isFreeformView ? generatedDocuments :
- generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
+ const set = !isFreeformView
+ ? generatedDocuments
+ : generatedDocuments.length > 1
+ ? generatedDocuments.map(d => {
+ DocUtils.iconify(d);
+ return d;
+ })
+ : [];
if (completed) completed(set);
else {
if (isFreeformView && generatedDocuments.length > 1) {
- addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!,);
+ addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!);
} else {
generatedDocuments.forEach(addDocument);
}
}
} else {
- if (text && !text.includes("https://")) {
+ if (text && !text.includes('https://')) {
addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
} else {
- alert("Document upload failed - possibly an unsupported file type.");
+ alert('Document upload failed - possibly an unsupported file type.');
}
}
disposer();
- }
+ };
}
return CollectionSubView;
}
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
-import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { OverlayView } from "../OverlayView";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; \ No newline at end of file
+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 { OverlayView } from '../OverlayView';
+import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ba72fb7b9..809a73a77 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,5 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
@@ -9,29 +9,28 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DragManager, dropActionType } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
import { DocumentView } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { StyleProp } from '../StyleProvider';
import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView } from "./CollectionSubView";
-import "./CollectionTreeView.scss";
-import { TreeView } from "./TreeView";
-import React = require("react");
-import { FieldViewProps } from "../nodes/FieldView";
-const _global = (window /* browser */ || global /* node */) as any;
+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 = {
- treeViewExpandedView?: "fields" | "layout" | "links" | "data";
+ treeViewExpandedView?: 'fields' | 'layout' | 'links' | 'data';
treeViewOpen?: boolean;
treeViewHideTitle?: boolean;
treeViewHideHeaderFields?: boolean;
@@ -45,9 +44,9 @@ export type collectionTreeViewProps = {
};
export enum TreeViewType {
- outline = "outline",
- fileSystem = "fileSystem",
- default = "default"
+ outline = 'outline',
+ fileSystem = 'fileSystem',
+ default = 'default',
}
@observer
@@ -61,13 +60,28 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
private observer: any; // observer for monitoring tree view items.
private static expandViewLabelSize = 20;
- @computed get doc() { return this.props.Document; }
- @computed get dataDoc() { return this.props.DataDoc || this.doc; }
- @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
- @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
- @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
- @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
- @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
+ @computed get doc() {
+ return this.props.Document;
+ }
+ @computed get dataDoc() {
+ return this.props.DataDoc || this.doc;
+ }
+ @computed get treeViewtruncateTitleWidth() {
+ return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+ }
+ @computed get treeChildren() {
+ TraceMobx();
+ return this.props.childDocuments || this.childDocs;
+ }
+ @computed get outlineMode() {
+ return this.doc.treeViewType === TreeViewType.outline;
+ }
+ @computed get fileSysMode() {
+ return this.doc.treeViewType === TreeViewType.fileSystem;
+ }
+ @computed get dashboardMode() {
+ return this.doc === Doc.MyDashboards;
+ }
@observable _explainerHeight = 0; // height of the description of the tree view
@@ -75,11 +89,9 @@ 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) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
- (this.props.isContentActive?.() || this.props.Document.forceActive ||
- this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
- this.props.rootSelected(outsideReaction)) ? true : false)
+ whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
+ isContentActive = (outsideReaction?: boolean) =>
+ Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false;
componentWillUnmount() {
this._isDisposing = true;
@@ -89,47 +101,51 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
componentDidMount() {
- this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight,
+ this._disposers.autoheight = reaction(
+ () => this.rootDoc.autoHeight,
auto => auto && this.computeHeight(),
- { fireImmediately: true });
+ { fireImmediately: true }
+ );
}
computeHeight = () => {
if (!this._isDisposing) {
- 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());
+ 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());
this.layoutDoc._autoHeightMargins = bodyHeight;
this.props.setHeight?.(bodyHeight + titleHeight);
}
- }
+ };
unobserveHeight = (ref: any) => {
this.refList.delete(ref);
this.rootDoc.autoHeight && this.computeHeight();
- }
+ };
observeHeight = (ref: any) => {
if (ref) {
this.refList.add(ref);
- this.observer = new _global.ResizeObserver(action((entries: any) => {
- if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
- this.computeHeight();
- }
- }));
+ this.observer = new _global.ResizeObserver(
+ action((entries: any) => {
+ if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+ this.computeHeight();
+ }
+ })
+ );
this.rootDoc.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.doc, this.onInternalPreDrop.bind(this));
+ };
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
if (dragData) {
const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
- dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
+ dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
}
- }
+ };
@action
remove = (doc: Doc | Doc[]): boolean => {
@@ -149,7 +165,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
return true;
}
return false;
- }
+ };
@action
addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
@@ -161,81 +177,85 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}, true);
if (this.doc.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.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
- layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
- layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, 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" });
- !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+ layoutItems.push({ description: 'Make tree state ' + (this.doc.treeViewOpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient), icon: 'paint-brush' });
+ layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields), icon: 'paint-brush' });
+ layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), 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' });
+ !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
}
- }
+ };
onTreeDrop = (e: React.DragEvent, addDocs?: (docs: Doc[]) => void) => this.onExternalDrop(e, {}, addDocs);
@undoBatch
makeTextCollection = (childDocs: Doc[]) => {
this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
- }
+ };
get editableTitle() {
- return <EditableView
- contents={this.dataDoc.title}
- display={"block"}
- maxHeight={72}
- height={"auto"}
- GetValue={() => StrCast(this.dataDoc.title)}
- SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
- if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
- this.dataDoc.title = value;
- return true;
- })} />;
+ return (
+ <EditableView
+ contents={this.dataDoc.title}
+ display={'block'}
+ maxHeight={72}
+ height={'auto'}
+ GetValue={() => StrCast(this.dataDoc.title)}
+ SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+ if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+ this.dataDoc.title = value;
+ return true;
+ })}
+ />
+ );
}
-
onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
- if (this.outlineMode && e.key === "Enter") {
+ if (this.outlineMode && e.key === 'Enter') {
e.stopPropagation();
this.makeTextCollection(this.treeChildren);
return true;
}
- }
+ };
get documentTitle() {
- return <FormattedTextBox
- {...this.props}
- fieldKey={"text"}
- 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 autoHeight
- PanelWidth={this.documentTitleWidth}
- PanelHeight={this.documentTitleHeight}
- scaling={returnOne}
- onKey={this.onKey}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.doc}
- ContainingCollectionView={this.props.CollectionView}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
- bringToFront={returnFalse}
- />;
+ return (
+ <FormattedTextBox
+ {...this.props}
+ fieldKey={'text'}
+ 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 autoHeight
+ PanelWidth={this.documentTitleWidth}
+ PanelHeight={this.documentTitleHeight}
+ scaling={returnOne}
+ onKey={this.onKey}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.doc}
+ ContainingCollectionView={this.props.CollectionView}
+ addDocument={returnFalse}
+ 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 }));
- }
+ };
@computed get treeViewElements() {
TraceMobx();
const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -266,36 +286,34 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
this.props.treeViewSkipFields,
true,
this.whenChildContentsActiveChanged,
- this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
+ this.props.dontRegisterView || Cast(this.props.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.hierarchyIndex
);
}
@computed get titleBar() {
- return this.dataDoc === null ? (null) :
- <div className="collectionTreeView-titleBar" key={this.doc[Id]}
- style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}
- ref={r => this._titleRef = r}>
+ return this.dataDoc === null ? null : (
+ <div className="collectionTreeView-titleBar" key={this.doc[Id]} style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} ref={r => (this._titleRef = r)}>
{this.outlineMode ? this.documentTitle : this.editableTitle}
- </div>;
+ </div>
+ );
}
@computed get noviceExplainer() {
- return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
- <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
+ return !Doc.noviceMode || !this.rootDoc.explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.explainer)} </div>;
}
return35 = () => 35;
@computed get buttonMenu() {
const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
// To create a multibutton menu add a CollectionLinearView
- return !menuDoc ? null :
- (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+ return !menuDoc ? null : (
+ <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
<DocumentView
Document={menuDoc}
DataDoc={menuDoc}
@@ -322,11 +340,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
ContainingCollectionView={undefined}
ContainingCollectionDoc={undefined}
/>
- </div>);
+ </div>
+ );
}
- @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
- @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
+ @computed get nativeWidth() {
+ return Doc.NativeWidth(this.Document, undefined, true);
+ }
+ @computed get nativeHeight() {
+ return Doc.NativeHeight(this.Document, undefined, true);
+ }
@computed get contentScaling() {
const nw = this.nativeWidth;
@@ -347,68 +370,73 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
- this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false
+ this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
contentFunc = () => {
const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
- const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
- const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar;
+ const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+ const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
return [
- <div className="collectionTreeView-contents" key="tree" style={{
- ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
- overflow: "auto",
- height: "100%"//this.layoutDoc._autoHeight ? "max-content" : "100%"
- }} >
+ <div
+ className="collectionTreeView-contents"
+ key="tree"
+ style={{
+ ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+ overflow: 'auto',
+ height: '100%', //this.layoutDoc._autoHeight ? "max-content" : "100%"
+ }}>
{titleBar}
- <div className="collectionTreeView-container"
+ <div
+ className="collectionTreeView-container"
style={{
- transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
+ transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
paddingLeft: `${this.marginX()}px`,
- width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
+ width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
}}
onContextMenu={this.onContextMenu}>
- {!this.buttonMenu && !this.noviceExplainer ? (null) :
+ {!this.buttonMenu && !this.noviceExplainer ? null : (
<div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
{this.buttonMenu}
{this.noviceExplainer}
</div>
- }
- <div className="collectionTreeView-dropTarget"
+ )}
+ <div
+ className="collectionTreeView-dropTarget"
style={{
background: background(),
height: `calc(100% - ${this._explainerHeight}px)`,
- pointerEvents: pointerEvents()
+ pointerEvents: pointerEvents(),
}}
onWheel={e => e.stopPropagation()}
onDrop={this.onTreeDrop}
ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
- <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
- {this.treeViewElements}
- </ul>
- </div >
+ <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+ </div>
</div>
- </div>
+ </div>,
];
- }
+ };
render() {
TraceMobx();
- return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
- this.doc.treeViewHasOverlay ?
- <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
- isAnnotationOverlay={true}
- isAnnotationOverlayScrollable={true}
- childDocumentsActive={this.props.isDocumentActive}
- 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} >
- {this.contentFunc}
- </CollectionFreeFormView> :
- this.contentFunc();
+ return !(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+ <CollectionFreeFormView
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ isAnnotationOverlay={true}
+ isAnnotationOverlayScrollable={true}
+ childDocumentsActive={this.props.isDocumentActive}
+ 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}>
+ {this.contentFunc}
+ </CollectionFreeFormView>
+ ) : (
+ this.contentFunc()
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ae0c01ef..2ab5f6247 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,7 +1,6 @@
import { computed, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
import * as React from 'react';
-import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
import { Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { ObjectField } from '../../../fields/ObjectField';
@@ -10,70 +9,50 @@ 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 { BranchCreate, BranchTask } from '../../documents/Gitlike';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { InteractionUtils } from '../../util/InteractionUtils';
-import { ContextMenu } from "../ContextMenu";
+import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
+import { DashboardView } from '../DashboardView';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
-import { CollectionDockingView } from "./CollectionDockingView";
+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 { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from "./collectionSchema/CollectionSchemaView";
+import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
import { CollectionStackingView } from './CollectionStackingView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
-import { CollectionTreeView } from "./CollectionTreeView";
+import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
-export enum CollectionViewType {
- Invalid = "invalid",
- Freeform = "freeform",
- Schema = "schema",
- Docking = "docking",
- Tree = 'tree',
- Stacking = "stacking",
- Masonry = "masonry",
- Multicolumn = "multicolumn",
- Multirow = "multirow",
- Time = "time",
- Carousel = "carousel",
- Carousel3D = "3D Carousel",
- Linear = "linear",
- //Staff = "staff",
- Map = "map",
- Grid = "grid",
- Pile = "pileup",
- StackedTimeline = "stacked timeline"
-}
-export interface CollectionViewProps extends FieldViewProps {
- isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+interface CollectionViewProps_ extends 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) => void) => void;
// property overrides for child documents
- children?: never | (() => JSX.Element[]) | React.ReactNode;
childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
- childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
+ childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
childFitWidth?: (child: Doc) => boolean;
childShowTitle?: () => string;
childOpacity?: () => number;
- childContextMenuItems?: () => { script: ScriptField, label: string }[];
+ 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
+ childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
childLayoutString?: string;
childIgnoreNativeSize?: boolean;
childClickScript?: ScriptField;
@@ -81,20 +60,25 @@ export interface CollectionViewProps extends FieldViewProps {
//TODO: [AL] add these fields
AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
- hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
+ 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>() {
- public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
+ public static LayoutString(fieldStr: string) {
+ return FieldView.LayoutString(CollectionView, fieldStr);
+ }
@observable private static _safeMode = false;
- public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
+ public static SetSafeMode(safeMode: boolean) {
+ this._safeMode = safeMode;
+ }
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
constructor(props: any) {
super(props);
- runInAction(() => this._annotationKeySuffix = returnEmptyString);
+ runInAction(() => (this._annotationKeySuffix = returnEmptyString));
}
get collectionViewType(): CollectionViewType | undefined {
@@ -102,15 +86,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
if (CollectionView._safeMode) {
switch (viewField) {
case CollectionViewType.Freeform:
- case CollectionViewType.Schema: return CollectionViewType.Tree;
- case CollectionViewType.Invalid: return CollectionViewType.Freeform;
+ case CollectionViewType.Schema:
+ return CollectionViewType.Tree;
+ case CollectionViewType.Invalid:
+ return CollectionViewType.Freeform;
}
}
return viewField as any as CollectionViewType;
}
showIsTagged = () => {
- return (null);
+ return null;
// this section would display an icon in the bototm right of a collection to indicate that all
// photos had been processed through Google's content analysis API and Google's tags had been
// assigned to the documents googlePhotosTags field.
@@ -119,9 +105,10 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
// const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
// return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
//this.isContentActive();
- }
+ };
- screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
+ screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
+ // prettier-ignore
private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
TraceMobx();
if (type === undefined) return null;
@@ -143,112 +130,131 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
//case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
}
- }
+ };
setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
const subItems: ContextMenuProps[] = [];
- subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
+ subItems.push({ description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' });
if (addExtras && CollectionView._safeMode) {
- ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+ ContextMenu.Instance.addItem({ description: 'Test Freeform', event: () => func(CollectionViewType.Invalid), icon: 'project-diagram' });
}
- subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
- subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
- subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
- subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
- subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
- subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
- subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
- subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
- !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
- !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
- subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
+ subItems.push({ description: 'Schema', event: () => func(CollectionViewType.Schema), icon: 'th-list' });
+ subItems.push({ description: 'Tree', event: () => func(CollectionViewType.Tree), icon: 'tree' });
+ subItems.push({ description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' });
+ subItems.push({ description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' });
+ subItems.push({ description: 'Multirow', event: () => func(CollectionViewType.Multirow), icon: 'columns' });
+ subItems.push({ description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' });
+ subItems.push({ description: 'Carousel', event: () => func(CollectionViewType.Carousel), icon: 'columns' });
+ subItems.push({ description: '3D Carousel', event: () => func(CollectionViewType.Carousel3D), icon: 'columns' });
+ !Doc.noviceMode && subItems.push({ description: 'Pivot/Time', event: () => func(CollectionViewType.Time), icon: 'columns' });
+ !Doc.noviceMode && subItems.push({ description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' });
+ subItems.push({ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' });
if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
const existingVm = ContextMenu.Instance.findByDescription(category);
- const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
- catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
- !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" });
+ const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
+ catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
+ !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' });
}
}
onContextMenu = (e: React.MouseEvent): void => {
const cm = ContextMenu.Instance;
- if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it. This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
- if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) { // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
- this.setupViewTypes("UI Controls...", vtype => {
- const newRendition = Doc.MakeAlias(this.rootDoc);
- newRendition._viewType = vtype;
- this.props.addDocTab(newRendition, "add:right");
- return newRendition;
- }, false);
+ if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== Doc.MainDocId) {
+ // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
+ this.setupViewTypes(
+ 'UI Controls...',
+ vtype => {
+ const newRendition = Doc.MakeAlias(this.rootDoc);
+ newRendition._viewType = vtype;
+ this.props.addDocTab(newRendition, 'add:right');
+ return newRendition;
+ },
+ false
+ );
- 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;
+ 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, "add:right"), icon: "project-diagram" });
+ optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, 'add:right'), 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, "add:right"), icon: "project-diagram" });
+ optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, 'add:right'), icon: 'project-diagram' });
}
- !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+ !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' });
if (!Doc.noviceMode) {
optionItems.push({
- description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
+ description: 'Create Branch',
+ event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), 'add:right'),
+ icon: 'project-diagram',
});
optionItems.push({
- description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
+ description: 'Pull Master',
+ event: () => BranchTask(this.rootDoc, 'pull'),
+ icon: 'project-diagram',
});
optionItems.push({
- description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
+ description: 'Merge Branches',
+ event: () => BranchTask(this.rootDoc, 'merge'),
+ icon: 'project-diagram',
});
}
if (this.Document._viewType === CollectionViewType.Docking) {
- optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
+ optionItems.push({ description: 'Create Dashboard', event: () => DashboardView.createNewDashboard(), icon: 'project-diagram' });
}
- !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
+ !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
- const existingOnClick = cm.findByDescription("OnClick...");
- const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
- const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
- funcs.map(func => onClicks.push({
- description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
- const alias = Doc.MakeAlias(this.rootDoc);
- DocUtils.makeCustomViewClicked(alias, undefined, func.key);
- this.props.addDocTab(alias, "add:right");
- }
- }));
- DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick =>
+ const existingOnClick = cm.findByDescription('OnClick...');
+ const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+ const funcs = [
+ { key: 'onChildClick', name: 'On Child Clicked' },
+ { key: 'onChildDoubleClick', name: 'On Child Double Clicked' },
+ ];
+ funcs.map(func =>
+ onClicks.push({
+ description: `Edit ${func.name} script`,
+ icon: 'edit',
+ event: (obj: any) => {
+ const alias = Doc.MakeAlias(this.rootDoc);
+ DocUtils.makeCustomViewClicked(alias, undefined, func.key);
+ this.props.addDocTab(alias, 'add:right');
+ },
+ })
+ );
+ DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null).data).forEach(childClick =>
onClicks.push({
description: `Set child ${childClick.title}`,
- icon: "edit",
- event: () => Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
- }));
- !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+ icon: 'edit',
+ event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
+ })
+ );
+ !Doc.IsSystem(this.rootDoc) && !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) });
- !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+ 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) });
+ !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
}
}
- }
+ };
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);
- @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
-
- isContentActive = (outsideReaction?: boolean) => {
- return this.props.isContentActive();
+ @computed get childLayoutString() {
+ return StrCast(this.rootDoc.childLayoutString);
}
+
+ isContentActive = (outsideReaction?: boolean) => this.props.isContentActive();
+
render() {
TraceMobx();
const props: SubCollectionViewProps = {
@@ -268,10 +274,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
childHideDecorationTitle: this.childHideDecorationTitle,
CollectionView: this,
};
- return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
- style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? "none" : undefined }}>
- {this.showIsTagged()}
- {this.renderSubView(this.collectionViewType, props)}
- </div>);
+ return (
+ <div className={'collectionView'} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
+ {this.showIsTagged()}
+ {this.renderSubView(this.collectionViewType, props)}
+ </div>
+ );
}
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 62d07b0e4..b8aaea622 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -1,43 +1,41 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
-import 'golden-layout/src/css/goldenlayout-base.css';
-import 'golden-layout/src/css/goldenlayout-dark-theme.css';
import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
-import { FieldId } from "../../../fields/RefField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils";
-import { DocServer } from "../../DocServer";
+import { FieldId } from '../../../fields/RefField';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
import { DocUtils } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DragManager, dropActionType } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
import { Colors, Shadows } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
import { MainView } from '../MainView';
-import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
+import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
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, CollectionViewType } from './CollectionView';
-import "./TabDocView.scss";
-import React = require("react");
-import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
-const _global = (window /* browser */ || global /* node */) as any;
+import { CollectionView } from './CollectionView';
+import './TabDocView.scss';
+import React = require('react');
+const _global = (window /* browser */ || global) /* node */ as any;
interface TabDocViewProps {
documentId: FieldId;
@@ -54,9 +52,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@observable _document: Doc | undefined;
@observable _view: DocumentView | undefined;
- @computed get layoutDoc() { return this._document && Doc.Layout(this._document); }
- @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); }
- @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 layoutDoc() {
+ return this._document && Doc.Layout(this._document);
+ }
+ @computed get tabColor() {
+ return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor)));
+ }
+ @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];
@@ -65,74 +69,91 @@ export class TabDocView extends React.Component<TabDocViewProps> {
// 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; }
- get tab() { return (this.props as any).glContainer.tab; }
- get view() { return this._view; }
+ get stack() {
+ return (this.props as any).glContainer.parent.parent;
+ }
+ get tab() {
+ return (this.props as any).glContainer.tab;
+ }
+ get view() {
+ return this._view;
+ }
_lastTab: any;
_lastView: DocumentView | undefined;
@action
init = (tab: any, doc: Opt<Doc>) => {
if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true;
- if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") {
+ if (tab.DashDoc !== doc && doc && tab.hasOwnProperty('contentItem') && tab.contentItem.config.type !== 'stack') {
tab._disposers = {} as { [name: string]: IReactionDisposer };
tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true);
tab.DashDoc = doc;
const iconType: IconProp = Doc.toIcon(doc);
// setup the title element and set its size according to the # of chars in the title. Show the full title when clicked.
const titleEle = tab.titleElement[0];
- const iconWrap = document.createElement("div");
- const closeWrap = document.createElement("div");
+ const iconWrap = document.createElement('div');
+ const closeWrap = document.createElement('div');
titleEle.size = StrCast(doc.title).length + 3;
titleEle.value = doc.title;
titleEle.onkeydown = (e: KeyboardEvent) => {
e.stopPropagation();
};
- titleEle.onchange = undoBatch(action((e: any) => {
- titleEle.size = e.currentTarget.value.length + 3;
- Doc.GetProto(doc).title = e.currentTarget.value;
- }));
+ titleEle.onchange = undoBatch(
+ action((e: any) => {
+ titleEle.size = e.currentTarget.value.length + 3;
+ Doc.GetProto(doc).title = e.currentTarget.value;
+ })
+ );
if (tab.element[0].children[1].children.length === 1) {
- iconWrap.className = "lm_iconWrap lm_moreInfo";
+ iconWrap.className = 'lm_iconWrap lm_moreInfo';
const dragBtnDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => {
- if (this.view) {
- SelectionManager.SelectView(this.view, false);
- let child = this.view.ContentDiv!.children[0];
- while (child.children.length) {
- const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
- if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
- if (next?.className?.toString().includes(DashFieldView.name)) break;
- if (next) child = next;
- else break;
+ setupMoveUpEvents(
+ this,
+ e,
+ e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY),
+ returnFalse,
+ action(e => {
+ if (this.view) {
+ SelectionManager.SelectView(this.view, false);
+ let child = this.view.ContentDiv!.children[0];
+ while (child.children.length) {
+ const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
+ if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
+ if (next?.className?.toString().includes(DashFieldView.name)) break;
+ if (next) child = next;
+ else break;
+ }
+ simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ } else {
+ this._activated = true;
+ setTimeout(() => this.view && SelectionManager.SelectView(this.view, false));
}
- simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
- }
- else { this._activated = true;
- setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false));
- }
- }));
+ })
+ );
};
-
+
const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
- const closeIcon = <FontAwesomeIcon icon={"eye"} />;
+ const closeIcon = <FontAwesomeIcon icon={'eye'} />;
ReactDOM.render(docIcon, iconWrap);
ReactDOM.render(closeIcon, closeWrap);
tab.reactComponents = [iconWrap, closeWrap];
tab.element[0].prepend(iconWrap);
- tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
+ tab._disposers.layerDisposer = reaction(
+ () => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
({ layer, color }) => {
// console.log("TabDocView: " + this.tabColor);
// console.log("lightOrDark: " + lightOrDark(this.tabColor));
const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
titleEle.style.color = textColor;
- titleEle.style.backgroundColor = "transparent";
+ titleEle.style.backgroundColor = 'transparent';
iconWrap.style.color = textColor;
closeWrap.style.color = textColor;
- tab.element[0].style.background = !layer ? color : "dimgrey";
- }, { fireImmediately: true });
+ tab.element[0].style.background = !layer ? color : 'dimgrey';
+ },
+ { fireImmediately: true }
+ );
}
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: MouseEvent) => {
@@ -142,157 +163,167 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
};
-
// select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
titleEle.onpointerdown = action((e: any) => {
- if (e.target.className !== "lm_iconWrap") {
+ if (e.target.className !== 'lm_iconWrap') {
if (this.view) SelectionManager.SelectView(this.view, false);
else this._activated = true;
if (Date.now() - titleEle.lastClick < 1000) titleEle.select();
titleEle.lastClick = Date.now();
- (document.activeElement !== titleEle) && titleEle.focus();
+ document.activeElement !== titleEle && titleEle.focus();
}
});
- tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc),
- action((selected) => {
+ tab._disposers.selectionDisposer = reaction(
+ () => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc),
+ action(selected => {
if (selected) this._activated = true;
const toggle = tab.element[0].children[1].children[0] as HTMLInputElement;
- selected && tab.contentItem !== tab.header.parent.getActiveContentItem() &&
- UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch");
+ selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch');
// toggle.style.fontWeight = selected ? "bold" : "";
// toggle.style.textTransform = selected ? "uppercase" : "";
- }));
-
+ })
+ );
// 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(
+ () => ({ 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 }
+ );
// clean up the tab when it is closed
- tab.closeElement.off('click') //unbind the current click handler
+ tab.closeElement
+ .off('click') //unbind the current click handler
.click(function () {
Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
SelectionManager.DeselectAll();
- UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
+ UndoManager.RunInBatch(() => tab.contentItem.remove(), 'delete tab');
});
}
- }
+ };
/**
* Adds a document to the presentation view
**/
@action
- public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
- const docList = ((docs instanceof Doc) ? [docs]: docs);
- const batch = UndoManager.StartBatch("pinning doc");
+ public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) {
+ const docList = docs instanceof Doc ? [docs] : docs;
+ const batch = UndoManager.StartBatch('pinning doc');
// all docs will be added to the ActivePresentation as stored on CurrentUserUtils
- const curPres = CurrentUserUtils.ActivePresentation;
- curPres && docList.forEach(doc => {
- // Edge Case 1: Cannot pin document to itself
- if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
- const pinDoc = Doc.MakeAlias(doc);
- pinDoc.presentationTargetDoc = doc;
- pinDoc.title = doc.title + " - Slide";
- pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' 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.presMovement = PresMovement.Zoom;
- pinDoc.groupWithUp = false;
- pinDoc.context = curPres;
- // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
- pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
- pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
- pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
- pinDoc.treeViewFieldKey = "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.treeViewExpandedView = "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.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header
- pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
- const presArray: Doc[] = PresBox.Instance?.sortArray();
- const size: number = PresBox.Instance?._selectedArray.size;
- const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
- const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
- // If pinWithView option set then update scale and x / y props of slide
- if (pinProps?.pinWithView) {
- const viewProps = pinProps.pinWithView;
- pinDoc.presPinView = true;
- pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
- pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
- pinDoc.presPinViewScale = viewProps.scale;
- pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]);
- }
- if (pinProps?.pinDocView) {
- const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
- const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG);
- if (scrollable) {
- const scroll = doc._scrollTop;
- pinDoc.presPinView = true;
- pinDoc.presPinViewScroll = scroll;
- } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
- pinDoc.presPinView = true;
- pinDoc.presStartTime = doc._currentTimecode;
- pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
- } else if (pannable) {
+ const curPres = Doc.ActivePresentation;
+ curPres &&
+ docList.forEach(doc => {
+ // Edge Case 1: Cannot pin document to itself
+ if (doc === curPres) {
+ alert('Cannot pin presentation document to itself');
+ return;
+ }
+ const pinDoc = Doc.MakeAlias(doc);
+ pinDoc.presentationTargetDoc = doc;
+ pinDoc.title = doc.title + ' - Slide';
+ pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' 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.presMovement = PresMovement.Zoom;
+ pinDoc.groupWithUp = false;
+ pinDoc.context = curPres;
+ // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
+ pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+ pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
+ pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+ pinDoc.treeViewFieldKey = '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.treeViewExpandedView = '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.treeViewGrowsHorizontally = true; // the document expands horizontally when displayed as a tree view header
+ pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
+ const presArray: Doc[] = PresBox.Instance?.sortArray();
+ const size: number = PresBox.Instance?._selectedArray.size;
+ const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
+ const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+ // If pinWithView option set then update scale and x / y props of slide
+ if (pinProps?.pinWithView) {
+ const viewProps = pinProps.pinWithView;
pinDoc.presPinView = true;
- pinDoc.presPinViewX = pinDoc._panX;
- pinDoc.presPinViewY = pinDoc._panY;
- pinDoc.presPinViewScale = pinDoc._viewScale;
- const pw = NumCast(pinProps.panelWidth);
- const ph = NumCast(pinProps.panelHeight);
- const ps = NumCast(pinDoc._viewScale);
- if (pw && ph && ps) {
- pinDoc.contentBounds = new List<number>([
- NumCast(pinDoc.panX)-pw/2/ps,
- NumCast(pinDoc.panY)-ph/2/ps,
- NumCast(pinDoc.panX)+pw/2/ps,
- NumCast(pinDoc.panY)+ph/2/ps]);
+ pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
+ pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
+ pinDoc.presPinViewScale = viewProps.scale;
+ pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left + viewProps.bounds.width, viewProps.bounds.top + viewProps.bounds.height]);
+ }
+ if (pinProps?.pinDocView) {
+ const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
+ const pannable: boolean = (pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG;
+ if (scrollable) {
+ const scroll = doc._scrollTop;
+ pinDoc.presPinView = true;
+ pinDoc.presPinViewScroll = scroll;
+ } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
+ pinDoc.presPinView = true;
+ pinDoc.presStartTime = doc._currentTimecode;
+ pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
+ } else if (pannable) {
+ pinDoc.presPinView = true;
+ pinDoc.presPinViewX = pinDoc._panX;
+ pinDoc.presPinViewY = pinDoc._panY;
+ pinDoc.presPinViewScale = pinDoc._viewScale;
+ const pw = NumCast(pinProps.panelWidth);
+ const ph = NumCast(pinProps.panelHeight);
+ const ps = NumCast(pinDoc._viewScale);
+ if (pw && ph && ps) {
+ pinDoc.contentBounds = new List<number>([NumCast(pinDoc.panX) - pw / 2 / ps, NumCast(pinDoc.panY) - ph / 2 / ps, NumCast(pinDoc.panX) + pw / 2 / ps, NumCast(pinDoc.panY) + ph / 2 / ps]);
+ }
+ } else if (doc.type === DocumentType.COMPARISON) {
+ const width = doc._clipWidth;
+ pinDoc.presPinClipWidth = width;
+ pinDoc.presPinView = true;
}
- } else if (doc.type === DocumentType.COMPARISON) {
- const width = doc._clipWidth;
- pinDoc.presPinClipWidth = width;
- pinDoc.presPinView = true;
}
- }
- pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)");
- Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
- if (!pinProps?.audioRange && duration !== undefined) {
- pinDoc.mediaStart = "manual";
- pinDoc.mediaStop = "manual";
- pinDoc.presStartTime = NumCast(doc.clipStart);
- pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
- }
- //save position
- if (pinProps?.setPosition || pinDoc.isInkMask) {
- pinDoc.setPosition = true;
- pinDoc.y = doc.y;
- pinDoc.x = doc.x;
- pinDoc.presHideAfter = true;
- pinDoc.presHideBefore = true;
- pinDoc.title = doc.title + " (move)";
- pinDoc.presMovement = PresMovement.None;
- }
- if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
- PresBox.Instance?._selectedArray.clear();
- pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
- });
- if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
- const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
+ pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
+ Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
+ if (!pinProps?.audioRange && duration !== undefined) {
+ pinDoc.mediaStart = 'manual';
+ pinDoc.mediaStop = 'manual';
+ pinDoc.presStartTime = NumCast(doc.clipStart);
+ pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
+ }
+ //save position
+ if (pinProps?.setPosition || pinDoc.isInkMask) {
+ pinDoc.setPosition = true;
+ pinDoc.y = doc.y;
+ pinDoc.x = doc.x;
+ pinDoc.presHideAfter = true;
+ pinDoc.presHideBefore = true;
+ pinDoc.title = doc.title + ' (move)';
+ pinDoc.presMovement = PresMovement.None;
+ }
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+ PresBox.Instance?._selectedArray.clear();
+ pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+ });
+ if (
+ !Array.from(CollectionDockingView.Instance.tabMap)
+ .map(d => d.DashDoc)
+ .includes(curPres)
+ ) {
+ const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []);
if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
- CollectionDockingView.AddSplit(curPres, "right");
+ CollectionDockingView.AddSplit(curPres, 'right');
setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
}
setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
}
componentDidMount() {
- new _global.ResizeObserver(action((entries: any) => {
- for (const entry of entries) {
- this._panelWidth = entry.contentRect.width;
- this._panelHeight = entry.contentRect.height;
- }
- })).observe(this.props.glContainer._element[0]);
- this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged);
+ new _global.ResizeObserver(
+ action((entries: any) => {
+ for (const entry of entries) {
+ this._panelWidth = entry.contentRect.width;
+ 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);
// this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }),
// ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""),
@@ -306,7 +337,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
this._tabReaction?.();
this._view && DocumentManager.Instance.RemoveView(this._view);
- this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
+ this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
}
@action.bound
@@ -326,25 +357,31 @@ export class TabDocView extends React.Component<TabDocViewProps> {
// inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack
addDocTab = (doc: Doc, location: string) => {
SelectionManager.DeselectAll();
- const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
- const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+ const locationFields = doc._viewType === CollectionViewType.Docking ? ['dashboard'] : location.split(':');
+ const locationParams = locationFields.length > 1 ? locationFields[1] : '';
switch (locationFields[0]) {
- case "dashboard": return CurrentUserUtils.openDashboard(doc);
- case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
- case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
- case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
+ case 'dashboard':
+ return DashboardView.openDashboard(doc);
+ case 'close':
+ return CollectionDockingView.CloseSplit(doc, locationParams);
+ case 'fullScreen':
+ return CollectionDockingView.OpenFullScreen(doc);
+ case 'replace':
+ return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
// case "lightbox": {
// // TabDocView.PinDoc(doc, { hidePresBox: true });
// return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
// }
- case "lightbox": return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
- case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
- case "inPlace":
- case "add":
+ case 'lightbox':
+ return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab);
+ case 'toggle':
+ return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
+ case 'inPlace':
+ case 'add':
default:
return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
}
- }
+ };
remDocTab = (doc: Doc | Doc[]) => {
if (doc === this._document) {
SelectionManager.DeselectAll();
@@ -352,11 +389,11 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return true;
}
return false;
- }
+ };
getCurrentFrame = () => {
return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
- }
+ };
@action
focusFunc = (doc: Doc, options?: DocFocusOptions) => {
const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
@@ -364,104 +401,113 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const focusSpeed = 1000;
shrinkwrap();
this._document._viewTransition = `transform ${focusSpeed}ms`;
- setTimeout(action(() => {
- this._document!._viewTransition = undefined;
- options?.afterFocus?.(false);
- }), focusSpeed);
+ setTimeout(
+ action(() => {
+ this._document!._viewTransition = undefined;
+ options?.afterFocus?.(false);
+ }),
+ focusSpeed
+ );
} else {
options?.afterFocus?.(false);
}
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)
}
- }
+ };
active = () => this._isActive;
@observable _forceInvalidateScreenToLocal = 0;
ScreenToLocalTransform = () => {
this._forceInvalidateScreenToLocal;
const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
- }
+ };
PanelWidth = () => this._panelWidth;
PanelHeight = () => this._panelHeight;
miniMapColor = () => this.tabColor;
tabView = () => this._view;
- disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
+ disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform;
hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
@computed get docView() {
- return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
- <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => {
- this._lastView && DocumentManager.Instance.RemoveView(this._lastView);
- this._view = r;
- this._lastView = this._view;
- })}
- renderDepth={0}
- Document={this._document}
- DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- onBrowseClick={MainView.Instance.exploreMode}
- isContentActive={returnTrue}
- PanelWidth={this.PanelWidth}
- PanelHeight={this.PanelHeight}
- styleProvider={DefaultStyleProvider}
- docFilters={CollectionDockingView.Instance.childDocFilters}
- docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
- searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
- addDocument={undefined}
- removeDocument={this.remDocTab}
- addDocTab={this.addDocTab}
- ScreenToLocalTransform={this.ScreenToLocalTransform}
- dontCenter={"y"}
- rootSelected={returnTrue}
- whenChildContentsActiveChanged={emptyFunction}
- focus={this.focusFunc}
- docViewPath={returnEmptyDoclist}
- bringToFront={emptyFunction}
- pinToPres={TabDocView.PinDoc} />
- <TabMinimapView key="minimap"
- hideMinimap={this.hideMinimap}
- addDocTab={this.addDocTab}
- PanelHeight={this.PanelHeight}
+ return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? null : (
+ <>
+ <DocumentView
+ key={this._document[Id]}
+ ref={action((r: DocumentView) => {
+ this._lastView && DocumentManager.Instance.RemoveView(this._lastView);
+ this._view = r;
+ this._lastView = this._view;
+ })}
+ renderDepth={0}
+ Document={this._document}
+ DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ onBrowseClick={MainView.Instance.exploreMode}
+ isContentActive={returnTrue}
PanelWidth={this.PanelWidth}
- background={this.miniMapColor}
- document={this._document}
- tabView={this.tabView} />
- <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
- <div className="miniMap-hidden"
+ PanelHeight={this.PanelHeight}
+ styleProvider={DefaultStyleProvider}
+ docFilters={CollectionDockingView.Instance.childDocFilters}
+ docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
+ searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+ addDocument={undefined}
+ removeDocument={this.remDocTab}
+ addDocTab={this.addDocTab}
+ ScreenToLocalTransform={this.ScreenToLocalTransform}
+ dontCenter={'y'}
+ rootSelected={returnTrue}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={this.focusFunc}
+ docViewPath={returnEmptyDoclist}
+ bringToFront={emptyFunction}
+ pinToPres={TabDocView.PinDoc}
+ />
+ <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
+ <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}>
+ <div
+ className="miniMap-hidden"
style={{
- display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined,
+ display: this.disableMinimap() || this._document._viewType !== 'freeform' ? 'none' : undefined,
color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
- boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined
+ boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined,
}}
onPointerDown={e => e.stopPropagation()}
- onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
- <FontAwesomeIcon icon={"globe-asia"} size="lg" />
+ onClick={action(e => {
+ e.stopPropagation();
+ this._document!.hideMinimap = !this._document!.hideMinimap;
+ })}>
+ <FontAwesomeIcon icon={'globe-asia'} size="lg" />
</div>
</Tooltip>
- </>;
+ </>
+ );
}
render() {
return (
- <div className="collectionDockingView-content" style={{
- fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined,
- height: "100%", width: "100%"
- }} ref={ref => {
- if (this._mainCont = ref) {
- if (this._lastTab) {
- this._view && DocumentManager.Instance.RemoveView(this._view);
+ <div
+ className="collectionDockingView-content"
+ style={{
+ fontFamily: Doc.UserDoc().renderStyle === 'comic' ? 'Comic Sans MS' : undefined,
+ height: '100%',
+ width: '100%',
+ }}
+ ref={ref => {
+ if ((this._mainCont = ref)) {
+ if (this._lastTab) {
+ this._view && DocumentManager.Instance.RemoveView(this._view);
+ }
+ this._lastTab = this.tab;
+ (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
+ DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
+ new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
}
- this._lastTab = this.tab;
- (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
- DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
- new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
- }
- }} >
+ }}>
{this.docView}
- </div >
+ </div>
);
}
}
@@ -479,29 +525,38 @@ interface TabMinimapViewProps {
export class TabMinimapView extends React.Component<TabMinimapViewProps> {
static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
if (doc) {
- switch (property.split(":")[0]) {
- default: return DefaultStyleProvider(doc, props, property);
- case StyleProp.PointerEvents: return "none";
+ switch (property.split(':')[0]) {
+ default:
+ return DefaultStyleProvider(doc, props, property);
+ case StyleProp.PointerEvents:
+ return 'none';
case StyleProp.DocContents:
const background = ((type: DocumentType) => {
switch (type) {
- case DocumentType.PDF: return "pink";
- case DocumentType.AUDIO: return "lightgreen";
- case DocumentType.WEB: return "brown";
- case DocumentType.IMG: return "blue";
- case DocumentType.MAP: return "orange";
- case DocumentType.VID: return "purple";
- case DocumentType.RTF: return "yellow";
- case DocumentType.COL: return undefined;
- default: return "gray";
+ case DocumentType.PDF:
+ return 'pink';
+ case DocumentType.AUDIO:
+ return 'lightgreen';
+ case DocumentType.WEB:
+ return 'brown';
+ case DocumentType.IMG:
+ return 'blue';
+ case DocumentType.MAP:
+ return 'orange';
+ case DocumentType.VID:
+ return 'purple';
+ case DocumentType.RTF:
+ return 'yellow';
+ case DocumentType.COL:
+ return undefined;
+ default:
+ return 'gray';
}
})(doc.type as DocumentType);
- return !background ?
- undefined :
- <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />;
+ return !background ? undefined : <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: 'absolute', display: 'block', background }} />;
}
}
- }
+ };
@computed get renderBounds() {
const compView = this.props.tabView()?.ComponentView as CollectionFreeFormView;
const bounds = compView?.freeformData?.(true)?.bounds;
@@ -517,20 +572,27 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
const doc = this.props.document;
const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
const miniSize = this.returnMiniSize();
- doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => {
- doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
- doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
- return false;
- }), emptyFunction, emptyFunction);
- }
+ doc &&
+ setupMoveUpEvents(
+ this,
+ e,
+ action((e: PointerEvent, down: number[], delta: number[]) => {
+ doc._panX = clamp(NumCast(doc._panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
+ doc._panY = clamp(NumCast(doc._panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
+ return false;
+ }),
+ emptyFunction,
+ emptyFunction
+ );
+ };
render() {
- if (!this.renderBounds) return (null);
- const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
- const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100;
- const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2;
- const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2;
+ if (!this.renderBounds) return null;
+ const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
+ const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100;
+ const miniLeft = 50 + ((NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
+ const miniTop = 50 + ((NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
const miniSize = this.returnMiniSize();
- return this.props.hideMinimap() ? (null) :
+ return this.props.hideMinimap() ? null : (
<div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
<CollectionFreeFormView
Document={this.props.document}
@@ -567,9 +629,10 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
fitContentsToBox={returnTrue}
/>
- <div className="miniOverlay" onPointerDown={this.miniDown} >
- <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
+ <div className="miniOverlay" onPointerDown={this.miniDown}>
+ <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
</div>
- </div>;
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 704b8989a..5a2103e98 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,7 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
+import { observer } from 'mobx-react';
import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -12,26 +12,26 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager, DocFocusOrOpen } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
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, CollectionViewType } from './CollectionView';
-import "./TreeView.scss";
-import React = require("react");
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { FieldViewProps } from '../nodes/FieldView';
+import { CollectionView } from './CollectionView';
+import './TreeView.scss';
+import React = require('react');
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -55,7 +55,7 @@ export interface TreeViewProps {
indentDocument?: (editTitle: boolean) => void;
outdentDocument?: (editTitle: boolean) => void;
ScreenToLocalTransform: () => Transform;
- contextMenuItems: { script: ScriptField, filter: ScriptField, icon: string, label: string }[];
+ contextMenuItems: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
dontRegisterView?: boolean;
styleProvider?: StyleProviderFunc | undefined;
treeViewHideHeaderFields: () => boolean;
@@ -70,24 +70,26 @@ export interface TreeViewProps {
hierarchyIndex?: number[];
}
-const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); };
+const treeBulletWidth = function () {
+ return Number(TREE_BULLET_WIDTH.replace('px', ''));
+};
export enum TreeSort {
- Up = "up",
- Down = "down",
- Zindex = "z",
- None = "none"
+ Up = 'up',
+ Down = 'down',
+ Zindex = 'z',
+ None = 'none',
}
/**
* Renders a treeView of a collection of documents
- *
+ *
* special fields:
* treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
* treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
*/
@observer
export class TreeView extends React.Component<TreeViewProps> {
- static _editTitleOnLoad: Opt<{ id: string, parent: TreeView | CollectionTreeView | undefined }>;
+ 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();
@@ -98,7 +100,9 @@ export class TreeView extends React.Component<TreeViewProps> {
private _openScript: (() => ScriptField) | undefined;
private _treedropDisposer?: DragManager.DragDropDisposer;
- get treeViewOpenIsTransient() { return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); }
+ get treeViewOpenIsTransient() {
+ return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc);
+ }
set treeViewOpen(c: boolean) {
if (this.treeViewOpenIsTransient) this._transientOpenState = c;
else {
@@ -109,26 +113,61 @@ export class TreeView extends React.Component<TreeViewProps> {
@observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
@observable _editTitle: boolean = false;
@observable _dref: DocumentView | undefined | null;
- get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive
+ get displayName() {
+ return 'TreeView(' + this.props.document.title + ')';
+ } // this makes mobx trace() statements more descriptive
get defaultExpandedView() {
- return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
- this.props.treeView.dashboardMode ? this.fieldKey :
- this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying
- this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
- }
-
- @computed get doc() { return this.props.document; }
- @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; }
- @computed get treeViewExpandedView() { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; }
- @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
- @computed get dataDoc() { return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym]; }
- @computed get layoutDoc() { return Doc.Layout(this.doc); }
- @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
- @computed get childDocs() { return this.childDocList(this.fieldKey); }
- @computed get childLinks() { return this.childDocList("links"); }
- @computed get childAliases() { return this.childDocList("aliases"); }
- @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
- @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
+ return this.doc.viewType === CollectionViewType.Docking
+ ? this.fieldKey
+ : this.props.treeView.dashboardMode
+ ? this.fieldKey
+ : this.props.treeView.fileSysMode
+ ? this.doc.isFolder
+ ? this.fieldKey
+ : 'aliases' // for displaying
+ : this.props.treeView.outlineMode || this.childDocs
+ ? this.fieldKey
+ : Doc.noviceMode
+ ? 'layout'
+ : StrCast(this.props.treeView.doc.treeViewExpandedView, 'fields');
+ }
+
+ @computed get doc() {
+ return this.props.document;
+ }
+ @computed get treeViewOpen() {
+ return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeViewOpen', 'boolean', true)) || this._transientOpenState;
+ }
+ @computed get treeViewExpandedView() {
+ return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
+ }
+ @computed get MAX_EMBED_HEIGHT() {
+ return NumCast(this.props.containerCollection.maxEmbedHeight, 200);
+ }
+ @computed get dataDoc() {
+ return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym];
+ }
+ @computed get layoutDoc() {
+ return Doc.Layout(this.doc);
+ }
+ @computed get fieldKey() {
+ return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc));
+ }
+ @computed get childDocs() {
+ return this.childDocList(this.fieldKey);
+ }
+ @computed get childLinks() {
+ return this.childDocList('links');
+ }
+ @computed get childAliases() {
+ return this.childDocList('aliases');
+ }
+ @computed get childAnnos() {
+ return this.childDocList(this.fieldKey + '-annotations');
+ }
+ @computed get selected() {
+ return SelectionManager.IsSelected(this._docRef);
+ }
// SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
@observable _presTimer!: NodeJS.Timeout;
@@ -136,64 +175,76 @@ export class TreeView extends React.Component<TreeViewProps> {
@observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
// the selected item's index
- @computed get itemIndex() { return NumCast(this.doc._itemIndex); }
- // the item that's active
- @computed get activeItem() { return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined; }
- @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+ @computed get itemIndex() {
+ return NumCast(this.doc._itemIndex);
+ }
+ // the item that's active
+ @computed get activeItem() {
+ return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined;
+ }
+ @computed get targetDoc() {
+ return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+ }
childDocList(field: string) {
const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
- return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+ return (
+ (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
(layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
- DocListCastOrNull(this.doc[field]); // otherwise use the document's data field
+ DocListCastOrNull(this.doc[field])
+ ); // otherwise use the document's data field
}
@undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
- if (this.doc !== target && addDoc !== returnFalse) { // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
+ if (this.doc !== target && addDoc !== returnFalse) {
+ // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
if (this.props.removeDoc?.(doc) === true) {
return addDoc(doc);
}
}
return false;
- }
+ };
@undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
this.props.treeView.props.select(false);
const ind = this.dataDoc[key].indexOf(doc);
const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false);
return res;
- }
+ };
@action setEditTitle = (docView?: DocumentView) => {
this._selDisposer?.();
if (!docView) {
this._editTitle = false;
- }
- else if (docView.isSelected()) {
+ } else if (docView.isSelected()) {
const doc = docView.Document;
SelectionManager.SelectSchemaViewDoc(doc);
this._editTitle = true;
- this._selDisposer = reaction(() => SelectionManager.SelectedSchemaDoc(), seldoc => seldoc !== doc && this.setEditTitle(undefined));
+ this._selDisposer = reaction(
+ () => SelectionManager.SelectedSchemaDoc(),
+ seldoc => seldoc !== doc && this.setEditTitle(undefined)
+ );
} else {
docView.select(false);
}
- }
+ };
@action
openLevel = (docView: DocumentView) => {
if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
this.treeViewOpen = !this.treeViewOpen;
} else {
// choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias
- const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+ const bestAlias =
+ docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
- this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox");
+ this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), 'lightbox');
}
- }
+ };
constructor(props: any) {
super(props);
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" });
+ 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!;
@@ -202,16 +253,16 @@ 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);
+ 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);
- }
+ this.props.observeHeight((this._treeEle = ele));
+ };
componentWillUnmount() {
this._selDisposer?.();
this._treeEle && this.props.unobserveHeight(this._treeEle);
- document.removeEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointermove", this.onDragUp, true);
+ 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);
}
@@ -225,49 +276,60 @@ export class TreeView extends React.Component<TreeViewProps> {
}
onDragUp = (e: PointerEvent) => {
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.removeEventListener("pointermove", this.onDragMove, true);
- }
+ document.removeEventListener('pointerup', this.onDragUp, true);
+ 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._header.current!.className = "treeView-header";
- document.removeEventListener("pointermove", this.onDragMove, true);
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.addEventListener("pointermove", this.onDragMove, true);
- document.addEventListener("pointerup", this.onDragUp, true);
+ this._header.current!.className = 'treeView-header';
+ document.removeEventListener('pointermove', this.onDragMove, true);
+ document.removeEventListener('pointerup', this.onDragUp, true);
+ document.addEventListener('pointermove', this.onDragMove, true);
+ document.addEventListener('pointerup', this.onDragUp, true);
}
- }
+ };
onPointerLeave = (e: React.PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
- if (this._header.current?.className !== "treeView-header-editing") {
- this._header.current!.className = "treeView-header";
+ if (this._header.current?.className !== 'treeView-header-editing') {
+ this._header.current!.className = 'treeView-header';
}
- document.removeEventListener("pointerup", this.onDragUp, true);
- document.removeEventListener("pointermove", this.onDragMove, true);
- }
+ document.removeEventListener('pointerup', this.onDragUp, true);
+ document.removeEventListener('pointermove', this.onDragMove, true);
+ };
onDragMove = (e: PointerEvent): void => {
Doc.UnBrushDoc(this.dataDoc);
const pt = [e.clientX, e.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
- this._header.current!.className = "treeView-header";
+ const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.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();
- }
+ };
public static makeTextBullet() {
- const bullet = Docs.Create.TextDocument("-text-", {
- layout: CollectionView.LayoutString("data"),
- title: "-title-",
- treeViewExpandedViewLock: true, treeViewExpandedView: "data",
- _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: TreeViewType.outline,
- x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10
+ const bullet = Docs.Create.TextDocument('-text-', {
+ layout: CollectionView.LayoutString('data'),
+ title: '-title-',
+ treeViewExpandedViewLock: true,
+ treeViewExpandedView: 'data',
+ _viewType: CollectionViewType.Tree,
+ hideLinkButton: true,
+ _showSidebar: true,
+ treeViewType: TreeViewType.outline,
+ x: 0,
+ y: 0,
+ _xMargin: 0,
+ _yMargin: 0,
+ _autoHeight: true,
+ _singleLine: true,
+ _width: 1000,
+ _height: 10,
});
Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -279,19 +341,19 @@ export class TreeView extends React.Component<TreeViewProps> {
const bullet = TreeView.makeTextBullet();
TreeView._editTitleOnLoad = { id: bullet[Id], parent: this };
return this.props.addDocument(bullet);
- }
+ };
makeFolder = () => {
- const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+ const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
return this.props.addDocument(folder);
- }
+ };
deleteItem = () => this.props.removeDoc?.(this.doc);
preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
- dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
- }
+ dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
+ };
@undoBatch
treeDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -299,11 +361,11 @@ export class TreeView extends React.Component<TreeViewProps> {
if (!this._header.current) return;
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] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
const destDoc = this.doc;
- DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
+ DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', '');
e.stopPropagation();
}
const docDragData = de.complete.docDragData;
@@ -313,22 +375,16 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
}
}
- }
+ };
dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
- const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add")) || forceAdd;
- const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false;
- const addDoc = !inside ? parentAddDoc :
- (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
- const move = (!dropAction || dropAction === "proto" || dropAction === "move" || dropAction === "same") && moveDocument;
+ const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd;
+ const localAdd = (doc: Doc) => (Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false);
+ const addDoc = !inside ? parentAddDoc : (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
+ const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
if (canAdd) {
- return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) =>
- (move ?
- move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false)
- :
- addDoc(d)) || added,
- false));
+ return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
}
return false;
}
@@ -339,7 +395,7 @@ export class TreeView extends React.Component<TreeViewProps> {
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]);
- }
+ };
docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
getTransform = () => this.refTransform(this._tref.current);
docWidth = () => {
@@ -348,22 +404,25 @@ export class TreeView extends React.Component<TreeViewProps> {
if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
- }
+ };
docHeight = () => {
const layoutDoc = this.layoutDoc;
- return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
- const aspect = Doc.NativeAspect(layoutDoc);
- if (aspect) return this.docWidth() / (aspect || 1);
- return layoutDoc._fitWidth ?
- (!Doc.NativeHeight(this.doc) ?
- NumCast(this.props.containerCollection._height)
- :
- Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))
- ))
- :
- (layoutDoc[HeightSym]() || 50);
- })()));
- }
+ return Math.max(
+ 70,
+ Math.min(
+ this.MAX_EMBED_HEIGHT,
+ (() => {
+ const aspect = Doc.NativeAspect(layoutDoc);
+ if (aspect) return this.docWidth() / (aspect || 1);
+ return layoutDoc._fitWidth
+ ? !Doc.NativeHeight(this.doc)
+ ? NumCast(this.props.containerCollection._height)
+ : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
+ : layoutDoc[HeightSym]() || 50;
+ })()
+ )
+ );
+ };
@computed get expandedField() {
const ids: { [key: string]: string } = {};
@@ -372,11 +431,11 @@ export class TreeView extends React.Component<TreeViewProps> {
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 === "treeViewOpen") continue;
+ if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeViewOpen') continue;
const contents = doc[key];
let contentElement: (JSX.Element | null)[] | JSX.Element = [];
- if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof Doc))) {
+ 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 localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
@@ -384,74 +443,109 @@ export class TreeView extends React.Component<TreeViewProps> {
return added;
};
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
- contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
- this.props.treeView, this, doc, undefined, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
- this.props.panelWidth, 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, emptyFunction, emptyFunction, this.childContextMenuItems(),
+ contentElement = TreeView.GetChildElements(
+ contents instanceof Doc ? [contents] : DocListCast(contents),
+ this.props.treeView,
+ this,
+ doc,
+ undefined,
+ this.props.containerCollection,
+ this.props.prevSibling,
+ addDoc,
+ remDoc,
+ this.move,
+ this.props.dropAction,
+ this.props.addDocTab,
+ this.titleStyleProvider,
+ this.props.ScreenToLocalTransform,
+ this.props.isContentActive,
+ this.props.panelWidth,
+ 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,
+ emptyFunction,
+ emptyFunction,
+ this.childContextMenuItems(),
// TODO: [AL] Add these
this.props.AddToMap,
this.props.RemFromMap,
this.props.hierarchyIndex
);
} else {
- contentElement = <EditableView key="editableView"
- contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
- height={13}
- fontSize={12}
- GetValue={() => Field.toKeyValueString(doc, key)}
- SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
+ contentElement = (
+ <EditableView
+ key="editableView"
+ contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+ height={13}
+ fontSize={12}
+ GetValue={() => Field.toKeyValueString(doc, key)}
+ SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+ />
+ );
}
- rows.push(<div style={{ display: "flex" }} key={key}>
- <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
- &nbsp;
- {contentElement}
- </div>);
+ rows.push(
+ <div style={{ display: 'flex' }} key={key}>
+ <span style={{ fontWeight: 'bold' }}>{key + ':'}</span>
+ &nbsp;
+ {contentElement}
+ </div>
+ );
}
- rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
- <EditableView
- key="editableView"
- contents={"+key:value"}
- height={13}
- fontSize={12}
- GetValue={returnEmptyString}
- SetValue={value => value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} />
- </div>);
+ rows.push(
+ <div style={{ display: 'flex' }} key={'newKeyValue'}>
+ <EditableView
+ key="editableView"
+ contents={'+key:value'}
+ height={13}
+ fontSize={12}
+ GetValue={returnEmptyString}
+ SetValue={value => value.indexOf(':') !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(':')), value.substring(value.indexOf(':') + 1, value.length), true)}
+ />
+ </div>
+ );
return rows;
}
rtfWidth = () => {
- const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
- return Math.min(layout[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
- }
+ const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+ return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1);
+ };
rtfHeight = () => {
- const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+ const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
- }
+ };
rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
expandPanelHeight = () => {
if (this.layoutDoc._fitWidth) return this.docHeight();
const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
const docAspect = this.docWidth() / this.docHeight();
- return (docAspect < aspect) ? this.docWidth() / aspect : this.docHeight();
- }
+ return docAspect < aspect ? this.docWidth() / aspect : this.docHeight();
+ };
expandPanelWidth = () => {
if (this.layoutDoc._fitWidth) return this.docWidth();
const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
const docAspect = this.docWidth() / this.docHeight();
- return (docAspect > aspect) ? this.docHeight() * aspect : this.docWidth();
- }
+ return docAspect > aspect ? this.docHeight() * aspect : this.docWidth();
+ };
@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, label: string } };
- if (["links", "annotations", "aliases", this.fieldKey].includes(expandKey)) {
+ const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+ if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
const sortKeys = Object.keys(sortings);
- const curSortIndex = Math.max(0, sortKeys.findIndex(val => val === sorting));
- const key = (expandKey === "annotations" ? `${this.fieldKey}-` : "") + expandKey;
+ const curSortIndex = Math.max(
+ 0,
+ sortKeys.findIndex(val => val === sorting)
+ );
+ const key = (expandKey === 'annotations' ? `${this.fieldKey}-` : '') + expandKey;
const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
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),
@@ -461,107 +555,161 @@ export class TreeView extends React.Component<TreeViewProps> {
const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
docs.push(doc);
- docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1).forEach((d, i) => d.zIndex = i);
+ docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i));
}
const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
added && (doc.context = this.doc.context);
return added;
};
const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
- const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
- let downX = 0, downY = 0;
- return <>
- {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
- {sortings[sorting]?.label}
- </div>}
- <ul key={expandKey + "more"} title="click to change sort order" className={this.doc.treeViewHideTitle ? "no-indent" : ""}
- onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }}
- onClick={(e) => {
- if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
- !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+ const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
+ let downX = 0,
+ downY = 0;
+ return (
+ <>
+ {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
+ <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }}>
+ {sortings[sorting]?.label}
+ </div>
+ )}
+ <ul
+ key={expandKey + 'more'}
+ title="click to change sort order"
+ className={this.doc.treeViewHideTitle ? 'no-indent' : ''}
+ onPointerDown={e => {
+ downX = e.clientX;
+ downY = e.clientY;
e.stopPropagation();
- }
- }}>
- {!docs ? (null) :
- TreeView.GetChildElements(docs, this.props.treeView, this, this.layoutDoc,
- this.dataDoc, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
- StrCast(this.doc.childDropAction, this.props.dropAction) 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, false, this.props.whenChildContentsActiveChanged,
- this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems(),
- // TODO: [AL] add these
- this.props.AddToMap,
- this.props.RemFromMap,
- this.props.hierarchyIndex)}
- </ul >
- </>;
- } else if (this.treeViewExpandedView === "fields") {
- return <ul key={this.doc[Id] + this.doc.title}>
- <div style={{ display: "inline-block" }} >
- {this.expandedField}
- </div>
- </ul>;
+ }}
+ onClick={e => {
+ if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+ !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+ e.stopPropagation();
+ }
+ }}>
+ {!docs
+ ? null
+ : TreeView.GetChildElements(
+ docs,
+ this.props.treeView,
+ this,
+ this.layoutDoc,
+ this.dataDoc,
+ this.props.containerCollection,
+ this.props.prevSibling,
+ addDoc,
+ remDoc,
+ this.move,
+ StrCast(this.doc.childDropAction, this.props.dropAction) 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,
+ false,
+ this.props.whenChildContentsActiveChanged,
+ this.props.dontRegisterView,
+ emptyFunction,
+ emptyFunction,
+ this.childContextMenuItems(),
+ // TODO: [AL] add these
+ this.props.AddToMap,
+ this.props.RemFromMap,
+ this.props.hierarchyIndex
+ )}
+ </ul>
+ </>
+ );
+ } else if (this.treeViewExpandedView === 'fields') {
+ return (
+ <ul key={this.doc[Id] + this.doc.title}>
+ <div style={{ display: 'inline-block' }}>{this.expandedField}</div>
+ </ul>
+ );
}
- return <ul onPointerDown={e => { e.preventDefault(); e.stopPropagation(); }}>{this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}</ul>; // "layout"
+ return (
+ <ul
+ onPointerDown={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ }}>
+ {this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}
+ </ul>
+ ); // "layout"
}
- get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
+ get onCheckedClick() {
+ return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick);
+ }
@action
bulletClick = (e: React.MouseEvent) => {
if (this.onCheckedClick) {
- this.onCheckedClick?.script.run({
- this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
- heading: this.props.containerCollection.title,
- checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check",
- containingTreeView: this.props.treeView.props.Document,
- }, console.log);
+ this.onCheckedClick?.script.run(
+ {
+ this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
+ heading: this.props.containerCollection.title,
+ checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
+ containingTreeView: this.props.treeView.props.Document,
+ },
+ console.log
+ );
} else {
this.treeViewOpen = !this.treeViewOpen;
}
e.stopPropagation();
- }
+ };
@computed get renderBullet() {
TraceMobx();
- const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ":open" : "")) || "question";
- const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
- return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"}
- title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
- onClick={this.bulletClick}
- style={this.props.treeView.outlineMode ?
- {
- opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity)
- } :
- {
- pointerEvents: this.props.isContentActive() ? "all" : undefined,
- opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4,
- color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
- }}>
- {this.props.treeView.outlineMode ?
- !(this.doc.text as RichTextField)?.Text ? (null) :
- <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> :
- <div className="treeView-bulletIcons" >
- <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}>
- <FontAwesomeIcon size="sm" icon={
- checked === "check" ? "check" :
- checked === "x" ? "times" :
- checked === "unchecked" ? "square" :
- !this.treeViewOpen ? "caret-right" : "caret-down"} />
+ const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : '')) || 'question';
+ const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
+ return (
+ <div
+ className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
+ key={'bullet'}
+ title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : 'view fields'}
+ onClick={this.bulletClick}
+ style={
+ this.props.treeView.outlineMode
+ ? {
+ opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity),
+ }
+ : {
+ pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+ opacity: checked === 'unchecked' || typeof iconType !== 'string' ? undefined : 0.4,
+ color: StrCast(this.doc.color, checked === 'unchecked' ? 'white' : 'inherit'),
+ }
+ }>
+ {this.props.treeView.outlineMode ? (
+ !(this.doc.text as RichTextField)?.Text ? null : (
+ <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />
+ )
+ ) : (
+ <div className="treeView-bulletIcons">
+ <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
+ <FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />
+ </div>
+ {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
</div>
- {this.onCheckedClick ? (null) : typeof iconType === "string" ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
- </div>
- }
- </div>;
+ )}
+ </div>
+ );
}
@computed get validExpandViewTypes() {
- const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : "";
- const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
- const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
- const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
- const fields = () => Doc.noviceMode ? "" : "fields";
- const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+ const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
+ const links = () => (DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? 'links' : '');
+ const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
+ const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases');
+ const fields = () => (Doc.noviceMode ? '' : 'fields');
+ const layout = Doc.noviceMode || this.doc.viewType === CollectionViewType.Docking ? [] : ['layout'];
return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
}
@action
@@ -571,49 +719,68 @@ export class TreeView extends React.Component<TreeViewProps> {
this.doc.treeViewExpandedView = next(this.validExpandViewTypes);
}
this.treeViewOpen = true;
- }
+ };
@computed get headerElements() {
- return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? (null)
- : <>
- {this.doc.hideContextMenu ? (null) : <FontAwesomeIcon title="context menu" key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />}
- {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? (null) :
+ return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
+ <>
+ {this.doc.hideContextMenu ? null : (
+ <FontAwesomeIcon
+ title="context menu"
+ key="bars"
+ icon="bars"
+ size="sm"
+ onClick={e => {
+ this.showContextMenu(e);
+ e.stopPropagation();
+ }}
+ />
+ )}
+ {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
<span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
{this.treeViewExpandedView}
- </span>}
- </>;
+ </span>
+ )}
+ </>
+ );
}
showContextMenu = (e: React.MouseEvent) => {
DocumentViewInternal.SelectAfterContextMenu = false;
simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
DocumentViewInternal.SelectAfterContextMenu = true;
- }
+ };
contextMenuItems = () => {
- const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "New Folder" };
- const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: "any" })!, icon: "folder-plus", label: "Delete" };
+ const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
+ const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
const folderOp = this.childDocs?.length ? [makeFolder] : [];
- const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: "copy", label: "Open Alias" };
- const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: "eye", label: "Focus or Open" };
- return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? folderOp :
- Doc.IsSystem(this.doc) ? [] :
- this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
- [openAlias, makeFolder] :
- this.doc.viewType === CollectionViewType.Docking ? [] :
- [deleteItem, openAlias, focusDoc])];
- }
+ const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: 'copy', label: 'Open Alias' };
+ const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
+ return [
+ ...this.props.contextMenuItems.filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
+ ...(this.doc.isFolder
+ ? folderOp
+ : Doc.IsSystem(this.doc)
+ ? []
+ : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
+ ? [openAlias, makeFolder]
+ : this.doc.viewType === CollectionViewType.Docking
+ ? []
+ : [deleteItem, openAlias, 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 }));
- }
+ };
onChildClick = () => {
return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
- }
+ };
- onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
+ onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
ignoreEvent = (e: any) => {
@@ -621,51 +788,59 @@ export class TreeView extends React.Component<TreeViewProps> {
e.stopPropagation();
e.preventDefault();
}
- }
- titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+ };
+ 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
- switch (property.split(":")[0]) {
- case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
- case StyleProp.BackgroundColor: return this.selected ? "#7089bb" : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
- case StyleProp.DocContents: return this.props.treeView.outlineMode ? (null) :
- <div className="treeView-label" style={{ // 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),
- }}>
- {StrCast(doc?.title)}
- </div>;
- default: return this.props?.treeView?.props.styleProvider?.(doc, props, property);
+ switch (property.split(':')[0]) {
+ case StyleProp.Opacity:
+ return this.props.treeView.outlineMode ? undefined : 1;
+ case StyleProp.BackgroundColor:
+ return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+ case StyleProp.DocContents:
+ return this.props.treeView.outlineMode ? null : (
+ <div
+ className="treeView-label"
+ style={{
+ // 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),
+ }}>
+ {StrCast(doc?.title)}
+ </div>
+ );
+ default:
+ return this.props?.treeView?.props.styleProvider?.(doc, props, property);
}
- }
- embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
- if (property.startsWith(StyleProp.Decorations)) return (null);
+ };
+ embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, 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
- }
+ };
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
switch (e.key) {
- case "Tab":
+ 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":
+ case 'Backspace':
if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) {
e.stopPropagation?.();
e.preventDefault?.();
return true;
}
break;
- case "Enter":
+ case 'Enter':
e.stopPropagation?.();
e.preventDefault?.();
- return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+ return UndoManager.RunInBatch(this.makeTextCollection, 'bullet');
}
}
return false;
- }
+ };
titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
return18 = () => 18;
@@ -675,28 +850,32 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed
get renderTitle() {
TraceMobx();
- const view = this._editTitle ? <EditableView key="_editTitle"
- oneLine={true}
- display={"inline-block"}
- editing={this._editTitle}
- background={"#7089bb"}
- contents={StrCast(this.doc.title)}
- height={12}
- sizeToContent={true}
- fontSize={12}
- GetValue={() => StrCast(this.doc.title)}
- OnTab={undoBatch((shift?: boolean) => {
- 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()}
- SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
- Doc.SetInPlace(this.doc, "title", value, false);
- this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
- })}
- />
- : <DocumentView key="title"
+ const view = this._editTitle ? (
+ <EditableView
+ key="_editTitle"
+ oneLine={true}
+ display={'inline-block'}
+ editing={this._editTitle}
+ background={'#7089bb'}
+ contents={StrCast(this.doc.title)}
+ height={12}
+ sizeToContent={true}
+ fontSize={12}
+ GetValue={() => StrCast(this.doc.title)}
+ OnTab={undoBatch((shift?: boolean) => {
+ 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()}
+ SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+ Doc.SetInPlace(this.doc, 'title', value, false);
+ this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
+ })}
+ />
+ ) : (
+ <DocumentView
+ 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) {
@@ -743,139 +922,161 @@ export class TreeView extends React.Component<TreeViewProps> {
ContainingCollectionView={undefined}
ContainingCollectionDoc={this.props.treeView.props.Document}
ContentScaling={returnOne}
- />;
-
- const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""));
- return <>
- <div className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
- style={{
- fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
- textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
- outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
- pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined
- }} >
- {view}
- </div >
- <div className="treeView-rightButtons">
- {buttons} {/* hide and lock buttons */}
- {this.headerElements}
- </div>
- </>;
- }
+ />
+ );
- renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
- return <>
- <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
- style={{ width: StrCast(this.doc.treeViewHeaderWidth, "max-content") }}
- ref={this._header}
- onClick={this.ignoreEvent}
- onPointerDown={this.ignoreEvent}
- onPointerEnter={this.onPointerEnter}
- onPointerLeave={this.onPointerLeave}>
- {contents}
- </div>
- {this.renderBorder}
- </>;
+ const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ':afterHeader' : ''));
+ return (
+ <>
+ <div
+ className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? '-system' : ''}`}
+ ref={this._tref}
+ title="click to edit title. Double Click or Drag to Open"
+ style={{
+ 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() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+ }}>
+ {view}
+ </div>
+ <div className="treeView-rightButtons">
+ {buttons} {/* hide and lock buttons */}
+ {this.headerElements}
+ </div>
+ </>
+ );
}
+ renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
+ return (
+ <>
+ <div
+ className={`treeView-header` + (editing ? '-editing' : '')}
+ key="titleheader"
+ style={{ width: StrCast(this.doc.treeViewHeaderWidth, 'max-content') }}
+ ref={this._header}
+ onClick={this.ignoreEvent}
+ onPointerDown={this.ignoreEvent}
+ onPointerEnter={this.onPointerEnter}
+ onPointerLeave={this.onPointerLeave}>
+ {contents}
+ </div>
+ {this.renderBorder}
+ </>
+ );
+ };
renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
const isExpandable = this.doc._treeViewGrowsHorizontally;
const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
- return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)}
- Document={this.doc}
- DataDoc={undefined}
- PanelWidth={panelWidth}
- PanelHeight={panelHeight}
- NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
- NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
- LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
- LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
- isContentActive={isActive}
- isDocumentActive={isActive}
- styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
- hideTitle={asText}
- fitContentsToBox={returnTrue}
- hideDecorationTitle={this.props.treeView.outlineMode}
- hideResizeHandles={this.props.treeView.outlineMode}
- onClick={this.onChildClick}
- focus={this.refocus}
- ContentScaling={returnOne}
- onKey={this.onKeyDown}
- hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
- dontRegisterView={BoolCast(this.props.treeView.props.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}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containerCollection}
- ContainingCollectionView={undefined}
- addDocument={this.props.addDocument}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.treeView.props.pinToPres}
- disableDocBrushing={this.props.treeView.props.disableDocBrushing}
- bringToFront={returnFalse}
- />;
- }
+ return (
+ <DocumentView
+ key={this.doc[Id]}
+ ref={action((r: DocumentView | null) => (this._dref = r))}
+ Document={this.doc}
+ DataDoc={undefined}
+ PanelWidth={panelWidth}
+ PanelHeight={panelHeight}
+ NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
+ NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
+ LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
+ LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
+ isContentActive={isActive}
+ isDocumentActive={isActive}
+ styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ hideTitle={asText}
+ fitContentsToBox={returnTrue}
+ hideDecorationTitle={this.props.treeView.outlineMode}
+ hideResizeHandles={this.props.treeView.outlineMode}
+ onClick={this.onChildClick}
+ focus={this.refocus}
+ ContentScaling={returnOne}
+ onKey={this.onKeyDown}
+ hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.props.treeView.props.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}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containerCollection}
+ ContainingCollectionView={undefined}
+ addDocument={this.props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.treeView.props.pinToPres}
+ disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+ bringToFront={returnFalse}
+ />
+ );
+ };
// 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.renderBullet}
- {this.renderTitle}
- </>;
+ return (
+ <>
+ {this.renderBullet}
+ {this.renderTitle}
+ </>
+ );
}
// renders the document in the header field instead of a text proxy.
renderDocumentAsHeader = (asText: boolean) => {
- return <>
- {this.renderBullet}
- {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
- </>;
- }
+ return (
+ <>
+ {this.renderBullet}
+ {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
+ </>
+ );
+ };
@computed get renderBorder() {
const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
- const sortings = this.props.styleProvider?.(this.doc, this.props.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 }}>
- {!this.treeViewOpen ? (null) : this.renderContent}
- </div>;
+ const sortings = this.props.styleProvider?.(this.doc, this.props.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 }}>
+ {!this.treeViewOpen ? null : this.renderContent}
+ </div>
+ );
}
onTreeDrop = (de: React.DragEvent) => {
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] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-
- const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, "copy", undefined, false));
- }
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+ const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, false));
+ };
render() {
TraceMobx();
const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && 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
- <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
+ 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
+ ) : (
+ <div
+ 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
- // onKeyDown={this.onKeyDown}
+ //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.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
- this.renderEmbeddedDocument(false, returnFalse) :
- this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+ {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader // should test for prop 'treeViewRenderDocWithBulletAsHeader"
+ ? this.renderEmbeddedDocument(false, returnFalse)
+ : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
</li>
- </div>;
+ </div>
+ );
}
public static sortDocs(childDocs: Doc[], criterion: string | undefined) {
@@ -883,9 +1084,10 @@ export class TreeView extends React.Component<TreeViewProps> {
if (criterion !== TreeSort.None) {
const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
const reN = /[0-9]*$/;
- const aA = a.replace(reN, "") ? a.replace(reN, "") : +a; // get rid of trailing numbers
- const bA = b.replace(reN, "") ? b.replace(reN, "") : +b;
- if (aA === bA) { // if header string matches, then compare numbers numerically
+ const aA = a.replace(reN, '') ? a.replace(reN, '') : +a; // get rid of trailing numbers
+ const bA = b.replace(reN, '') ? b.replace(reN, '') : +b;
+ if (aA === bA) {
+ // if header string matches, then compare numbers numerically
const aN = parseInt(a.match(reN)![0], 10);
const bN = parseInt(b.match(reN)![0], 10);
return aN === bN ? 0 : aN > bN ? 1 : -1;
@@ -894,11 +1096,11 @@ export class TreeView extends React.Component<TreeViewProps> {
}
};
docs.sort(function (d1, d2): 0 | 1 | -1 {
- const a = (criterion === TreeSort.Up ? d2 : d1);
- const b = (criterion === TreeSort.Up ? d1 : d2);
- const first = a[criterion === TreeSort.Zindex ? "zIndex" : "title"];
- const second = b[criterion === TreeSort.Zindex ? "zIndex" : "title"];
- if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1;
+ const a = criterion === TreeSort.Up ? d2 : d1;
+ const b = criterion === TreeSort.Up ? d1 : d2;
+ const first = a[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+ const second = b[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+ if (typeof first === 'number' && typeof second === 'number') return first - second > 0 ? 1 : -1;
if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second);
return criterion ? 1 : -1;
});
@@ -934,11 +1136,11 @@ export class TreeView extends React.Component<TreeViewProps> {
dontRegisterView: boolean | undefined,
observerHeight: (ref: any) => void,
unobserveHeight: (ref: any) => void,
- contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[]),
+ contextMenuItems: { script: ScriptField; filter: ScriptField; label: string; icon: string }[],
// TODO: [AL] add these
AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
- hierarchyIndex?: number[],
+ hierarchyIndex?: number[]
) {
const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
@@ -948,68 +1150,83 @@ export class TreeView extends React.Component<TreeViewProps> {
const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None));
const rowWidth = () => panelWidth() - treeBulletWidth();
const treeViewRefs = new Map<Doc, TreeView | undefined>();
- return docs.filter(child => child instanceof Doc).map((child, i) => {
- const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
- if (!pair.layout || pair.data instanceof Promise) {
- return (null);
- }
-
- const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
- if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
- const fieldKey = Doc.LayoutFieldKey(newParent);
- if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
- remove(child);
- FormattedTextBox.SelectOnLoad = child[Id];
- TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
- Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
- newParent.treeViewOpen = true;
- child.context = treeView.Document;
+ return docs
+ .filter(child => child instanceof Doc)
+ .map((child, i) => {
+ const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
+ if (!pair.layout || pair.data instanceof Promise) {
+ return null;
}
- };
- const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
- const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined));
- const addDocument = (doc: Doc | Doc[], 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[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]());
- };
- return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
- document={pair.layout}
- dataDoc={pair.data}
- containerCollection={containerCollection}
- prevSibling={docs[i]}
- // TODO: [AL] add these
- hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
- AddToMap={AddToMap}
- RemFromMap={RemFromMap}
- treeView={treeView}
- indentDocument={indent}
- outdentDocument={outdent}
- onCheckedClick={onCheckedClick}
- onChildClick={onChildClick}
- renderDepth={renderDepth}
- removeDoc={StrCast(containerCollection.freezeChildren).includes("remove") ? undefined : remove}
- addDocument={addDocument}
- styleProvider={styleProvider}
- panelWidth={rowWidth}
- panelHeight={rowHeight}
- dontRegisterView={dontRegisterView}
- moveDocument={move}
- dropAction={dropAction}
- addDocTab={addDocTab}
- ScreenToLocalTransform={screenToLocalXf}
- isContentActive={isContentActive}
- treeViewHideHeaderFields={treeViewHideHeaderFields}
- renderedIds={renderedIds}
- skipFields={skipFields}
- firstLevel={firstLevel}
- whenChildContentsActiveChanged={whenChildContentsActiveChanged}
- parentTreeView={parentTreeView}
- observeHeight={observerHeight}
- unobserveHeight={unobserveHeight}
- contextMenuItems={contextMenuItems}
- />;
- });
+
+ const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
+ if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
+ const fieldKey = Doc.LayoutFieldKey(newParent);
+ if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
+ remove(child);
+ FormattedTextBox.SelectOnLoad = child[Id];
+ TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
+ Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
+ newParent.treeViewOpen = true;
+ child.context = treeView.Document;
+ }
+ };
+ const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
+ const outdent =
+ parentCollectionDoc?._viewType !== CollectionViewType.Tree
+ ? undefined
+ : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
+ const addDocument = (doc: Doc | Doc[], 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[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+ };
+ return (
+ <TreeView
+ key={child[Id]}
+ ref={r => treeViewRefs.set(child, r ? r : undefined)}
+ document={pair.layout}
+ dataDoc={pair.data}
+ containerCollection={containerCollection}
+ prevSibling={docs[i]}
+ // TODO: [AL] add these
+ hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
+ AddToMap={AddToMap}
+ RemFromMap={RemFromMap}
+ treeView={treeView}
+ indentDocument={indent}
+ outdentDocument={outdent}
+ onCheckedClick={onCheckedClick}
+ onChildClick={onChildClick}
+ renderDepth={renderDepth}
+ removeDoc={StrCast(containerCollection.freezeChildren).includes('remove') ? undefined : remove}
+ addDocument={addDocument}
+ styleProvider={styleProvider}
+ panelWidth={rowWidth}
+ panelHeight={rowHeight}
+ dontRegisterView={dontRegisterView}
+ moveDocument={move}
+ dropAction={dropAction}
+ addDocTab={addDocTab}
+ ScreenToLocalTransform={screenToLocalXf}
+ isContentActive={isContentActive}
+ treeViewHideHeaderFields={treeViewHideHeaderFields}
+ renderedIds={renderedIds}
+ skipFields={skipFields}
+ firstLevel={firstLevel}
+ whenChildContentsActiveChanged={whenChildContentsActiveChanged}
+ parentTreeView={parentTreeView}
+ observeHeight={observerHeight}
+ unobserveHeight={unobserveHeight}
+ contextMenuItems={contextMenuItems}
+ />
+ );
+ });
}
-} \ No newline at end of file
+}
+
+ScriptingGlobals.add(function TreeView_addNewFolder() {
+ TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
+ const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true };
+ return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
+});
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9de2cfcf9..a0ebe4cdc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,13 +1,12 @@
-import { Doc, Field, FieldResult, HeightSym, WidthSym } from "../../../../fields/Doc";
-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 { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import React = require("react");
-import { ColorScheme } from "../../../util/SettingsManager";
+import { Doc, Field, FieldResult, HeightSym, WidthSym } from '../../../../fields/Doc';
+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 { ColorScheme } from '../../../util/SettingsManager';
+import React = require('react');
export interface ViewDefBounds {
type: string;
@@ -25,7 +24,7 @@ export interface ViewDefBounds {
color?: string;
opacity?: number;
replica?: string;
- pair?: { layout: Doc, data?: Doc };
+ pair?: { layout: Doc; data?: Doc };
}
export interface PoolData {
@@ -40,7 +39,7 @@ export interface PoolData {
transition?: string;
highlight?: boolean;
replica: string;
- pair: { layout: Doc, data?: Doc };
+ pair: { layout: Doc; data?: Doc };
}
export interface ViewDefResult {
@@ -48,7 +47,7 @@ export interface ViewDefResult {
bounds?: ViewDefBounds;
}
function toLabel(target: FieldResult<Field>) {
- if (typeof target === "number" || Number(target)) {
+ if (typeof target === 'number' || Number(target)) {
const truncated = Number(Number(target).toFixed(0));
const precise = Number(Number(target).toFixed(2));
return truncated === precise ? Number(target).toFixed(0) : Number(target).toFixed(2);
@@ -60,16 +59,16 @@ function toLabel(target: FieldResult<Field>) {
}
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- *
+ *
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- *
+ *
* @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
function getTextWidth(text: string, font: string): number {
// re-use canvas object for better performance
- const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
- const context = canvas.getContext("2d");
+ const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas'));
+ const context = canvas.getContext('2d');
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
@@ -81,14 +80,7 @@ interface PivotColumn {
filters: string[];
}
-export function computerPassLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
childPairs.forEach(({ layout, data }, i) => {
docMap.set(layout[Id], {
@@ -97,60 +89,49 @@ export function computerPassLayout(
width: layout[WidthSym](),
height: layout[HeightSym](),
pair: { layout, data },
- replica: ""
+ replica: '',
});
});
return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
}
-export function computerStarburstLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computerStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
const docMap = new Map<string, PoolData>();
- const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+ const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
childPairs.forEach(({ layout, data }, i) => {
- const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
- const deg = i / childPairs.length * Math.PI * 2;
+ const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75
+ const deg = (i / childPairs.length) * Math.PI * 2;
docMap.set(layout[Id], {
x: Math.cos(deg) * burstRadius[0] - docSize / 2,
- y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
- width: docSize,//layout[WidthSym](),
- height: docSize * layout[HeightSym]() / layout[WidthSym](),
+ y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2,
+ width: docSize, //layout[WidthSym](),
+ height: (docSize * layout[HeightSym]()) / layout[WidthSym](),
zIndex: NumCast(layout.zIndex),
pair: { layout, data },
- replica: ""
+ replica: '',
});
});
- const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
+ const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
}
-
-export function computePivotLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps: any
-) {
+export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
const docMap = new Map<string, PoolData>();
- const fieldKey = "data";
+ const fieldKey = 'data';
const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
let nonNumbers = 0;
- const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || "author";
+ const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
childPairs.map(pair => {
- const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) :
- Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+ const lval =
+ pivotFieldKey === '#' || pivotFieldKey === 'tags'
+ ? Array.from(Object.keys(Doc.GetProto(pair.layout)))
+ .filter(k => k.startsWith('#'))
+ .map(k => k.substring(1))
+ : Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
const num = toNumber(pair.layout[pivotFieldKey]);
if (num === undefined || Number.isNaN(num)) {
@@ -166,7 +147,7 @@ export function computePivotLayout(
} else if (val) {
!pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
pivotColumnGroups.get(val)!.docs.push(pair.layout);
- pivotColumnGroups.get(val)!.replicas.push("");
+ pivotColumnGroups.get(val)!.replicas.push('');
} else {
docMap.set(pair.layout[Id], {
x: 0,
@@ -175,11 +156,11 @@ export function computePivotLayout(
width: 0,
height: 0,
pair,
- replica: ""
+ replica: '',
});
}
});
- const pivotNumbers = nonNumbers / childPairs.length < .1;
+ const pivotNumbers = nonNumbers / childPairs.length < 0.1;
if (pivotColumnGroups.size > 10) {
const arrayofKeys = Array.from(pivotColumnGroups.keys());
const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
@@ -196,9 +177,11 @@ export function computePivotLayout(
}
}
}
- const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+ const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`;
- const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number);
+ const textlen = Array.from(pivotColumnGroups.keys())
+ .map(c => getTextWidth(toLabel(c), desc))
+ .reduce((p, c) => Math.max(p, c), 0 as number);
const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
@@ -222,7 +205,7 @@ export function computePivotLayout(
const groupNames: ViewDefBounds[] = [];
const expander = 1.05;
- const gap = .15;
+ const gap = 0.15;
const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
let x = 0;
const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
@@ -232,14 +215,14 @@ export function computePivotLayout(
let xCount = 0;
const text = toLabel(key);
groupNames.push({
- type: "text",
+ type: 'text',
text,
x,
y: pivotAxisWidth,
width: pivotAxisWidth * expander * numCols,
height: max_text,
fontSize,
- payload: val
+ payload: val,
});
val.docs.forEach((doc, i) => {
const layoutDoc = Doc.Layout(doc);
@@ -249,13 +232,13 @@ export function computePivotLayout(
hgt = pivotAxisWidth;
wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
}
- docMap.set(doc[Id] + (val.replicas || ""), {
- x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
+ docMap.set(doc[Id] + (val.replicas || ''), {
+ x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? ((numCols - val.docs.length) * pivotAxisWidth) / 2 : 0),
y: -y + (pivotAxisWidth - hgt) / 2,
width: wid,
height: hgt,
pair: { layout: doc },
- replica: val.replicas[i]
+ replica: val.replicas[i],
});
xCount++;
if (xCount >= numCols) {
@@ -266,13 +249,14 @@ export function computePivotLayout(
x += pivotAxisWidth * (numCols * expander + gap);
});
- const dividers = sortedPivotKeys.map((key, i) =>
- ({
- type: "div", color: "lightGray",
- x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
- y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+ const dividers = sortedPivotKeys.map((key, i) => ({
+ type: 'div',
+ color: 'lightGray',
+ x: i * pivotAxisWidth * (numCols * expander + gap) - (pivotAxisWidth * (expander - 1)) / 2,
+ y: -maxColHeight + pivotAxisWidth,
+ width: pivotAxisWidth * numCols * expander,
height: maxColHeight,
- payload: pivotColumnGroups.get(key)!.filters
+ payload: pivotColumnGroups.get(key)!.filters,
}));
groupNames.push(...dividers);
return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
@@ -282,24 +266,17 @@ function toNumber(val: FieldResult<Field>) {
return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
}
-export function computeTimelineLayout(
- poolData: Map<string, PoolData>,
- pivotDoc: Doc,
- childPairs: { layout: Doc, data?: Doc }[],
- panelDim: number[],
- viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
- engineProps?: any
-) {
- const fieldKey = "data";
+export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps?: any) {
+ const fieldKey = 'data';
const pivotDateGroups = new Map<number, Doc[]>();
const docMap = new Map<string, PoolData>();
const groupNames: ViewDefBounds[] = [];
const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
- const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
- const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null);
- const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTime && (curTime - curTimeSpan);
- const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTime && (curTime + curTimeSpan);
- const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+ const curTime = toNumber(pivotDoc[fieldKey + '-timelineCur']);
+ const curTimeSpan = Cast(pivotDoc[fieldKey + '-timelineSpan'], 'number', null);
+ const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMinReq'], 'number', null) : curTime && curTime - curTimeSpan;
+ const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMaxReq'], 'number', null) : curTime && curTime + curTimeSpan;
+ const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
const fontHeight = panelDim[1] > 58 ? 30 : panelDim[1] / 2;
const findStack = (time: number, stack: number[]) => {
const index = stack.findIndex(val => val === undefined || val < x);
@@ -325,8 +302,8 @@ export function computeTimelineLayout(
}
}
setTimeout(() => {
- pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
- pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
+ pivotDoc[fieldKey + '-timelineMin'] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
+ pivotDoc[fieldKey + '-timelineMax'] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
}, 0);
if (maxTime === minTime) {
@@ -340,10 +317,10 @@ export function computeTimelineLayout(
let prevKey = Math.floor(minTime);
if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
- groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
}
if (!sortedKeys.length && curTime !== undefined) {
- groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: 'orange', y: 0, height: fontHeight, fontSize, payload: undefined });
}
const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -351,26 +328,26 @@ export function computeTimelineLayout(
let zind = 0;
sortedKeys.forEach(key => {
if (curTime !== undefined && curTime > prevKey && curTime <= key) {
- groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+ groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: key });
}
const keyDocs = pivotDateGroups.get(key)!;
x += scaling * (key - prevKey);
const stack = findStack(x, stacking);
prevKey = key;
if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
- groupNames.push({ type: "text", text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
}
layoutDocsAtTime(keyDocs, key);
});
if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
x = (curTime - minTime) * scaling;
- groupNames.push({ type: "text", text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: undefined });
}
if (Math.ceil(maxTime - minTime) * scaling > x + 25) {
- groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
+ groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -384,13 +361,14 @@ export function computeTimelineLayout(
wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
}
docMap.set(doc[Id], {
- x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
- zIndex: (curTime === key ? 1000 : zind++),
+ x: x,
+ y: (-Math.sqrt(stack) * pivotAxisWidth) / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+ zIndex: curTime === key ? 1000 : zind++,
highlight: curTime === key,
- width: wid / (Math.max(stack, 1)),
- height: hgt / (Math.max(stack, 1)),
+ width: wid / Math.max(stack, 1),
+ height: hgt / Math.max(stack, 1),
pair: { layout: doc },
- replica: ""
+ replica: '',
});
stacking[stack] = x + pivotAxisWidth;
});
@@ -407,41 +385,49 @@ function normalizeResults(
minWidth: number,
extras: ViewDefBounds[]
): ViewDefResult[] {
- const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
+ const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height } as ViewDefBounds));
const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
- const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
+ const aggBounds = aggregateBounds(
+ extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: 'doc', payload: '' })))).filter(e => e.zIndex !== -99),
+ 0,
+ 0
+ );
aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
- let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
+ let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale;
if (Number.isNaN(scale)) scale = 1;
- Array.from(docMap.entries()).filter(ele => ele[1].pair).map(ele => {
- const newPosRaw = ele[1];
- if (newPosRaw) {
- const newPos = {
- x: newPosRaw.x * scale,
- y: newPosRaw.y * scale,
- z: newPosRaw.z,
- replica: newPosRaw.replica,
- highlight: newPosRaw.highlight,
- zIndex: newPosRaw.zIndex,
- width: (newPosRaw.width || 0) * scale,
- height: newPosRaw.height! * scale,
- pair: ele[1].pair
- };
- poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "all 1s", ...newPos });
- }
- });
+ Array.from(docMap.entries())
+ .filter(ele => ele[1].pair)
+ .map(ele => {
+ const newPosRaw = ele[1];
+ if (newPosRaw) {
+ const newPos = {
+ x: newPosRaw.x * scale,
+ y: newPosRaw.y * scale,
+ z: newPosRaw.z,
+ replica: newPosRaw.replica,
+ highlight: newPosRaw.highlight,
+ zIndex: newPosRaw.zIndex,
+ width: (newPosRaw.width || 0) * scale,
+ height: newPosRaw.height! * scale,
+ pair: ele[1].pair,
+ };
+ poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
+ }
+ });
- return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
- type: gname.type,
- text: gname.text,
- x: gname.x * scale,
- y: gname.y * scale,
- color: gname.color,
- width: gname.width === undefined ? undefined : gname.width * scale,
- height: gname.height === -1 ? 1 : gname.type === "text" ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
- fontSize: gname.fontSize,
- payload: gname.payload
- })));
+ return viewDefsToJSX(
+ extras.concat(groupNames).map(gname => ({
+ type: gname.type,
+ text: gname.text,
+ x: gname.x * scale,
+ y: gname.y * scale,
+ color: gname.color,
+ width: gname.width === undefined ? undefined : gname.width * scale,
+ height: gname.height === -1 ? 1 : gname.type === 'text' ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
+ fontSize: gname.fontSize,
+ payload: gname.payload,
+ }))
+ );
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 5f890c810..d979ef961 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,19 +1,18 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Field } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, NumCast } from '../../../../fields/Types';
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { LinkManager } from "../../../util/LinkManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { DocumentView } from "../../nodes/DocumentView";
-import "./CollectionFreeFormLinkView.scss";
-import React = require("react");
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { Colors } from "../../global/globalEnums";
-
+import { LinkManager } from '../../../util/LinkManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
+import './CollectionFreeFormLinkView.scss';
+import React = require('react');
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
@@ -27,31 +26,41 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
@observable _start = 0;
_anchorDisposer: IReactionDisposer | undefined;
_timeout: NodeJS.Timeout | undefined;
- componentWillUnmount() { this._anchorDisposer?.(); }
- @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25)));
+ componentWillUnmount() {
+ this._anchorDisposer?.();
+ }
+ @action timeout = 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)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
- this.props.B.props.ScreenToLocalTransform(),
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
- Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
- ],
+ this._anchorDisposer = reaction(
+ () => [
+ this.props.A.props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
+ this.props.B.props.ScreenToLocalTransform(),
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+ Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
+ ],
action(() => {
this._start = Date.now();
this._timeout && clearTimeout(this._timeout);
this._timeout = setTimeout(this.timeout, 25);
setTimeout(this.placeAnchors);
- })
- , { fireImmediately: true });
+ }),
+ { fireImmediately: true }
+ );
}
placeAnchors = () => {
const { A, B, LinkDocs } = this.props;
const linkDoc = LinkDocs[0];
if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
- setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
- setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
+ setTimeout(
+ action(() => (this._opacity = 0.75)),
+ 0
+ ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+ setTimeout(
+ action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+ 750
+ ); // this will unhighlight the link line.
const a = A.ContentDiv.getBoundingClientRect();
const b = B.ContentDiv.getBoundingClientRect();
const { left: aleft, top: atop, width: awidth, height: aheight } = A.ContentDiv.parentElement!.getBoundingClientRect();
@@ -60,7 +69,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
// really hacky stuff to make the LinkAnchorBox display where we want it to:
- // if there's an element in the DOM with a classname containing a link anchor's id,
+ // if there's an element in the DOM with a classname containing a link anchor's id,
// then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
// otherwise, we just use the computed nearest point on the document boundary to the target Document
const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
@@ -68,8 +77,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
if (!targetAhyperlink) {
if (linkDoc.linkAutoMove) {
- linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100;
- linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100;
+ linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
+ linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
}
} else {
const m = targetAhyperlink.getBoundingClientRect();
@@ -78,13 +87,13 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const mpy = mp[1] / A.props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
- if (getComputedStyle(targetAhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+ if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
else linkDoc.opacity = 1;
}
if (!targetBhyperlink) {
if (linkDoc.linkAutoMove) {
- linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100;
- linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100;
+ linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+ linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
}
} else {
const m = targetBhyperlink.getBoundingClientRect();
@@ -93,80 +102,86 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const mpy = mp[1] / B.props.PanelHeight();
if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
- if (getComputedStyle(targetBhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+ if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
else linkDoc.opacity = 1;
}
- }
-
+ };
pointerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, (e, down, delta) => {
- this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
- this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
- return false;
- }, emptyFunction, () => {
- // OverlayView.Instance.addElement(
- // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
- // showLinks={action(() => { })}
- // />, { x: 300, y: 300 });
- });
-
-
- }
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
+ this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+ return false;
+ },
+ emptyFunction,
+ () => {
+ // OverlayView.Instance.addElement(
+ // <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+ // showLinks={action(() => { })}
+ // />, { x: 300, y: 300 });
+ }
+ );
+ };
visibleY = (el: any) => {
let rect = el.getBoundingClientRect();
- const top = rect.top, height = rect.height;
+ const top = rect.top,
+ height = rect.height;
var el = el.parentNode;
while (el && el !== document.body) {
rect = el.getBoundingClientRect?.();
if (rect?.width) {
- if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom;
+ if (top <= rect.bottom === false && getComputedStyle(el).overflow === 'hidden') return rect.bottom;
// Check if the element is out of view due to a container scrolling
- if ((top + height) <= rect.top && getComputedStyle(el).overflow === "hidden") return rect.top;
+ if (top + height <= rect.top && getComputedStyle(el).overflow === 'hidden') return rect.top;
}
el = el.parentNode;
}
// Check its within the document viewport
return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- }
+ };
visibleX = (el: any) => {
let rect = el.getBoundingClientRect();
- const left = rect.left, width = rect.width;
+ const left = rect.left,
+ width = rect.width;
var el = el.parentNode;
while (el && el !== document.body) {
rect = el?.getBoundingClientRect();
if (rect?.width) {
- if (left <= rect.right === false && getComputedStyle(el).overflow === "hidden") return rect.right;
+ if (left <= rect.right === false && getComputedStyle(el).overflow === 'hidden') return rect.right;
// Check if the element is out of view due to a container scrolling
- if ((left + width) <= rect.left && getComputedStyle(el).overflow === "hidden") return rect.left;
+ if (left + width <= rect.left && getComputedStyle(el).overflow === 'hidden') return rect.left;
}
el = el.parentNode;
}
// Check its within the document viewport
return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
- }
+ };
@action
toggleProperties = () => {
- if (CurrentUserUtils.propertiesWidth > 0) {
- CurrentUserUtils.propertiesWidth = 0;
+ if (SettingsManager.propertiesWidth > 0) {
+ SettingsManager.propertiesWidth = 0;
} else {
- CurrentUserUtils.propertiesWidth = 250;
+ SettingsManager.propertiesWidth = 250;
}
- }
+ };
onClickLine = () => {
SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
this.toggleProperties();
- }
+ };
@computed.struct get renderData() {
- this._start; SnappingManager.GetIsDragging();
+ this._start;
+ SnappingManager.GetIsDragging();
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");
+ const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+ const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
const adiv = acont.length ? acont[0] : A.ContentDiv;
const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
@@ -185,11 +200,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2 = [bleft + b.width / 2, btop + b.width / 2];
const pt1vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt1[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt1[1]];
const pt2vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt2[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt2[1]];
- const pt1len = Math.sqrt((pt1vec[0] * pt1vec[0]) + (pt1vec[1] * pt1vec[1]));
- const pt2len = Math.sqrt((pt2vec[0] * pt2vec[0]) + (pt2vec[1] * pt2vec[1]));
+ const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
+ const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
- const pt1norm = clipped ? [0, 0] : [pt1vec[0] / pt1len * ptlen, pt1vec[1] / pt1len * ptlen];
- const pt2norm = clipped ? [0, 0] : [pt2vec[0] / pt2len * ptlen, pt2vec[1] / pt2len * ptlen];
+ const pt1norm = clipped ? [0, 0] : [(pt1vec[0] / pt1len) * ptlen, (pt1vec[1] / pt1len) * ptlen];
+ const pt2norm = clipped ? [0, 0] : [(pt2vec[0] / pt2len) * ptlen, (pt2vec[1] / pt2len) * ptlen];
const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
@@ -203,7 +218,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
}
render() {
- if (!this.renderData) return (null);
+ if (!this.renderData) return null;
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
LinkManager.currentLink = this.props.LinkDocs[0];
@@ -216,31 +231,37 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
//access stroke color using index of the relationship in the color list (default black)
- const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex];
+ const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
// const hexStroke = this.rgbToHex(stroke)
//calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
//thickness varies linearly from 3px to 12px for increasing link count
- const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px";
+ const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
if (this.props.LinkDocs[0].displayArrow === undefined) {
this.props.LinkDocs[0].displayArrow = false;
}
- return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
- <defs>
- <marker id="arrowhead" markerWidth="4" markerHeight="3"
- refX="0" refY="1.5" orient="auto">
- <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
- </marker>
- </defs>
- <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
- onClick={this.onClickLine}
- d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
- markerEnd={this.props.LinkDocs[0].displayArrow ? "url(#arrowhead)" : ""} />
- {textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
- {Field.toString(this.props.LinkDocs[0].description as any as Field)}
- </text>}
- </>);
+ return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+ <>
+ <defs>
+ <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+ <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+ </marker>
+ </defs>
+ <path
+ className="collectionfreeformlinkview-linkLine"
+ style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+ onClick={this.onClickLine}
+ d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
+ markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+ />
+ {textX === undefined ? null : (
+ <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+ {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+ </text>
+ )}
+ </>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index dacbb3508..8720c9097 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,25 +1,23 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { Id } from "../../../../fields/FieldSymbols";
-import { DocumentManager } from "../../../util/DocumentManager";
-import "./CollectionFreeFormLinksView.scss";
-import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
-import React = require("react");
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Id } from '../../../../fields/FieldSymbols';
+import { DocumentManager } from '../../../util/DocumentManager';
+import './CollectionFreeFormLinksView.scss';
+import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
+import React = require('react');
@observer
-export class CollectionFreeFormLinksView extends React.Component {
+export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
@computed get uniqueConnections() {
- return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
- <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
- );
+ return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c => <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />);
}
render() {
- return <div className="collectionfreeformlinksview-container">
- <svg className="collectionfreeformlinksview-svgCanvas">
- {this.uniqueConnections}
- </svg>
- {this.props.children}
- </div>;
+ return (
+ <div className="collectionfreeformlinksview-container">
+ <svg className="collectionfreeformlinksview-svgCanvas">{this.uniqueConnections}</svg>
+ {this.props.children}
+ </div>
+ );
}
-} \ 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 9f6938e67..9e8d92d7d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,79 +1,82 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../fields/CursorField";
-import { FieldResult } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast } from "../../../../fields/Types";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { CollectionViewProps } from "../CollectionView";
-import "./CollectionFreeFormView.scss";
-import React = require("react");
-import v5 = require("uuid/v5");
+import CursorField from '../../../../fields/CursorField';
+import { Doc, FieldResult } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
+import { CollectionViewProps } from '../CollectionView';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+import v5 = require('uuid/v5');
@observer
export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
-
@computed protected get cursors(): CursorField[] {
const doc = this.props.Document;
let cursors: FieldResult<List<CursorField>>;
- const { id } = CurrentUserUtils;
+ const id = Doc.UserDoc()[Id];
if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
return [];
}
const now = mobxUtils.now();
- return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && (now - metadata.timestamp) < 1000);
+ return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000);
}
@computed get renderedCursors() {
- return this.cursors.map(({ data: { metadata, position: { x, y } } }) => {
- return (
- <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont"
- style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}
- >
- <canvas className="collectionFreeFormRemoteCursors-canvas"
- ref={(el) => {
- if (el) {
- const ctx = el.getContext('2d');
- if (ctx) {
- ctx.fillStyle = "#" + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + "22";
- ctx.fillRect(0, 0, 20, 20);
+ return this.cursors.map(
+ ({
+ data: {
+ metadata,
+ position: { x, y },
+ },
+ }) => {
+ return (
+ <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
+ <canvas
+ className="collectionFreeFormRemoteCursors-canvas"
+ ref={el => {
+ if (el) {
+ const ctx = el.getContext('2d');
+ if (ctx) {
+ ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+ ctx.fillRect(0, 0, 20, 20);
- ctx.fillStyle = "black";
- ctx.lineWidth = 0.5;
+ ctx.fillStyle = 'black';
+ ctx.lineWidth = 0.5;
- ctx.beginPath();
+ ctx.beginPath();
- ctx.moveTo(10, 0);
- ctx.lineTo(10, 8);
+ ctx.moveTo(10, 0);
+ ctx.lineTo(10, 8);
- ctx.moveTo(10, 20);
- ctx.lineTo(10, 12);
+ ctx.moveTo(10, 20);
+ ctx.lineTo(10, 12);
- ctx.moveTo(0, 10);
- ctx.lineTo(8, 10);
+ ctx.moveTo(0, 10);
+ ctx.lineTo(8, 10);
- ctx.moveTo(20, 10);
- ctx.lineTo(12, 10);
+ ctx.moveTo(20, 10);
+ ctx.lineTo(12, 10);
- ctx.stroke();
+ ctx.stroke();
+ }
}
- }
- }}
- width={20}
- height={20}
- />
- <p className="collectionFreeFormRemoteCursors-symbol">
- {metadata.identifier[0].toUpperCase()}
- </p>
- </div>
- );
- });
+ }}
+ width={20}
+ height={20}
+ />
+ <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
+ </div>
+ );
+ }
+ );
}
render() {
return this.renderedCursors;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 13cccb7dd..4174661d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,65 +1,60 @@
-import { Bezier } from "bezier-js";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { DateField } from "../../../../fields/DateField";
-import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { ObjectField } from "../../../../fields/ObjectField";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { listSpec } from "../../../../fields/Schema";
-import { ScriptField } from "../../../../fields/ScriptField";
-import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { TraceMobx } from "../../../../fields/util";
-import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
-import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { DocServer } from "../../../DocServer";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager, dropActionType } from "../../../util/DragManager";
-import { HistoryUtil } from "../../../util/History";
-import { InteractionUtils } from "../../../util/InteractionUtils";
-import { LinkManager } from "../../../util/LinkManager";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
-import { SearchUtil } from "../../../util/SearchUtil";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { ColorScheme } from "../../../util/SettingsManager";
-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, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from "../../InkingStroke";
-import { LightboxView } from "../../LightboxView";
-import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
-import { FieldViewProps } from "../../nodes/FieldView";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PresBox } from "../../nodes/trails/PresBox";
-import { VideoBox } from "../../nodes/VideoBox";
-import { CreateImage } from "../../nodes/WebBoxRenderer";
-import { StyleProp } from "../../StyleProvider";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { CollectionSubView } from "../CollectionSubView";
-import { TreeViewType } from "../CollectionTreeView";
-import { CollectionViewType } from "../CollectionView";
-import { TabDocView } from "../TabDocView";
-import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
-import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
-import "./CollectionFreeFormView.scss";
-import { MarqueeView } from "./MarqueeView";
-import React = require("react");
-import e = require("connect-flash");
-import { ReplayMovements } from "../../../util/ReplayMovements";
-
+import { Bezier } from 'bezier-js';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { DateField } from '../../../../fields/DateField';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { ObjectField } from '../../../../fields/ObjectField';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { listSpec } from '../../../../fields/Schema';
+import { ScriptField } from '../../../../fields/ScriptField';
+import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { TraceMobx } from '../../../../fields/util';
+import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
+import { aggregateBounds, emptyFunction, intersectRect, returnFalse, 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 { HistoryUtil } from '../../../util/History';
+import { InteractionUtils } from '../../../util/InteractionUtils';
+import { RecordingApi } from '../../../util/RecordingApi';
+import { ReplayMovements } from '../../../util/ReplayMovements';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { ColorScheme } from '../../../util/SettingsManager';
+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, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { LightboxView } from '../../LightboxView';
+import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { CreateImage } from '../../nodes/WebBoxRenderer';
+import { StyleProp } from '../../StyleProvider';
+import { CollectionDockingView } from '../CollectionDockingView';
+import { CollectionSubView } from '../CollectionSubView';
+import { TreeViewType } from '../CollectionTreeView';
+import { TabDocView } from '../TabDocView';
+import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
+import './CollectionFreeFormView.scss';
+import { MarqueeView } from './MarqueeView';
+import React = require('react');
+import e = require('connect-flash');
export type collectionFreeformViewProps = {
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -69,13 +64,15 @@ export type collectionFreeformViewProps = {
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
dontScaleFilter?: (doc: Doc) => boolean; // whether this collection should scale documents to fit their panel vs just scrolling them
- 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.
+ 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() + ")"; } // this makes mobx trace() statements more descriptive
+ public get displayName() {
+ return 'CollectionFreeFormView(' + this.props.Document.title?.toString() + ')';
+ } // this makes mobx trace() statements more descriptive
private _lastNudge: any;
private _lastX: number = 0;
@@ -90,27 +87,33 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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 _layoutSizeData = observable.map<string, { width?: number; height?: number }>();
private _cachedPool: Map<string, PoolData> = new Map();
private _lastTap = 0;
private _batch: UndoManager.Batch | undefined = undefined;
// private isWritingMode: boolean = true;
- // private writingModeDocs: Doc[] = [];
+ // private writingModeDocs: Doc[] = [];
- private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
- private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
- private get borderWidth() { return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH; }
+ private get isAnnotationOverlay() {
+ return this.props.isAnnotationOverlay;
+ }
+ private get scaleFieldKey() {
+ return this.props.scaleField || '_viewScale';
+ }
+ private get borderWidth() {
+ return this.isAnnotationOverlay ? 0 : COLLECTION_BORDER_WIDTH;
+ }
@observable.shallow _layoutElements: ViewDefResult[] = []; // shallow because some layout items (eg pivot labels) are just generated 'divs' and can't be frozen as observables
- @observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
+ @observable _viewTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _hLines: number[] | undefined;
@observable _vLines: number[] | undefined;
@observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
@observable _pullCoords: number[] = [0, 0];
- @observable _pullDirection: string = "";
+ @observable _pullDirection: string = '';
@observable _showAnimTimeline = false;
- @observable _clusterSets: (Doc[])[] = [];
+ @observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeRef = React.createRef<HTMLDivElement>();
@@ -118,43 +121,57 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _keyframeEditing = false;
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
+ @computed get views() {
+ return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele);
+ }
@computed get fitToContentVals() {
return {
bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
- scale: !this.childDocs.length ? 1 :
- Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y),
- this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
+ scale: !this.childDocs.length ? 1 : Math.min(this.props.PanelHeight() / (this.contentBounds.b - this.contentBounds.y), this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x)),
};
}
- @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
- @computed get contentBounds() {
- const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
- return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} :
- this.props.contentBounds?.() ?? aggregateBounds(this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!), NumCast(this.layoutDoc._xPadding, 10), NumCast(this.layoutDoc._yPadding, 10));
+ @computed get fitContentsToBox() {
+ return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay;
+ }
+ @computed get contentBounds() {
+ const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
+ return cb
+ ? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
+ : this.props.contentBounds?.() ??
+ aggregateBounds(
+ this._layoutElements.filter(e => e.bounds && !e.bounds.z).map(e => e.bounds!),
+ NumCast(this.layoutDoc._xPadding, 10),
+ NumCast(this.layoutDoc._yPadding, 10)
+ );
+ }
+ @computed get nativeWidth() {
+ return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
+ }
+ @computed get nativeHeight() {
+ return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null));
}
- @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
- @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
@computed get cachedCenteringShiftX(): number {
const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
- return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
- return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
+ return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 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());
+ return Transform.Identity()
+ .scale(1 / this.zoomScaling())
+ .translate(this.panX(), this.panY());
}
@computed get cachedGetContainerTransform(): Transform {
return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
}
@computed get cachedGetTransform(): Transform {
- return this.getContainerTransform().translate(- this.cachedCenteringShiftX, - this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+ return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
}
- changeKeyFrame = (back=false) => {
- const currentFrame = Cast(this.Document._currentFrame, "number", null);
+ changeKeyFrame = (back = false) => {
+ const currentFrame = Cast(this.Document._currentFrame, 'number', null);
if (currentFrame === undefined) {
this.Document._currentFrame = 0;
CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
@@ -167,8 +184,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
- }
- @action setKeyFrameEditing = (set: boolean) => this._keyframeEditing = set;
+ };
+ @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);
@@ -179,32 +196,35 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.layoutDoc._panX = vals.bounds.cx;
this.layoutDoc._panY = vals.bounds.cy;
this.layoutDoc._viewScale = vals.scale;
- }
- freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
- reverseNativeScaling = () => this.fitContentsToBox ? true : false;
+ };
+ freeformData = (force?: boolean) => (!this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined);
+ reverseNativeScaling = () => (this.fitContentsToBox ? true : false);
// panx, pany, zoomscale 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
+ // 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._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.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.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 ? "" : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
+ contentTransform = () =>
+ !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && 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();
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
+ 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);
- }
+ };
selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)).map(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);
}
@@ -213,14 +233,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// bcz: deal with clusters
}
if (retVal) {
- const newBoxes = (newBox instanceof Doc) ? [newBox] : newBox;
+ const newBoxes = newBox instanceof Doc ? [newBox] : newBox;
for (const newBox of newBoxes) {
if (newBox.activeFrame !== undefined) {
const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field]);
CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]);
CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]);
delete newBox.activeFrame;
- CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== "opacity" && (newBox[field] = vals[i]));
+ CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== 'opacity' && (newBox[field] = vals[i]));
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
@@ -228,13 +248,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
return retVal;
- }
+ };
isCurrent(doc: Doc) {
const dispTime = NumCast(doc._timecodeToShow, -1);
const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
const curTime = NumCast(this.Document._currentTimecode, -1);
- return dispTime === -1 || ((curTime - dispTime) >= -1e-4 && curTime <= endTime);
+ return dispTime === -1 || (curTime - dispTime >= -1e-4 && curTime <= endTime);
}
@action
@@ -246,8 +266,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const z = NumCast(refDoc.z);
const x = (z ? xpo : xp) - docDragData.offset[0];
const y = (z ? ypo : yp) - docDragData.offset[1];
- const zsorted = this.childLayoutPairs.map(pair => pair.layout).slice().sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
- zsorted.forEach((doc, index) => doc.zIndex = doc.isInkMask ? 5000 : index + 1);
+ const zsorted = this.childLayoutPairs
+ .map(pair => pair.layout)
+ .slice()
+ .sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
+ zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)];
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
@@ -267,8 +290,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
d._lastModified = new DateField();
const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
layoutDoc._width = NumCast(layoutDoc._width, 300);
- layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
- (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+ layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? (nd[1] / nd[0]) * NumCast(layoutDoc._width) : 300);
+ (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -292,13 +315,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@undoBatch
internalLinkDrop(e: Event, de: DragManager.DropEvent, linkDragData: DragManager.LinkDragData, xp: number, yp: number) {
- if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) { // dragged document is a child of this collection
- if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) { // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
- const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, x: xp, y: yp, title: "dropped annotation" });
+ if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
+ // dragged document is a child of this collection
+ if (!linkDragData.linkDragView.props.CollectionFreeFormDocumentView?.() || linkDragData.dragDocument.context !== this.props.Document) {
+ // if the source doc view's context isn't this same freeformcollectionlinkDragData.dragDocument.context === this.props.Document
+ const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
this.props.addDocument?.(source);
- de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, "annotated by:annotation of", ""); // TODODO this is where in text links get passed
+ de.complete.linkDocument = DocUtils.MakeLink({ doc: linkDragData.linkSourceGetAnchor() }, { doc: source }, 'annotated by:annotation of', ''); // TODODO this is where in text links get passed
}
- e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document
+ e.stopPropagation(); // do nothing if link is dropped into any freeform view parent of dragged document
return true;
}
return false;
@@ -310,25 +335,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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);
return false;
- }
+ };
onExternalDrop = (e: React.DragEvent) => {
return (pt => super.onExternalDrop(e, { x: pt[0], y: pt[1] }))(this.getTransform().transformPoint(e.pageX, e.pageY));
- }
+ };
pickCluster(probe: number[]) {
- return this.childLayoutPairs.map(pair => pair.layout).reduce((cluster, cd) => {
- const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
- if (grouping !== -1) {
- const layoutDoc = Doc.Layout(cd);
- const cx = NumCast(cd.x) - this._clusterDistance;
- const cy = NumCast(cd.y) - this._clusterDistance;
- const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
- const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
- return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
- }
- return cluster;
- }, -1);
+ return this.childLayoutPairs
+ .map(pair => pair.layout)
+ .reduce((cluster, cd) => {
+ const grouping = this.props.Document._useClusters ? NumCast(cd.cluster, -1) : NumCast(cd.group, -1);
+ if (grouping !== -1) {
+ const layoutDoc = Doc.Layout(cd);
+ const cx = NumCast(cd.x) - this._clusterDistance;
+ const cy = NumCast(cd.y) - this._clusterDistance;
+ const cw = NumCast(layoutDoc._width) + 2 * this._clusterDistance;
+ const ch = NumCast(layoutDoc._height) + 2 * this._clusterDistance;
+ return !layoutDoc.z && intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 }) ? grouping : cluster;
+ }
+ return cluster;
+ }, -1);
}
tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) {
@@ -338,10 +365,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._useClusters ? NumCast(cd.cluster) : NumCast(cd.group, -1)) === cluster);
const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.CollectionView)!);
const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
- const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? "alias" : undefined);
+ const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'alias' : undefined);
de.moveDocument = this.props.moveDocument;
de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
- DragManager.StartDocumentDrag(clusterDocs.map(v => v.ContentDiv!), de, ptsParent.clientX, ptsParent.clientY, { hideSource: !de.dropAction });
+ DragManager.StartDocumentDrag(
+ clusterDocs.map(v => v.ContentDiv!),
+ de,
+ ptsParent.clientX,
+ ptsParent.clientY,
+ { hideSource: !de.dropAction }
+ );
return true;
}
}
@@ -364,12 +397,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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.cluster);
- docs.map(doc => doc.cluster = -1);
- docs.map(doc => this._clusterSets.map((set, i) => set.map(member => {
- if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
- docFirst.cluster = i;
- }
- })));
+ docs.map(doc => (doc.cluster = -1));
+ docs.map(doc =>
+ this._clusterSets.map((set, i) =>
+ set.map(member => {
+ if (docFirst.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ docFirst.cluster = i;
+ }
+ })
+ )
+ );
if (docFirst.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
docFirst.cluster = preferredInd;
}
@@ -385,7 +422,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
} else if (this._clusterSets.length) {
for (let i = this._clusterSets.length; i <= NumCast(docFirst.cluster); i++) !this._clusterSets[i] && this._clusterSets.push([]);
- docs.map(doc => this._clusterSets[doc.cluster = NumCast(docFirst.cluster)].push(doc));
+ docs.map(doc => this._clusterSets[(doc.cluster = NumCast(docFirst.cluster))].push(doc));
}
childLayouts.map(child => !this._clusterSets.some((set, i) => Doc.IndexOf(child, set) !== -1 && child.cluster === i) && this.updateCluster(child));
}
@@ -399,11 +436,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
const preferredInd = NumCast(doc.cluster);
doc.cluster = -1;
- this._clusterSets.forEach((set, i) => set.forEach(member => {
- if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
- doc.cluster = i;
- }
- }));
+ this._clusterSets.forEach((set, i) =>
+ set.forEach(member => {
+ if (doc.cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ doc.cluster = i;
+ }
+ })
+ );
if (doc.cluster === -1 && preferredInd !== -1 && this._clusterSets.length > preferredInd && (!this._clusterSets[preferredInd] || !this._clusterSets[preferredInd].filter(member => Doc.IndexOf(member, childLayouts) !== -1).length)) {
doc.cluster = preferredInd;
}
@@ -423,7 +462,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
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
+ let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
if (property !== StyleProp.BackgroundColor) return styleProp;
const cluster = NumCast(doc?.cluster);
if (this.Document._useClusters) {
@@ -431,15 +470,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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)"];
+ 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));
+ set?.map(s => (styleProp = StrCast(s.backgroundColor)));
}
} //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
return styleProp;
- }
+ };
trySelectCluster = (addToSel: boolean) => {
if (this._hitCluster !== -1) {
@@ -449,31 +488,32 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return true;
}
return false;
- }
+ };
@action
onPenUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointerup", this.onPenUp);
- const currentCol = DocListCast(this.rootDoc.currentInkDoc)
+ document.removeEventListener('pointerup', this.onPenUp);
+ const currentCol = DocListCast(this.rootDoc.currentInkDoc);
const rootDocList = DocListCast(this.rootDoc.data);
currentCol.push(rootDocList[rootDocList.length - 1]);
console.log(currentCol);
this._batch?.end();
}
- }
+ };
@action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- if (!e.nativeEvent.cancelBubble &&
+ 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)) {
- switch (CurrentUserUtils.ActiveTool) {
+ !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
+ ) {
+ switch (Doc.ActiveTool) {
case InkTool.Highlighter:
break;
// TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -482,49 +522,46 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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:
- document.addEventListener("pointermove", this.onEraserMove);
- document.addEventListener("pointerup", this.onEraserUp);
- this._batch = UndoManager.StartBatch("collectionErase");
+ document.addEventListener('pointermove', this.onEraserMove);
+ document.addEventListener('pointerup', this.onEraserUp);
+ this._batch = UndoManager.StartBatch('collectionErase');
e.stopPropagation();
e.preventDefault();
break;
case InkTool.None:
if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
- document.addEventListener("pointermove", this.onPointerMove);
- document.addEventListener("pointerup", this.onPointerUp);
+ document.addEventListener('pointermove', this.onPointerMove);
+ document.addEventListener('pointerup', this.onPointerUp);
}
break;
}
}
}
- }
+ };
@action
handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
- if (!e.nativeEvent.cancelBubble) {
- // 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 (CurrentUserUtils.ActiveTool === InkTool.None) {
- this._lastX = pt.pageX;
- this._lastY = pt.pageY;
- e.preventDefault();
- e.stopPropagation();
- }
- else {
- e.preventDefault();
- }
+ // 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>();
@@ -534,9 +571,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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);
- const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
- { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
- if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+ const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
+ title: 'ink stroke',
+ x: B.x - ActiveInkWidth() / 2,
+ y: B.y - ActiveInkWidth() / 2,
+ _width: B.width + ActiveInkWidth(),
+ _height: B.height + ActiveInkWidth(),
+ });
+ if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
}
@@ -561,7 +603,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
return pass;
});
- this.addDocument(Docs.Create.FreeformDocument(sel, { title: "nested collection", x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
+ this.addDocument(Docs.Create.FreeformDocument(sel, { title: 'nested collection', x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
sel.forEach(d => this.props.removeDocument?.(d));
e.stopPropagation();
break;
@@ -573,17 +615,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.EndBracket:
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 => s.proto?.type === "rtf" && s.color);
- const sets = setDocs.map((sd) => {
+ const setDocs = this.getActiveDocuments().filter(s => s.proto?.type === 'rtf' && s.color);
+ const sets = setDocs.map(sd => {
return 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])));
+ 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") {
+ if (doc.type === 'ink') {
const l = NumCast(doc.x);
const r = l + doc[WidthSym]();
const t = NumCast(doc.y);
@@ -599,15 +641,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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]);
+ 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 })));
}
});
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
- const wordResults = results.filter((r: any) => r.category === "inkWord");
+ 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 => {
@@ -619,8 +661,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
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) {
+ } 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());
@@ -641,27 +682,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
}
}
- }
+ };
@action
onEraserUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointermove", this.onEraserMove);
- document.removeEventListener("pointerup", this.onEraserUp);
+ document.removeEventListener('pointermove', this.onEraserMove);
+ document.removeEventListener('pointerup', this.onEraserUp);
this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
this._deleteList = [];
this._batch?.end();
}
- }
+ };
@action
onPointerUp = (e: PointerEvent): void => {
if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
this.removeMoveListeners();
this.removeEndListeners();
}
- }
+ };
onClick = (e: React.MouseEvent) => {
if (this.onBrowseClickHandler()) {
@@ -670,10 +711,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
e.stopPropagation();
e.preventDefault();
- }
- else if ((Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3)) {
+ } else if (Math.abs(e.pageX - this._downX) < 3 && Math.abs(e.pageY - this._downY) < 3) {
if (e.shiftKey) {
- if (Date.now() - this._lastTap < 300) { // reset zoom of freeform view to 1-to-1 on a shift + double click
+ if (Date.now() - this._lastTap < 300) {
+ // reset zoom of freeform view to 1-to-1 on a shift + double click
this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
}
e.stopPropagation();
@@ -681,15 +722,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
this._lastTap = Date.now();
}
- }
+ };
@action
- pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
+ pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
- }
+ };
/**
* Erases strokes by intersecting them with an invisible "eraser stroke".
@@ -703,14 +744,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => {
if (!this._deleteList.includes(intersect.inkView)) {
this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || "1");
- SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || "black");
+ SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black');
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- !e.shiftKey && this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
- GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points
- .map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })
- ], [] as PointData[])));
+ !e.shiftKey &&
+ this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+ GestureOverlay.Instance.dispatchGesture(
+ GestureUtils.Gestures.Stroke,
+ segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
+ )
+ );
// Lower ink opacity to give the user a visual indicator of deletion.
intersect.inkView.layoutDoc.opacity = 0.5;
}
@@ -720,55 +763,64 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
- }
+ };
@action
onPointerMove = (e: PointerEvent): void => {
if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- CurrentUserUtils.ActiveTool = InkTool.None;
+ Doc.ActiveTool = InkTool.None;
if (this.props.isContentActive(true)) e.stopPropagation();
} else if (!e.cancelBubble) {
if (this.tryDragCluster(e, this._hitCluster)) {
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
- }
- else this.pan(e);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
+ } else this.pan(e);
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
}
- }
+ };
/**
* Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
* @returns an array of tuples containing the intersected ink DocumentView and the t-value where it was intersected
*/
- getEraserIntersections = (lastPoint: { X: number, Y: number }, currPoint: { X: number, Y: number }) => {
+ getEraserIntersections = (lastPoint: { X: number; Y: number }, currPoint: { X: number; Y: number }) => {
const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) };
const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) };
return this.childDocs
.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView))
.filter(inkView => inkView?.ComponentView instanceof InkingStroke)
.map(inkView => ({ inkViewBounds: inkView!.getBounds(), inkStroke: inkView!.ComponentView as InkingStroke, inkView: inkView! }))
- .filter(({ inkViewBounds }) => inkViewBounds && // bounding box of eraser segment and ink stroke overlap
- eraserMin.X <= inkViewBounds.right && eraserMin.Y <= inkViewBounds.bottom &&
- eraserMax.X >= inkViewBounds.left && eraserMax.Y >= inkViewBounds.top)
+ .filter(
+ ({ inkViewBounds }) =>
+ inkViewBounds && // bounding box of eraser segment and ink stroke overlap
+ eraserMin.X <= inkViewBounds.right &&
+ eraserMin.Y <= inkViewBounds.bottom &&
+ eraserMax.X >= inkViewBounds.left &&
+ eraserMax.Y >= inkViewBounds.top
+ )
.reduce((intersections, { inkStroke, inkView }) => {
const { inkData } = inkStroke.inkScaledData();
// Convert from screen space to ink space for the intersection.
const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
for (var i = 0; i < inkData.length - 3; i += 4) {
- const intersects = Array.from(new Set(InkField.Segment(inkData, i).intersects({ // compute all unique intersections
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
- }) as (number | string)[])); // convert to more manageable union array type
+ const intersects = Array.from(
+ new Set(
+ InkField.Segment(inkData, i).intersects({
+ // compute all unique intersections
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
+ }) as (number | string)[]
+ )
+ ); // convert to more manageable union array type
// return tuples of the inkingStroke intersected, and the t value of the intersection
- intersections.push(...intersects.map(t => ({ inkView, t: (+t) + Math.floor(i / 4) })));// convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
+ intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
}
return intersections;
- }, [] as { t: number, inkView: DocumentView }[]);
- }
+ }, [] as { t: number; inkView: DocumentView }[]);
+ };
/**
* Performs segmentation of the ink stroke - creates "segments" or subsections of the current ink stroke at points in which the
@@ -805,11 +857,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
segment.push(inkSegment);
}
}
- if (excludeT < startSegmentT || excludeT > (inkData.length / 4)) {
+ if (excludeT < startSegmentT || excludeT > inkData.length / 4) {
segment.length && segments.push(segment);
}
return segments;
- }
+ };
/**
* Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all
@@ -837,24 +889,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
curve.intersects(otherCurve).forEach((val: string | number, i: number) => {
// Converting the Bezier.js Split type to a t-value number.
- const t = +val.toString().split("/")[0];
- if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
+ const t = +val.toString().split('/')[0];
+ if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
});
}
});
return tVals;
- }
+ };
handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
if (!e.cancelBubble) {
const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
if (myTouches[0]) {
- if (CurrentUserUtils.ActiveTool === InkTool.None) {
+ if (Doc.ActiveTool === InkTool.None) {
if (this.tryDragCluster(e, this._hitCluster)) {
e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers
e.preventDefault();
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
return;
}
// TODO: nda - this allows us to pan collections with finger -> only want to do this when collection is selected'
@@ -864,7 +916,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// e.stopPropagation();
e.preventDefault();
}
- }
+ };
handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
// pinch zooming
@@ -887,7 +939,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
// calculate the raw delta value
- const rawDelta = (dir * (d1 + d2));
+ const rawDelta = dir * (d1 + d2);
// this floors and ceils the delta value to prevent jitteriness
const delta = Math.sign(rawDelta) * Math.min(Math.abs(rawDelta), 8);
@@ -902,7 +954,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const centerY = Math.min(pt1.clientY, pt2.clientY) + Math.abs(pt2.clientY - pt1.clientY) / 2;
// const transformed = this.getTransform().inverse().transformPoint(centerX, centerY);
- if (!this._pullDirection) { // if we are not bezel movement
+ if (!this._pullDirection) {
+ // if we are not bezel movement
this.pan({ clientX: centerX, clientY: centerY });
} else {
this._pullCoords = [centerX, centerY];
@@ -916,11 +969,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// e.stopPropagation();
e.preventDefault();
}
- }
+ };
@action
handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
- if (!e.nativeEvent.cancelBubble && this.props.isContentActive(true)) {
+ if (this.props.isContentActive(true)) {
// const pt1: React.Touch | null = e.targetTouches.item(0);
// const pt2: React.Touch | null = e.targetTouches.item(1);
// // if (!pt1 || !pt2) return;
@@ -934,21 +987,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._lastY = centerY;
const screenBox = this._mainCont?.getBoundingClientRect();
-
// determine if we are using a bezel movement
if (screenBox) {
- if ((screenBox.right - centerX) < 100) {
+ if (screenBox.right - centerX < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "right";
+ this._pullDirection = 'right';
} else if (centerX - screenBox.left < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "left";
+ this._pullDirection = 'left';
} else if (screenBox.bottom - centerY < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "bottom";
+ this._pullDirection = 'bottom';
} else if (centerY - screenBox.top < 100) {
this._pullCoords = [centerX, centerY];
- this._pullDirection = "top";
+ this._pullDirection = 'top';
}
}
@@ -959,27 +1011,30 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
}
}
- }
+ };
cleanUpInteractions = () => {
switch (this._pullDirection) {
- case "left": case "right": case "top": case "bottom":
- CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: "New Collection" }), this._pullDirection);
+ case 'left':
+ case 'right':
+ case 'top':
+ case 'bottom':
+ CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection);
}
- this._pullDirection = "";
+ this._pullDirection = '';
this._pullCoords = [0, 0];
- document.removeEventListener("pointermove", this.onPointerMove);
- document.removeEventListener("pointerup", this.onPointerUp);
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.removeEventListener('pointerup', this.onPointerUp);
this.removeMoveListeners();
this.removeEndListeners();
- }
+ };
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document._isGroup) return;
- let deltaScale = deltaY > 0 ? (1 / 1.05) : 1.05;
+ 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();
@@ -996,59 +1051,74 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.props.Document[this.scaleFieldKey] = Math.abs(safeScale);
this.setPan(-localTransform.TranslateX / safeScale, -localTransform.TranslateY / safeScale);
}
- }
+ };
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
- if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
+ if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+ if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
+ // things that can scroll vertically should do that instead of zooming
e.stopPropagation();
- }
- else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
e.stopPropagation();
e.preventDefault();
!this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
}
- }
+ };
@action
setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
+ // set the current respective FFview to the tab being panned.
+ Doc.UserDoc()?.presentationMode === 'recording' && RecordingApi.Instance.setRecordingFFView(this);
+ // TODO: make this based off the specific recording FFView
+ Doc.UserDoc()?.presentationMode === 'none' && RecordingApi.Instance.setPlayFFView(this);
+
+ // TODO: zzz + michael to figure out this merge in case of strange behaviour
+ // if (Doc.UserDoc()?.presentationMode === 'watching') {
+ // RecordingApi.Instance.pauseVideoAndMovements();
+ // Doc.UserDoc().presentationMode = 'none';
+ // // RecordingApi.Instance.pauseMovements()
+ // }
// this is the easiest way to do this -> will talk with Bob about using mobx to do this to remove this line of code.
if (Doc.UserDoc()?.presentationMode === 'watching') ReplayMovements.Instance.pauseFromInteraction();
if (!this.isAnnotationOverlay && clamp) {
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
- const measuredDocs = docs.map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ""), size: this.childSizeProviderUnmemoized(doc, "") }))
- .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! }));
+ const measuredDocs = docs
+ .map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ''), size: this.childSizeProviderUnmemoized(doc, '') }))
+ .filter(({ pos, size }) => pos && size)
+ .map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
- const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content
- ({
- xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
- yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
- })
- , {
+ const ranges = measuredDocs.reduce(
+ (
+ { xrange, yrange },
+ { pos, size } // computes range of content
+ ) => ({
+ xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
+ yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) },
+ }),
+ {
xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
- yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
- });
+ yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
+ }
+ );
const panelDim = [this.props.PanelWidth() / this.zoomScaling(), this.props.PanelHeight() / this.zoomScaling()];
- if (ranges.xrange.min >= (panX + panelDim[0] / 2)) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds
- else if (ranges.xrange.max <= (panX - panelDim[0] / 2)) panX = ranges.xrange.min - panelDim[0] / 2;
- if (ranges.yrange.min >= (panY + panelDim[1] / 2)) panY = ranges.yrange.max + panelDim[1] / 2;
- else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
+ if (ranges.xrange.min >= panX + panelDim[0] / 2) panX = ranges.xrange.max + panelDim[0] / 2; // snaps pan position of range of content goes out of bounds
+ else if (ranges.xrange.max <= panX - panelDim[0] / 2) panX = ranges.xrange.min - panelDim[0] / 2;
+ if (ranges.yrange.min >= panY + panelDim[1] / 2) panY = ranges.yrange.max + panelDim[1] / 2;
+ else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2;
}
}
- if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
+ if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
this._viewTransition = panTime;
const scale = this.getLocalTransform().inverse().Scale;
const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
const minPanX = NumCast(this.rootDoc._panXMin, 0);
const minPanY = NumCast(this.rootDoc._panYMin, 0);
- const newPanX = Math.min(
- minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
- const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) :
- minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight)), Math.max(minPanY, panY));
+ const newPanX = Math.min(minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+ const newPanY = Math.min(this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) : minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight), Math.max(minPanY, panY));
!this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX);
!this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY);
}
@@ -1056,16 +1126,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
nudge = (x: number, y: number, nudgeTime: number = 500) => {
- if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ||
- this.props.ContainingCollectionDoc._panX !== undefined) { // bcz: this isn't ideal, but want to try it out...
- this.setPan(NumCast(this.layoutDoc._panX) + this.props.PanelWidth() / 2 * x / this.zoomScaling(),
- NumCast(this.layoutDoc._panY) + this.props.PanelHeight() / 2 * (-y) / this.zoomScaling(), nudgeTime, true);
+ if (this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform || this.props.ContainingCollectionDoc._panX !== undefined) {
+ // bcz: this isn't ideal, but want to try it out...
+ this.setPan(NumCast(this.layoutDoc._panX) + ((this.props.PanelWidth() / 2) * x) / this.zoomScaling(), NumCast(this.layoutDoc._panY) + ((this.props.PanelHeight() / 2) * -y) / this.zoomScaling(), nudgeTime, true);
this._lastNudge && clearTimeout(this._lastNudge);
- this._lastNudge = setTimeout(action(() => this._viewTransition = 0), nudgeTime);
+ this._lastNudge = setTimeout(
+ action(() => (this._viewTransition = 0)),
+ nudgeTime
+ );
return true;
}
return false;
- }
+ };
@action
bringToFront = (doc: Doc, sendToBack?: boolean) => {
@@ -1083,12 +1155,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
doc.zIndex = zlast + 1;
}
- }
+ };
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
if (this.Document._isGroup) return;
- setTimeout(action(() => this._viewTransition = 0), this._viewTransition = transitionTime); // set transition to be smooth, then reset
+ setTimeout(
+ action(() => (this._viewTransition = 0)),
+ (this._viewTransition = transitionTime)
+ ); // set transition to be smooth, then reset
const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
@@ -1103,7 +1178,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// TODO This technically isn't correct if type !== "doc", as
// currently nothing is done, but we should probably push a new state
- if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) {
+ if (state.type === 'doc' && this.Document._panX !== undefined && this.Document._panY !== undefined) {
const init = state.initializers![this.Document[Id]];
if (!init) {
state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) };
@@ -1123,9 +1198,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoom ? this.Document[this.scaleFieldKey] : undefined };
const newState = HistoryUtil.getState();
- const cantTransform = /*this.props.isAnnotationOverlay ||*/ ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
- const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || .75 : undefined);
- if (!cantTransform) { // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
+ const cantTransform = /*this.props.isAnnotationOverlay ||*/ (this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc;
+ const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || 0.75 : undefined);
+ if (!cantTransform) {
+ // only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
newState.initializers![this.Document[Id]] = { panX: panX, panY: panY };
HistoryUtil.pushState(newState);
}
@@ -1145,30 +1221,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const resetView = options?.afterFocus ? await options?.afterFocus(moved) : ViewAdjustment.doNothing;
if (resetView) {
const restoreState = (!LightboxView.LightboxDoc || LightboxView.LightboxDoc === this.props.Document) && savedState;
- if (typeof restoreState !== "boolean") {
+ if (typeof restoreState !== 'boolean') {
this.Document._panX = restoreState.panX;
this.Document._panY = restoreState.panY;
this.Document[this.scaleFieldKey] = restoreState.scale;
}
- runInAction(() => this._viewTransition = 0);
+ runInAction(() => (this._viewTransition = 0));
}
return resetView;
};
- const xf = !cantTransform ? Transform.Identity() :
- this.props.isAnnotationOverlay ?
- new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
- :
- new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX),
- NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
+ const xf = !cantTransform
+ ? Transform.Identity()
+ : this.props.isAnnotationOverlay
+ ? new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
+ : new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
this.props.focus(cantTransform ? doc : this.rootDoc, {
...options,
docTransform: xf,
- afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res =>
- setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime))))
+ afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))),
});
}
- }
+ };
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
const layoutdoc = Doc.Layout(doc);
@@ -1178,11 +1252,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (scale) {
const maxZoom = 5; // sets the limit for how far we will zoom. this is useful for preventing small text boxes from filling the screen. So probably needs to be more sophisticated to consider more about the target and context
- const newScale = Math.min(maxZoom, 1 / (this.contentScaling || 1) * scale * Math.min(this.props.PanelWidth() / Math.abs(bounds.width), this.props.PanelHeight() / Math.abs(bounds.height)));
+ const newScale = Math.min(maxZoom, (1 / (this.contentScaling || 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,
- scale: newScale
+ scale: newScale,
};
}
const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1);
@@ -1190,19 +1264,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const cx = NumCast(this.layoutDoc._panX);
const cy = NumCast(this.layoutDoc._panY);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
- if ((screen.right - screen.left) < (bounds.right - bounds.left) ||
- (screen.bot - screen.top) < (bounds.bot - bounds.top)) {
+ if (screen.right - screen.left < bounds.right - bounds.left || screen.bot - screen.top < bounds.bot - bounds.top) {
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: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
panY: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
};
- }
+ };
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
@@ -1210,190 +1283,215 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
const docView = fieldProps.DocumentView?.();
- if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
+ if (docView && (e.metaKey || e.ctrlKey || e.altKey || docView.rootDoc._singleLine) && ['Tab', 'Enter'].includes(e.key)) {
e.stopPropagation?.();
- const below = !e.altKey && e.key !== "Tab";
+ const below = !e.altKey && e.key !== 'Tab';
const layoutKey = StrCast(docView.LayoutFieldKey);
const newDoc = Doc.MakeCopy(docView.rootDoc, true);
const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
newDoc[DataSym][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 (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+ if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
newDoc[layoutKey] = docView.rootDoc[layoutKey];
}
Doc.GetProto(newDoc).text = undefined;
FormattedTextBox.SelectOnLoad = newDoc[Id];
return this.addDocument?.(newDoc);
}
- }
+ };
pointerEvents = () => {
const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
- const pointerEvents = this.props.isContentActive() === false ? "none" :
- this.props.childPointerEvents ? "all" :
- (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.();
+ const pointerEvents = this.props.isContentActive() === false ? 'none' : this.props.childPointerEvents ? 'all' : this.props.viewDefDivClick || (engine === 'pass' && !this.props.isSelected(true)) ? 'none' : this.props.pointerEvents?.();
return pointerEvents;
- }
+ };
getChildDocView(entry: PoolData) {
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
- return <CollectionFreeFormDocumentView key={childLayout[Id] + (entry.replica || "")}
- DataDoc={childData}
- Document={childLayout}
- renderDepth={this.props.renderDepth + 1}
- replica={entry.replica}
- suppressSetHeight={this.layoutEngine ? true : false}
- renderCutoffProvider={this.renderCutoffProvider}
- ContainingCollectionView={this.props.CollectionView}
- ContainingCollectionDoc={this.props.Document}
- CollectionFreeFormView={this}
- LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
- LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
- rootSelected={childData ? this.rootSelected : returnFalse}
- onClick={this.onChildClickHandler}
- onKey={this.onKeyDown}
- onDoubleClick={this.onChildDoubleClickHandler}
- onBrowseClick={this.onBrowseClickHandler}
- ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
- PanelWidth={childLayout[WidthSym]}
- PanelHeight={childLayout[HeightSym]}
- docFilters={this.childDocFilters}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
- isContentActive={emptyFunction}
- focus={this.focusDocument}
- 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}
- dataProvider={this.childDataProvider}
- sizeProvider={this.childSizeProvider}
- dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
- bringToFront={this.bringToFront}
- showTitle={this.props.childShowTitle}
- dontScaleFilter={this.props.dontScaleFilter}
- dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
- pointerEvents={this.pointerEvents}
- jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
- //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
- />;
+ return (
+ <CollectionFreeFormDocumentView
+ key={childLayout[Id] + (entry.replica || '')}
+ DataDoc={childData}
+ Document={childLayout}
+ renderDepth={this.props.renderDepth + 1}
+ replica={entry.replica}
+ suppressSetHeight={this.layoutEngine ? true : false}
+ renderCutoffProvider={this.renderCutoffProvider}
+ ContainingCollectionView={this.props.CollectionView}
+ ContainingCollectionDoc={this.props.Document}
+ CollectionFreeFormView={this}
+ LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
+ LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
+ rootSelected={childData ? this.rootSelected : returnFalse}
+ onClick={this.onChildClickHandler}
+ onKey={this.onKeyDown}
+ onDoubleClick={this.onChildDoubleClickHandler}
+ onBrowseClick={this.onBrowseClickHandler}
+ ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
+ PanelWidth={childLayout[WidthSym]}
+ PanelHeight={childLayout[HeightSym]}
+ docFilters={this.childDocFilters}
+ docRangeFilters={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
+ isContentActive={emptyFunction}
+ focus={this.focusDocument}
+ 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}
+ dataProvider={this.childDataProvider}
+ sizeProvider={this.childSizeProvider}
+ dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
+ bringToFront={this.bringToFront}
+ showTitle={this.props.childShowTitle}
+ dontScaleFilter={this.props.dontScaleFilter}
+ dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
+ pointerEvents={this.pointerEvents}
+ jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
+ //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+ />
+ );
}
addDocTab = action((doc: Doc, where: string) => {
- if (where === "inParent") {
- ((doc instanceof Doc) ? [doc] : doc).forEach(doc => {
+ if (where === 'inParent') {
+ (doc instanceof Doc ? [doc] : doc).forEach(doc => {
const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = pt[0];
doc.y = pt[1];
});
return this.props.addDocument?.(doc) || false;
}
- if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+ if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
this.dataDoc[this.props.fieldKey] = doc instanceof Doc ? doc : new List<Doc>(doc as any as Doc[]);
return true;
}
return this.props.addDocTab(doc, where);
});
- getCalculatedPositions(params: { pair: { layout: Doc, data?: Doc }, index: number, collection: Doc }): PoolData {
+ getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
const layoutDoc = Doc.Layout(params.pair.layout);
const { z, color, zIndex } = params.pair.layout;
- const { x, y, opacity } = this.Document._currentFrame === undefined ?
- { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } :
- CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
+ const { x, y, opacity } =
+ this.Document._currentFrame === undefined
+ ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
+ : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
return {
- x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
- transition: StrCast(layoutDoc.dataTransition), opacity: this._keyframeEditing ? 1 : Cast(opacity, "number", null),
- width: Cast(layoutDoc._width, "number"), height: Cast(layoutDoc._height, "number"), pair: params.pair, replica: ""
+ x: NumCast(x),
+ y: NumCast(y),
+ z: Cast(z, 'number'),
+ color: StrCast(color),
+ zIndex: Cast(zIndex, 'number'),
+ transition: StrCast(layoutDoc.dataTransition),
+ opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number', null),
+ width: Cast(layoutDoc._width, 'number'),
+ height: Cast(layoutDoc._height, 'number'),
+ pair: params.pair,
+ replica: '',
};
}
onViewDefDivClick = (e: React.MouseEvent, payload: any) => {
(this.props.viewDefDivClick || ScriptCast(this.props.Document.onViewDefDivClick))?.script.run({ this: this.props.Document, payload });
e.stopPropagation();
- }
+ };
viewDefsToJSX = (views: ViewDefBounds[]) => {
return !Array.isArray(views) ? [] : views.filter(ele => this.viewDefToJSX(ele)).map(ele => this.viewDefToJSX(ele)!);
- }
+ };
viewDefToJSX(viewDef: ViewDefBounds): Opt<ViewDefResult> {
const { x, y, z } = viewDef;
const color = StrCast(viewDef.color);
- const width = Cast(viewDef.width, "number");
- const height = Cast(viewDef.height, "number");
+ const width = Cast(viewDef.width, 'number');
+ const height = Cast(viewDef.height, 'number');
const transform = `translate(${x}px, ${y}px)`;
- if (viewDef.type === "text") {
- const text = Cast(viewDef.text, "string"); // don't use NumCast, StrCast, etc since we want to test for undefined below
- const fontSize = Cast(viewDef.fontSize, "string");
- return [text, x, y].some(val => val === undefined) ? undefined :
- {
- ele: <div className="collectionFreeform-customText" key={(text || "") + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
- {text}
- </div>,
- bounds: viewDef
- };
- } else if (viewDef.type === "div") {
- return [x, y].some(val => val === undefined) ? undefined :
- {
- ele: <div className="collectionFreeform-customDiv" title={viewDef.payload?.join(" ")} key={"div" + x + y + z + viewDef.payload} onClick={e => this.onViewDefDivClick(e, viewDef)}
- style={{ width, height, backgroundColor: color, transform }} />,
- bounds: viewDef
- };
+ if (viewDef.type === 'text') {
+ const text = Cast(viewDef.text, 'string'); // don't use NumCast, StrCast, etc since we want to test for undefined below
+ const fontSize = Cast(viewDef.fontSize, 'string');
+ return [text, x, y].some(val => val === undefined)
+ ? undefined
+ : {
+ ele: (
+ <div className="collectionFreeform-customText" key={(text || '') + x + y + z + color} style={{ width, height, color, fontSize, transform }}>
+ {text}
+ </div>
+ ),
+ bounds: viewDef,
+ };
+ } else if (viewDef.type === 'div') {
+ return [x, y].some(val => val === undefined)
+ ? undefined
+ : {
+ ele: (
+ <div
+ className="collectionFreeform-customDiv"
+ title={viewDef.payload?.join(' ')}
+ key={'div' + x + y + z + viewDef.payload}
+ onClick={e => this.onViewDefDivClick(e, viewDef)}
+ style={{ width, height, backgroundColor: color, transform }}
+ />
+ ),
+ bounds: viewDef,
+ };
}
}
-
- renderCutoffProvider = computedFn(function renderCutoffProvider(this: any, doc: Doc) {
- return !this._renderCutoffData.get(doc[Id] + "");
- }.bind(this));
-
+ renderCutoffProvider = computedFn(
+ function renderCutoffProvider(this: any, doc: Doc) {
+ return !this._renderCutoffData.get(doc[Id] + '');
+ }.bind(this)
+ );
childPositionProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutPoolData.get(doc[Id] + (replica || ""));
- }
- childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) {
- return this._layoutPoolData.get(doc[Id] + (replica || ""));
- }.bind(this));
+ return this._layoutPoolData.get(doc[Id] + (replica || ''));
+ };
+ childDataProvider = computedFn(
+ function childDataProvider(this: any, doc: Doc, replica: string) {
+ return this._layoutPoolData.get(doc[Id] + (replica || ''));
+ }.bind(this)
+ );
childSizeProviderUnmemoized = (doc: Doc, replica: string) => {
- return this._layoutSizeData.get(doc[Id] + (replica || ""));
- }
- childSizeProvider = computedFn(function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this._layoutSizeData.get(doc[Id] + (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 this._layoutSizeData.get(doc[Id] + (replica || ''));
+ };
+ childSizeProvider = computedFn(
+ function childSizeProvider(this: any, doc: Doc, replica: string) {
+ return this._layoutSizeData.get(doc[Id] + (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);
}
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, index: i, collection: this.Document })));
return [] as ViewDefResult[];
}
- @computed get layoutEngine() { return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); }
+ @computed get layoutEngine() {
+ return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
+ }
@computed get doInternalLayoutComputation() {
TraceMobx();
const newPool = new Map<string, PoolData>();
switch (this.layoutEngine) {
- case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
- case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
- case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
- case "starburst": return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
+ case 'pass':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
+ case 'timeline':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
+ case 'pivot':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
+ case 'starburst':
+ return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
}
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
@@ -1417,15 +1515,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._cachedPool.clear();
Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
const elements = computedElementData.slice();
- Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) =>
- elements.push({
- ele: this.getChildDocView(entry[1]),
- bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
- }));
+ Array.from(newPool.entries())
+ .filter(entry => this.isCurrent(entry[1].pair.layout))
+ .forEach((entry, i) =>
+ elements.push({
+ ele: this.getChildDocView(entry[1]),
+ bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica),
+ })
+ );
- if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) { // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+ if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) {
+ // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling());
- else this.props.Document[this.scaleFieldKey] = Math.max(1,this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
+ else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
}
this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1446,60 +1548,69 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
return 0;
- }
+ };
getAnchor = () => {
if (this.props.Document.annotationOn) {
return this.rootDoc;
}
- const anchor = Docs.Create.TextanchorDocument({ title: "ViewSpec - " + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
+ const anchor = Docs.Create.TextanchorDocument({ title: 'ViewSpec - ' + StrCast(this.layoutDoc._viewType), annotationOn: this.rootDoc });
const proto = Doc.GetProto(anchor);
- proto[ViewSpecPrefix + "_viewType"] = this.layoutDoc._viewType;
+ proto[ViewSpecPrefix + '_viewType'] = this.layoutDoc._viewType;
proto.docFilters = ObjectField.MakeCopy(this.layoutDoc.docFilters as ObjectField) || new List<string>([]);
- 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
componentDidMount() {
super.componentDidMount?.();
this.props.setContentView?.(this);
- setTimeout(action(() => {
- this._firstRender = false;
- this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
- (elements) => this._layoutElements = elements || [],
- { fireImmediately: true, name: "doLayout" });
-
- this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
-
- this._disposers.groupBounds = reaction(() => {
- if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
- return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
- }
- return undefined;
- },
- (cbounds) => {
- if (cbounds) {
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
- const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
- const pbounds = {
- x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
- r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
- };
- this.layoutDoc._width = (pbounds.r - pbounds.x);
- this.layoutDoc._height = (pbounds.b - pbounds.y);
- this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
- this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
- this.layoutDoc.x = pbounds.x;
- this.layoutDoc.y = pbounds.y;
- }
- }, { fireImmediately: true });
- }));
+ setTimeout(
+ action(() => {
+ this._firstRender = false;
+ this._disposers.layoutComputation = reaction(
+ () => this.doLayoutComputation,
+ elements => (this._layoutElements = elements || []),
+ { fireImmediately: true, name: 'doLayout' }
+ );
+
+ this._marqueeRef.current?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+
+ this._disposers.groupBounds = reaction(
+ () => {
+ if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+ return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
+ }
+ return undefined;
+ },
+ cbounds => {
+ if (cbounds) {
+ const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+ const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+ const pbounds = {
+ x: (cbounds.x - p[0]) * this.zoomScaling() + c[0],
+ y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+ r: (cbounds.r - p[0]) * this.zoomScaling() + c[0],
+ b: (cbounds.b - p[1]) * this.zoomScaling() + c[1],
+ };
+ this.layoutDoc._width = pbounds.r - pbounds.x;
+ this.layoutDoc._height = pbounds.b - pbounds.y;
+ this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+ this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+ this.layoutDoc.x = pbounds.x;
+ this.layoutDoc.y = pbounds.y;
+ }
+ },
+ { fireImmediately: true }
+ );
+ })
+ );
}
static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
@@ -1509,15 +1620,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
if (oldDiv instanceof HTMLCanvasElement) {
- if (oldDiv.className === "collectionFreeFormView-grid") {
+ if (oldDiv.className === 'collectionFreeFormView-grid') {
const newCan = newDiv as HTMLCanvasElement;
const parEle = newCan.parentElement as HTMLElement;
parEle.removeChild(newCan);
- parEle.appendChild(document.createElement('div'))
+ parEle.appendChild(document.createElement('div'));
} else {
const canvas = oldDiv;
const img = document.createElement('img'); // create a Image Element
- img.src = canvas.toDataURL(); //image source
+ img.src = canvas.toDataURL(); //image source
img.style.width = canvas.style.width;
img.style.height = canvas.style.height;
const newCan = newDiv as HTMLCanvasElement;
@@ -1528,27 +1639,38 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
- updateIcon = () => CollectionFreeFormView.UpdateIcon(
- this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
- this.props.docViewPath().lastElement().ContentDiv!,
- this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](),
- 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;
- });
+ updateIcon = () =>
+ CollectionFreeFormView.UpdateIcon(
+ this.layoutDoc[Id] + '-icon' + new Date().getTime(),
+ this.props.docViewPath().lastElement().ContentDiv!,
+ this.layoutDoc[WidthSym](),
+ this.layoutDoc[HeightSym](),
+ 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;
+ }
+ );
public static UpdateIcon(
- filename:string, docViewContent:HTMLElement,
- width: number, height: number,
- panelWidth:number, panelHeight: number,
- scrollTop:number,
+ filename: string,
+ docViewContent: HTMLElement,
+ width: number,
+ height: number,
+ panelWidth: number,
+ panelHeight: number,
+ scrollTop: number,
realNativeHeight: number,
noSuffix: boolean,
- replaceRootFilename: string| undefined,
- cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any)
- {
+ replaceRootFilename: string | undefined,
+ cb: (iconFile: string, nativeWidth: number, nativeHeight: number) => any
+ ) {
const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
newDiv.style.width = width.toString();
newDiv.style.height = height.toString();
@@ -1556,15 +1678,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const htmlString = new XMLSerializer().serializeToString(newDiv);
const nativeWidth = width;
const nativeHeight = height;
- return CreateImage(
- Utils.prepend(""),
- document.styleSheets,
- htmlString,
- nativeWidth,
- nativeWidth * panelHeight / panelWidth,
- scrollTop * panelHeight / realNativeHeight
- ).then
- (async (data_url: any) => {
+ return CreateImage(Utils.prepend(''), document.styleSheets, htmlString, nativeWidth, (nativeWidth * panelHeight) / panelWidth, (scrollTop * panelHeight) / realNativeHeight)
+ .then(async (data_url: any) => {
const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename);
cb(returnedFilename as string, nativeWidth, nativeHeight);
})
@@ -1575,13 +1690,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
- this._marqueeRef.current?.removeEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+ this._marqueeRef.current?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
}
@action
onCursorMove = (e: React.PointerEvent) => {
// super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
- }
+ };
@action
onDragAutoScroll = (e: CustomEvent<React.DragEvent>) => {
@@ -1601,7 +1716,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
e.stopPropagation();
- }
+ };
@undoBatch
promoteCollection = () => {
@@ -1611,9 +1726,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.x = scr?.[0];
doc.y = scr?.[1];
});
- this.props.addDocTab(childDocs as any as Doc, "inParent");
+ this.props.addDocTab(childDocs as any as Doc, 'inParent');
this.props.ContainingCollectionView?.removeDocument(this.props.Document);
- }
+ };
@undoBatch
layoutDocsInGrid = () => {
@@ -1622,73 +1737,88 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
const dim = Math.ceil(Math.sqrt(docs.length));
docs.forEach((doc, i) => {
- doc.x = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2;
- doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2;
+ doc.x = NumCast(this.Document._panX) + (i % dim) * width - (width * dim) / 2;
+ doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - (height * dim) / 2;
});
- }
+ };
@undoBatch
- toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight)
+ toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight);
onContextMenu = (e: React.MouseEvent) => {
if (this.props.isAnnotationOverlay || this.props.Document.annotationOn || !ContextMenu.Instance) return;
- const appearance = ContextMenu.Instance.findByDescription("Appearance...");
- const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
- appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
- !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
- appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
+ const appearance = ContextMenu.Instance.findByDescription('Appearance...');
+ const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
+ appearanceItems.push({
+ description: 'Reset View',
+ event: () => {
+ this.props.Document._panX = this.props.Document._panY = 0;
+ this.props.Document[this.scaleFieldKey] = 1;
+ },
+ icon: 'compress-arrows-alt',
+ });
+ !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
+ appearanceItems.push({
+ description: `${this.fitContentsToBox ? 'Make Zoomable' : 'Scale to Window'}`,
+ event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox),
+ icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
+ });
+ appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinDocView: true, panelWidth: this.props.PanelWidth(), panelHeight: this.props.PanelHeight() }), icon: 'map-pin' });
//appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
- this.props.ContainingCollectionView &&
- appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
+ this.props.ContainingCollectionView && 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.props.Document._isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: () => this.transcribeStrokes(false), icon: 'font' });
// this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
- !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : 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: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null;
- !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._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 && !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" });
+ !Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : 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: (SnappingManager.GetShowSnapLines() ? 'Hide' : 'Show') + ' Snap Lines', event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: 'compress-arrows-alt' })
+ : null;
+ !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._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 &&
+ !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' });
if (!Doc.noviceMode) {
- optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
+ optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? 'Freeze' : 'Unfreeze') + ' Aspect', event: this.toggleNativeDimensions, icon: 'snowflake' });
}
- !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
- const mores = ContextMenu.Instance.findByDescription("More...");
- const moreItems = mores && "subitems" in mores ? mores.subitems : [];
+ !options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
+ const mores = ContextMenu.Instance.findByDescription('More...');
+ const moreItems = mores && 'subitems' in mores ? mores.subitems : [];
if (!Doc.noviceMode) {
e.persist();
- moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
- moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) });
+ moreItems.push({ description: 'Export collection', icon: 'download', event: async () => Doc.Zip(this.props.Document) });
+ moreItems.push({ description: 'Import exported collection', icon: 'upload', event: ({ x, y }) => this.importDocument(e.clientX, e.clientY) });
}
- !mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" });
- }
+ !mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' });
+ };
importDocument = (x: number, y: number) => {
- const input = document.createElement("input");
- input.type = "file";
- input.accept = ".zip";
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.zip';
input.onchange = _e => {
- input.files && Doc.importDocument(input.files[0]).then(doc => {
- if (doc instanceof Doc) {
- const [xx, yy] = this.getTransform().transformPoint(x, y);
- doc.x = xx, doc.y = yy;
- this.props.addDocument?.(doc);}
- });
+ input.files &&
+ Doc.importDocument(input.files[0]).then(doc => {
+ if (doc instanceof Doc) {
+ const [xx, yy] = this.getTransform().transformPoint(x, y);
+ (doc.x = xx), (doc.y = yy);
+ this.props.addDocument?.(doc);
+ }
+ });
};
input.click();
- }
+ };
@undoBatch
@action
@@ -1697,13 +1827,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!math) {
const text = StrCast(this.props.Document.transcription);
- const lines = text.split("\n");
+ const lines = text.split('\n');
const height = 30 + 15 * lines.length;
this.props.ContainingCollectionView?.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 }));
}
}
- }
+ };
@action
setupDragLines = (snapToDraggedDoc: boolean = false) => {
@@ -1711,37 +1841,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const size = this.getTransform().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 isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
- let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
+ let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
!snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
!snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.getTransform().inverse();
- snappableDocs.filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc)).forEach(doc => {
- const { left, top, width, height } = docDims(doc);
- const topLeftInScreen = invXf.transformPoint(left, top);
- const docSize = invXf.transformDirection(width, height);
+ snappableDocs
+ .filter(doc => snapToDraggedDoc || !DragManager.docsBeingDragged.includes(Cast(doc.rootDocument, Doc, null) || doc))
+ .forEach(doc => {
+ const { left, top, width, height } = docDims(doc);
+ const topLeftInScreen = invXf.transformPoint(left, top);
+ const docSize = invXf.transformDirection(width, height);
- horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
- vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]);// right line
- });
+ horizLines.push(topLeftInScreen[1], topLeftInScreen[1] + docSize[1] / 2, topLeftInScreen[1] + docSize[1]); // horiz center line
+ vertLines.push(topLeftInScreen[0], topLeftInScreen[0] + docSize[0] / 2, topLeftInScreen[0] + docSize[0]); // right line
+ });
DragManager.SetSnapLines(horizLines, vertLines);
- }
+ };
onPointerOver = (e: React.PointerEvent) => {
e.stopPropagation();
- }
+ };
incrementalRender = action(() => {
if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
const loadIncrement = 5;
for (var i = 0; i < Math.min(unrendered.length, loadIncrement); i++) {
- this._renderCutoffData.set(unrendered[i][Id] + "", true);
+ this._renderCutoffData.set(unrendered[i][Id] + '', true);
}
}
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
@@ -1749,64 +1881,67 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
children = () => {
this.incrementalRender();
- const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
- return [
- ...children,
- ...this.views,
- <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
- ];
- }
+ const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : [];
+ 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.title?.toString()}</span>
- </div>;
+ return (
+ <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
+ <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
+ </div>
+ );
}
@computed get marqueeView() {
TraceMobx();
- return <MarqueeView
- {...this.props}
- ref={this._marqueeViewRef}
- ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
- nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
- addDocTab={this.addDocTab}
- trySelectCluster={this.trySelectCluster}
- activeDocuments={this.getActiveDocuments}
- selectDocuments={this.selectDocuments}
- addDocument={this.addDocument}
- addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform}
- getTransform={this.getTransform}
- isAnnotationOverlay={this.isAnnotationOverlay}>
- <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
- {this.layoutDoc._backgroundGridShow ?
- <div><CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn 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}
- zoomScaling={this.zoomScaling}
- layoutDoc={this.layoutDoc}
+ return (
+ <MarqueeView
+ {...this.props}
+ ref={this._marqueeViewRef}
+ ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
+ nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
+ addDocTab={this.addDocTab}
+ trySelectCluster={this.trySelectCluster}
+ activeDocuments={this.getActiveDocuments}
+ selectDocuments={this.selectDocuments}
+ addDocument={this.addDocument}
+ addLiveTextDocument={this.addLiveTextBox}
+ getContainerTransform={this.getContainerTransform}
+ getTransform={this.getTransform}
+ isAnnotationOverlay={this.isAnnotationOverlay}>
+ <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
+ {this.layoutDoc._backgroundGridShow ? (
+ <div>
+ <CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn 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}
+ zoomScaling={this.zoomScaling}
+ layoutDoc={this.layoutDoc}
+ isAnnotationOverlay={this.isAnnotationOverlay}
+ cachedCenteringShiftX={this.cachedCenteringShiftX}
+ cachedCenteringShiftY={this.cachedCenteringShiftY}
+ />
+ </div>
+ ) : null}
+ <CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
- cachedCenteringShiftX={this.cachedCenteringShiftX}
- cachedCenteringShiftY={this.cachedCenteringShiftY}
- /></div> : (null)}
- <CollectionFreeFormViewPannableContents
- isAnnotationOverlay={this.isAnnotationOverlay}
- isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
- transform={this.contentTransform}
- zoomScaling={this.zoomScaling}
- presPaths={BoolCast(this.Document.presPathView)}
- progressivize={BoolCast(this.Document.editProgressivize)}
- presPinView={BoolCast(this.Document.presPinView)}
- transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, "string", null)}
- viewDefDivClick={this.props.viewDefDivClick}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
- </div>
- {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : (null)}
- </MarqueeView>;
+ isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
+ transform={this.contentTransform}
+ zoomScaling={this.zoomScaling}
+ presPaths={BoolCast(this.Document.presPathView)}
+ progressivize={BoolCast(this.Document.editProgressivize)}
+ presPinView={BoolCast(this.Document.presPinView)}
+ transition={this._viewTransition ? `transform ${this._viewTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', null)}
+ viewDefDivClick={this.props.viewDefDivClick}>
+ {this.children}
+ </CollectionFreeFormViewPannableContents>
+ </div>
+ {this._showAnimTimeline ? <Timeline ref={this._timelineRef} {...this.props} /> : null}
+ </MarqueeView>
+ );
}
@computed get contentScaling() {
@@ -1819,66 +1954,83 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
private groupDropDisposer?: DragManager.DragDropDisposer;
- protected createGroupEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ protected createGroupEventsTarget = (ele: HTMLDivElement) => {
+ //used for stacking and masonry view
this.groupDropDisposer?.();
if (ele) {
this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
}
- }
+ };
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
- return <div className={"collectionfreeformview-container"} ref={this.createDashEventsTarget}
- onPointerOver={this.onPointerOver}
- onWheel={this.onPointerWheel}
- onClick={this.onClick}
- onPointerDown={this.onPointerDown}
- onPointerMove={this.onCursorMove}
- onDrop={this.onExternalDrop.bind(this)}
- onDragOver={e => e.preventDefault()}
- onContextMenu={this.onContextMenu}
- style={{
- pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" : // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
- (SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())) ? "all" : this.props.pointerEvents?.() as any,
- transform: `scale(${this.contentScaling || 1})`,
- width: `${100 / (this.contentScaling || 1)}%`,
- height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
- }}>
- {this._firstRender ?
- this.placeholder : this.marqueeView}
- {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
-
-
- <div className={"pullpane-indicator"}
+ return (
+ <div
+ className={'collectionfreeformview-container'}
+ ref={this.createDashEventsTarget}
+ onPointerOver={this.onPointerOver}
+ onWheel={this.onPointerWheel}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}
+ onPointerMove={this.onCursorMove}
+ onDrop={this.onExternalDrop.bind(this)}
+ onDragOver={e => e.preventDefault()}
+ onContextMenu={this.onContextMenu}
style={{
- display: this._pullDirection ? "block" : "none",
- top: clientRect ? this._pullDirection === "bottom" ? this._pullCoords[1] - clientRect.y : 0 : "auto",
- left: clientRect ? this._pullDirection === "right" ? this._pullCoords[0] - clientRect.x : 0 : "auto",
- width: clientRect ? this._pullDirection === "left" ? this._pullCoords[0] - clientRect.left : this._pullDirection === "right" ? clientRect.right - this._pullCoords[0] : clientRect.width : 0,
- height: clientRect ? this._pullDirection === "top" ? this._pullCoords[1] - clientRect.top : this._pullDirection === "bottom" ? clientRect.bottom - this._pullCoords[1] : clientRect.height : 0,
-
+ pointerEvents:
+ this.props.Document.type === DocumentType.MARKER
+ ? 'none' // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
+ : SnappingManager.GetIsDragging() && this.childDocs.includes(DragManager.docsBeingDragged.lastElement())
+ ? 'all'
+ : (this.props.pointerEvents?.() as any),
+ transform: `scale(${this.contentScaling || 1})`,
+ width: `${100 / (this.contentScaling || 1)}%`,
+ height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`, // : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
}}>
+ {this._firstRender ? this.placeholder : this.marqueeView}
+ {this.props.noOverlay ? null : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
+
+ <div
+ className={'pullpane-indicator'}
+ style={{
+ display: this._pullDirection ? 'block' : 'none',
+ top: clientRect ? (this._pullDirection === 'bottom' ? this._pullCoords[1] - clientRect.y : 0) : 'auto',
+ left: clientRect ? (this._pullDirection === 'right' ? this._pullCoords[0] - clientRect.x : 0) : 'auto',
+ width: clientRect ? (this._pullDirection === 'left' ? this._pullCoords[0] - clientRect.left : this._pullDirection === 'right' ? clientRect.right - this._pullCoords[0] : clientRect.width) : 0,
+ height: clientRect ? (this._pullDirection === 'top' ? this._pullCoords[1] - clientRect.top : this._pullDirection === 'bottom' ? clientRect.bottom - this._pullCoords[1] : clientRect.height) : 0,
+ }}></div>
+ {
+ // uncomment to show snap lines
+ <div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
+ <svg style={{ width: '100%', height: '100%' }}>
+ {this._hLines?.map(l => (
+ <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
+ ))}
+ {this._vLines?.map(l => (
+ <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
+ ))}
+ </svg>
+ </div>
+ }
+
+ {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? (
+ <div
+ className="collectionFreeForm-groupDropper"
+ ref={this.createGroupEventsTarget}
+ style={{
+ width: this.ChildDrag ? '10000' : '100%',
+ height: this.ChildDrag ? '10000' : '100%',
+ left: this.ChildDrag ? '-5000' : 0,
+ top: this.ChildDrag ? '-5000' : 0,
+ position: 'absolute',
+ background: '#0009930',
+ pointerEvents: 'all',
+ }}
+ />
+ ) : null}
</div>
- {// uncomment to show snap lines
- <div className="snapLines" style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
- <svg style={{ width: "100%", height: "100%" }}>
- {this._hLines?.map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />)}
- {this._vLines?.map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)}
- </svg>
- </div>}
-
- {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ?
- <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
- width: this.ChildDrag ? "10000" : "100%",
- height: this.ChildDrag ? "10000" : "100%",
- left: this.ChildDrag ? "-5000" : 0,
- top: this.ChildDrag ? "-5000" : 0,
- position: "absolute",
- background: "#0009930",
- pointerEvents: "all"
- }} /> : (null)}
- </div >;
+ );
}
}
@@ -1887,9 +2039,12 @@ interface CollectionFreeFormOverlayViewProps {
}
@observer
-class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps>{
+class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps> {
render() {
- return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele);
+ return this.props
+ .elements()
+ .filter(ele => ele.bounds?.z)
+ .map(ele => ele.ele);
}
}
@@ -1907,50 +2062,50 @@ interface CollectionFreeFormViewPannableContentsProps {
}
@observer
-class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps>{
+class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps> {
@observable _drag: string = '';
//Adds event listener so knows pointer is down and moving
onPointerDown = (e: React.PointerEvent): void => {
e.stopPropagation();
e.preventDefault();
- this._drag = (e.target as any)?.id ?? "";
+ this._drag = (e.target as any)?.id ?? '';
document.getElementById(this._drag) && setupMoveUpEvents(e.target, e, this.onPointerMove, emptyFunction, emptyFunction);
- }
+ };
//Adjusts the value in NodeStore
@action
onPointerMove = (e: PointerEvent) => {
const doc = document.getElementById('resizable');
- const toNumber = (original: number, delta: number) => original + (delta * this.props.zoomScaling());
+ const toNumber = (original: number, delta: number) => original + delta * this.props.zoomScaling();
if (doc) {
switch (this._drag) {
- case "resizer-br":
+ case 'resizer-br':
doc.style.width = toNumber(doc.offsetWidth, e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px';
break;
- case "resizer-bl":
+ case 'resizer-bl':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
break;
- case "resizer-tr":
+ case 'resizer-tr':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px';
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
- case "resizer-tl":
+ case 'resizer-tl':
doc.style.width = toNumber(doc.offsetWidth, -e.movementX) + 'px';
doc.style.height = toNumber(doc.offsetHeight, -e.movementY) + 'px';
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
- case "resizable":
+ case 'resizable':
doc.style.top = toNumber(doc.offsetTop, e.movementY) + 'px';
doc.style.left = toNumber(doc.offsetLeft, e.movementX) + 'px';
}
return false;
}
return true;
- }
+ };
// scale: NumCast(targetDoc._viewScale),
@computed get zoomProgressivizeContainer() {
@@ -1961,66 +2116,73 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
const top = NumCast(activeItem.presPinViewY);
const width = 100;
const height = 100;
- return !this.props.presPinView ? (null) :
+ return !this.props.presPinView ? null : (
<div key="resizable" className="resizable" onPointerDown={this.onPointerDown} style={{ width, height, top, left, position: 'absolute' }}>
- <div className='resizers' key={'resizer' + activeItem.id}>
- <div className='resizer top-left' onPointerDown={this.onPointerDown} />
- <div className='resizer top-right' onPointerDown={this.onPointerDown} />
- <div className='resizer bottom-left' onPointerDown={this.onPointerDown} />
- <div className='resizer bottom-right' onPointerDown={this.onPointerDown} />
+ <div className="resizers" key={'resizer' + activeItem.id}>
+ <div className="resizer top-left" onPointerDown={this.onPointerDown} />
+ <div className="resizer top-right" onPointerDown={this.onPointerDown} />
+ <div className="resizer bottom-left" onPointerDown={this.onPointerDown} />
+ <div className="resizer bottom-right" onPointerDown={this.onPointerDown} />
</div>
- </div>;
+ </div>
+ );
}
}
@computed get zoomProgressivize() {
- return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : (null);
+ return PresBox.Instance?.activeItem?.presPinView && PresBox.Instance.layoutDoc.presStatus === 'edit' ? this.zoomProgressivizeContainer : null;
}
@computed get progressivize() {
- return PresBox.Instance && this.props.progressivize ? PresBox.Instance.progressivizeChildDocs : (null);
+ return PresBox.Instance && this.props.progressivize ? PresBox.Instance.progressivizeChildDocs : null;
}
@computed get presPaths() {
- const presPaths = "presPaths" + (this.props.presPaths ? "" : "-hidden");
- return !PresBox.Instance || !this.props.presPaths ? (null) : <>
- <div key="presorder">{PresBox.Instance.order}</div>
- <svg key="svg" className={presPaths}>
- <defs>
- <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
- <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
- </marker>
- <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
- <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
- </marker>
- <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4" orient="auto" overflow="visible">
- <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
- </marker>
- </defs>
- {PresBox.Instance.paths}
- </svg>
- </>;
+ const presPaths = 'presPaths' + (this.props.presPaths ? '' : '-hidden');
+ return !PresBox.Instance || !this.props.presPaths ? null : (
+ <>
+ <div key="presorder">{PresBox.Instance.order}</div>
+ <svg key="svg" className={presPaths}>
+ <defs>
+ <marker id="markerSquare" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ <marker id="markerSquareFilled" markerWidth="3" markerHeight="3" refX="1.5" refY="1.5" orient="auto" overflow="visible">
+ <rect x="0" y="0" width="3" height="3" stroke="#69a6db" strokeWidth="1" fill="#69a6db" />
+ </marker>
+ <marker id="markerArrow" markerWidth="3" markerHeight="3" refX="2" refY="4" orient="auto" overflow="visible">
+ <path d="M2,2 L2,6 L6,4 L2,2 Z" stroke="#69a6db" strokeLinejoin="round" strokeWidth="1" fill="white" fillOpacity="0.8" />
+ </marker>
+ </defs>
+ {PresBox.Instance.paths}
+ </svg>
+ </>
+ );
}
render() {
- return <div className={"collectionfreeformview" + (this.props.viewDefDivClick ? "-viewDef" : "-none")}
- onScroll={e => {
- const target = e.target as any;
- if (getComputedStyle(target)?.overflow === "visible") { // if collection is visible, then scrolling will mess things up since there are no scroll bars
- target.scrollTop = target.scrollLeft = 0;
- }
- }}
- 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
- //willChange: "transform"
- }}>
- {this.props.children()}
- {this.presPaths}
- {this.progressivize}
- {this.zoomProgressivize}
- </div>;
+ return (
+ <div
+ className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target)?.overflow === 'visible') {
+ // if collection is visible, then scrolling will mess things up since there are no scroll bars
+ target.scrollTop = target.scrollLeft = 0;
+ }
+ }}
+ 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
+ //willChange: "transform"
+ }}>
+ {this.props.children()}
+ {this.presPaths}
+ {this.progressivize}
+ {this.zoomProgressivize}
+ </div>
+ );
}
}
@@ -2037,63 +2199,73 @@ interface CollectionFreeFormViewBackgroundGridProps {
}
@observer
class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
-
-
chooseGridSpace = (gridSpace: number): number => {
if (!this.props.zoomScaling()) return 50;
const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3;
return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
- }
+ };
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 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() + 2 * renderGridSpace;
const h = this.props.PanelHeight() + 2 * renderGridSpace;
- const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
- return <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);
+ const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+ return (
+ <canvas
+ className="collectionFreeFormView-grid"
+ width={w}
+ height={h}
+ style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={el => {
+ const ctx = el?.getContext('2d');
if (ctx) {
- ctx.strokeStyle = strokeStyle;
- ctx.beginPath();
- 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);
+ 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.beginPath();
+ 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);
+ }
+ ctx.stroke();
}
- ctx.stroke();
}
- }
- }} />;
+ }}
+ />
+ );
}
}
export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY: number) {
SelectionManager.DeselectAll();
dv.props.focus(dv.props.Document, {
- willZoom: true, afterFocus: async (didMove) => {
+ willZoom: true,
+ afterFocus: async didMove => {
if (!didMove) {
const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
- const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || "_viewScale"] !== 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
+ const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 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), 0.5);
}
return ViewAdjustment.doNothing;
- }
+ },
});
Doc.linkFollowHighlight(dv?.props.Document, false);
}
ScriptingGlobals.add(CollectionBrowseClick);
-ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(); });
-ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) { !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true); }); \ No newline at end of file
+ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
+ !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame();
+});
+ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
+ !readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
+});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8a34d5a..4513ffb39 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,36 +1,33 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCastAsync, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { intersectRect, returnFalse, Utils } from "../../../../Utils";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
-import { PresMovement } from "../../nodes/trails/PresEnums";
-import { VideoBox } from "../../nodes/VideoBox";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
-import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { SubCollectionViewProps } from "../CollectionSubView";
-import { TreeView } from "../TreeView";
-import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
-import "./MarqueeView.scss";
-import React = require("react");
-import { TabDocView } from "../TabDocView";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { intersectRect, returnFalse, Utils } from '../../../../Utils';
+import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PinViewProps, PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { PreviewCursor } from '../../PreviewCursor';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { TabDocView } from '../TabDocView';
+import { TreeView } from '../TreeView';
+import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import './MarqueeView.scss';
+import React = require('react');
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -46,15 +43,14 @@ interface MarqueeViewProps {
}
export interface MarqueeViewBounds {
- left: number;
- top: number;
- width: number;
- height: number;
+ left: number;
+ top: number;
+ width: number;
+ height: number;
}
@observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
-{
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
private _commandExecuted = false;
@observable _lastX: number = 0;
@observable _lastY: number = 0;
@@ -64,18 +60,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@observable _lassoPts: [number, number][] = [];
@observable _lassoFreehand: boolean = false;
- @computed get Transform() { return this.props.getTransform(); }
+ @computed get Transform() {
+ 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
const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
// nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
- const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+ 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; }
+ 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);
@@ -84,14 +88,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
cleanupInteractions = (all: boolean = false, hideMarquee: boolean = true) => {
if (all) {
- document.removeEventListener("pointerup", this.onPointerUp, true);
- document.removeEventListener("pointermove", this.onPointerMove, true);
+ document.removeEventListener('pointerup', this.onPointerUp, true);
+ document.removeEventListener('pointermove', this.onPointerMove, true);
}
- document.removeEventListener("keydown", this.marqueeCommand, true);
+ document.removeEventListener('keydown', this.marqueeCommand, true);
hideMarquee && this.hideMarquee();
this._lassoPts = [];
- }
+ };
@undoBatch
@action
@@ -100,76 +104,75 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// tslint:disable-next-line:prefer-const
const cm = ContextMenu.Instance;
const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
- if (e.key === "?") {
- cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
- Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: "bing", useCors: true }), "add:right"));
+ 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', useCors: true }), 'add:right'));
cm.displayMenu(this._downX, this._downY, undefined, true);
e.stopPropagation();
- } else
- if (e.key === "u" && this.props.ungroup) {
- e.stopPropagation();
- this.props.ungroup();
- }
- else if (e.key === ":") {
- DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+ } else if (e.key === 'u' && this.props.ungroup) {
+ e.stopPropagation();
+ this.props.ungroup();
+ } else if (e.key === ':') {
+ 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());
- e.stopPropagation();
- } else if (e.key === "q" && e.ctrlKey) {
- e.preventDefault();
- (async () => {
- const text: string = await navigator.clipboard.readText();
- const ns = text.split("\n").filter(t => t.trim() !== "\r" && t.trim() !== "");
- for (let i = 0; i < ns.length - 1; i++) {
- while (!(ns[i].trim() === "" || ns[i].endsWith("-\r") || ns[i].endsWith("-") ||
- ns[i].endsWith(";\r") || ns[i].endsWith(";") ||
- ns[i].endsWith(".\r") || ns[i].endsWith(".") ||
- ns[i].endsWith(":\r") || ns[i].endsWith(":")) && i < ns.length - 1) {
- const sub = ns[i].endsWith("\r") ? 1 : 0;
- const br = ns[i + 1].trim() === "";
- ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
- if (br) break;
- }
+ 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());
+ e.stopPropagation();
+ } else if (e.key === 'q' && e.ctrlKey) {
+ e.preventDefault();
+ (async () => {
+ const text: string = await navigator.clipboard.readText();
+ const ns = text.split('\n').filter(t => t.trim() !== '\r' && t.trim() !== '');
+ for (let i = 0; i < ns.length - 1; i++) {
+ while (
+ !(ns[i].trim() === '' || ns[i].endsWith('-\r') || ns[i].endsWith('-') || ns[i].endsWith(';\r') || ns[i].endsWith(';') || ns[i].endsWith('.\r') || ns[i].endsWith('.') || ns[i].endsWith(':\r') || ns[i].endsWith(':')) &&
+ i < ns.length - 1
+ ) {
+ const sub = ns[i].endsWith('\r') ? 1 : 0;
+ const br = ns[i + 1].trim() === '';
+ ns.splice(i, 2, ns[i].substr(0, ns[i].length - sub) + ns[i + 1].trimLeft());
+ if (br) break;
}
- let ypos = y;
- 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);
- ypos += 40 * this.Transform.Scale;
- });
- })();
- e.stopPropagation();
- } else if (e.key === "b" && e.ctrlKey) {
- document.body.focus(); // so that we can access the clipboard without an error
- setTimeout(() =>
- pasteImageBitmap((data: any, error: any) => {
- error && console.log(error);
- data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
- this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+ }
+ let ypos = y;
+ 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);
+ ypos += 40 * this.Transform.Scale;
+ });
+ })();
+ e.stopPropagation();
+ } else if (e.key === 'b' && e.ctrlKey) {
+ document.body.focus(); // so that we can access the clipboard without an error
+ setTimeout(() =>
+ pasteImageBitmap((data: any, error: any) => {
+ error && console.log(error);
+ data &&
+ VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+ this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
});
- }));
- } else if (e.key === "s" && e.ctrlKey) {
- e.preventDefault();
- const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
- slide.x = x;
- slide.y = y;
- FormattedTextBox.SelectOnLoad = slide[Id];
- TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
- this.props.addDocument?.(slide);
- e.stopPropagation();
- } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
- FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : "";
- FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
- this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0));
- e.stopPropagation();
- }
- }
+ })
+ );
+ } else if (e.key === 's' && e.ctrlKey) {
+ e.preventDefault();
+ const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
+ slide.x = x;
+ slide.y = y;
+ FormattedTextBox.SelectOnLoad = slide[Id];
+ TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
+ this.props.addDocument?.(slide);
+ e.stopPropagation();
+ } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
+ FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+ FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('live text batch');
+ this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
+ e.stopPropagation();
+ }
+ };
//heuristically converts pasted text into a table.
// assumes each entry is separated by a tab
// skips all rows until it gets to a row with more than one entry
@@ -178,26 +181,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
// assumes each cell is a string or a number
pasteTable(ns: string[], x: number, y: number) {
- while (ns.length > 0 && ns[0].split("\t").length < 2) {
+ while (ns.length > 0 && ns[0].split('\t').length < 2) {
ns.splice(0, 1);
}
if (ns.length > 0) {
- const columns = ns[0].split("\t");
+ const columns = ns[0].split('\t');
const docList: Doc[] = [];
- let groupAttr: string | number = "";
+ let groupAttr: string | number = '';
const rowProto = new Doc();
rowProto.title = rowProto.Id;
rowProto._width = 200;
rowProto.isPrototype = true;
for (let i = 1; i < ns.length - 1; i++) {
- const values = ns[i].split("\t");
+ const values = ns[i].split('\t');
if (values.length === 1 && columns.length > 1) {
groupAttr = values[0];
continue;
}
const docDataProto = Doc.MakeDelegate(rowProto);
docDataProto.isPrototype = true;
- columns.forEach((col, i) => docDataProto[columns[i]] = (values.length > i ? ((values[i].indexOf(Number(values[i]).toString()) !== -1) ? Number(values[i]) : values[i]) : undefined));
+ columns.forEach((col, i) => (docDataProto[columns[i]] = values.length > i ? (values[i].indexOf(Number(values[i]).toString()) !== -1 ? Number(values[i]) : values[i]) : undefined));
if (groupAttr) {
docDataProto._group = groupAttr;
}
@@ -206,7 +209,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
doc._width = 200;
docList.push(doc);
}
- const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField("_group", "#f1efeb")] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, "#f1efeb"))], docList, { x: x, y: y, title: "droppedTable", _width: 300, _height: 100 });
+ const newCol = Docs.Create.SchemaDocument([...(groupAttr ? [new SchemaHeaderField('_group', '#f1efeb')] : []), ...columns.filter(c => c).map(c => new SchemaHeaderField(c, '#f1efeb'))], docList, {
+ x: x,
+ y: y,
+ title: 'droppedTable',
+ _width: 300,
+ _height: 100,
+ });
this.props.addDocument?.(newCol);
}
@@ -227,10 +236,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// }
// bcz: do we need this? it kills the context menu on the main collection if !altKey
// e.stopPropagation();
- }
- else PreviewCursor.Visible = false;
+ } else PreviewCursor.Visible = false;
}
- }
+ };
@action
onPointerMove = (e: PointerEvent): void => {
@@ -238,8 +246,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 (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
if (!this._commandExecuted) {
this.showMarquee();
}
@@ -253,7 +260,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (PresBox.startMarquee) {
e.stopPropagation();
}
- }
+ };
@action
onPointerUp = (e: PointerEvent): void => {
@@ -270,14 +277,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const hideMarquee = () => {
this.hideMarquee();
MarqueeOptionsMenu.Instance.fadeOut(true);
- document.removeEventListener("pointerdown", hideMarquee);
- document.removeEventListener("wheel", hideMarquee);
+ document.removeEventListener('pointerdown', hideMarquee);
+ document.removeEventListener('wheel', hideMarquee);
};
if (PresBox.startMarquee) {
this.pinWithView();
PresBox.startMarquee = false;
}
- if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
+ if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100 && !PresBox.startMarquee) {
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
@@ -285,19 +292,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
- document.addEventListener("pointerdown", hideMarquee);
- document.addEventListener("wheel", hideMarquee);
+ document.addEventListener('pointerdown', hideMarquee);
+ document.addEventListener('wheel', hideMarquee);
} else {
this.hideMarquee();
}
this.cleanupInteractions(true, this._commandExecuted);
e.altKey && e.preventDefault();
- }
+ };
clearSelection() {
- if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
- else if (document.getSelection()) { document.getSelection()?.empty(); }
+ if (window.getSelection) {
+ window.getSelection()?.removeAllRanges();
+ } else if (document.getSelection()) {
+ document.getSelection()?.empty();
+ }
}
setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
@@ -312,9 +322,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._commandExecuted = false;
PreviewCursor.Visible = false;
this.cleanupInteractions(true);
- document.addEventListener("pointermove", this.onPointerMove, true);
- document.addEventListener("pointerup", this.onPointerUp, true);
- document.addEventListener("keydown", this.marqueeCommand, true);
+ document.addEventListener('pointermove', this.onPointerMove, true);
+ document.addEventListener('pointerup', this.onPointerUp, true);
+ document.addEventListener('keydown', this.marqueeCommand, true);
} else {
this._downX = x;
this._downY = y;
@@ -328,9 +338,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onClick = (e: React.MouseEvent): void => {
- if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
- Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
- if (CurrentUserUtils.ActiveTool === InkTool.None) {
+ if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+ if (Doc.ActiveTool === InkTool.None) {
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -339,17 +348,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
}
// let the DocumentView stopPropagation of this event when it selects this document
- } else { // why do we get a click event when the cursor have moved a big distance?
+ } else {
+ // why do we get a click event when the cursor have moved a big distance?
// let's cut it off here so no one else has to deal with it.
e.stopPropagation();
}
- }
+ };
@action
- showMarquee = () => { this._visible = true; }
+ showMarquee = () => {
+ this._visible = true;
+ };
@action
- hideMarquee = () => { this._visible = false; }
+ hideMarquee = () => {
+ this._visible = false;
+ };
@undoBatch
@action
@@ -361,16 +375,18 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
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";
- !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
- doc._panX = doc._panY = 0;
- return doc;
- })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+ 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';
+ !this.props.isAnnotationOverlay && Doc.AddDocToList(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+ doc._panX = doc._panY = 0;
+ return doc;
+ })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.system = undefined;
newCollection._width = this.Bounds.width;
newCollection._height = this.Bounds.height;
@@ -378,7 +394,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
newCollection.forceActive = makeGroup;
newCollection.x = this.Bounds.left;
newCollection.y = this.Bounds.top;
- selected.forEach(d => d.context = newCollection);
+ selected.forEach(d => (d.context = newCollection));
this.hideMarquee();
return newCollection;
});
@@ -393,74 +409,76 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
-
- /**
- * This triggers the TabDocView.PinDoc method which is the universal method
- * used to pin documents to the currently active presentation trail.
- *
- * This one is unique in that it includes the bounds associated with marquee view.
- */
+ };
+
+ /**
+ * This triggers the TabDocView.PinDoc method which is the universal method
+ * used to pin documents to the currently active presentation trail.
+ *
+ * This one is unique in that it includes the bounds associated with marquee view.
+ */
@undoBatch
@action
pinWithView = async () => {
const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
- const doc = this.props.Document;
- const viewOptions:PinViewProps = {
- bounds: this.Bounds,
- scale: scale
- };
- TabDocView.PinDoc(doc, {pinWithView: viewOptions});
- MarqueeOptionsMenu.Instance.fadeOut(true);
+ const doc = this.props.Document;
+ const viewOptions: PinViewProps = {
+ bounds: this.Bounds,
+ scale: scale,
+ };
+ TabDocView.PinDoc(doc, { pinWithView: viewOptions });
+ MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
+ };
@undoBatch
@action
collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
const selected = this.marqueeSelect(false);
- if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) {
- selected.map(action(d => {
- const dx = NumCast(d.x);
- const dy = NumCast(d.y);
- delete d.x;
- delete d.y;
- delete d.activeFrame;
- delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
- d.x = dx - this.Bounds.left - this.Bounds.width / 2;
- d.y = dy - this.Bounds.top - this.Bounds.height / 2;
- return d;
- }));
+ if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
+ selected.map(
+ action(d => {
+ const dx = NumCast(d.x);
+ const dy = NumCast(d.y);
+ delete d.x;
+ delete d.y;
+ delete d.activeFrame;
+ delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+ d.x = dx - this.Bounds.left - this.Bounds.width / 2;
+ d.y = dy - this.Bounds.top - this.Bounds.height / 2;
+ return d;
+ })
+ );
this.props.removeDocument?.(selected);
}
// TODO: nda - this is the code to actually get a new grouped collection
- const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
+ const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
this.props.addDocument?.(newCollection);
this.props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
- }
+ };
@undoBatch
@action
syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
- if (e instanceof KeyboardEvent ? e.key === "i" : true) {
+ if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
const inks = selected.filter(s => s.type === DocumentType.INK);
const setDocs = selected.filter(s => s.type === DocumentType.RTF && s.color);
- const sets = setDocs.map((sd) => Cast(sd.data, RichTextField)?.Text as string);
+ const sets = setDocs.map(sd => Cast(sd.data, RichTextField)?.Text as string);
const colors = setDocs.map(sd => FieldValue(sd.color) as string);
const wordToColor = new Map<string, string>();
- sets.forEach((st: string, i: number) => st.split(",").forEach(word => wordToColor.set(word, colors[i])));
+ sets.forEach((st: string, i: number) => st.split(',').forEach(word => wordToColor.set(word, colors[i])));
const strokes: InkData[] = [];
inks.filter(i => Cast(i.data, InkField)).forEach(i => {
const d = Cast(i.data, InkField, null);
- const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
- const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+ const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+ const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
});
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+ 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;
@@ -501,12 +519,12 @@ 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");
+ 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 }));
});
}
- }
+ };
@undoBatch
@action
@@ -517,15 +535,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
d.y = NumCast(d.y) - this.Bounds.top;
return d;
});
- const summary = Docs.Create.TextDocument("", { backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
- const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
- DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
+ const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+ const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+ DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', '');
portal.hidden = true;
this.props.addDocument?.(portal);
this.props.addLiveTextDocument(summary);
MarqueeOptionsMenu.Instance.fadeOut(true);
- }
+ };
@action
background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
@@ -534,33 +552,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
setTimeout(() => this.props.selectDocuments([newCollection]));
- }
+ };
@undoBatch
marqueeCommand = action((e: KeyboardEvent) => {
if (this._commandExecuted || (e as any).propagationIsStopped) {
return;
}
- if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
+ if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd') {
this._commandExecuted = true;
e.stopPropagation();
(e as any).propagationIsStopped = true;
this.delete();
e.stopPropagation();
}
- if ("cbtsSpg".indexOf(e.key) !== -1) {
+ if ('cbtsSpg'.indexOf(e.key) !== -1) {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
(e as any).propagationIsStopped = true;
- if (e.key === "g") this.collection(e, true);
- if (e.key === "c" || e.key === "t") this.collection(e);
- if (e.key === "s" || e.key === "S") this.summary(e);
- if (e.key === "b") this.background(e);
- if (e.key === "p") this.pileup(e);
+ if (e.key === 'g') this.collection(e, true);
+ if (e.key === 'c' || e.key === 't') this.collection(e);
+ if (e.key === 's' || e.key === 'S') this.summary(e);
+ if (e.key === 'b') this.background(e);
+ if (e.key === 'p') this.pileup(e);
this.cleanupInteractions(false);
}
- if (e.key === "r" || e.key === " ") {
+ if (e.key === 'r' || e.key === ' ') {
this._commandExecuted = true;
e.stopPropagation();
e.preventDefault();
@@ -568,18 +586,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
}
});
- touchesLine(r1: { left: number, top: number, width: number, height: number }) {
+ touchesLine(r1: { left: number; top: number; width: number; height: number }) {
for (const lassoPt of this._lassoPts) {
const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
- if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width &&
- r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
+ if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
return true;
}
}
return false;
}
- boundingShape(r1: { left: number, top: number, width: number, height: number }) {
+ boundingShape(r1: { left: number; top: number; width: number; height: number }) {
const xs = this._lassoPts.map(pair => pair[0]);
const ys = this._lassoPts.map(pair => pair[1]);
const tl = this.Transform.transformPoint(Math.min(...xs), Math.min(...ys));
@@ -592,10 +609,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
let hasRight = false;
for (const lassoPt of this._lassoPts) {
const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
- hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
- hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
- hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
- hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+ hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+ hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+ hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+ hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
if (hasTop && hasLeft && hasBottom && hasRight) {
return true;
}
@@ -615,9 +632,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
(this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
}
};
- this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
- if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
- if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
+ this.props
+ .activeDocuments()
+ .filter(doc => !doc.z && !doc._lockedPosition)
+ .map(selectFunc);
+ if (!selection.length && selectBackgrounds)
+ this.props
+ .activeDocuments()
+ .filter(doc => doc.z === undefined)
+ .map(selectFunc);
+ if (!selection.length)
+ this.props
+ .activeDocuments()
+ .filter(doc => doc.z !== undefined)
+ .map(selectFunc);
return selection;
}
@@ -625,31 +653,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
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);
- return <div className="marquee" style={{
- transform: `translate(${p[0]}px, ${p[1]}px)`,
- width: Math.abs(v[0]),
- height: Math.abs(v[1]),
- zIndex: 2000
- }}> {this._lassoFreehand ?
- <svg height={2000} width={2000}>
- <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + "," + pt[1] + " ", "")} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
- </svg>
- :
- <span className="marquee-legend" />}
- </div>;
+ return (
+ <div
+ className="marquee"
+ style={{
+ transform: `translate(${p[0]}px, ${p[1]}px)`,
+ width: Math.abs(v[0]),
+ height: Math.abs(v[1]),
+ zIndex: 2000,
+ }}>
+ {' '}
+ {this._lassoFreehand ? (
+ <svg height={2000} width={2000}>
+ <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
+ </svg>
+ ) : (
+ <span className="marquee-legend" />
+ )}
+ </div>
+ );
}
render() {
- return <div className="marqueeView"
- style={{
- overflow: StrCast(this.props.Document._overflow),
- cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
- }}
-
- onDragOver={e => e.preventDefault()}
- onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
- {this._visible ? this.marqueeDiv : null}
- {this.props.children}
- </div>;
+ return (
+ <div
+ className="marqueeView"
+ style={{
+ overflow: StrCast(this.props.Document._overflow),
+ cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible || PresBox.startMarquee ? 'crosshair' : 'pointer',
+ }}
+ onDragOver={e => e.preventDefault()}
+ onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 3d2ed0cf9..3d1d87aa0 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,14 +1,13 @@
import * as React from 'react';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
-import "../../../../../node_modules/react-grid-layout/css/styles.css";
-import "../../../../../node_modules/react-resizable/css/styles.css";
+import '../../../../../node_modules/react-grid-layout/css/styles.css';
+import '../../../../../node_modules/react-resizable/css/styles.css';
import * as GridLayout from 'react-grid-layout';
import { Layout } from 'react-grid-layout';
export { Layout } from 'react-grid-layout';
-
interface GridProps {
width: number;
nodeList: JSX.Element[] | null;
@@ -29,9 +28,10 @@ interface GridProps {
@observer
export default class Grid extends React.Component<GridProps> {
render() {
- const compactType = this.props.compactType === "vertical" || this.props.compactType === "horizontal" ? this.props.compactType : null;
+ const compactType = this.props.compactType === 'vertical' || this.props.compactType === 'horizontal' ? this.props.compactType : null;
return (
- <GridLayout className="layout"
+ <GridLayout
+ className="layout"
layout={this.props.layout}
cols={this.props.numCols}
rowHeight={this.props.rowHeight}
@@ -43,9 +43,8 @@ export default class Grid extends React.Component<GridProps> {
useCSSTransforms={true}
onLayoutChange={this.props.setLayout}
preventCollision={this.props.preventCollision}
- transformScale={1 / this.props.transformScale} // still doesn't work :(
- margin={[this.props.margin, this.props.margin]}
- >
+ transformScale={1 / this.props.transformScale} // still doesn't work :( ??
+ margin={[this.props.margin, this.props.margin]}>
{this.props.nodeList}
</GridLayout>
);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c31267e87..0d7d67dd8 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -7,27 +7,23 @@ import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
-import { DocumentManager } from "../../../util/DocumentManager";
+import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
import { Colors, Shadows } from '../../global/globalEnums';
-import { AudioBox } from '../../nodes/AudioBox';
import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { DocumentView } from '../../nodes/DocumentView';
import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
import { StyleProp } from '../../StyleProvider';
import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
-import { CollectionViewType } from '../CollectionView';
-import "./CollectionLinearView.scss";
-
+import './CollectionLinearView.scss';
/**
- * CollectionLinearView is the class for rendering the horizontal collection
+ * CollectionLinearView is the class for rendering the horizontal collection
* of documents, it useful for horizontal menus. It can either be expandable
- * or not using the linearViewExpandable field.
+ * or not using the linearViewExpandable field.
* It is used in the following locations:
* - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
* - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
@@ -48,45 +44,47 @@ export class CollectionLinearView extends CollectionSubView() {
}
componentDidMount() {
- this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
+ this._widthDisposer = reaction(
+ () => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * this.rootDoc[HeightSym]() : 10),
width => this.childDocs.length && (this.layoutDoc._width = width),
{ fireImmediately: true }
);
this._selectedDisposer = reaction(
() => NumCast(this.layoutDoc.selectedIndex),
- (i) => runInAction(() => {
- this._selectedIndex = i;
- let selected: any = undefined;
- this.childLayoutPairs.map(async (pair, ind) => {
- const isSelected = this._selectedIndex === ind;
- if (isSelected) {
- selected = pair;
- }
- else {
- ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+ i =>
+ runInAction(() => {
+ this._selectedIndex = i;
+ let selected: any = undefined;
+ this.childLayoutPairs.map(async (pair, ind) => {
+ const isSelected = this._selectedIndex === ind;
+ if (isSelected) {
+ selected = pair;
+ } else {
+ ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+ }
+ });
+ if (selected && selected.layout) {
+ ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
}
- });
- if (selected && selected.layout) {
- ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
- }
- }),
+ }),
{ fireImmediately: true }
);
}
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+ //used for stacking and masonry view
this._dropDisposer && this._dropDisposer();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
}
- }
+ };
dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
getTransform = (ele: Opt<HTMLDivElement>) => {
if (!ele) return Transform.Identity();
const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
return new Transform(-translateX, -translateY, 1);
- }
+ };
@action
exitLongLinks = () => {
@@ -99,28 +97,27 @@ export class CollectionLinearView extends CollectionSubView() {
}
DocumentLinksButton.StartLink = undefined;
DocumentLinksButton.StartLinkView = undefined;
- }
+ };
@action
changeDescriptionSetting = () => {
if (LinkDescriptionPopup.showDescriptions) {
- if (LinkDescriptionPopup.showDescriptions === "ON") {
- LinkDescriptionPopup.showDescriptions = "OFF";
+ if (LinkDescriptionPopup.showDescriptions === 'ON') {
+ LinkDescriptionPopup.showDescriptions = 'OFF';
LinkDescriptionPopup.descriptionPopup = false;
} else {
- LinkDescriptionPopup.showDescriptions = "ON";
+ LinkDescriptionPopup.showDescriptions = 'ON';
}
} else {
- LinkDescriptionPopup.showDescriptions = "OFF";
+ LinkDescriptionPopup.showDescriptions = 'OFF';
LinkDescriptionPopup.descriptionPopup = false;
}
- }
+ };
myContextMenu = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
- }
-
+ };
getDisplayDoc = (doc: Doc, preview: boolean = false) => {
const nested = doc._viewType === CollectionViewType.Linear;
@@ -129,44 +126,50 @@ export class CollectionLinearView extends CollectionSubView() {
let dref: Opt<HTMLDivElement>;
const docXf = () => this.getTransform(dref);
// const scalable = pair.layout.onClick || pair.layout.onDragStart;
- return hidden ? (null) : <div className={preview ? "preview" : `collectionLinearView-docBtn`} key={doc[Id]} ref={r => dref = r || undefined}
- style={{
- pointerEvents: "all",
- width: nested ? undefined : NumCast(doc._width),
- height: nested ? undefined : NumCast(doc._height),
- marginLeft: !nested ? 2.5 : 0,
- marginRight: !nested ? 2.5 : 0,
- // width: NumCast(pair.layout._width),
- // height: NumCast(pair.layout._height),
- }} >
- <DocumentView
- Document={doc}
- isContentActive={this.props.isContentActive}
- isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={docXf}
- PanelWidth={nested ? doc[WidthSym] : this.dimension}
- PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
- renderDepth={this.props.renderDepth + 1}
- dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
- focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined}
- hideResizeHandles={true} />
- </div>;
- }
+ return hidden ? null : (
+ <div
+ className={preview ? 'preview' : `collectionLinearView-docBtn`}
+ key={doc[Id]}
+ ref={r => (dref = r || undefined)}
+ style={{
+ pointerEvents: 'all',
+ width: nested ? undefined : NumCast(doc._width),
+ height: nested ? undefined : NumCast(doc._height),
+ marginLeft: !nested ? 2.5 : 0,
+ marginRight: !nested ? 2.5 : 0,
+ // width: NumCast(pair.layout._width),
+ // height: NumCast(pair.layout._height),
+ }}>
+ <DocumentView
+ Document={doc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={docXf}
+ PanelWidth={nested ? doc[WidthSym] : this.dimension}
+ PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
+ renderDepth={this.props.renderDepth + 1}
+ dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ hideResizeHandles={true}
+ />
+ </div>
+ );
+ };
render() {
const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
@@ -178,66 +181,99 @@ export class CollectionLinearView extends CollectionSubView() {
const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
- const menuOpener = <label htmlFor={`${guid}`}
- style={{
- boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
- color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
- backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
- }}
- onPointerDown={e => e.stopPropagation()} >
- <div className="collectionLinearView-menuOpener">
- {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
- </div>
- </label>;
-
- return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
- <div className="collectionLinearView" ref={this.createDashEventsTarget}
- onContextMenu={this.myContextMenu} >
- {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
- {menuOpener}
- </Tooltip>}
- <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
- onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
-
- <div className="collectionLinearView-content"
- style={{
- height: this.dimension(),
- flexDirection: flexDir,
- gap: flexGap
- }}>
- {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
- </div>
- {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null :
- <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
- onPointerDown={e => e.stopPropagation()} >
- <span className="bottomPopup-text" >
- Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
- </span>
+ const menuOpener = (
+ <label
+ htmlFor={`${guid}`}
+ style={{
+ boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
+ color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
+ backgroundColor: backgroundColor === color ? 'black' : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY,
+ }}
+ onPointerDown={e => e.stopPropagation()}>
+ <div className="collectionLinearView-menuOpener">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={'minus'} /> : icon ? icon : <FontAwesomeIcon icon={'plus'} />}</div>
+ </label>
+ );
- <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"}
- </span>
+ return (
+ <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : 'transparent' }}>
+ <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu}>
+ {!expandable ? null : (
+ <Tooltip
+ title={
+ <>
+ <div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}</div>
+ </>
+ }
+ placement="top">
+ {menuOpener}
</Tooltip>
+ )}
+ <input
+ id={`${guid}`}
+ type="checkbox"
+ checked={BoolCast(this.props.Document.linearViewIsExpanded)}
+ ref={this.addMenuToggle}
+ onChange={action(() => (this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked))}
+ />
- <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
- <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
- Stop
+ <div
+ className="collectionLinearView-content"
+ style={{
+ height: this.dimension(),
+ flexDirection: flexDir,
+ gap: flexGap,
+ }}>
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+ </div>
+ {!DocumentLinksButton.StartLink || this.layoutDoc !== Doc.MyDockedBtns ? null : (
+ <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+ <span className="bottomPopup-text">
+ Creating link from:{' '}
+ <b>
+ {(DocumentLinksButton.AnnotationId ? 'Annotation in ' : ' ') +
+ (StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...')}
+ </b>
</span>
- </Tooltip>
- </span>}
- {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) :
- <span className="bottomPopup-background">
- <span className="bottomPopup-text">
- Currently playing:
- {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
- <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
- {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
- </span>)}
- </span>
- </span>}
+ <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'}
+ </span>
+ </Tooltip>
+
+ <Tooltip
+ title={
+ <>
+ <div className="dash-tooltip">Exit linking mode</div>
+ </>
+ }
+ placement="top">
+ <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+ Stop
+ </span>
+ </Tooltip>
+ </span>
+ )}
+ {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== Doc.MyDockedBtns ? null : (
+ <span className="bottomPopup-background">
+ <span className="bottomPopup-text">
+ Currently playing:
+ {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
+ <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
+ {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '' : ',')}
+ </span>
+ ))}
+ </span>
+ </span>
+ )}
+ </div>
</div>
- </div>;
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
index 2df95ffd8..28d2e6ab1 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
@@ -1,20 +1,13 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
-import { Doc } from "../../../../fields/Doc";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, StrCast } from "../../../../fields/Types";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager, dropActionType, SetupDrag } from "../../../util/DragManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import "./CollectionSchemaView.scss";
+import React = require('react');
+import { action } from 'mobx';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { DragManager } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import './CollectionSchemaView.scss';
export interface MovableColumnProps {
- columnRenderer: TableCellRenderer;
+ columnRenderer: React.ReactNode;
columnValue: SchemaHeaderField;
allColumns: SchemaHeaderField[];
reorderColumns: (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columns: SchemaHeaderField[]) => void;
@@ -26,7 +19,7 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
// The container of the function that is responsible for moving the column over to a new plac
private _colDropDisposer?: DragManager.DragDropDisposer;
// initial column position
- private _startDragPosition: { x: number, y: number } = { x: 0, y: 0 };
+ private _startDragPosition: { x: number; y: number } = { x: 0, y: 0 };
// sensitivity to being dragged, in pixels
private _sensitivity: number = 16;
// Column reference ID
@@ -35,45 +28,45 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
onPointerEnter = (e: React.PointerEvent): void => {
// if the column is left-clicked and it is being dragged
if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
- this._header!.current!.className = "collectionSchema-col-wrapper";
- document.addEventListener("pointermove", this.onDragMove, true);
+ this._header!.current!.className = 'collectionSchema-col-wrapper';
+ document.addEventListener('pointermove', this.onDragMove, true);
}
- }
+ };
onPointerLeave = (e: React.PointerEvent): void => {
- this._header!.current!.className = "collectionSchema-col-wrapper";
- document.removeEventListener("pointermove", this.onDragMove, true);
- !e.buttons && document.removeEventListener("pointermove", this.onPointerMove);
- }
+ this._header!.current!.className = 'collectionSchema-col-wrapper';
+ document.removeEventListener('pointermove', this.onDragMove, true);
+ !e.buttons && document.removeEventListener('pointermove', this.onPointerMove);
+ };
onDragMove = (e: PointerEvent): void => {
// only take into account the horizonal direction when a column is dragged
const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
const rect = this._header!.current!.getBoundingClientRect();
// Now store the point at the top center of the column when it was in its original position
- const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
// to be compared with its new horizontal position
const before = x[0] < bounds[0];
- this._header!.current!.className = "collectionSchema-col-wrapper";
- if (before) this._header!.current!.className += " col-before";
- if (!before) this._header!.current!.className += " col-after";
+ this._header!.current!.className = 'collectionSchema-col-wrapper';
+ if (before) this._header!.current!.className += ' col-before';
+ if (!before) this._header!.current!.className += ' col-after';
e.stopPropagation();
- }
+ };
createColDropTarget = (ele: HTMLDivElement) => {
this._colDropDisposer?.();
if (ele) {
this._colDropDisposer = DragManager.MakeDropTarget(ele, this.colDrop.bind(this));
}
- }
+ };
colDrop = (e: Event, de: DragManager.DropEvent) => {
- document.removeEventListener("pointermove", this.onDragMove, true);
+ document.removeEventListener('pointermove', this.onDragMove, true);
// we only care about whether the column is shifted to the side
const x = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
// get the dimensions of the smallest rectangle that bounds the header
const rect = this._header!.current!.getBoundingClientRect();
- const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + ((rect.right - rect.left) / 2), rect.top);
+ const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left + (rect.right - rect.left) / 2, rect.top);
// get whether the column was dragged before or after where it is now
const before = x[0] < bounds[0];
const colDragData = de.complete.columnDragData;
@@ -84,20 +77,20 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
return true;
}
return false;
- }
+ };
onPointerMove = (e: PointerEvent) => {
const onRowMove = (e: PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointermove', onRowMove);
document.removeEventListener('pointerup', onRowUp);
const dragData = new DragManager.ColumnDragData(this.props.columnValue);
DragManager.StartColumnDrag(this._dragRef.current!, dragData, e.x, e.y);
};
const onRowUp = (): void => {
- document.removeEventListener("pointermove", onRowMove);
+ document.removeEventListener('pointermove', onRowMove);
document.removeEventListener('pointerup', onRowUp);
};
// if the left mouse button is the one being held
@@ -105,30 +98,29 @@ export class MovableColumn extends React.Component<MovableColumnProps> {
const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX - this._startDragPosition.x, e.clientY - this._startDragPosition.y);
// If the movemnt of the drag exceeds the sensitivity value
if (Math.abs(dx) + Math.abs(dy) > this._sensitivity) {
- document.removeEventListener("pointermove", this.onPointerMove);
+ document.removeEventListener('pointermove', this.onPointerMove);
e.stopPropagation();
- document.addEventListener("pointermove", onRowMove);
- document.addEventListener("pointerup", onRowUp);
+ document.addEventListener('pointermove', onRowMove);
+ document.addEventListener('pointerup', onRowUp);
}
}
- }
+ };
onPointerUp = (e: React.PointerEvent) => {
- document.removeEventListener("pointermove", this.onPointerMove);
- }
+ document.removeEventListener('pointermove', this.onPointerMove);
+ };
@action
onPointerDown = (e: React.PointerEvent, ref: React.RefObject<HTMLDivElement>) => {
this._dragRef = ref;
const [dx, dy] = this.props.ScreenToLocalTransform().transformDirection(e.clientX, e.clientY);
// If the cell thing dragged is not being edited
- if (!(e.target as any)?.tagName.includes("INPUT")) {
+ if (!(e.target as any)?.tagName.includes('INPUT')) {
this._startDragPosition = { x: dx, y: dy };
- document.addEventListener("pointermove", this.onPointerMove);
+ document.addEventListener('pointermove', this.onPointerMove);
}
- }
-
+ };
render() {
const reference = React.createRef<HTMLDivElement>();
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
index 0e19ef3d9..f872637e5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
@@ -1,17 +1,16 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action } from "mobx";
-import { ReactTableDefaults, RowInfo, TableCellRenderer } from "react-table";
-import { Doc } from "../../../../fields/Doc";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, StrCast } from "../../../../fields/Types";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager, dropActionType, SetupDrag } from "../../../util/DragManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import "./CollectionSchemaView.scss";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action } from 'mobx';
+import * as React from 'react';
+import { ReactTableDefaults, RowInfo } from 'react-table';
+import { Doc } from '../../../../fields/Doc';
+import { Cast, FieldValue, StrCast } from '../../../../fields/Types';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager, dropActionType, SetupDrag } from '../../../util/DragManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import './CollectionSchemaView.scss';
export interface MovableRowProps {
rowInfo: RowInfo;
@@ -25,7 +24,7 @@ export interface MovableRowProps {
addDocTab: any;
}
-export class MovableRow extends React.Component<MovableRowProps> {
+export class MovableRow extends React.Component<React.PropsWithChildren<MovableRowProps>> {
private _header?: React.RefObject<HTMLDivElement> = React.createRef();
private _rowDropDisposer?: DragManager.DragDropDisposer;
@@ -33,28 +32,27 @@ export class MovableRow extends React.Component<MovableRowProps> {
// Create one when the mouse starts hovering...
onPointerEnter = (e: React.PointerEvent): void => {
if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
- this._header!.current!.className = "collectionSchema-row-wrapper";
- document.addEventListener("pointermove", this.onDragMove, true);
+ this._header!.current!.className = 'collectionSchema-row-wrapper';
+ document.addEventListener('pointermove', this.onDragMove, true);
}
- }
+ };
// ... and delete it when the mouse leaves
onPointerLeave = (e: React.PointerEvent): void => {
- this._header!.current!.className = "collectionSchema-row-wrapper";
- document.removeEventListener("pointermove", this.onDragMove, true);
- }
+ this._header!.current!.className = 'collectionSchema-row-wrapper';
+ document.removeEventListener('pointermove', this.onDragMove, true);
+ };
// The method for the event listener, reorders columns when dragged to their new locations.
onDragMove = (e: PointerEvent): void => {
const x = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY);
const rect = this._header!.current!.getBoundingClientRect();
const bounds = this.props.ScreenToLocalTransform().transformPoint(rect.left, rect.top + rect.height / 2);
const before = x[1] < bounds[1];
- this._header!.current!.className = "collectionSchema-row-wrapper";
- if (before) this._header!.current!.className += " row-above";
- if (!before) this._header!.current!.className += " row-below";
+ this._header!.current!.className = 'collectionSchema-row-wrapper';
+ if (before) this._header!.current!.className += ' row-above';
+ if (!before) this._header!.current!.className += ' row-below';
e.stopPropagation();
- }
+ };
componentWillUnmount() {
-
this._rowDropDisposer?.();
}
//
@@ -63,7 +61,7 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (ele) {
this._rowDropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this));
}
- }
+ };
// Controls what hppens when a row is dragged and dropped
rowDrop = (e: Event, de: DragManager.DropEvent) => {
this.onPointerLeave(e as any);
@@ -81,34 +79,34 @@ export class MovableRow extends React.Component<MovableRowProps> {
if (docDragData.draggedDocuments[0] === rowDoc) return true;
const addDocument = (doc: Doc | Doc[]) => this.props.addDoc(doc, rowDoc, before);
const movedDocs = docDragData.draggedDocuments;
- return (docDragData.dropAction || docDragData.userDropAction) ?
- docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
- : (docDragData.moveDocument) ?
- movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
- : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
+ return docDragData.dropAction || docDragData.userDropAction
+ ? docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before) || added, false)
+ : docDragData.moveDocument
+ ? movedDocs.reduce((added: boolean, d) => docDragData.moveDocument?.(d, rowDoc, addDocument) || added, false)
+ : docDragData.droppedDocuments.reduce((added: boolean, d) => this.props.addDoc(d, rowDoc, before), false);
}
return false;
- }
+ };
onRowContextMenu = (e: React.MouseEvent): void => {
- const description = this.props.rowWrapped ? "Unwrap text on row" : "Text wrap row";
- ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: "file-pdf" });
- }
+ const description = this.props.rowWrapped ? 'Unwrap text on row' : 'Text wrap row';
+ ContextMenu.Instance.addItem({ description: description, event: () => this.props.textWrapRow(this.props.rowInfo.original), icon: 'file-pdf' });
+ };
@undoBatch
@action
move: DragManager.MoveFunction = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDoc) => {
const targetView = targetCollection && DocumentManager.Instance.getDocumentView(targetCollection);
return doc !== targetCollection && doc !== targetView?.props.ContainingCollectionDoc && this.props.removeDoc(doc) && addDoc(doc);
- }
+ };
@action
onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
- console.log("yes");
- if (e.key === "Backspace" || e.key === "Delete") {
+ console.log('yes');
+ if (e.key === 'Backspace' || e.key === 'Delete') {
undoBatch(() => this.props.removeDoc(this.props.rowInfo.original));
}
- }
+ };
render() {
const { children = null, rowInfo } = this.props;
@@ -120,23 +118,29 @@ export class MovableRow extends React.Component<MovableRowProps> {
const { original } = rowInfo;
const doc = FieldValue(Cast(original, Doc));
- if (!doc) return (null);
+ if (!doc) return null;
const reference = React.createRef<HTMLDivElement>();
const onItemDown = SetupDrag(reference, () => doc, this.move, StrCast(this.props.dropAction) as dropActionType);
- let className = "collectionSchema-row";
- if (this.props.rowFocused) className += " row-focused";
- if (this.props.rowWrapped) className += " row-wrapped";
+ let className = 'collectionSchema-row';
+ if (this.props.rowFocused) className += ' row-focused';
+ if (this.props.rowWrapped) className += ' row-wrapped';
return (
<div className={className} onKeyPress={this.onKeyDown} ref={this.createRowDropTarget} onContextMenu={this.onRowContextMenu}>
<div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
- <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
+ <ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown}>
<div className="row-dragger">
- <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
- <div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
- <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+ <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}>
+ <FontAwesomeIcon icon="trash" size="sm" />
+ </div>
+ <div className="row-option" style={{ cursor: 'grab' }} ref={reference} onPointerDown={onItemDown}>
+ <FontAwesomeIcon icon="grip-vertical" size="sm" />
+ </div>
+ <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, 'add:right')}>
+ <FontAwesomeIcon icon="external-link-alt" size="sm" />
+ </div>
</div>
{children}
</ReactTableDefaults.TrComponent>
@@ -144,4 +148,4 @@ export class MovableRow extends React.Component<MovableRowProps> {
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index b64e9dac1..19401c7f0 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,4 +1,5 @@
-@import "../../global/globalCssVariables.scss";
+@import '../../global/globalCssVariables.scss';
+@import '../../../../../node_modules/react-table/react-table.css';
.collectionSchemaView-container {
border-width: $COLLECTION_BORDER_WIDTH;
border-color: $medium-gray;
@@ -218,8 +219,6 @@
}
}
-
-
.collectionSchemaView-header {
height: 100%;
color: gray;
@@ -289,8 +288,6 @@ button.add-column {
}
}
-
-
.keys-dropdown {
position: relative;
//width: 100%;
@@ -300,13 +297,12 @@ button.add-column {
padding: 3px;
height: 28px;
font-weight: bold;
- letter-spacing: "2px";
- text-transform: "uppercase";
+ letter-spacing: '2px';
+ text-transform: 'uppercase';
&:focus {
font-weight: normal;
}
}
-
}
.columnMenu-colors {
display: flex;
@@ -338,7 +334,6 @@ button.add-column {
margin-right: 5px;
font-size: 10px;
border-radius: 3px;
-
}
.keys-options-wrapper {
@@ -348,7 +343,7 @@ button.add-column {
top: 100%;
z-index: 21;
background-color: #ffffff;
- box-shadow: 0px 3px 4px rgba(0,0,0,30%);
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%);
padding: 1px;
.key-option {
cursor: pointer;
@@ -473,8 +468,8 @@ button.add-column {
}
.collectionSchemaView-cellContents-docButton {
float: right;
- width: "15px";
- height: "15px";
+ width: '15px';
+ height: '15px';
}
.collectionSchemaView-dropdownWrapper {
border: grey;
@@ -490,10 +485,10 @@ button.add-column {
display: inline-block;
//float: right;
height: 100%;
- display: "flex";
+ display: 'flex';
font-size: 13;
- justify-content: "center";
- align-items: "center";
+ justify-content: 'center';
+ align-items: 'center';
}
}
.collectionSchemaView-dropdownContainer {
@@ -601,4 +596,4 @@ button.add-column {
font-size: 10.5px;
margin-left: 50px;
margin-top: 10px;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 9eba788a9..c4ee1805f 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,30 +1,29 @@
-import React = require("react");
+import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked } from "mobx";
-import { observer } from "mobx-react";
-import Measure from "react-measure";
-import { Resize } from "react-table";
-import "react-table/react-table.css";
-import { Doc, Opt } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, NumCast } from "../../../../fields/Types";
-import { TraceMobx } from "../../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { DocUtils } from "../../../documents/Documents";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import { ContextMenuProps } from "../../ContextMenuItem";
+import { action, computed, observable, untracked } from 'mobx';
+import { observer } from 'mobx-react';
+import Measure from 'react-measure';
+import { Resize } from 'react-table';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { Cast, NumCast } from '../../../../fields/Types';
+import { TraceMobx } from '../../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { DocUtils } from '../../../documents/Documents';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { ContextMenuProps } from '../../ContextMenuItem';
import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView } from "../../nodes/DocumentView";
-import { DefaultStyleProvider } from "../../StyleProvider";
-import { CollectionSubView } from "../CollectionSubView";
-import "./CollectionSchemaView.scss";
-import { SchemaTable } from "./SchemaTable";
+import { DocumentView } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { CollectionSubView } from '../CollectionSubView';
+import './CollectionSchemaView.scss';
+import { SchemaTable } from './SchemaTable';
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
export enum ColumnType {
@@ -35,14 +34,21 @@ export enum ColumnType {
Doc,
Image,
List,
- Date
+ Date,
}
// this map should be used for keys that should have a const type of value
const columnTypes: Map<string, ColumnType> = new Map([
- ["title", ColumnType.String],
- ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
- ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
- ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+ ['title', ColumnType.String],
+ ['x', ColumnType.Number],
+ ['y', ColumnType.Number],
+ ['_width', ColumnType.Number],
+ ['_height', ColumnType.Number],
+ ['_nativeWidth', ColumnType.Number],
+ ['_nativeHeight', ColumnType.Number],
+ ['isPrototype', ColumnType.Boolean],
+ ['_curPage', ColumnType.Number],
+ ['_currentTimecode', ColumnType.Number],
+ ['zIndex', ColumnType.Number],
]);
@observer
@@ -51,7 +57,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _previewDoc: Doc | undefined = undefined;
@observable _focusedTable: Doc = this.props.Document;
- @observable _col: any = "";
+ @observable _col: any = '';
@observable _menuWidth = 0;
@observable _headerOpen = false;
@observable _headerIsEditing = false;
@@ -60,19 +66,33 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _pointerY = 0;
@observable _openTypes: boolean = false;
- @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
- @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
- @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); }
- @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
- @computed get scale() { return this.props.ScreenToLocalTransform().Scale; }
- @computed get columns() { return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); }
- set columns(columns: SchemaHeaderField[]) { this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); }
+ @computed get previewWidth() {
+ return () => NumCast(this.props.Document.schemaPreviewWidth);
+ }
+ @computed get previewHeight() {
+ return () => this.props.PanelHeight() - 2 * this.borderWidth;
+ }
+ @computed get tableWidth() {
+ return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+ }
+ @computed get borderWidth() {
+ return Number(COLLECTION_BORDER_WIDTH);
+ }
+ @computed get scale() {
+ return this.props.ScreenToLocalTransform().Scale;
+ }
+ @computed get columns() {
+ return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []);
+ }
+ set columns(columns: SchemaHeaderField[]) {
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns);
+ }
@computed get menuCoordinates() {
let searchx = 0;
let searchy = 0;
if (this.props.Document._searchDoc) {
- const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
+ const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0];
if (el !== undefined) {
const rect = el.getBoundingClientRect();
searchx = rect.x;
@@ -93,13 +113,13 @@ export class CollectionSchemaView extends CollectionSubView() {
// then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
// is displayed (unlikely) it won't show up until something else changes.
//TODO Types
- untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false))));
+ untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)))));
- this.columns.forEach(key => keys[key.heading] = true);
+ this.columns.forEach(key => (keys[key.heading] = true));
return Array.from(Object.keys(keys));
}
- @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
+ @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing);
@undoBatch
setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
@@ -124,7 +144,7 @@ export class CollectionSchemaView extends CollectionSubView() {
columns[index] = columnField;
this.columns = columns; // need to set the columns to trigger rerender
}
- }
+ };
@undoBatch
@action
@@ -137,80 +157,109 @@ export class CollectionSchemaView extends CollectionSubView() {
column.setDesc(descending);
columns[index] = column;
this.columns = columns;
- }
+ };
renderTypes = (col: any) => {
- if (columnTypes.get(col.heading)) return (null);
+ if (columnTypes.get(col.heading)) return null;
const type = col.type;
- const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}>
- <FontAwesomeIcon icon={"align-justify"} size="sm" />
- Any
- </div>;
-
- const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}>
- <FontAwesomeIcon icon={"hashtag"} size="sm" />
- Number
- </div>;
-
- const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}>
- <FontAwesomeIcon icon={"font"} size="sm" />
- Text
- </div>;
-
- const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
- <FontAwesomeIcon icon={"check-square"} size="sm" />
- Checkbox
- </div>;
-
- const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}>
- <FontAwesomeIcon icon={"list-ul"} size="sm" />
- List
- </div>;
-
- const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
- <FontAwesomeIcon icon={"file"} size="sm" />
- Document
- </div>;
-
- const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}>
- <FontAwesomeIcon icon={"image"} size="sm" />
- Image
- </div>;
-
- const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}>
- <FontAwesomeIcon icon={"calendar"} size="sm" />
- Date
- </div>;
-
-
- const allColumnTypes = <div className="columnMenu-types">
- {anyType}
- {numType}
- {textType}
- {boolType}
- {listType}
- {docType}
- {imageType}
- {dateType}
- </div>;
-
- const justColType = type === ColumnType.Any ? anyType : type === ColumnType.Number ? numType :
- type === ColumnType.String ? textType : type === ColumnType.Boolean ? boolType :
- type === ColumnType.List ? listType : type === ColumnType.Doc ? docType :
- type === ColumnType.Date ? dateType : imageType;
+ const anyType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}>
+ <FontAwesomeIcon icon={'align-justify'} size="sm" />
+ Any
+ </div>
+ );
+
+ const numType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}>
+ <FontAwesomeIcon icon={'hashtag'} size="sm" />
+ Number
+ </div>
+ );
+
+ const textType = (
+ <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}>
+ <FontAwesomeIcon icon={'font'} size="sm" />
+ Text
+ </div>
+ );
+
+ const boolType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
+ <FontAwesomeIcon icon={'check-square'} size="sm" />
+ Checkbox
+ </div>
+ );
+
+ const listType = (
+ <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}>
+ <FontAwesomeIcon icon={'list-ul'} size="sm" />
+ List
+ </div>
+ );
+
+ const docType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
+ <FontAwesomeIcon icon={'file'} size="sm" />
+ Document
+ </div>
+ );
+
+ const imageType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}>
+ <FontAwesomeIcon icon={'image'} size="sm" />
+ Image
+ </div>
+ );
+
+ const dateType = (
+ <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}>
+ <FontAwesomeIcon icon={'calendar'} size="sm" />
+ Date
+ </div>
+ );
+
+ const allColumnTypes = (
+ <div className="columnMenu-types">
+ {anyType}
+ {numType}
+ {textType}
+ {boolType}
+ {listType}
+ {docType}
+ {imageType}
+ {dateType}
+ </div>
+ );
+
+ const justColType =
+ type === ColumnType.Any
+ ? anyType
+ : type === ColumnType.Number
+ ? numType
+ : type === ColumnType.String
+ ? textType
+ : type === ColumnType.Boolean
+ ? boolType
+ : type === ColumnType.List
+ ? listType
+ : type === ColumnType.Doc
+ ? docType
+ : type === ColumnType.Date
+ ? dateType
+ : imageType;
return (
- <div className="collectionSchema-headerMenu-group" onClick={action(() => this._openTypes = !this._openTypes)}>
+ <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}>
<div>
- <label style={{ cursor: "pointer" }}>Column type:</label>
- <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right", transform: `rotate(${this._openTypes ? "180deg" : 0})`, transition: "0.2s all ease" }} />
+ <label style={{ cursor: 'pointer' }}>Column type:</label>
+ <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} />
</div>
{this._openTypes ? allColumnTypes : justColType}
- </div >
+ </div>
);
- }
+ };
renderSorting = (col: any) => {
const sort = col.desc;
@@ -218,11 +267,11 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="collectionSchema-headerMenu-group">
<label>Sort by:</label>
<div className="columnMenu-sort">
- <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.setColumnSort(col, true)}>
+ <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}>
<FontAwesomeIcon icon="sort-amount-down" size="sm" />
Sort descending
</div>
- <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.setColumnSort(col, false)}>
+ <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}>
<FontAwesomeIcon icon="sort-amount-up" size="sm" />
Sort ascending
</div>
@@ -233,42 +282,42 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
</div>
);
- }
+ };
renderColors = (col: any) => {
const selected = col.color;
- const pink = PastelSchemaPalette.get("pink2");
- const purple = PastelSchemaPalette.get("purple2");
- const blue = PastelSchemaPalette.get("bluegreen1");
- const yellow = PastelSchemaPalette.get("yellow4");
- const red = PastelSchemaPalette.get("red2");
- const gray = "#f1efeb";
+ const pink = PastelSchemaPalette.get('pink2');
+ const purple = PastelSchemaPalette.get('purple2');
+ const blue = PastelSchemaPalette.get('bluegreen1');
+ const yellow = PastelSchemaPalette.get('yellow4');
+ const red = PastelSchemaPalette.get('red2');
+ const gray = '#f1efeb';
return (
<div className="collectionSchema-headerMenu-group">
<label>Color:</label>
<div className="columnMenu-colors">
- <div className={"columnMenu-colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
+ <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
</div>
</div>
);
- }
+ };
@undoBatch
@action
changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
const columns = this.columns;
if (columns === undefined) {
- this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
+ this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]);
} else {
if (addNew) {
- columns.push(new SchemaHeaderField(newKey, "f1efeb"));
+ columns.push(new SchemaHeaderField(newKey, 'f1efeb'));
this.columns = columns;
} else {
const index = columns.map(c => c.heading).indexOf(oldKey);
@@ -278,15 +327,14 @@ export class CollectionSchemaView extends CollectionSubView() {
columns[index] = column;
this.columns = columns;
if (filter) {
- Doc.setDocFilter(this.props.Document, newKey, filter, "match");
- }
- else {
+ Doc.setDocFilter(this.props.Document, newKey, filter, 'match');
+ } else {
this.props.Document._docFilters = undefined;
}
}
}
}
- }
+ };
@action
openHeader = (col: any, screenx: number, screeny: number) => {
@@ -294,10 +342,12 @@ export class CollectionSchemaView extends CollectionSubView() {
this._headerOpen = true;
this._pointerX = screenx;
this._pointerY = screeny;
- }
+ };
@action
- closeHeader = () => { this._headerOpen = false; }
+ closeHeader = () => {
+ this._headerOpen = false;
+ };
@undoBatch
@action
@@ -313,16 +363,16 @@ export class CollectionSchemaView extends CollectionSubView() {
}
}
this.closeHeader();
- }
+ };
getPreviewTransform = (): Transform => {
- return this.props.ScreenToLocalTransform().translate(- this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, - this.borderWidth);
- }
+ return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth);
+ };
@action
onHeaderClick = (e: React.PointerEvent) => {
e.stopPropagation();
- }
+ };
@action
onWheel(e: React.WheelEvent) {
@@ -332,40 +382,45 @@ export class CollectionSchemaView extends CollectionSubView() {
@computed get renderMenuContent() {
TraceMobx();
- return <div className="collectionSchema-header-menuOptions">
- {this.renderTypes(this._col)}
- {this.renderColors(this._col)}
- <div className="collectionSchema-headerMenu-group">
- <button onClick={() => { this.deleteColumn(this._col.heading); }}>
- Hide Column
- </button>
+ return (
+ <div className="collectionSchema-header-menuOptions">
+ {this.renderTypes(this._col)}
+ {this.renderColors(this._col)}
+ <div className="collectionSchema-headerMenu-group">
+ <button
+ onClick={() => {
+ this.deleteColumn(this._col.heading);
+ }}>
+ Hide Column
+ </button>
+ </div>
</div>
- </div>;
+ );
}
private createTarget = (ele: HTMLDivElement) => {
this._previewCont = ele;
super.CreateDropTarget(ele);
- }
+ };
isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
- @action setFocused = (doc: Doc) => this._focusedTable = doc;
+ @action setFocused = (doc: Doc) => (this._focusedTable = doc);
@action setPreviewDoc = (doc: Opt<Doc>) => {
SelectionManager.SelectSchemaViewDoc(doc);
this._previewDoc = doc;
- }
+ };
//toggles preview side-panel of schema
@action
toggleExpander = () => {
this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
- }
+ };
onDividerDown = (e: React.PointerEvent) => {
setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
- }
+ };
@action
onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
const nativeWidth = this._previewCont!.getBoundingClientRect();
@@ -375,136 +430,146 @@ export class CollectionSchemaView extends CollectionSubView() {
const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
this.props.Document.schemaPreviewWidth = width;
return false;
- }
+ };
onPointerDown = (e: React.PointerEvent): void => {
if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
if (this.props.isSelected(true)) e.stopPropagation();
else this.props.select(false);
}
- }
+ };
@computed
- get previewDocument(): Doc | undefined { return this._previewDoc; }
+ get previewDocument(): Doc | undefined {
+ return this._previewDoc;
+ }
@computed
get dividerDragger() {
- return this.previewWidth() === 0 ? (null) :
- <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} >
+ return this.previewWidth() === 0 ? null : (
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}>
<div className="collectionSchemaView-dividerDragger" />
- </div>;
+ </div>
+ );
}
@computed
get previewPanel() {
- return <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
- {!this.previewDocument ? (null) :
- <DocumentView
- Document={this.previewDocument}
- DataDoc={undefined}
- fitContentsToBox={returnTrue}
- dontCenter={"y"}
- focus={DocUtils.DefaultFocus}
- renderDepth={this.props.renderDepth}
- rootSelected={this.rootSelected}
- PanelWidth={this.previewWidth}
- PanelHeight={this.previewHeight}
- isContentActive={returnTrue}
- isDocumentActive={returnFalse}
- ScreenToLocalTransform={this.getPreviewTransform}
- docFilters={this.childDocFilters}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- styleProvider={DefaultStyleProvider}
- docViewPath={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.CollectionView?.props.Document}
- ContainingCollectionView={this.props.CollectionView}
- moveDocument={this.props.moveDocument}
- addDocument={this.props.addDocument}
- removeDocument={this.props.removeDocument}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}
- />}
- </div>;
+ return (
+ <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
+ {!this.previewDocument ? null : (
+ <DocumentView
+ Document={this.previewDocument}
+ DataDoc={undefined}
+ fitContentsToBox={returnTrue}
+ dontCenter={'y'}
+ focus={DocUtils.DefaultFocus}
+ renderDepth={this.props.renderDepth}
+ rootSelected={this.rootSelected}
+ PanelWidth={this.previewWidth}
+ PanelHeight={this.previewHeight}
+ isContentActive={returnTrue}
+ isDocumentActive={returnFalse}
+ ScreenToLocalTransform={this.getPreviewTransform}
+ docFilters={this.childDocFilters}
+ docRangeFilters={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ styleProvider={DefaultStyleProvider}
+ docViewPath={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+ ContainingCollectionView={this.props.CollectionView}
+ moveDocument={this.props.moveDocument}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ />
+ )}
+ </div>
+ );
}
@computed
get schemaTable() {
- return <SchemaTable
- Document={this.props.Document}
- PanelHeight={this.props.PanelHeight}
- PanelWidth={this.props.PanelWidth}
- childDocs={this.childDocs}
- CollectionView={this.props.CollectionView}
- ContainingCollectionView={this.props.ContainingCollectionView}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- fieldKey={this.props.fieldKey}
- renderDepth={this.props.renderDepth}
- moveDocument={this.props.moveDocument}
- ScreenToLocalTransform={this.props.ScreenToLocalTransform}
- active={this.props.isContentActive}
- onDrop={this.onExternalDrop}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- isSelected={this.props.isSelected}
- isFocused={this.isFocused}
- setFocused={this.setFocused}
- setPreviewDoc={this.setPreviewDoc}
- deleteDocument={this.props.removeDocument}
- addDocument={this.props.addDocument}
- dataDoc={this.props.DataDoc}
- columns={this.columns}
- documentKeys={this.documentKeys}
- headerIsEditing={this._headerIsEditing}
- openHeader={this.openHeader}
- onClick={this.onTableClick}
- onPointerDown={emptyFunction}
- onResizedChange={this.onResizedChange}
- setColumns={this.setColumns}
- reorderColumns={this.reorderColumns}
- changeColumns={this.changeColumns}
- setHeaderIsEditing={this.setHeaderIsEditing}
- changeColumnSort={this.setColumnSort}
- />;
+ return (
+ <SchemaTable
+ Document={this.props.Document}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ childDocs={this.childDocs}
+ CollectionView={this.props.CollectionView}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ fieldKey={this.props.fieldKey}
+ renderDepth={this.props.renderDepth}
+ moveDocument={this.props.moveDocument}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ active={this.props.isContentActive}
+ onDrop={this.onExternalDrop}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ isSelected={this.props.isSelected}
+ isFocused={this.isFocused}
+ setFocused={this.setFocused}
+ setPreviewDoc={this.setPreviewDoc}
+ deleteDocument={this.props.removeDocument}
+ addDocument={this.props.addDocument}
+ dataDoc={this.props.DataDoc}
+ columns={this.columns}
+ documentKeys={this.documentKeys}
+ headerIsEditing={this._headerIsEditing}
+ openHeader={this.openHeader}
+ onClick={this.onTableClick}
+ onPointerDown={emptyFunction}
+ onResizedChange={this.onResizedChange}
+ setColumns={this.setColumns}
+ reorderColumns={this.reorderColumns}
+ changeColumns={this.changeColumns}
+ setHeaderIsEditing={this.setHeaderIsEditing}
+ changeColumnSort={this.setColumnSort}
+ />
+ );
}
@computed
public get schemaToolbar() {
- return <div className="collectionSchemaView-toolbar">
- <div className="collectionSchemaView-toolbar-item">
- <div id="preview-schema-checkbox-div">
- <input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
- Show Preview
+ return (
+ <div className="collectionSchemaView-toolbar">
+ <div className="collectionSchemaView-toolbar-item">
+ <div id="preview-schema-checkbox-div">
+ <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
+ Show Preview
+ </div>
</div>
</div>
- </div>;
+ );
}
onSpecificMenu = (e: React.MouseEvent) => {
- if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) {
+ if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) {
const cm = ContextMenu.Instance;
- const options = cm.findByDescription("Options...");
- const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
- optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" });
- !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+ const options = cm.findByDescription('Options...');
+ const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+ optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' });
+ !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
cm.displayMenu(e.clientX, e.clientY);
(e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
e.stopPropagation();
}
- }
+ };
@action
onTableClick = (e: React.MouseEvent): void => {
- if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) {
+ if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) {
this.setPreviewDoc(undefined);
} else {
e.stopPropagation();
}
this.setFocused(this.props.Document);
this.closeHeader();
- }
+ };
onResizedChange = (newResized: Resize[], event: any) => {
const columns = this.columns;
@@ -515,23 +580,23 @@ export class CollectionSchemaView extends CollectionSubView() {
columns[index] = column;
});
this.columns = columns;
- }
+ };
@action
- setColumns = (columns: SchemaHeaderField[]) => this.columns = columns
+ setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns);
@undoBatch
reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
const columns = [...columnsValues];
const oldIndex = columns.indexOf(toMove);
const relIndex = columns.indexOf(relativeTo);
- const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
+ const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex;
if (oldIndex === newIndex) return;
columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
this.columns = columns;
- }
+ };
onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
@@ -539,35 +604,44 @@ export class CollectionSchemaView extends CollectionSubView() {
TraceMobx();
if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
const menuContent = this.renderMenuContent;
- const menu = <div className="collectionSchema-header-menu"
- onWheel={e => this.onZoomMenu(e)}
- onPointerDown={e => this.onHeaderClick(e)}
- style={{ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)` }}>
- <Measure offset onResize={action((r: any) => {
- const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
- this._menuWidth = dim[0]; this._menuHeight = dim[1];
- })}>
- {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
- </Measure>
- </div>;
- return <div className={"collectionSchemaView" + (this.props.Document._searchDoc ? "-searchContainer" : "-container")}
- style={{
- overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
- pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
- width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
- }} >
- <div className="collectionSchemaView-tableContainer"
- style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
- onContextMenu={this.onSpecificMenu}
- onPointerDown={this.onPointerDown}
- onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
- onDrop={e => this.onExternalDrop(e, {})}
- ref={this.createTarget}>
- {this.schemaTable}
+ const menu = (
+ <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}>
+ <Measure
+ offset
+ onResize={action((r: any) => {
+ const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
+ this._menuWidth = dim[0];
+ this._menuHeight = dim[1];
+ })}>
+ {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
+ </Measure>
</div>
- {this.dividerDragger}
- {!this.previewWidth() ? (null) : this.previewPanel}
- {this._headerOpen && this.props.isContentActive() ? menu : null}
- </div>;
+ );
+ return (
+ <div
+ className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')}
+ style={{
+ overflow: this.props.scrollOverflow === true ? 'scroll' : undefined,
+ backgroundColor: 'white',
+ pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+ width: this.props.PanelWidth() || '100%',
+ height: this.props.PanelHeight() || '100%',
+ position: 'relative',
+ }}>
+ <div
+ className="collectionSchemaView-tableContainer"
+ style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
+ onContextMenu={this.onSpecificMenu}
+ onPointerDown={this.onPointerDown}
+ onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
+ onDrop={e => this.onExternalDrop(e, {})}
+ ref={this.createTarget}>
+ {this.schemaTable}
+ </div>
+ {this.dividerDragger}
+ {!this.previewWidth() ? null : this.previewPanel}
+ {this._headerOpen && this.props.isContentActive() ? menu : null}
+ </div>
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 43266a571..fafea5ce3 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -1,37 +1,47 @@
-import React = require("react");
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, trace } from "mobx";
-import { observer } from "mobx-react";
-import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
-import { DateField } from "../../../../fields/DateField";
-import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../../Utils";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CompileScript, Transformer, ts } from "../../../util/Scripting";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { ContextMenu } from "../../ContextMenu";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from 'react-table';
+import { DateField } from '../../../../fields/DateField';
+import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { Cast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../../Utils';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CompileScript, Transformer, ts } from '../../../util/Scripting';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
import '../../../views/DocumentDecorations.scss';
-import { DocumentView } from "../../nodes/DocumentView";
-import { DefaultStyleProvider } from "../../StyleProvider";
-import { CellProps, CollectionSchemaButtons, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDateCell, CollectionSchemaDocCell, CollectionSchemaImageCell, CollectionSchemaListCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
-import { CollectionSchemaAddColumnHeader, KeysDropdown } from "./CollectionSchemaHeaders";
-import { MovableColumn } from "./CollectionSchemaMovableColumn";
-import { MovableRow } from "./CollectionSchemaMovableRow";
-import "./CollectionSchemaView.scss";
-import { CollectionView } from "../CollectionView";
-
+import { ContextMenu } from '../../ContextMenu';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+import { DocumentView } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { CollectionView } from '../CollectionView';
+import {
+ CellProps,
+ CollectionSchemaButtons,
+ CollectionSchemaCell,
+ CollectionSchemaCheckboxCell,
+ CollectionSchemaDateCell,
+ CollectionSchemaDocCell,
+ CollectionSchemaImageCell,
+ CollectionSchemaListCell,
+ CollectionSchemaNumberCell,
+ CollectionSchemaStringCell,
+} from './CollectionSchemaCells';
+import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
+import { MovableColumn } from './CollectionSchemaMovableColumn';
+import { MovableRow } from './CollectionSchemaMovableRow';
+import './CollectionSchemaView.scss';
enum ColumnType {
Any,
@@ -41,15 +51,22 @@ enum ColumnType {
Doc,
Image,
List,
- Date
+ Date,
}
// this map should be used for keys that should have a const type of value
const columnTypes: Map<string, ColumnType> = new Map([
- ["title", ColumnType.String],
- ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
- ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
- ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+ ['title', ColumnType.String],
+ ['x', ColumnType.Number],
+ ['y', ColumnType.Number],
+ ['_width', ColumnType.Number],
+ ['_height', ColumnType.Number],
+ ['_nativeWidth', ColumnType.Number],
+ ['_nativeHeight', ColumnType.Number],
+ ['isPrototype', ColumnType.Boolean],
+ ['_curPage', ColumnType.Number],
+ ['_currentTimecode', ColumnType.Number],
+ ['zIndex', ColumnType.Number],
]);
export interface SchemaTableProps {
@@ -92,18 +109,24 @@ export interface SchemaTableProps {
@observer
export class SchemaTable extends React.Component<SchemaTableProps> {
@observable _cellIsEditing: boolean = false;
- @observable _focusedCell: { row: number, col: number } = { row: 0, col: 0 };
- @observable _openCollections: Set<number> = new Set;
+ @observable _focusedCell: { row: number; col: number } = { row: 0, col: 0 };
+ @observable _openCollections: Set<number> = new Set();
@observable _showDoc: Doc | undefined;
- @observable _showDataDoc: any = "";
+ @observable _showDataDoc: any = '';
@observable _showDocPos: number[] = [];
@observable _showTitleDropdown: boolean = false;
- @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
- @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
- @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); }
+ @computed get previewWidth() {
+ return () => NumCast(this.props.Document.schemaPreviewWidth);
+ }
+ @computed get previewHeight() {
+ return () => this.props.PanelHeight() - 2 * this.borderWidth;
+ }
+ @computed get tableWidth() {
+ return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth();
+ }
@computed get childDocs() {
if (this.props.childDocs) return this.props.childDocs;
@@ -117,17 +140,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
@computed get textWrappedRows() {
- return Cast(this.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+ return Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
}
set textWrappedRows(textWrappedRows: string[]) {
this.props.Document.textwrappedSchemaRows = new List<string>(textWrappedRows);
}
- @computed get resized(): { id: string, value: number }[] {
+ @computed get resized(): { id: string; value: number }[] {
return this.props.columns.reduce((resized, shf) => {
- (shf.width > -1) && resized.push({ id: shf.heading, value: shf.width });
+ shf.width > -1 && resized.push({ id: shf.heading, value: shf.width });
return resized;
- }, [] as { id: string, value: number }[]);
+ }, [] as { id: string; value: number }[]);
}
@computed get sorted(): SortingRule[] {
return this.props.columns.reduce((sorted, shf) => {
@@ -139,12 +162,14 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
@action
changeSorting = (col: any) => {
this.props.changeColumnSort(col, col.desc === true ? false : col.desc === false ? undefined : true);
- }
+ };
@action
- changeTitleMode = () => this._showTitleDropdown = !this._showTitleDropdown
+ changeTitleMode = () => (this._showTitleDropdown = !this._showTitleDropdown);
- @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
+ @computed get borderWidth() {
+ return Number(COLLECTION_BORDER_WIDTH);
+ }
@computed get tableColumns(): Column<Doc>[] {
const possibleKeys = this.props.documentKeys.filter(key => this.props.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1);
const columns: Column<Doc>[] = [];
@@ -154,149 +179,185 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isEditable = !this.props.headerIsEditing;
columns.push({
- expander: true, Header: "", width: 58,
- Expander: (rowInfo) => {
- return rowInfo.original.type !== DocumentType.COL ? (null) :
- <div className="collectionSchemaView-expander" onClick={action(() => (this._openCollections[rowInfo.isExpanded ? "delete" : "add"])(rowInfo.viewIndex))}>
- <FontAwesomeIcon icon={rowInfo.isExpanded ? "caret-down" : "caret-right"} size="lg" />
- </div>;
- }
+ expander: true,
+ Header: '',
+ width: 58,
+ Expander: rowInfo => {
+ return rowInfo.original.type !== DocumentType.COL ? null : (
+ <div className="collectionSchemaView-expander" onClick={action(() => this._openCollections[rowInfo.isExpanded ? 'delete' : 'add'](rowInfo.viewIndex))}>
+ <FontAwesomeIcon icon={rowInfo.isExpanded ? 'caret-down' : 'caret-right'} size="lg" />
+ </div>
+ );
+ },
});
- columns.push(...this.props.columns.map(col => {
- const icon: IconProp = this.getColumnType(col) === ColumnType.Number ? "hashtag" : this.getColumnType(col) === ColumnType.String ? "font" :
- this.getColumnType(col) === ColumnType.Boolean ? "check-square" : this.getColumnType(col) === ColumnType.Doc ? "file" :
- this.getColumnType(col) === ColumnType.Image ? "image" : this.getColumnType(col) === ColumnType.List ? "list-ul" :
- this.getColumnType(col) === ColumnType.Date ? "calendar" : "align-justify";
-
- const keysDropdown = <KeysDropdown
- keyValue={col.heading}
- possibleKeys={possibleKeys}
- existingKeys={this.props.columns.map(c => c.heading)}
- canAddNew={true}
- addNew={false}
- onSelect={this.props.changeColumns}
- setIsEditing={this.props.setHeaderIsEditing}
- docs={this.props.childDocs}
- Document={this.props.Document}
- dataDoc={this.props.dataDoc}
- fieldKey={this.props.fieldKey}
- ContainingCollectionDoc={this.props.ContainingCollectionDoc}
- ContainingCollectionView={this.props.ContainingCollectionView}
- active={this.props.active}
- openHeader={this.props.openHeader}
- icon={icon}
- col={col}
- // try commenting this out
- width={"100%"}
- />;
-
- const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
- const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
- {keysDropdown}
- <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "pointer" }}>
- <FontAwesomeIcon icon={sortIcon} size="lg" />
- </div>
- {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
- </div>;
-
- return {
- Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
- accessor: (doc: Doc) => doc ? Field.toString(doc[col.heading] as Field) : 0,
- id: col.heading,
- Cell: (rowProps: CellInfo) => {
- const rowIndex = rowProps.index;
- const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
- const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
-
- const props: CellProps = {
- row: rowIndex,
- col: columnIndex,
- rowProps: rowProps,
- isFocused: isFocused,
- changeFocusedCellByIndex: this.changeFocusedCellByIndex,
- CollectionView: this.props.CollectionView,
- ContainingCollection: this.props.ContainingCollectionView,
- Document: this.props.Document,
- fieldKey: this.props.fieldKey,
- renderDepth: this.props.renderDepth,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- moveDocument: this.props.moveDocument,
- setIsEditing: this.setCellIsEditing,
- isEditable: isEditable,
- setPreviewDoc: this.props.setPreviewDoc,
- setComputed: this.setComputed,
- getField: this.getField,
- showDoc: this.showDoc,
- };
-
-
- switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
- case ColumnType.Number: return <CollectionSchemaNumberCell {...props} />;
- case ColumnType.String: return <CollectionSchemaStringCell {...props} />;
- case ColumnType.Boolean: return <CollectionSchemaCheckboxCell {...props} />;
- case ColumnType.Doc: return <CollectionSchemaDocCell {...props} />;
- case ColumnType.Image: return <CollectionSchemaImageCell {...props} />;
- case ColumnType.List: return <CollectionSchemaListCell {...props} />;
- case ColumnType.Date: return <CollectionSchemaDateCell {...props} />;
- default:
- return <CollectionSchemaCell {...props} />;
- }
- },
- minWidth: 200,
- };
- }));
+ columns.push(
+ ...this.props.columns.map(col => {
+ const icon: IconProp =
+ this.getColumnType(col) === ColumnType.Number
+ ? 'hashtag'
+ : this.getColumnType(col) === ColumnType.String
+ ? 'font'
+ : this.getColumnType(col) === ColumnType.Boolean
+ ? 'check-square'
+ : this.getColumnType(col) === ColumnType.Doc
+ ? 'file'
+ : this.getColumnType(col) === ColumnType.Image
+ ? 'image'
+ : this.getColumnType(col) === ColumnType.List
+ ? 'list-ul'
+ : this.getColumnType(col) === ColumnType.Date
+ ? 'calendar'
+ : 'align-justify';
+
+ const keysDropdown = (
+ <KeysDropdown
+ keyValue={col.heading}
+ possibleKeys={possibleKeys}
+ existingKeys={this.props.columns.map(c => c.heading)}
+ canAddNew={true}
+ addNew={false}
+ onSelect={this.props.changeColumns}
+ setIsEditing={this.props.setHeaderIsEditing}
+ docs={this.props.childDocs}
+ Document={this.props.Document}
+ dataDoc={this.props.dataDoc}
+ fieldKey={this.props.fieldKey}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ active={this.props.active}
+ openHeader={this.props.openHeader}
+ icon={icon}
+ col={col}
+ // try commenting this out
+ width={'100%'}
+ />
+ );
+
+ const sortIcon = col.desc === undefined ? 'caret-right' : col.desc === true ? 'caret-down' : 'caret-up';
+ const header = (
+ <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: '2px', display: 'flex', cursor: 'default', height: '100%' }}>
+ {keysDropdown}
+ <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: 'inline', zIndex: 1, background: 'inherit', cursor: 'pointer' }}>
+ <FontAwesomeIcon icon={sortIcon} size="lg" />
+ </div>
+ {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
+ </div>
+ );
+
+ return {
+ Header: <MovableColumn columnRenderer={header} columnValue={col} allColumns={this.props.columns} reorderColumns={this.props.reorderColumns} ScreenToLocalTransform={this.props.ScreenToLocalTransform} />,
+ accessor: (doc: Doc) => (doc ? Field.toString(doc[col.heading] as Field) : 0),
+ id: col.heading,
+ Cell: (rowProps: CellInfo) => {
+ const rowIndex = rowProps.index;
+ const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
+ const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
+
+ const props: CellProps = {
+ row: rowIndex,
+ col: columnIndex,
+ rowProps: rowProps,
+ isFocused: isFocused,
+ changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+ CollectionView: this.props.CollectionView,
+ ContainingCollection: this.props.ContainingCollectionView,
+ Document: this.props.Document,
+ fieldKey: this.props.fieldKey,
+ renderDepth: this.props.renderDepth,
+ addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
+ moveDocument: this.props.moveDocument,
+ setIsEditing: this.setCellIsEditing,
+ isEditable: isEditable,
+ setPreviewDoc: this.props.setPreviewDoc,
+ setComputed: this.setComputed,
+ getField: this.getField,
+ showDoc: this.showDoc,
+ };
+
+ switch (this.getColumnType(col, rowProps.original, rowProps.column.id)) {
+ case ColumnType.Number:
+ return <CollectionSchemaNumberCell {...props} />;
+ case ColumnType.String:
+ return <CollectionSchemaStringCell {...props} />;
+ case ColumnType.Boolean:
+ return <CollectionSchemaCheckboxCell {...props} />;
+ case ColumnType.Doc:
+ return <CollectionSchemaDocCell {...props} />;
+ case ColumnType.Image:
+ return <CollectionSchemaImageCell {...props} />;
+ case ColumnType.List:
+ return <CollectionSchemaListCell {...props} />;
+ case ColumnType.Date:
+ return <CollectionSchemaDateCell {...props} />;
+ default:
+ return <CollectionSchemaCell {...props} />;
+ }
+ },
+ minWidth: 200,
+ };
+ })
+ );
columns.push({
Header: <CollectionSchemaAddColumnHeader createColumn={this.createColumn} />,
accessor: (doc: Doc) => 0,
- id: "add",
+ id: 'add',
Cell: (rowProps: CellInfo) => {
const rowIndex = rowProps.index;
const columnIndex = this.props.columns.map(c => c.heading).indexOf(rowProps.column.id!);
const isFocused = focusedRow === rowIndex && focusedCol === columnIndex && tableIsFocused;
- return <CollectionSchemaButtons {...{
- row: rowProps.index,
- col: columnIndex,
- rowProps: rowProps,
- isFocused: isFocused,
- changeFocusedCellByIndex: this.changeFocusedCellByIndex,
- CollectionView: this.props.CollectionView,
- ContainingCollection: this.props.ContainingCollectionView,
- Document: this.props.Document,
- fieldKey: this.props.fieldKey,
- renderDepth: this.props.renderDepth,
- addDocTab: this.props.addDocTab,
- pinToPres: this.props.pinToPres,
- moveDocument: this.props.moveDocument,
- setIsEditing: this.setCellIsEditing,
- isEditable: isEditable,
- setPreviewDoc: this.props.setPreviewDoc,
- setComputed: this.setComputed,
- getField: this.getField,
- showDoc: this.showDoc,
- }} />;
+ return (
+ <CollectionSchemaButtons
+ {...{
+ row: rowProps.index,
+ col: columnIndex,
+ rowProps: rowProps,
+ isFocused: isFocused,
+ changeFocusedCellByIndex: this.changeFocusedCellByIndex,
+ CollectionView: this.props.CollectionView,
+ ContainingCollection: this.props.ContainingCollectionView,
+ Document: this.props.Document,
+ fieldKey: this.props.fieldKey,
+ renderDepth: this.props.renderDepth,
+ addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
+ moveDocument: this.props.moveDocument,
+ setIsEditing: this.setCellIsEditing,
+ isEditable: isEditable,
+ setPreviewDoc: this.props.setPreviewDoc,
+ setComputed: this.setComputed,
+ getField: this.getField,
+ showDoc: this.showDoc,
+ }}
+ />
+ );
},
width: 28,
- resizable: false
+ resizable: false,
});
return columns;
}
-
constructor(props: SchemaTableProps) {
super(props);
if (this.props.Document._schemaHeaders === undefined) {
- this.props.Document._schemaHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("title", "#f1efeb"), new SchemaHeaderField("author", "#f1efeb"), new SchemaHeaderField("*lastModified", "#f1efeb", ColumnType.Date),
- new SchemaHeaderField("text", "#f1efeb", ColumnType.String), new SchemaHeaderField("type", "#f1efeb"), new SchemaHeaderField("context", "#f1efeb", ColumnType.Doc)]);
+ this.props.Document._schemaHeaders = new List<SchemaHeaderField>([
+ new SchemaHeaderField('title', '#f1efeb'),
+ new SchemaHeaderField('author', '#f1efeb'),
+ new SchemaHeaderField('*lastModified', '#f1efeb', ColumnType.Date),
+ new SchemaHeaderField('text', '#f1efeb', ColumnType.String),
+ new SchemaHeaderField('type', '#f1efeb'),
+ new SchemaHeaderField('context', '#f1efeb', ColumnType.Doc),
+ ]);
}
}
componentDidMount() {
- document.addEventListener("keydown", this.onKeyDown);
+ document.addEventListener('keydown', this.onKeyDown);
}
componentWillUnmount() {
- document.removeEventListener("keydown", this.onKeyDown);
+ document.removeEventListener('keydown', this.onKeyDown);
}
tableAddDoc = (doc: Doc, relativeTo?: Doc, before?: boolean) => {
@@ -305,25 +366,27 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
if (effectiveAcl !== AclPrivate && effectiveAcl !== AclReadonly) {
doc.context = this.props.Document;
- tableDoc[this.props.fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
+ tableDoc[this.props.fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
return Doc.AddDocToList(this.props.Document, this.props.fieldKey, doc, relativeTo, before);
}
return false;
- }
+ };
private getTrProps: ComponentPropsGetterR = (state, rowInfo) => {
- return !rowInfo ? {} : {
- ScreenToLocalTransform: this.props.ScreenToLocalTransform,
- addDoc: this.tableAddDoc,
- removeDoc: this.props.deleteDocument,
- rowInfo,
- rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
- textWrapRow: this.toggleTextWrapRow,
- rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
- dropAction: StrCast(this.props.Document.childDropAction),
- addDocTab: this.props.addDocTab
- };
- }
+ return !rowInfo
+ ? {}
+ : {
+ ScreenToLocalTransform: this.props.ScreenToLocalTransform,
+ addDoc: this.tableAddDoc,
+ removeDoc: this.props.deleteDocument,
+ rowInfo,
+ rowFocused: !this.props.headerIsEditing && rowInfo.index === this._focusedCell.row && this.props.isFocused(this.props.Document, true),
+ textWrapRow: this.toggleTextWrapRow,
+ rowWrapped: this.textWrappedRows.findIndex(id => rowInfo.original[Id] === id) > -1,
+ dropAction: StrCast(this.props.Document.childDropAction),
+ addDocTab: this.props.addDocTab,
+ };
+ };
private getTdProps: ComponentPropsGetterR = (state, rowInfo, column, instance) => {
if (!rowInfo || column) return {};
@@ -334,16 +397,17 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isFocused = this._focusedCell.row === row && this._focusedCell.col === col && this.props.isFocused(this.props.Document, true);
// TODO: editing border doesn't work :(
return {
- style: { border: !this.props.headerIsEditing && isFocused ? "2px solid rgb(255, 160, 160)" : "1px solid #f1efeb" }
+ style: { border: !this.props.headerIsEditing && isFocused ? '2px solid rgb(255, 160, 160)' : '1px solid #f1efeb' },
};
- }
+ };
- @action setCellIsEditing = (isEditing: boolean) => this._cellIsEditing = isEditing;
+ @action setCellIsEditing = (isEditing: boolean) => (this._cellIsEditing = isEditing);
@action
onKeyDown = (e: KeyboardEvent): void => {
- if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {// && this.props.isSelected(true)) {
- const direction = e.key === "Tab" ? "tab" : e.which === 39 ? "right" : e.which === 37 ? "left" : e.which === 38 ? "up" : e.which === 40 ? "down" : "";
+ if (!this._cellIsEditing && !this.props.headerIsEditing && this.props.isFocused(this.props.Document, true)) {
+ // && this.props.isSelected(true)) {
+ const direction = e.key === 'Tab' ? 'tab' : e.which === 39 ? 'right' : e.which === 37 ? 'left' : e.which === 38 ? 'up' : e.which === 40 ? 'down' : '';
this._focusedCell = this.changeFocusedCellByDirection(direction, this._focusedCell.row, this._focusedCell.col);
if (direction) {
@@ -353,20 +417,25 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
}
} else if (e.keyCode === 27) {
this.props.setPreviewDoc(undefined);
- e.stopPropagation(); // stopPropagation for left/right arrows
+ e.stopPropagation(); // stopPropagation for left/right arrows
}
- }
+ };
changeFocusedCellByDirection = (direction: string, curRow: number, curCol: number) => {
switch (direction) {
- case "tab": return { row: (curRow + 1 === this.childDocs.length ? 0 : curRow + 1), col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
- case "right": return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
- case "left": return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
- case "up": return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
- case "down": return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
+ case 'tab':
+ return { row: curRow + 1 === this.childDocs.length ? 0 : curRow + 1, col: curCol + 1 === this.props.columns.length ? 0 : curCol + 1 };
+ case 'right':
+ return { row: curRow, col: curCol + 1 === this.props.columns.length ? curCol : curCol + 1 };
+ case 'left':
+ return { row: curRow, col: curCol === 0 ? curCol : curCol - 1 };
+ case 'up':
+ return { row: curRow === 0 ? curRow : curRow - 1, col: curCol };
+ case 'down':
+ return { row: curRow + 1 === this.childDocs.length ? curRow : curRow + 1, col: curCol };
}
return this._focusedCell;
- }
+ };
@action
changeFocusedCellByIndex = (row: number, col: number): void => {
@@ -374,25 +443,25 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
this._focusedCell = { row: row, col: col };
}
this.props.setFocused(this.props.Document);
- }
+ };
@undoBatch
createRow = action(() => {
- this.props.addDocument?.(Docs.Create.TextDocument("", { title: "", _width: 100, _height: 30 }));
+ this.props.addDocument?.(Docs.Create.TextDocument('', { title: '', _width: 100, _height: 30 }));
this._focusedCell = { row: this.childDocs.length, col: this._focusedCell.col };
});
@undoBatch
@action
createColumn = () => {
- const newFieldName = (index: number) => `New field${index ? ` (${index})` : ""}`;
+ const newFieldName = (index: number) => `New field${index ? ` (${index})` : ''}`;
for (let index = 0; index < 100; index++) {
if (this.props.columns.findIndex(col => col.heading === newFieldName(index)) === -1) {
- this.props.columns.push(new SchemaHeaderField(newFieldName(index), "#f1efeb"));
+ this.props.columns.push(new SchemaHeaderField(newFieldName(index), '#f1efeb'));
break;
}
}
- }
+ };
@action
getColumnType = (column: SchemaHeaderField, doc?: Doc, field?: string): ColumnType => {
@@ -407,15 +476,15 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return column.type;
}
if (columnTypes.get(column.heading)) {
- return column.type = columnTypes.get(column.heading)!;
+ return (column.type = columnTypes.get(column.heading)!);
}
- return column.type = ColumnType.Any;
- }
+ return (column.type = ColumnType.Any);
+ };
@undoBatch
@action
toggleTextwrap = async () => {
- const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec("string"), []);
+ const textwrappedRows = Cast(this.props.Document.textwrappedSchemaRows, listSpec('string'), []);
if (textwrappedRows.length) {
this.props.Document.textwrappedSchemaRows = new List<string>([]);
} else {
@@ -423,7 +492,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
this.props.Document.textwrappedSchemaRows = new List<string>(allRows);
}
- }
+ };
@action
toggleTextWrapRow = (doc: Doc): void => {
@@ -433,41 +502,50 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
index > -1 ? textWrapped.splice(index, 1) : textWrapped.push(doc[Id]);
this.textWrappedRows = textWrapped;
- }
+ };
@computed
get reactTable() {
const children = this.childDocs;
const hasCollectionChild = children.reduce((found, doc) => found || doc.type === DocumentType.COL, false);
const expanded: { [name: string]: any } = {};
- Array.from(this._openCollections.keys()).map(col => expanded[col.toString()] = true);
+ Array.from(this._openCollections.keys()).map(col => (expanded[col.toString()] = true));
const rerender = [...this.textWrappedRows]; // TODO: get component to rerender on text wrap change without needign to console.log :((((
- return <ReactTable
- style={{ position: "relative" }}
- data={children}
- page={0}
- pageSize={children.length}
- showPagination={false}
- columns={this.tableColumns}
- getTrProps={this.getTrProps}
- getTdProps={this.getTdProps}
- sortable={false}
- TrComponent={MovableRow}
- sorted={this.sorted}
- expanded={expanded}
- resized={this.resized}
- onResizedChange={this.props.onResizedChange}
- // if it has a child, render another table with the children
- SubComponent={!hasCollectionChild ? undefined : row => (row.original.type !== DocumentType.COL) ? (null) :
- <div style={{ paddingLeft: 57 + "px" }} className="reactTable-sub"><SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} /></div>}
-
- />;
+ return (
+ <ReactTable
+ style={{ position: 'relative' }}
+ data={children}
+ page={0}
+ pageSize={children.length}
+ showPagination={false}
+ columns={this.tableColumns}
+ getTrProps={this.getTrProps}
+ getTdProps={this.getTdProps}
+ sortable={false}
+ TrComponent={MovableRow}
+ sorted={this.sorted}
+ expanded={expanded}
+ resized={this.resized}
+ onResizedChange={this.props.onResizedChange}
+ // if it has a child, render another table with the children
+ SubComponent={
+ !hasCollectionChild
+ ? undefined
+ : row =>
+ row.original.type !== DocumentType.COL ? null : (
+ <div style={{ paddingLeft: 57 + 'px' }} className="reactTable-sub">
+ <SchemaTable {...this.props} Document={row.original} dataDoc={undefined} childDocs={undefined} />
+ </div>
+ )
+ }
+ />
+ );
}
onContextMenu = (e: React.MouseEvent): void => {
- ContextMenu.Instance.addItem({ description: "Toggle text wrapping", event: this.toggleTextwrap, icon: "table" });
- }
+ ContextMenu.Instance.addItem({ description: 'Toggle text wrapping', event: this.toggleTextwrap, icon: 'table' });
+ };
getField = (row: number, col?: number) => {
const docs = this.childDocs;
@@ -484,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return doc[column];
}
return undefined;
- }
+ };
createTransformer = (row: number, col: number): Transformer => {
const self = this;
@@ -498,11 +576,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const isntPropAccess = !ts.isPropertyAccessExpression(node.parent) || node.parent.expression === node;
const isntPropAssign = !ts.isPropertyAssignment(node.parent) || node.parent.name !== node;
if (isntPropAccess && isntPropAssign) {
- if (node.text === "$r") {
+ if (node.text === '$r') {
return ts.createNumericLiteral(row.toString());
- } else if (node.text === "$c") {
+ } else if (node.text === '$c') {
return ts.createNumericLiteral(col.toString());
- } else if (node.text === "$") {
+ } else if (node.text === '$') {
if (ts.isCallExpression(node.parent)) {
// captures.doc = self.props.Document;
// captures.key = self.props.fieldKey;
@@ -521,12 +599,11 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
// return { capturedVariables: captures };
// };
- return { transformer, /*getVars*/ };
- }
+ return { transformer /*getVars*/ };
+ };
setComputed = (script: string, doc: Doc, field: string, row: number, col: number): boolean => {
- script =
- `const $ = (row:number, col?:number) => {
+ script = `const $ = (row:number, col?:number) => {
const rval = (doc as any)[key][row + ${row}];
return col === undefined ? rval : rval[(doc as any)._schemaHeaders[col + ${col}].heading];
}
@@ -537,7 +614,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
return true;
}
return false;
- }
+ };
@action
showDoc = (doc: Doc | undefined, dataDoc?: Doc, screenX?: number, screenY?: number) => {
@@ -545,55 +622,72 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
if (dataDoc && screenX && screenY) {
this._showDocPos = this.props.ScreenToLocalTransform().transformPoint(screenX, screenY);
}
- }
+ };
onOpenClick = () => {
- this._showDoc && this.props.addDocTab(this._showDoc, "add:right");
- }
+ this._showDoc && this.props.addDocTab(this._showDoc, 'add:right');
+ };
getPreviewTransform = (): Transform => {
- return this.props.ScreenToLocalTransform().translate(- this.borderWidth - 4 - this.tableWidth, - this.borderWidth);
- }
+ return this.props.ScreenToLocalTransform().translate(-this.borderWidth - 4 - this.tableWidth, -this.borderWidth);
+ };
render() {
- const preview = "";
- return <div className="collectionSchemaView-table"
- onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
- onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
- {this.reactTable}
- {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
- {!this._showDoc ? (null) :
- <div className="collectionSchemaView-documentPreview" ref="overlay"
- style={{
- position: "absolute", width: 150, height: 150,
- background: "dimgray", display: "block", top: 0, left: 0,
- transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`
- }} >
- <DocumentView
- Document={this._showDoc}
- DataDoc={this._showDataDoc}
- styleProvider={DefaultStyleProvider}
- docViewPath={returnEmptyDoclist}
- focus={DocUtils.DefaultFocus}
- renderDepth={this.props.renderDepth}
- rootSelected={returnFalse}
- isContentActive={returnTrue}
- isDocumentActive={returnFalse}
- PanelWidth={() => 150}
- PanelHeight={() => 150}
- ScreenToLocalTransform={this.getPreviewTransform}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.CollectionView?.props.Document}
- ContainingCollectionView={this.props.CollectionView}
- moveDocument={this.props.moveDocument}
- whenChildContentsActiveChanged={emptyFunction}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.pinToPres}
- bringToFront={returnFalse}>
- </DocumentView>
- </div>}
- </div>;
+ const preview = '';
+ return (
+ <div
+ className="collectionSchemaView-table"
+ onPointerDown={this.props.onPointerDown}
+ onClick={this.props.onClick}
+ onWheel={e => this.props.active(true) && e.stopPropagation()}
+ onDrop={e => this.props.onDrop(e, {})}
+ onContextMenu={this.onContextMenu}>
+ {this.reactTable}
+ {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : (
+ <div className="collectionSchemaView-addRow" onClick={this.createRow}>
+ + new
+ </div>
+ )}
+ {!this._showDoc ? null : (
+ <div
+ className="collectionSchemaView-documentPreview"
+ ref="overlay"
+ style={{
+ position: 'absolute',
+ width: 150,
+ height: 150,
+ background: 'dimgray',
+ display: 'block',
+ top: 0,
+ left: 0,
+ transform: `translate(${this._showDocPos[0]}px, ${this._showDocPos[1] - 180}px)`,
+ }}>
+ <DocumentView
+ Document={this._showDoc}
+ DataDoc={this._showDataDoc}
+ styleProvider={DefaultStyleProvider}
+ docViewPath={returnEmptyDoclist}
+ focus={DocUtils.DefaultFocus}
+ renderDepth={this.props.renderDepth}
+ rootSelected={returnFalse}
+ isContentActive={returnTrue}
+ isDocumentActive={returnFalse}
+ PanelWidth={() => 150}
+ PanelHeight={() => 150}
+ ScreenToLocalTransform={this.getPreviewTransform}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+ ContainingCollectionView={this.props.CollectionView}
+ moveDocument={this.props.moveDocument}
+ whenChildContentsActiveChanged={emptyFunction}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}></DocumentView>
+ </div>
+ )}
+ </div>
+ );
}
-} \ No newline at end of file
+}