aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorsrichman333 <sarah_n_richman@brown.edu>2023-11-24 17:59:13 -0500
committersrichman333 <sarah_n_richman@brown.edu>2023-11-24 17:59:13 -0500
commit0b38b0629496973d6c4571208710096deb91b7d7 (patch)
treef797da626587c198535c0ea54aee9d467226262a /src/client/views/collections
parent1b412d402c77a2aae82cf86b1f6a23f8a4f82caf (diff)
merge
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.scss20
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx15
-rw-r--r--src/client/views/collections/CollectionMenu.tsx27
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx14
-rw-r--r--src/client/views/collections/CollectionPileView.tsx17
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx8
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx14
-rw-r--r--src/client/views/collections/CollectionStaffView.scss13
-rw-r--r--src/client/views/collections/CollectionStaffView.tsx53
-rw-r--r--src/client/views/collections/CollectionSubView.tsx15
-rw-r--r--src/client/views/collections/CollectionTreeView.scss1
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx6
-rw-r--r--src/client/views/collections/CollectionView.tsx38
-rw-r--r--src/client/views/collections/TabDocView.scss1
-rw-r--r--src/client/views/collections/TabDocView.tsx92
-rw-r--r--src/client/views/collections/TreeView.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx75
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx17
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx20
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx60
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx660
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx46
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx2
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx2
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx136
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx22
28 files changed, 572 insertions, 826 deletions
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index c0530ab81..333ba9f32 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -56,7 +56,7 @@
// }
.lm_header .lm_controls {
position: absolute;
- right: 3px;
+ right: 0px;
}
.lm_header .lm_controls > li {
cursor: pointer;
@@ -64,16 +64,12 @@
width: 18px;
height: 18px;
text-align: center;
- top: 3px;
}
.lm_header ul {
margin: 0;
padding: 0;
list-style-type: none;
}
-.lm_header .lm_tabs {
- position: absolute;
-}
.lm_header .lm_tab {
cursor: pointer;
float: left;
@@ -329,8 +325,9 @@
}
.lm_header .lm_tabs {
+ position: absolute;
overflow-y: hidden;
- width: 100%;
+ width: calc(100% - 5px);
}
ul.lm_tabs::before {
content: ' ';
@@ -484,8 +481,6 @@ ul.lm_tabs::before {
.collectiondockingview-container {
width: 100%;
height: 100%;
- border-style: solid;
- border-width: $COLLECTION_BORDER_WIDTH;
position: absolute;
top: 0;
left: 0;
@@ -508,6 +503,7 @@ ul.lm_tabs::before {
display: flex;
align-content: center;
justify-content: center;
+ background: transparent !important;
}
.lm_controls > li {
@@ -518,7 +514,11 @@ ul.lm_tabs::before {
.lm_controls .lm_popout {
background-image: unset;
- left: -3;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ background: #93939347;
+ z-index: 100;
+ //left: -3;
&:hover {
background: gray;
color: white !important;
@@ -528,7 +528,7 @@ ul.lm_tabs::before {
content: '+';
margin: auto;
font-size: x-large;
- top: -6;
+ top: -4;
position: relative;
}
.lm_maximise {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 4873a61ff..f155e64b5 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -85,6 +85,7 @@ export class CollectionDockingView extends CollectionSubView() {
const dragSource = CollectionDockingView.Instance?._goldenLayout.createDragSource(document.createElement('div'), config);
this.tabDragStart(dragSource, finishDrag);
dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 });
+ return true;
};
tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
@@ -130,7 +131,7 @@ export class CollectionDockingView extends CollectionSubView() {
@undoBatch
@action
- public static ReplaceTab(document: Doc, panelName: OpenWhereMod, stack: any, addToSplit?: boolean, keyValue?: boolean): boolean {
+ public static ReplaceTab(document: Doc, mods: OpenWhereMod, stack: any, panelName: string, addToSplit?: boolean, keyValue?: boolean): boolean {
const instance = CollectionDockingView.Instance;
if (!instance) return false;
const newConfig = CollectionDockingView.makeDocumentConfig(document, panelName, undefined, keyValue);
@@ -151,7 +152,7 @@ export class CollectionDockingView extends CollectionSubView() {
}
return false;
}
- return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
+ return CollectionDockingView.AddSplit(document, mods, stack, panelName);
}
@undoBatch
@@ -406,8 +407,9 @@ export class CollectionDockingView extends CollectionSubView() {
window.addEventListener('mouseup', this.onPointerUp);
if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) {
const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : '';
- if (className.includes('lm_maximise')) this._flush = UndoManager.StartBatch('tab maximize');
- else {
+ if (className.includes('lm_maximise')) {
+ // this._flush = UndoManager.StartBatch('tab maximize');
+ } else {
const tabTarget = (e.target as HTMLElement)?.parentElement?.className.includes('lm_tab') ? (e.target as HTMLElement).parentElement : (e.target as HTMLElement);
const map = Array.from(this.tabMap).find(tab => tab.element[0] === tabTarget);
if (map?.DashDoc && DocumentManager.Instance.getFirstDocumentView(map.DashDoc)) {
@@ -589,7 +591,7 @@ export class CollectionDockingView extends CollectionSubView() {
ScriptingGlobals.add(
function openInLightbox(doc: any) {
- LightboxView.AddDocTab(doc, OpenWhere.lightbox);
+ LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
},
'opens up document in a lightbox',
'(doc: any)'
@@ -618,6 +620,3 @@ ScriptingGlobals.add(
'opens up document in screen overlay layer',
'(doc: any)'
);
-ScriptingGlobals.add(function useRightSplit(doc: any, addToRightSplit?: boolean) {
- CollectionDockingView.ReplaceTab(doc, OpenWhereMod.right, undefined, addToRightSplit);
-});
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 52cf40635..cf154be8d 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -40,8 +40,6 @@ import { CollectionDockingView } from './CollectionDockingView';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionLinearView } from './collectionLinear';
import './CollectionMenu.scss';
-import { COLLECTION_BORDER_WIDTH } from './CollectionView';
-import { TabDocView } from './TabDocView';
interface CollectionMenuProps {
panelHeight: () => number;
@@ -557,25 +555,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
} else return false;
}
- @computed
- get pinButton() {
- const targetDoc = this.selectedDoc;
- const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
- return !targetDoc ? null : (
- <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? 'Unpin from presentation' : 'Pin to presentation'}</div>} placement="top">
- <button
- className="antimodeMenu-button"
- style={{ backgroundColor: isPinned ? '121212' : undefined, borderLeft: '1px solid gray' }}
- onClick={e =>
- TabDocView.PinDoc(targetDoc, {
- /* unpin: isPinned*/
- })
- }>
- <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
- </button>
- </Tooltip>
- );
- }
@undoBatch
@action
@@ -653,7 +632,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
className="antimodeMenu-button"
onPointerDown={() => {
const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
- LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
+ LightboxView.Instance.SetLightboxDoc(targetDoc, undefined, docs);
}}>
<FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
</button>
@@ -673,7 +652,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
pointerEvents: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'none' : undefined,
color: this.props.docView.props.docViewPath().lastElement()?.rootDoc?._type_collection !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
}}
- onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
+ onClick={undoBatch(() => this.props.docView.CollectionFreeFormDocumentView?.float())}>
<FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
</button>
</Tooltip>
@@ -1261,7 +1240,7 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
@undoBatch
togglePreview = () => {
const dividerWidth = 4;
- const borderWidth = Number(COLLECTION_BORDER_WIDTH);
+ const borderWidth = 0;
const panelWidth = this.props.docView.props.PanelWidth();
const previewWidth = NumCast(this.document.schema_previewWidth);
const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index afeef5a8f..ac916fef3 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,9 +1,9 @@
import React = require('react');
import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Field, Opt } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -289,7 +289,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const existingHeader = this.colHeaderData.find(sh => sh.heading === heading);
const existingWidth = existingHeader?.width ? existingHeader.width : 0;
const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
- const width = d.layout_fitWidth ? maxWidth : d[Width]();
+ const width = d.layout_fitWidth ? maxWidth : NumCast(d._width);
return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
}
@@ -299,8 +299,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
- const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
- const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
+ const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
+ const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
if (nw && nh) {
const docWid = this.getDocWidth(d);
return Math.min(maxHeight, (docWid * nh) / nw);
@@ -336,7 +336,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// onPointerMove is used to preview where a document will drop in a column once a drag is complete.
@action
onPointerMove = (force: boolean, ex: number, ey: number) => {
- if (this.childDocList && (this.childDocList.includes(DragManager.DocDragData?.draggedDocuments.lastElement()!) || force || this.isContentActive())) {
+ if (this.childDocList?.includes(DragManager.DocDragData?.draggedDocuments?.lastElement() as any) || force || this.isContentActive()) {
// get the current docs for the column based on the mouse's x coordinate
const xCoord = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[0] - 2 * this.gridGap;
const colDocs = this.getDocsFromXCoord(xCoord);
@@ -443,7 +443,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
if (!this.props.addDocument?.(source)) e.preventDefault();
de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 91701b213..abb12a8ab 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,7 +1,6 @@
import { action, computed, IReactionDisposer } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast } from '../../../fields/Doc';
-import { Height, Width } from '../../../fields/DocSymbols';
import { ScriptField } from '../../../fields/ScriptField';
import { NumCast, StrCast } from '../../../fields/Types';
import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
@@ -79,12 +78,12 @@ export class CollectionPileView extends CollectionSubView() {
toggleStarburst = action(() => {
this.layoutDoc._freeform_scale = undefined;
if (this.layoutEngine() === computeStarburstLayout.name) {
- if (this.rootDoc[Width]() !== NumCast(this.rootDoc._starburstDiameter, 500)) {
- this.rootDoc._starburstDiameter = this.rootDoc[Width]();
+ if (NumCast(this.rootDoc._width) !== NumCast(this.rootDoc._starburstDiameter, 500)) {
+ this.rootDoc._starburstDiameter = NumCast(this.rootDoc._width);
}
const defaultSize = 110;
- this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
- this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
+ this.rootDoc.x = NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) / 2 - NumCast(this.layoutDoc._freeform_pileWidth, defaultSize) / 2;
+ this.rootDoc.y = NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) / 2 - NumCast(this.layoutDoc._freeform_pileHeight, defaultSize) / 2;
this.layoutDoc._width = NumCast(this.layoutDoc._freeform_pileWidth, defaultSize);
this.layoutDoc._height = NumCast(this.layoutDoc._freeform_pileHeight, defaultSize);
DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
@@ -93,10 +92,10 @@ export class CollectionPileView extends CollectionSubView() {
this.props.Document._freeform_pileEngine = computePassLayout.name;
} else {
const defaultSize = NumCast(this.rootDoc._starburstDiameter, 400);
- this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[Width]() / 2 - defaultSize / 2;
- this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[Height]() / 2 - defaultSize / 2;
- this.layoutDoc._freeform_pileWidth = this.layoutDoc[Width]();
- this.layoutDoc._freeform_pileHeight = this.layoutDoc[Height]();
+ this.rootDoc.x = NumCast(this.rootDoc.x) + NumCast(this.layoutDoc._width) / 2 - defaultSize / 2;
+ this.rootDoc.y = NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) / 2 - defaultSize / 2;
+ this.layoutDoc._freeform_pileWidth = NumCast(this.layoutDoc._width);
+ this.layoutDoc._freeform_pileHeight = NumCast(this.layoutDoc._height);
this.layoutDoc._freeform_panX = this.layoutDoc._freeform_panY = 0;
this.layoutDoc._width = this.layoutDoc._height = defaultSize;
this.layoutDoc.background;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 7c61bc4da..3351ca48e 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -270,7 +270,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
!wasPlaying && doubleTap && this.props.Play();
}
},
- this.props.isSelected(true) || this.props.isContentActive(),
+ this.props.isSelected() || this.props.isContentActive(),
undefined,
() => {
if (shiftKey) {
@@ -681,7 +681,7 @@ interface StackedTimelineAnchorProps {
_timeline: HTMLDivElement | null;
focus: DocFocusFunc;
currentTimecode: () => number;
- isSelected: (outsideReaction?: boolean) => boolean;
+ isSelected: () => boolean;
stackedTimeline: CollectionStackedTimeline;
trimStart: number;
trimEnd: number;
@@ -784,7 +784,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
// renders anchor LabelBox
renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
- const anchor = observable({ view: undefined as any });
+ const anchor = observable({ view: undefined as Opt<DocumentView> | null });
const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => {
this.props.playLink(mark, options);
return undefined;
@@ -838,7 +838,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
return (
<div style={{ pointerEvents: this.noEvents ? 'none' : undefined }}>
{inner.view}
- {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+ {!inner.anchor.view || !inner.anchor.view.SELECTED ? null : (
<>
<div key="left" className="collectionStackedTimeline-left-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
<div key="right" className="collectionStackedTimeline-resizer" style={{ pointerEvents: this.noEvents ? 'none' : undefined }} onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0b29e7286..da00093dd 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,17 +4,18 @@ import { CursorProperty } from 'csstype';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType } from '../../documents/DocumentTypes';
import { DragManager, dropActionType } from '../../util/DragManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoBatch, UndoManager } from '../../util/UndoManager';
@@ -31,7 +32,6 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView } from './CollectionSubView';
-import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
@@ -378,7 +378,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const maxWidth = this.columnWidth / this.numGroupColumns;
if (!this.layoutDoc._columnsFill && !(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d))) {
- return Math.min(d[Width](), maxWidth);
+ return Math.min(NumCast(d._width), maxWidth);
}
return maxWidth;
}
@@ -387,8 +387,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this.props.DataDoc;
const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
- const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Width]() : 0);
- const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? d[Height]() : 0);
+ const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._width) : 0);
+ const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._layout_fitWidth || this.props.childLayoutFitWidth?.(d)) ? NumCast(d._height) : 0);
if (nw && nh) {
const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
@@ -471,7 +471,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return true;
}
- } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ } else if (de.complete.linkDragData?.dragDocument.embedContainer === this.props.Document && de.complete.linkDragData?.linkDragView?.CollectionFreeFormDocumentView) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _layout_fitWidth: true, title: 'dropped annotation' });
if (!this.props.addDocument?.(source)) e.preventDefault();
de.complete.linkDocument = DocUtils.MakeLink(source, de.complete.linkDragData.linkSourceGetAnchor(), { link_relationship: 'doc annotation' }); // TODODO this is where in text links get passed
diff --git a/src/client/views/collections/CollectionStaffView.scss b/src/client/views/collections/CollectionStaffView.scss
deleted file mode 100644
index 493a5f670..000000000
--- a/src/client/views/collections/CollectionStaffView.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-.collectionStaffView {
- .collectionStaffView-staff {
- width: 100%;
- margin-top: 100px;
- margin-bottom: 100px;
- }
-
- .collectionStaffView-line {
- margin: 10px;
- height: 2px;
- background: black;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
deleted file mode 100644
index c025e94a8..000000000
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { CollectionSubView } from "./CollectionSubView";
-import React = require("react");
-import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx";
-import { NumCast } from "../../../fields/Types";
-import "./CollectionStaffView.scss";
-import { observer } from "mobx-react";
-
-@observer
-export class CollectionStaffView extends CollectionSubView() {
- private _reactionDisposer: IReactionDisposer | undefined;
- @observable private _staves = NumCast(this.props.Document.staves);
-
- componentWillUnmount() {
- this._reactionDisposer?.();
- }
- componentDidMount = () => {
- this._reactionDisposer = reaction(() => NumCast(this.props.Document.staves),
- (staves) => runInAction(() => this._staves = staves)
- );
-
- this.props.Document.staves = 5;
- }
-
- @computed get addStaffButton() {
- return <div onPointerDown={this.addStaff}>+</div>;
- }
-
- @computed get staves() {
- const staves = [];
- for (let i = 0; i < this._staves; i++) {
- const rows = [];
- for (let j = 0; j < 5; j++) {
- rows.push(<div key={`staff-${i}-${j}`} className="collectionStaffView-line"></div>);
- }
- staves.push(<div key={`staff-${i}`} className="collectionStaffView-staff">
- {rows}
- </div>);
- }
- return staves;
- }
-
- @action
- addStaff = (e: React.PointerEvent) => {
- this.props.Document.staves = this._staves + 1;
- }
-
- render() {
- return <div className="collectionStaffView">
- {this.staves}
- {this.addStaffButton}
- </div>;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8a1ba0df1..328b060c4 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -25,6 +25,7 @@ import { DocComponent } from '../DocComponent';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { CollectionView, CollectionViewProps } from './CollectionView';
import React = require('react');
+import { LoadingBox } from '../nodes/LoadingBox';
export interface SubCollectionViewProps extends CollectionViewProps {
isAnyChildContentActive: () => boolean;
@@ -34,19 +35,16 @@ export function CollectionSubView<X>(moreProps?: X) {
class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private gestureDisposer?: GestureUtils.GestureEventDisposer;
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
protected _mainCont?: HTMLDivElement;
@observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
@observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
this.gestureDisposer?.();
- this._multiTouchDisposer?.();
if (ele) {
this._mainCont = ele;
this.dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
- this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
}
};
protected CreateDropTarget(ele: HTMLDivElement) {
@@ -56,16 +54,13 @@ export function CollectionSubView<X>(moreProps?: X) {
componentWillUnmount() {
this.gestureDisposer?.();
- this._multiTouchDisposer?.();
}
@computed get dataDoc() {
return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
}
- rootSelected = (outsideReaction?: boolean) => {
- return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction));
- };
+ rootSelected = () => this.props.isSelected() || (this.rootDoc && this.props.rootSelected());
// The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
// When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
@@ -125,7 +120,7 @@ export function CollectionSubView<X>(moreProps?: X) {
const docsforFilter: Doc[] = [];
childDocs.forEach(d => {
// dragging facets
- const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
+ const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragDocsFilter));
if (dragged && SnappingManager.GetCanEmbed() && DragManager.docsBeingDragged.includes(d)) return false;
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0;
if (notFiltered) {
@@ -450,13 +445,13 @@ export function CollectionSubView<X>(moreProps?: X) {
if (typeof files === 'string') {
const loading = Docs.Create.LoadingDocument(files, options);
generatedDocuments.push(loading);
- Doc.addCurrentlyLoading(loading);
+ LoadingBox.addCurrentlyLoading(loading);
DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
generatedDocuments.push(
...files.map(file => {
const loading = Docs.Create.LoadingDocument(file, options);
- Doc.addCurrentlyLoading(loading);
+ LoadingBox.addCurrentlyLoading(loading);
DocUtils.uploadFileToDoc(file, {}, loading);
return loading;
})
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2bf649caf..21efeba44 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -4,7 +4,6 @@
transform-origin: top left;
}
.collectionTreeView-dropTarget {
- border-width: $COLLECTION_BORDER_WIDTH;
border-color: transparent;
border-style: solid;
border-radius: inherit;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e408c193a..761192a22 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,7 +1,7 @@
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
@@ -371,8 +371,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
marginX = () => NumCast(this.doc._xMargin);
marginTop = () => NumCast(this.doc._yMargin);
marginBot = () => NumCast(this.doc._yMargin);
- documentTitleWidth = () => Math.min(this.layoutDoc?.[Width](), this.panelWidth());
- documentTitleHeight = () => (this.layoutDoc?.[Height]() || 0) - NumCast(this.layoutDoc.layout_autoHeightMargins);
+ documentTitleWidth = () => Math.min(NumCast(this.layoutDoc?._width), this.panelWidth());
+ documentTitleHeight = () => NumCast(this.layoutDoc?._height) - NumCast(this.layoutDoc.layout_autoHeightMargins);
truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1));
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c2062e8ab..389a9a534 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -33,7 +33,6 @@ import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from './CollectionTreeView';
import './CollectionView.scss';
-export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
interface CollectionViewProps_ extends FieldViewProps {
@@ -81,8 +80,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
private reactionDisposer: IReactionDisposer | undefined;
@observable _isContentActive: boolean | undefined;
- protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
-
constructor(props: any) {
super(props);
runInAction(() => (this._annotationKeySuffix = returnEmptyString));
@@ -124,23 +121,22 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
if (type === undefined) return null;
switch (type) {
default:
- case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
- case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
- case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
- case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
- case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
- case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
- case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
- case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
- case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
- case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
- case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
- case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
- case CollectionViewType.NoteTaking: return <CollectionNoteTakingView key="collview" {...props} />;
- case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
- case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
- case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
- //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
+ case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
+ case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
+ case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
+ case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
+ case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
+ case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
+ case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
+ case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
+ case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
+ case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
+ case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
+ case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
+ case CollectionViewType.NoteTaking: return <CollectionNoteTakingView key="collview" {...props} />;
+ case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
+ case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+ case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
}
};
@@ -175,7 +171,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
if (cm && !e.isPropagationStopped()) {
// need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
!Doc.noviceMode &&
- this.setupViewTypes('UI Controls...', vtype => {
+ this.setupViewTypes('Appearance...', vtype => {
const newRendition = Doc.MakeEmbedding(this.rootDoc);
newRendition._type_collection = vtype;
this.props.addDocTab(newRendition, OpenWhere.addRight);
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 13bb3a577..d447991a1 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -15,7 +15,6 @@ input.lm_title {
}
input.lm_title {
- transition-delay: 0.35s;
width: max-content;
cursor: pointer;
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 26aa5a121..6e1e6cf8d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -6,12 +6,12 @@ import { action, computed, IReactionDisposer, observable, ObservableSet, reactio
import { observer } from 'mobx-react';
import * as ReactDOM from 'react-dom/client';
import { Doc, Opt } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { FieldId } from '../../../fields/RefField';
import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
+import { DashColor, emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
@@ -53,7 +53,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@observable _isActive: boolean = false;
@observable _isAnyChildContentActive = false;
@computed get _isUserActivated() {
- return SelectionManager.Views().some(view => view.rootDoc === this._document) || this._isAnyChildContentActive;
+ return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive;
}
@computed get _isContentActive() {
return this._isUserActivated || this._hovering;
@@ -64,24 +64,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@computed get layoutDoc() {
return this._document && Doc.Layout(this._document);
}
- @computed get tabBorderColor() {
- const highlight = DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting);
- if (highlight?.highlightIndex === Doc.DocBrushStatus.highlighted) return highlight.highlightColor;
- return 'transparent';
- }
- @computed get tabColor() {
- return this._isUserActivated ? Colors.WHITE : this._hovering ? Colors.LIGHT_GRAY : Colors.MEDIUM_GRAY;
- }
- @computed get tabTextColor() {
- return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color)));
- }
- // @computed get renderBounds() {
- // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0];
- // const xbounds = bounds[2] - bounds[0];
- // const ybounds = bounds[3] - bounds[1];
- // const dim = Math.max(xbounds, ybounds);
- // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim };
- // }
get stack() {
return (this.props as any).glContainer.parent.parent;
@@ -163,14 +145,31 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.reactComponents = [iconWrap, closeWrap];
tab.element[0].prepend(iconWrap);
tab._disposers.color = reaction(
- () => ({ color: this.tabColor, borderColor: this.tabBorderColor }),
- coloring => {
- const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
+ () => ({ variant: SettingsManager.userVariantColor, degree: Doc.GetBrushStatus(doc), highlight: DefaultStyleProvider(this._document, undefined, StyleProp.Highlighting) }),
+ ({ variant, degree, highlight }) => {
+ const color = highlight?.highlightIndex === Doc.DocBrushStatus.highlighted ? highlight.highlightColor : degree ? ['transparent', variant, variant, 'orange'][degree] : variant;
+
+ const textColor = color === variant ? SettingsManager.userColor : lightOrDark(color);
titleEle.style.color = textColor;
- titleEle.style.backgroundColor = coloring.borderColor;
iconWrap.style.color = textColor;
closeWrap.style.color = textColor;
- tab.element[0].style.background = coloring.color;
+ tab.element[0].style.background =
+ color === variant
+ ? DashColor(color)
+ .fade(
+ this._isUserActivated
+ ? 0
+ : this._hovering
+ ? 0.25
+ : degree === Doc.DocBrushStatus.selfBrushed
+ ? 0.5
+ : degree === Doc.DocBrushStatus.protoBrushed //
+ ? 0.7
+ : 0.9
+ )
+ .rgb()
+ .toString()
+ : color;
},
{ fireImmediately: true }
);
@@ -181,6 +180,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.header.parent.setActiveContentItem(tab.contentItem);
tab.setActive(true);
}
+ this._document && Doc.BrushDoc(this._document);
+ };
+ tab.element[0].onmouseleave = (e: MouseEvent) => {
+ this._document && Doc.UnBrushDoc(this._document);
};
tab.element[0].oncontextmenu = (e: MouseEvent) => {
@@ -203,29 +206,25 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
});
tab._disposers.selectionDisposer = reaction(
- () => SelectionManager.Views().some(view => view.rootDoc === this._document),
+ () => SelectionManager.IsSelected(this._document),
action(selected => {
if (selected) this._activated = true;
const toggle = tab.element[0].children[2].children[0] as HTMLInputElement;
if (selected && tab.contentItem !== tab.header.parent.getActiveContentItem()) {
undoable(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch')();
}
- toggle.style.fontWeight = selected ? 'bold' : '';
+ //toggle.style.fontWeight = selected ? 'bold' : '';
// toggle.style.textTransform = selected ? "uppercase" : "";
}),
{ fireImmediately: true }
);
// highlight the tab when the tab document is brushed in any part of the UI
- // tab._disposers.reactionDisposer = reaction(
- // () => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }),
- // ({ title, degree }) => {
- // titleEle.value = title;
- // titleEle.style.padding = degree ? 0 : 2;
- // titleEle.style.border = `${['gray', 'gray', 'gray'][degree]} ${['none', 'dashed', 'solid'][degree]} 2px`;
- // },
- // { fireImmediately: true }
- // );
+ tab._disposers.reactionDisposer = reaction(
+ () => doc?.title,
+ title => (titleEle.value = title),
+ { fireImmediately: true }
+ );
// clean up the tab when it is closed
tab.closeElement
@@ -346,11 +345,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged);
}
+ // Flag indicating that when a tab is activated, it should not select it's document.
+ // this is used by the link properties menu when it wants to display the link target without selecting the target (which would make the link property window go away since it would no longer be selected)
+ public static DontSelectOnActivate = 'dontSelectOnActivate';
+
@action.bound
private onActiveContentItemChanged(contentItem: any) {
if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
- if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== 'dontSelectOnActivate') setTimeout(() => SelectionManager.SelectView(this._view, false));
+ if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== TabDocView.DontSelectOnActivate) setTimeout(() => SelectionManager.SelectView(this._view, false));
!this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
}
}
@@ -365,8 +368,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
addDocTab = (doc: Doc, location: OpenWhere) => {
SelectionManager.DeselectAll();
const whereFields = location.split(':');
- const keyValue = whereFields[1]?.includes('KeyValue');
- const whereMods: OpenWhereMod = whereFields.length > 1 ? (whereFields[1].replace('KeyValue', '') as OpenWhereMod) : OpenWhereMod.none;
+ const keyValue = whereFields.includes(OpenWhereMod.keyvalue);
+ const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none;
+ const panelName = whereFields.length > 1 ? whereFields.lastElement() : '';
if (doc.dockingConfig && !keyValue) return DashboardView.openDashboard(doc);
// prettier-ignore
switch (whereFields[0]) {
@@ -379,10 +383,10 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return true;
}
}
- return LightboxView.AddDocTab(doc, location);
+ return LightboxView.Instance.AddDocTab(doc, OpenWhere.lightbox);
case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods);
- case OpenWhere.replace: return CollectionDockingView.ReplaceTab(doc, whereMods, this.stack, undefined, keyValue);
- case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, "dontSelectOnActivate", keyValue);
+ case OpenWhere.replace: return CollectionDockingView.ReplaceTab(doc, whereMods, this.stack, panelName, undefined, keyValue);
+ case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, TabDocView.DontSelectOnActivate, keyValue);
case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods, this.stack, undefined, keyValue);
}
};
@@ -539,7 +543,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
default: return 'gray';
}
})(doc.type as DocumentType);
- return !background ? undefined : <div style={{ width: doc[Width](), height: doc[Height](), position: 'absolute', display: 'block', background }} />;
+ return !background ? undefined : <div style={{ width: NumCast(doc._width), height: NumCast(doc._height), position: 'absolute', display: 'block', background }} />;
}
}
};
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 193c70add..004857ed1 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,9 +1,10 @@
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton, Size } from 'browndash-components';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc';
-import { DocData, Height, Width } from '../../../fields/DocSymbols';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
@@ -19,6 +20,7 @@ import { DragManager, dropActionType } from '../../util/DragManager';
import { LinkManager } from '../../util/LinkManager';
import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable, undoBatch, UndoManager } from '../../util/UndoManager';
@@ -32,11 +34,9 @@ import { KeyValueBox } from '../nodes/KeyValueBox';
import { StyleProp } from '../StyleProvider';
import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
import { CollectionView } from './CollectionView';
+import { TreeSort } from './TreeSort';
import './TreeView.scss';
import React = require('react');
-import { IconButton, Size } from 'browndash-components';
-import { TreeSort } from './TreeSort';
-import { SettingsManager } from '../../util/SettingsManager';
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -165,7 +165,7 @@ export class TreeView extends React.Component<TreeViewProps> {
return this.childDocList(this.fieldKey + '_annotations');
}
@computed get selected() {
- return SelectionManager.IsSelected(this._docRef);
+ return this._docRef?.SELECTED;
}
childDocList(field: string) {
@@ -455,7 +455,7 @@ export class TreeView extends React.Component<TreeViewProps> {
embeddedPanelHeight = () => {
const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
return Math.min(
- layoutDoc[Height](),
+ NumCast(layoutDoc._height),
this.MAX_EMBED_HEIGHT,
(() => {
const aspect = Doc.NativeAspect(layoutDoc);
@@ -464,7 +464,7 @@ export class TreeView extends React.Component<TreeViewProps> {
? !Doc.NativeHeight(layoutDoc)
? NumCast(layoutDoc._height)
: Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.treeViewParent._height)))
- : (this.embeddedPanelWidth() * layoutDoc[Height]()) / layoutDoc[Width]();
+ : (this.embeddedPanelWidth() * NumCast(layoutDoc._height)) / NumCast(layoutDoc._width);
})()
);
};
@@ -741,7 +741,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<div
className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
key="bullet"
- title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : 'view fields'}
+ title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : `view ${this.props.document.type} content`}
onClick={this.bulletClick}
style={
this.props.treeView.outlineMode
@@ -872,7 +872,7 @@ export class TreeView extends React.Component<TreeViewProps> {
case StyleProp.Highlighting: if (this.props.treeView.outlineMode) return undefined;
case StyleProp.BoxShadow: return undefined;
case StyleProp.DocContents:
- const highlightIndex = this.props.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.isBrushedHighlightedDegree(doc);
+ const highlightIndex = this.props.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.GetBrushHighlightStatus(doc);
const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
return treeView.outlineMode ? null : (
<div
@@ -1265,7 +1265,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const childLayout = Doc.Layout(pair.layout);
const rowHeight = () => {
const aspect = Doc.NativeAspect(childLayout);
- return aspect ? Math.min(childLayout[Width](), rowWidth()) / aspect : childLayout[Height]();
+ return aspect ? Math.min(NumCast(childLayout._width), rowWidth()) / aspect : NumCast(childLayout._height);
};
return (
<TreeView
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
new file mode 100644
index 000000000..00505dbe3
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormBackgroundGrid.tsx
@@ -0,0 +1,75 @@
+import { observer } from 'mobx-react';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast } from '../../../../fields/Types';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+
+export interface CollectionFreeFormViewBackgroundGridProps {
+ panX: () => number;
+ panY: () => number;
+ PanelWidth: () => number;
+ PanelHeight: () => number;
+ color: () => string;
+ isAnnotationOverlay?: boolean;
+ nativeDimScaling: () => number;
+ zoomScaling: () => number;
+ layoutDoc: Doc;
+ cachedCenteringShiftX: number;
+ cachedCenteringShiftY: number;
+}
+@observer
+export class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
+ chooseGridSpace = (gridSpace: number): number => {
+ if (!this.props.zoomScaling()) return gridSpace;
+ const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace;
+ return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2);
+ };
+ render() {
+ const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
+ const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling();
+ const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling();
+ const renderGridSpace = gridSpace * this.props.zoomScaling();
+ const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace;
+ const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace;
+ const strokeStyle = this.props.color();
+ return !this.props.nativeDimScaling() ? null : (
+ <canvas
+ className="collectionFreeFormView-grid"
+ width={w}
+ height={h}
+ style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+ ref={el => {
+ const ctx = el?.getContext('2d');
+ if (ctx) {
+ const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
+ const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
+ ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
+ ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+ ctx.clearRect(0, 0, w, h);
+ if (ctx) {
+ ctx.strokeStyle = strokeStyle;
+ ctx.fillStyle = strokeStyle;
+ ctx.beginPath();
+ if (this.props.zoomScaling() > 1) {
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+ ctx.moveTo(x, Cy - h);
+ ctx.lineTo(x, Cy + h);
+ }
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.moveTo(Cx - w, y);
+ ctx.lineTo(Cx + w, y);
+ }
+ } else {
+ for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace)
+ for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+ ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
+ }
+ }
+ ctx.stroke();
+ }
+ }
+ }}
+ />
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index d93e44ab7..403fba67b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,5 +1,4 @@
import { Doc, Field, FieldResult } from '../../../../fields/Doc';
-import { Height, Width } from '../../../../fields/DocSymbols';
import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
@@ -29,6 +28,8 @@ export interface ViewDefBounds {
}
export interface PoolData {
+ pair: { layout: Doc; data?: Doc };
+ replica: string;
x: number;
y: number;
z?: number;
@@ -41,9 +42,7 @@ export interface PoolData {
opacity?: number;
transition?: string;
highlight?: boolean;
- replica: string;
- pointerEvents?: string; // without this, toggling lockPosition of a group/collection in a freeform view won't update until something else invalidates the freeform view's documents forcing -- this is a problem with doLayoutComputation which makes a performance test to insure somethingChanged
- pair: { layout: Doc; data?: Doc };
+ pointerEvents?: string;
}
export interface ViewDefResult {
@@ -91,8 +90,8 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
docMap.set(layout[Id], {
x: NumCast(layout.x),
y: NumCast(layout.y),
- width: layout[Width](),
- height: layout[Height](),
+ width: NumCast(layout._width),
+ height: NumCast(layout._height),
pair: { layout, data },
transition: 'all .3s',
replica: '',
@@ -106,8 +105,8 @@ export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc
const burstDiam = [NumCast(pivotDoc._width), NumCast(pivotDoc._height)];
const burstScale = NumCast(pivotDoc._starburstDocScale, 1);
childPairs.forEach(({ layout, data }, i) => {
- const aspect = layout[Height]() / layout[Width]();
- const docSize = Math.min(Math.min(400, layout[Width]()), Math.min(400, layout[Width]()) / aspect) * burstScale;
+ const aspect = NumCast(layout._height) / NumCast(layout._width);
+ const docSize = Math.min(Math.min(400, NumCast(layout._width)), Math.min(400, NumCast(layout._width)) / aspect) * burstScale;
const deg = (i / childPairs.length) * Math.PI * 2;
docMap.set(layout[Id], {
x: Math.min(burstDiam[0] / 2 - docSize, Math.max(-burstDiam[0] / 2, (Math.cos(deg) * burstDiam[0]) / 2 - docSize / 2)),
@@ -156,7 +155,7 @@ export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Do
x: 0,
y: 0,
zIndex: 0,
- width: 0, // should make doc hidden in CollectionFreefromDocumentView
+ width: 0, // should make doc hidden in CollectionFreeFormDocumentView
height: 0,
pair,
replica: '',
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 24a758d8c..aca6df3c9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,7 +1,7 @@
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Field } from '../../../../fields/Doc';
-import { DocCss } from '../../../../fields/DocSymbols';
+import { Brushed, DocCss } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
@@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
- const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
- const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
+ const aActive = A.isSelected() || A.rootDoc[Brushed];
+ const bActive = B.isSelected() || B.rootDoc[Brushed];
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
@@ -239,11 +239,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
textX,
textY,
// fully connected
- // pt1,
- // pt2,
+ // pt1,
+ // pt2,
// this code adds space between links
- pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt1,
- pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt2,
+ pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt1,
+ pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt2,
};
}
@@ -269,12 +269,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
//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 arrowScale = NumCast(link.link_displayArrow_scale, 3)
+ const arrowScale = NumCast(link.link_displayArrow_scale, 3);
return link.opacity === 0 || !a.width || !b.width || (!(Doc.UserDoc().showLinkLines || link.link_displayLine) && !aActive && !bActive) ? null : (
<>
<defs>
- <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4*arrowScale}`} markerHeight={`${3*arrowScale}`} refX="0" refY={`${1.5*arrowScale}`} orient="auto">
- <polygon points={`0 0, ${3*arrowScale} ${1.5*arrowScale}, 0 ${3*arrowScale}`} fill={stroke} />
+ <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4 * arrowScale}`} markerHeight={`${3 * arrowScale}`} refX="0" refY={`${1.5 * arrowScale}`} orient="auto">
+ <polygon points={`0 0, ${3 * arrowScale} ${1.5 * arrowScale}, 0 ${3 * arrowScale}`} fill={stroke} />
</marker>
<filter id="outline">
<feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
new file mode 100644
index 000000000..856e195a3
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormPannableContents.tsx
@@ -0,0 +1,60 @@
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../../fields/Doc';
+import { ScriptField } from '../../../../fields/ScriptField';
+import { PresBox } from '../../nodes/trails/PresBox';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+import { CollectionFreeFormView } from './CollectionFreeFormView';
+
+export interface CollectionFreeFormPannableContentsProps {
+ rootDoc: Doc;
+ viewDefDivClick?: ScriptField;
+ children?: React.ReactNode | undefined;
+ transition?: string;
+ isAnnotationOverlay: boolean | undefined;
+ transform: () => string;
+ brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
+}
+
+@observer
+export class CollectionFreeFormPannableContents extends React.Component<CollectionFreeFormPannableContentsProps> {
+ @computed get presPaths() {
+ return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null;
+ }
+ // rectangle highlight used when following trail/link to a region of a collection that isn't a document
+ showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
+ !viewport ? null : (
+ <div
+ className="collectionFreeFormView-brushView"
+ style={{
+ transform: `translate(${viewport.panX}px, ${viewport.panY}px)`,
+ width: viewport.width,
+ height: viewport.height,
+ border: `orange solid ${viewport.width * 0.005}px`,
+ }}
+ />
+ );
+
+ render() {
+ return (
+ <div
+ className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
+ onScroll={e => {
+ const target = e.target as any;
+ if (getComputedStyle(target)?.overflow === 'visible') {
+ target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
+ }
+ }}
+ style={{
+ transform: this.props.transform(),
+ transition: this.props.transition,
+ width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
+ }}>
+ {this.props.children}
+ {this.presPaths}
+ {this.showViewport(this.props.brushedView())}
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index da0f7c893..e350c35cc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,20 +9,18 @@ import { DocData, Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool, PointData, Segment } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, DashColor, emptyFunction, intersectRect, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
-import { InteractionUtils } from '../../../util/InteractionUtils';
import { FollowLinkScript } from '../../../util/LinkFollower';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
@@ -31,13 +29,13 @@ import { freeformScrollMode } 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, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { CtrlKey } from '../../GlobalKeyHandler';
+import { ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
-import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView';
+import { CollectionFreeFormDocumentView, CollectionFreeFormDocumentViewWrapper } from '../../nodes/CollectionFreeFormDocumentView';
import { DocFocusOptions, DocumentView, DocumentViewProps, OpenWhere } from '../../nodes/DocumentView';
import { FieldViewProps } from '../../nodes/FieldView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
@@ -46,8 +44,9 @@ import { CreateImage } from '../../nodes/WebBoxRenderer';
import { StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
-import { TabDocView } from '../TabDocView';
+import { CollectionFreeFormBackgroundGrid } from './CollectionFreeFormBackgroundGrid';
import { computePassLayout, computePivotLayout, computeStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormPannableContents } from './CollectionFreeFormPannableContents';
import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
@@ -81,16 +80,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _downX: number = 0;
private _downY: number = 0;
private _downTime = 0;
- private _inkToTextStartX: number | undefined;
- private _inkToTextStartY: number | undefined;
- private _wordPalette: Map<string, string> = new Map<string, string>();
private _clusterDistance: number = 75;
private _hitCluster: number = -1;
private _disposers: { [name: string]: IReactionDisposer } = {};
private _renderCutoffData = observable.map<string, boolean>();
- private _layoutPoolData = observable.map<string, PoolData>();
- private _layoutSizeData = observable.map<string, { width?: number; height?: number }>();
- private _cachedPool: Map<string, PoolData> = new Map();
private _batch: UndoManager.Batch | undefined = undefined;
private _brushtimer: any;
private _brushtimer1: any;
@@ -110,9 +103,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private get autoResetFieldKey() {
return (this.props.viewField ?? '') + '_freeform_autoReset';
}
- 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 _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@@ -122,10 +112,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
- @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
@observable _brushedView: { width: number; height: number; panX: number; panY: number } | undefined; // highlighted region of freeform canvas used by presentations to indicate a region
+ @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
- @computed get views() {
+ @computed get contentViews() {
const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
if (viewsMask.length) renderableEles.push(<div className={`collectionfreeformview-mask${this._layoutElements.some(ele => (ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}</div>);
@@ -170,19 +160,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.layout_fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
return this.props.isAnnotationOverlay || this.props.originTopLeft ? 0 : (aspect * this.props.PanelWidth()) / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
- @computed get cachedGetLocalTransform(): Transform {
- return Transform.Identity()
- .scale(1 / this.zoomScaling())
- .translate(this.panX(), this.panY());
- }
- @computed get cachedGetContainerTransform(): Transform {
- return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
+ @computed get panZoomXf() {
+ return new Transform(this.panX(), this.panY(), 1 / this.zoomScaling());
}
- @computed get cachedGetTransform(): Transform {
- return this.getContainerTransform()
- .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+ @computed get screenToLocalXf() {
+ return this.props
+ .ScreenToLocalTransform()
+ .scale(this.props.isAnnotationOverlay ? 1 : 1)
.translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
- .transform(this.cachedGetLocalTransform);
+ .transform(this.panZoomXf);
}
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
@@ -247,11 +233,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document[this.panXFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panX, 1));
panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document[this.panYFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.freeform_panY, 1));
zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
- contentTransform = () =>
+ PanZoomCenterXf = () =>
this.props.isAnnotationOverlay && this.zoomScaling() === 1 ? `` : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
- getTransform = () => this.cachedGetTransform.copy();
- getLocalTransform = () => this.cachedGetLocalTransform.copy();
- getContainerTransform = () => this.cachedGetContainerTransform.copy();
+ ScreenToLocalXf = () => this.screenToLocalXf.copy();
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
isAnyChildContentActive = () => this.props.isAnyChildContentActive();
addLiveTextBox = (newBox: Doc) => {
@@ -260,7 +244,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
selectDocuments = (docs: Doc[]) => {
SelectionManager.DeselectAll();
- docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).map(dv => dv && SelectionManager.SelectView(dv, true));
+ docs.map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.DocumentView?.())).forEach(dv => dv && SelectionManager.SelectView(dv, true));
};
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
@@ -331,19 +315,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- getView = async (doc: Doc): Promise<Opt<DocumentView>> => {
- return new Promise<Opt<DocumentView>>(res => {
+ getView = async (doc: Doc): Promise<Opt<DocumentView>> =>
+ new Promise<Opt<DocumentView>>(res => {
if (doc.hidden && this._lightboxDoc !== doc) doc.hidden = false;
const findDoc = (finish: (dv: DocumentView) => void) => DocumentManager.Instance.AddViewRenderedCb(doc, dv => finish(dv));
findDoc(dv => res(dv));
});
- };
@action
internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
if (!super.onInternalDrop(e, de)) return false;
const refDoc = docDragData.droppedDocuments[0];
- const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
+ const [xpo, ypo] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
const z = NumCast(refDoc.z);
const x = (z ? xpo : xp) - docDragData.offset[0];
const y = (z ? ypo : yp) - docDragData.offset[1];
@@ -358,16 +341,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
const layoutDoc = Doc.Layout(d);
+ const delta = Utils.rotPt(x - dropPos[0], y - dropPos[1], this.props.ScreenToLocalTransform().Rotate);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000), false); // get non-default values for everything else
- vals.x = x + NumCast(pvals.x) - dropPos[0];
- vals.y = y + NumCast(pvals.y) - dropPos[1];
+ vals.x = NumCast(pvals.x) + delta.x;
+ vals.y = NumCast(pvals.y) + delta.y;
CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
} else {
- d.x = x + NumCast(d.x) - dropPos[0];
- d.y = y + NumCast(d.y) - dropPos[1];
+ d.x = NumCast(d.x) + delta.x;
+ d.y = NumCast(d.y) + delta.y;
}
d._layout_modificationDate = new DateField();
const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
@@ -451,15 +435,28 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
- const [xp, yp] = this.getTransform().transformPoint(de.x, de.y);
+ const [xp, yp] = this.screenToLocalXf.transformPoint(de.x, de.y);
if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData, xp, yp);
else if (de.complete.linkDragData) return this.internalLinkDrop(e, de, de.complete.linkDragData, xp, yp);
else if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, xp, yp);
return false;
};
- onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.getTransform().transformPoint(e.pageX, e.pageY));
-
+ onExternalDrop = (e: React.DragEvent) => (([x, y]) => super.onExternalDrop(e, { x, y }))(this.screenToLocalXf.transformPoint(e.pageX, e.pageY));
+
+ static overlapping(doc1: Doc, doc2: Doc, clusterDistance: number) {
+ const doc2Layout = Doc.Layout(doc2);
+ const doc1Layout = Doc.Layout(doc1);
+ const x2 = NumCast(doc2.x) - clusterDistance;
+ const y2 = NumCast(doc2.y) - clusterDistance;
+ const w2 = NumCast(doc2Layout._width) + clusterDistance;
+ const h2 = NumCast(doc2Layout._height) + clusterDistance;
+ const x = NumCast(doc1.x) - clusterDistance;
+ const y = NumCast(doc1.y) - clusterDistance;
+ const w = NumCast(doc1Layout._width) + clusterDistance;
+ const h = NumCast(doc1Layout._height) + clusterDistance;
+ return doc1.z === doc2.z && intersectRect({ left: x, top: y, width: w, height: h }, { left: x2, top: y2, width: w2, height: h2 });
+ }
pickCluster(probe: number[]) {
return this.childLayoutPairs
.map(pair => pair.layout)
@@ -477,16 +474,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}, -1);
}
- tryDragCluster(e: PointerEvent | TouchEvent, cluster: number) {
+ tryDragCluster(e: PointerEvent, cluster: number) {
if (cluster !== -1) {
- const ptsParent = e instanceof PointerEvent ? e : e.targetTouches.item(0);
+ const ptsParent = e;
if (ptsParent) {
const eles = this.childLayoutPairs.map(pair => pair.layout).filter(cd => (this.props.Document._freeform_useClusters ? NumCast(cd.layout_cluster) : NumCast(cd.group, -1)) === cluster);
const clusterDocs = eles.map(ele => DocumentManager.Instance.getDocumentView(ele, this.props.DocumentView?.())!);
const { left, top } = clusterDocs[0].getBounds() || { left: 0, top: 0 };
const de = new DragManager.DocumentDragData(eles, e.ctrlKey || e.altKey ? 'embed' : undefined);
de.moveDocument = this.props.moveDocument;
- de.offset = this.getTransform().transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
+ de.offset = this.screenToLocalXf.transformDirection(ptsParent.clientX - left, ptsParent.clientY - top);
DragManager.StartDocumentDrag(
clusterDocs.map(v => v.ContentDiv!),
de,
@@ -519,7 +516,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
docs.map(doc =>
this._clusterSets.map((set, i) =>
set.map(member => {
- if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ if (docFirst.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
docFirst.layout_cluster = i;
}
})
@@ -552,7 +549,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
@action
- updateCluster(doc: Doc) {
+ updateCluster = (doc: Doc) => {
const childLayouts = this.childLayoutPairs.map(pair => pair.layout);
if (this.props.Document._freeform_useClusters) {
this._clusterSets.forEach(set => Doc.IndexOf(doc, set) !== -1 && set.splice(Doc.IndexOf(doc, set), 1));
@@ -560,7 +557,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
doc.layout_cluster = -1;
this._clusterSets.forEach((set, i) =>
set.forEach(member => {
- if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && Doc.overlapping(doc, member, this._clusterDistance)) {
+ if (doc.layout_cluster === -1 && Doc.IndexOf(member, childLayouts) !== -1 && CollectionFreeFormView.overlapping(doc, member, this._clusterDistance)) {
doc.layout_cluster = i;
}
})
@@ -581,9 +578,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._clusterSets[doc.layout_cluster ?? 0].push(doc);
}
}
- }
+ };
- getClusterColor = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
+ clusterStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1
switch (property) {
case StyleProp.BackgroundColor:
@@ -620,40 +617,26 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- onPenUp = (e: PointerEvent): void => {
- if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- document.removeEventListener('pointerup', this.onPenUp);
- const currentCol = DocListCast(this.rootDoc.currentInkDoc);
- const rootDocList = DocListCast(this.rootDoc.data);
- currentCol.push(rootDocList[rootDocList.length - 1]);
-
- this._batch?.end();
- }
- };
-
- @action
onPointerDown = (e: React.PointerEvent): void => {
this._downX = this._lastX = e.pageX;
this._downY = this._lastY = e.pageY;
this._downTime = Date.now();
- if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- if (
- !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
- !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
- !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
- ) {
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+ if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this.props.isContentActive(true)) {
+ if (!this.props.Document._isGroup) {
+ // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
switch (Doc.ActiveTool) {
- case InkTool.Highlighter: break;
+ case InkTool.Highlighter: break;
case InkTool.Write: break;
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.Eraser:
this._batch = UndoManager.StartBatch('collectionErase');
setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
break;
case InkTool.None:
if (!(this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- this._hitCluster = this.pickCluster(this.getTransform().transformPoint(e.clientX, e.clientY));
+ this._hitCluster = this.pickCluster(this.screenToLocalXf.transformPoint(e.clientX, e.clientY));
setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, this._hitCluster !== -1 ? true : false, false);
}
break;
@@ -662,29 +645,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
- @action
- handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
- // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
- const pt = me.changedTouches[0];
- if (pt) {
- this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY));
- if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
- this.removeMoveListeners();
- this.addMoveListeners();
- this.removeEndListeners();
- this.addEndListeners();
- if (Doc.ActiveTool === InkTool.None) {
- this._lastX = pt.pageX;
- this._lastY = pt.pageY;
- e.preventDefault();
- e.stopPropagation();
- } else {
- e.preventDefault();
- }
- }
- }
- };
-
public unprocessedDocs: Doc[] = [];
public static collectionsWithUnprocessedInk = new Set<CollectionFreeFormView>();
@undoBatch
@@ -697,25 +657,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
case GestureUtils.Gestures.Triangle:
case GestureUtils.Gestures.Stroke:
const points = ge.points;
- const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- console.log(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
+ const B = this.screenToLocalXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
+ const inkWidth = ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale;
const inkDoc = Docs.Create.InkDocument(
- ActiveInkColor(),
- ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
points,
- ActiveIsInkMask(),
- {
- title: ge.gesture.toString(),
- x: B.x - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
- y: B.y - (ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale) / 2,
- _width: B.width + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- _height: B.height + ActiveInkWidth() * this.props.ScreenToLocalTransform().Scale,
- }
+ { title: ge.gesture.toString(),
+ x: B.x - inkWidth / 2,
+ y: B.y - inkWidth / 2,
+ _width: B.width + inkWidth,
+ _height: B.height + inkWidth }, // prettier-ignore
+ inkWidth
);
if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
@@ -725,69 +676,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
break;
case GestureUtils.Gestures.Rectangle:
- if (this._inkToTextStartX && this._inkToTextStartY) {
- const end = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const setDocs = this.getActiveDocuments().filter(s => DocCast(s.proto)?.type === DocumentType.RTF && s.color);
- const sets = setDocs.map(sd => Cast(sd.text, RichTextField)?.Text as string);
- if (sets.length && sets[0]) {
- this._wordPalette.clear();
- const colors = setDocs.map(sd => FieldValue(sd.color) as string);
- sets.forEach((st: string, i: number) => st.split(',').forEach(word => this._wordPalette.set(word, colors[i])));
- }
- const inks = this.getActiveDocuments().filter(doc => {
- if (doc.type === 'ink') {
- const l = NumCast(doc.x);
- const r = l + doc[Width]();
- const t = NumCast(doc.y);
- const b = t + doc[Height]();
- const pass = !(this._inkToTextStartX! > r || end[0] < l || this._inkToTextStartY! > b || end[1] < t);
- return pass;
- }
- return false;
- });
- // const inkFields = inks.map(i => Cast(i.data, InkField));
- const strokes: InkData[] = [];
- inks.forEach(i => {
- const d = Cast(i.data, InkField);
- const x = NumCast(i.x);
- const y = NumCast(i.y);
- const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
- const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
- if (d) {
- strokes.push(d.inkData.map(pd => ({ X: pd.X + x - left, Y: pd.Y + y - top })));
- }
+ const strokes = this.getActiveDocuments()
+ .filter(doc => doc.type === DocumentType.INK)
+ .map(i => {
+ const d = Cast(i.stroke, InkField);
+ const x = NumCast(i.x) - Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+ const y = NumCast(i.y) - Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
+ return !d ? [] : d.inkData.map(pd => ({ X: x + pd.X, Y: y + pd.Y }));
});
- CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {
- const wordResults = results.filter((r: any) => r.category === 'inkWord');
- for (const word of wordResults) {
- const indices: number[] = word.strokeIds;
- indices.forEach(i => {
- const otherInks: Doc[] = [];
- indices.forEach(i2 => i2 !== i && otherInks.push(inks[i2]));
- inks[i].relatedInks = new List<Doc>(otherInks);
- const uniqueColors: string[] = [];
- Array.from(this._wordPalette.values()).forEach(c => uniqueColors.indexOf(c) === -1 && uniqueColors.push(c));
- inks[i].alternativeColors = new List<string>(uniqueColors);
- if (this._wordPalette.has(word.recognizedText.toLowerCase())) {
- inks[i].color = this._wordPalette.get(word.recognizedText.toLowerCase());
- } else if (word.alternates) {
- for (const alt of word.alternates) {
- if (this._wordPalette.has(alt.recognizedString.toLowerCase())) {
- inks[i].color = this._wordPalette.get(alt.recognizedString.toLowerCase());
- break;
- }
- }
- }
- });
- }
- });
- this._inkToTextStartX = end[0];
- }
+ CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {});
break;
case GestureUtils.Gestures.Text:
if (ge.text) {
- const B = this.getTransform().transformPoint(ge.points[0].X, ge.points[0].Y);
+ const B = this.screenToLocalXf.transformPoint(ge.points[0].X, ge.points[0].Y);
this.addDocument(Docs.Create.TextDocument(ge.text, { title: ge.text, x: B[0], y: B[1] }));
e.stopPropagation();
}
@@ -810,7 +712,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.preventDefault();
} else if (this.isContentActive() && e.shiftKey) {
// reset zoom of freeform view to 1-to-1 on a shift + double click
- this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 1);
+ this.zoomSmoothlyAboutPt(this.screenToLocalXf.transformPoint(e.clientX, e.clientY), 1);
e.stopPropagation();
e.preventDefault();
}
@@ -824,11 +726,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
+ pan = (e: PointerEvent): void => {
+ const ctrlKey = e.ctrlKey && !e.shiftKey;
+ const shiftKey = e.shiftKey && !e.ctrlKey;
PresBox.Instance?.pauseAutoPres();
this.props.DocumentView?.().clearViewTransition();
- const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
- this.setPan(NumCast(this.Document[this.panXFieldKey]) - dx, NumCast(this.Document[this.panYFieldKey]) - dy, 0, true);
+ const [dxi, dyi] = this.screenToLocalXf.transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
+ const { x: dx, y: dy } = Utils.rotPt(dxi, dyi, this.props.ScreenToLocalTransform().Rotate);
+ this.setPan(NumCast(this.Document[this.panXFieldKey]) - (ctrlKey ? 0 : dx), NumCast(this.Document[this.panYFieldKey]) - (shiftKey ? 0 : dy), 0, true);
this._lastX = e.clientX;
this._lastY = e.clientY;
};
@@ -874,20 +779,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- onPointerMove = (e: PointerEvent): boolean => {
- if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false;
- if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
- Doc.ActiveTool = InkTool.None;
- } else {
- if (this.tryDragCluster(e, this._hitCluster)) {
- e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over
- return true;
- }
- // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
- if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.getLocalTransform().inverse().Scale) {
- this.pan(e);
- e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
- }
+ onPointerMove = (e: PointerEvent) => {
+ if (this.tryDragCluster(e, this._hitCluster)) {
+ e.stopPropagation(); // we're moving a cluster, so stop propagation and return true to end panning and let the document drag take over
+ return true;
+ }
+ // pan the view if this is a regular collection, or it's an overlay and the overlay is zoomed (otherwise, there's nothing to pan)
+ if (!this.props.isAnnotationOverlay || 1 - NumCast(this.rootDoc._freeform_scale_min, 1) / this.zoomScaling()) {
+ this.pan(e);
+ e.stopPropagation(); // if we are actually panning, stop propagation -- this will preven things like the overlayView from dragging the document while we're panning
}
return false;
};
@@ -1025,18 +925,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
- cleanUpInteractions = () => {
- this.removeMoveListeners();
- this.removeEndListeners();
- };
-
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
- const [x, y] = this.getTransform().transformPoint(pointX, pointY);
- const invTransform = this.getLocalTransform().inverse();
+ const [x, y] = this.screenToLocalXf.transformPoint(pointX, pointY);
+ const invTransform = this.panZoomXf.inverse();
if (deltaScale * invTransform.Scale > 20) {
deltaScale = 20 / invTransform.Scale;
}
@@ -1067,11 +962,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
e.stopPropagation();
const docHeight = NumCast(this.rootDoc[Doc.LayoutFieldKey(this.rootDoc) + '_nativeHeight'], this.nativeHeight);
const scrollable = NumCast(this.layoutDoc[this.scaleFieldKey], 1) === 1 && docHeight > this.props.PanelHeight() / this.nativeDimScaling + 1e-4;
- switch (!e.ctrlKey ? Doc.UserDoc().freeformScrollMode : freeformScrollMode.Pan) {
+ switch (
+ !e.ctrlKey && !e.shiftKey && !e.metaKey && !e.altKey ?//
+ Doc.UserDoc().freeformScrollMode : // no modifiers, do assigned mode
+ e.ctrlKey && !CtrlKey? // otherwise, if ctrl key (pinch gesture) try to zoom else pan
+ freeformScrollMode.Zoom : freeformScrollMode.Pan // prettier-ignore
+ ) {
case freeformScrollMode.Pan:
- // if ctrl is selected then zoom
- if (!e.ctrlKey && this.props.isContentActive(true)) {
- this.scrollPan({ deltaX: -e.deltaX * this.getTransform().Scale, deltaY: e.shiftKey ? 0 : -e.deltaY * this.getTransform().Scale });
+ if (((!e.metaKey && !e.altKey) || Doc.UserDoc().freeformScrollMode === freeformScrollMode.Zoom) && this.props.isContentActive(true)) {
+ const deltaX = e.shiftKey ? e.deltaX : e.ctrlKey ? 0 : e.deltaX;
+ const deltaY = e.shiftKey ? 0 : e.ctrlKey ? e.deltaY : e.deltaY;
+ this.scrollPan({ deltaX: -deltaX * this.screenToLocalXf.Scale, deltaY: e.shiftKey ? 0 : -deltaY * this.screenToLocalXf.Scale });
break;
}
default:
@@ -1093,7 +994,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
const measuredDocs = docs
- .map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ''), size: this.childSizeProviderUnmemoized(doc, '') }))
+ .map(doc => ({ pos: { x: NumCast(doc.x), y: NumCast(doc.y) }, size: { width: NumCast(doc.width), height: NumCast(doc.height) } }))
.filter(({ pos, size }) => pos && size)
.map(({ pos, size }) => ({ pos: pos!, size: size! }));
if (measuredDocs.length) {
@@ -1110,11 +1011,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
yrange: { min: this.props.originTopLeft ? 0 : Number.MAX_VALUE, max: -Number.MAX_VALUE },
}
);
-
- const panelWidMax = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelWidMin = (this.props.PanelWidth() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1);
- const panelHgtMax = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
- const panelHgtMin = (this.props.PanelHeight() / this.zoomScaling()) * (this.props.originTopLeft ? 0 : 1);
+ const scaling = this.zoomScaling() * (this.props.NativeDimScaling?.() || 1);
+ const panelWidMax = (this.props.PanelWidth() / scaling) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelWidMin = (this.props.PanelWidth() / scaling) * (this.props.originTopLeft ? 0 : 1);
+ const panelHgtMax = (this.props.PanelHeight() / scaling) * (this.props.originTopLeft ? 2 / this.nativeDimScaling : 1);
+ const panelHgtMin = (this.props.PanelHeight() / scaling) * (this.props.originTopLeft ? 0 : 1);
if (ranges.xrange.min >= panX + panelWidMax / 2) panX = ranges.xrange.max + (this.props.originTopLeft ? 0 : panelWidMax / 2);
else if (ranges.xrange.max <= panX - panelWidMin / 2) panX = ranges.xrange.min - (this.props.originTopLeft ? panelWidMax / 2 : panelWidMin / 2);
if (ranges.yrange.min >= panY + panelHgtMax / 2) panY = ranges.yrange.max + (this.props.originTopLeft ? 0 : panelHgtMax / 2);
@@ -1124,7 +1025,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc) {
this.setPanZoomTransition(panTime);
const minScale = NumCast(this.rootDoc._freeform_scale_min, 1);
- const scale = 1 - minScale / this.getLocalTransform().inverse().Scale;
+ const scale = 1 - minScale / this.zoomScaling();
const minPanX = NumCast(this.rootDoc._freeform_panX_min, 0);
const minPanY = NumCast(this.rootDoc._freeform_panY_min, 0);
const maxPanX = NumCast(this.rootDoc._freeform_panX_max, this.nativeWidth);
@@ -1205,11 +1106,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
if (this.Document._isGroup) return;
this.setPanZoomTransition(transitionTime);
- const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
+ const screenXY = this.screenToLocalXf.inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
- const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
+ const newScreenXY = this.screenToLocalXf.inverse().transformPoint(docpt[0], docpt[1]);
const scrDelta = { x: screenXY[0] - newScreenXY[0], y: screenXY[1] - newScreenXY[1] };
- const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y);
+ const newpan = this.screenToLocalXf.transformDirection(scrDelta.x, scrDelta.y);
this.layoutDoc[this.panXFieldKey] = NumCast(this.layoutDoc[this.panXFieldKey]) - newpan[0];
this.layoutDoc[this.panYFieldKey] = NumCast(this.layoutDoc[this.panYFieldKey]) - newpan[1];
}
@@ -1217,7 +1118,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
calculatePanIntoView = (doc: Doc, xf: Transform, scale?: number) => {
const layoutdoc = Doc.Layout(doc);
const pt = xf.transformPoint(NumCast(doc.x), NumCast(doc.y));
- const pt2 = xf.transformPoint(NumCast(doc.x) + layoutdoc[Width](), NumCast(doc.y) + layoutdoc[Height]());
+ const pt2 = xf.transformPoint(NumCast(doc.x) + NumCast(layoutdoc._width), NumCast(doc.y) + NumCast(layoutdoc._height));
const bounds = { left: pt[0], right: pt2[0], top: pt[1], bot: pt2[1], width: pt2[0] - pt[0], height: pt2[1] - pt[1] };
if (scale !== undefined) {
@@ -1284,11 +1185,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
? 'none'
: this.props.childPointerEvents?.() ??
(this.props.viewDefDivClick || //
- (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+ (engine === computePassLayout.name && !this.props.isSelected()) ||
this.isContentActive() === false
? 'none'
: this.props.pointerEvents?.());
- console.log(`${this.rootDoc.title} pe = ` + pointerevents);
return pointerevents;
}
@@ -1299,16 +1199,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const childLayout = entry.pair.layout;
const childData = entry.pair.data;
return (
- <CollectionFreeFormDocumentView
+ <CollectionFreeFormDocumentViewWrapper
+ {...OmitKeys(entry, ['replica', 'pair']).omit}
key={childLayout[Id] + (entry.replica || '')}
DataDoc={childData}
Document={childLayout}
+ dragStarting={this.dragStarting}
+ dragEnding={this.dragEnding}
isGroupActive={this.props.isGroupActive}
renderDepth={this.props.renderDepth + 1}
- replica={entry.replica}
hideDecorations={BoolCast(childLayout._layout_isSvg && childLayout.type === DocumentType.LINK)}
suppressSetHeight={this.layoutEngine ? true : false}
- renderCutoffProvider={this.renderCutoffProvider}
+ RenderCutoffProvider={this.renderCutoffProvider}
CollectionFreeFormView={this}
LayoutTemplate={childLayout.z ? undefined : this.props.childLayoutTemplate}
LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
@@ -1318,7 +1220,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onKey={this.onKeyDown}
onDoubleClick={this.onChildDoubleClickHandler}
onBrowseClick={this.onBrowseClickHandler}
- ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
+ ScreenToLocalTransform={childLayout.z ? this.props.ScreenToLocalTransform : this.ScreenToLocalXf}
PanelWidth={childLayout[Width]}
PanelHeight={childLayout[Height]}
childFilters={this.childDocFilters}
@@ -1334,10 +1236,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
pinToPres={this.props.pinToPres}
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
docViewPath={this.props.docViewPath}
- styleProvider={this.getClusterColor}
+ styleProvider={this.clusterStyleProvider}
dragAction={(this.rootDoc.childDragAction ?? this.props.childDragAction) as dropActionType}
- dataProvider={this.childDataProvider}
- sizeProvider={this.childSizeProvider}
bringToFront={this.bringToFront}
layout_showTitle={this.props.childlayout_showTitle}
dontRegisterView={this.props.dontRegisterView}
@@ -1356,7 +1256,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return (
(this.addDocument?.(
(doc instanceof Doc ? [doc] : doc).map(doc => {
- const pt = this.getTransform().transformPoint(NumCast(doc.x), NumCast(doc.y));
+ const pt = this.screenToLocalXf.transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = pt[0];
doc.y = pt[1];
return doc;
@@ -1400,9 +1300,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
- rotation: rotation,
+ rotation,
color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
- backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.getClusterColor(childDoc, this.props, StyleProp.BackgroundColor),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.clusterStyleProvider(childDoc, this.props, StyleProp.BackgroundColor),
opacity: !_width ? 0 : this._keyframeEditing ? 1 : Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
width: _width,
@@ -1466,20 +1366,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}.bind(this)
);
- childPositionProviderUnmemoized = (doc: Doc, replica: string) => this._layoutPoolData.get(doc[Id] + (replica || ''));
- childDataProvider = computedFn(
- function childDataProvider(this: any, doc: Doc, replica: string) {
- return this.childPositionProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
- childSizeProviderUnmemoized = (doc: Doc, replica: string) => this._layoutSizeData.get(doc[Id] + (replica || ''));
- childSizeProvider = computedFn(
- function childSizeProvider(this: any, doc: Doc, replica: string) {
- return this.childSizeProviderUnmemoized(doc, replica);
- }.bind(this)
- );
-
doEngineLayout(
poolData: Map<string, PoolData>,
engine: (poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) => ViewDefResult[]
@@ -1508,55 +1394,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
- @observable _numLoaded = 1;
- _lastPoolSize = 0;
@action
doLayoutComputation = (newPool: Map<string, PoolData>, computedElementData: ViewDefResult[]) => {
- const array = Array.from(newPool.entries());
- this._lastPoolSize = array.length;
- for (const entry of array) {
- const lastPos = this._cachedPool.get(entry[0]); // last computed pos
- const newPos = entry[1];
- if (
- !lastPos ||
- newPos.color !== lastPos.color ||
- newPos.backgroundColor !== lastPos.backgroundColor ||
- newPos.opacity !== lastPos.opacity ||
- newPos.x !== lastPos.x ||
- newPos.y !== lastPos.y ||
- newPos.z !== lastPos.z ||
- newPos.rotation !== lastPos.rotation ||
- newPos.zIndex !== lastPos.zIndex ||
- newPos.transition !== lastPos.transition ||
- newPos.pointerEvents !== lastPos.pointerEvents
- ) {
- this._layoutPoolData.set(entry[0], newPos);
- }
- if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
- this._layoutSizeData.set(entry[0], { width: newPos.width, height: newPos.height });
- }
- }
- // by returning undefined, we prevent an edit being made to layoutElements when nothing has happened
- // this short circuit, prevents lots of downstream mobx invalidations which would have no effect but cause
- // a distinct lag at the start of dragging.
- // The reason we're here in the first place without a change is that when dragging a document,
- // filters are changed on the annotation layers (eg. WebBox) which invalidate the childDoc list
- // for the overlay views -- however, in many cases, this filter change doesn't actually affect anything
- // (e.g, no annotations, or only opaque annotations).
- 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) => {
- const childData: ViewDefBounds = this.childDataProvider(entry[1].pair.layout, entry[1].replica);
- const childSize = this.childSizeProvider(entry[1].pair.layout, entry[1].replica);
+ .forEach(entry =>
elements.push({
ele: this.getChildDocView(entry[1]),
- bounds: childData.opacity === 0 ? { ...childData, width: 0, height: 0 } : { ...childData, width: childSize.width, height: childSize.height },
+ bounds: (entry[1].opacity === 0 ? { ...entry[1], width: 0, height: 0 } : { ...entry[1] }) as any,
inkMask: BoolCast(entry[1].pair.layout.stroke_isInkMask) ? NumCast(entry[1].pair.layout.opacity, 1) : -1,
- });
- });
+ })
+ );
this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
return elements;
@@ -1586,14 +1435,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._disposers.groupBounds = reaction(
() => {
if (this.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[Width](), height: cd[Height]() }));
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
}
return undefined;
},
cbounds => {
if (cbounds) {
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[Width]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[Height]() / 2];
+ const c = [NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) / 2, NumCast(this.layoutDoc.y) + NumCast(this.layoutDoc._height) / 2];
const p = [NumCast(this.layoutDoc[this.panXFieldKey]), NumCast(this.layoutDoc[this.panYFieldKey])];
const pbounds = {
x: cbounds.x - p[0] + c[0],
@@ -1621,7 +1470,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
? 'none'
: this.props.childPointerEvents?.() ??
(this.props.viewDefDivClick || //
- (engine === computePassLayout.name && !this.props.isSelected(true)) ||
+ (engine === computePassLayout.name && !this.props.isSelected()) ||
this.isContentActive() === false
? 'none'
: this.props.pointerEvents?.());
@@ -1630,18 +1479,18 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{ fireImmediately: true }
);
- this._disposers.layoutComputation = reaction(
- () => this.doInternalLayoutComputation,
- ({ newPool, computedElementData }) => (this._layoutElements = this.doLayoutComputation(newPool, computedElementData)),
- { fireImmediately: true, name: 'layoutComputationReaction' }
- );
-
this._disposers.active = reaction(
() => this.isContentActive(), // if autoreset is on, then whenever the view is selected, it will be restored to it default pan/zoom positions
active => !SnappingManager.GetIsDragging() && this.rootDoc[this.autoResetFieldKey] && active && this.resetView()
);
})
);
+ this._disposers.layoutElements = reaction(
+ // layoutElements can't be a computed value because doLayoutComputation() is an action that has side effect of updating clusters
+ () => this.doInternalLayoutComputation,
+ computation => (this._layoutElements = this.doLayoutComputation(computation.newPool, computation.computedElementData)),
+ { fireImmediately: true }
+ );
}
static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
@@ -1680,8 +1529,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
CollectionFreeFormView.UpdateIcon(
this.layoutDoc[Id] + '-icon' + new Date().getTime(),
this.props.docViewPath().lastElement().ContentDiv!,
- this.layoutDoc[Width](),
- this.layoutDoc[Height](),
+ NumCast(this.layoutDoc._width),
+ NumCast(this.layoutDoc._height),
this.props.PanelWidth(),
this.props.PanelHeight(),
0,
@@ -1690,8 +1539,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
'',
(iconFile, nativeWidth, nativeHeight) => {
this.dataDoc.icon = new ImageField(iconFile);
- this.dataDoc['icon_nativeWidth'] = nativeWidth;
- this.dataDoc['icon_nativeHeight'] = nativeHeight;
+ this.dataDoc.icon_nativeWidth = nativeWidth;
+ this.dataDoc.icon_nativeHeight = nativeHeight;
}
);
@@ -1726,6 +1575,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
componentWillUnmount() {
+ this.rootDoc[this.autoResetFieldKey] && this.resetView();
Object.values(this._disposers).forEach(disposer => disposer?.());
}
@@ -1738,7 +1588,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
promoteCollection = () => {
const childDocs = this.childDocs.slice();
childDocs.forEach(doc => {
- const scr = this.getTransform().inverse().transformPoint(NumCast(doc.x), NumCast(doc.y));
+ const scr = this.screenToLocalXf.inverse().transformPoint(NumCast(doc.x), NumCast(doc.y));
doc.x = scr?.[0];
doc.y = scr?.[1];
});
@@ -1802,20 +1652,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return;
}
!Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: 'Reset default note style', event: () => (Doc.UserDoc().defaultTextLayout = undefined), icon: 'eye' });
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
+ appearanceItems.push({ description: `Pin View`, event: () => this.props.pinToPres(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
!Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
this.props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
- this.props.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, icon: 'font' });
!Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
+ !Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
- const viewctrls = ContextMenu.Instance.findByDescription('UI Controls...');
- const viewCtrlItems = viewctrls && 'subitems' in viewctrls ? viewctrls.subitems : [];
- !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
- !viewctrls && ContextMenu.Instance.addItem({ description: 'UI Controls...', subitems: viewCtrlItems, icon: 'eye' });
-
const options = ContextMenu.Instance.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
!this.props.isAnnotationOverlay &&
@@ -1834,16 +1680,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@undoBatch
@action
- transcribeStrokes = (math: boolean) => {
+ transcribeStrokes = () => {
if (this.props.Document._isGroup && this.props.Document.transcription) {
- if (!math) {
- const text = StrCast(this.props.Document.transcription);
-
- const lines = text.split('\n');
- const height = 30 + 15 * lines.length;
+ const text = StrCast(this.props.Document.transcription);
+ const lines = text.split('\n');
+ const height = 30 + 15 * lines.length;
- this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
- }
+ this.addDocument(Docs.Create.TextDocument(text, { title: lines[0], x: NumCast(this.layoutDoc.x) + NumCast(this.layoutDoc._width) + 20, y: NumCast(this.layoutDoc.y), _width: 200, _height: height }));
}
};
@@ -1853,31 +1696,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
SnappingManager.clearSnapLines();
};
@action
- dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean, visited = new Set<Doc>()) => {
+ dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
if (visited.has(this.rootDoc)) return;
visited.add(this.rootDoc);
showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document._isGroup));
- if (this.rootDoc._isGroup && this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView) {
- this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.dragStarting(snapToDraggedDoc, false, visited);
- }
const activeDocs = this.getActiveDocuments();
- const size = this.getTransform().transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
+ const size = this.screenToLocalXf.transformDirection(this.props.PanelWidth(), this.props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
const docDims = (doc: Doc) => ({ left: NumCast(doc.x), top: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
- activeDocs.forEach(
- doc =>
- doc._isGroup &&
- SnappingManager.GetIsResizing() !== doc &&
- !DragManager.docsBeingDragged.includes(doc) &&
- (DocumentManager.Instance.getDocumentView(doc)?.ComponentView as CollectionFreeFormView)?.dragStarting(snapToDraggedDoc, false, visited)
- );
+ activeDocs
+ .filter(doc => doc._isGroup && SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))
+ .forEach(doc => DocumentManager.Instance.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
- const invXf = this.getTransform().inverse();
+ const invXf = this.screenToLocalXf.inverse();
snappableDocs
.filter(doc => !doc._isGroup && (snapToDraggedDoc || (SnappingManager.GetIsResizing() !== doc && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
@@ -1904,15 +1740,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
- get children() {
- this.incrementalRender();
- const children = typeof this.props.children === 'function' ? ((this.props.children as any)() as JSX.Element[]) : this.props.children ? [this.props.children] : [];
- return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
+ // if a freeform view has any children, then the children will likely consist of a single child
+ // which will be a DocumentView. In this sitation, this freeform views acts as an annotation overlay for
+ // the underlying DocumentView and will pan and scoll with the underlying Documen tView.
+ @computed get underlayViews() {
+ return this.props.children ? [this.props.children] : [];
}
@computed get placeholder() {
return (
- <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
+ <div className="collectionfreeformview-placeholder" style={{ background: this.props.styleProvider?.(this.Document, this.props, StyleProp.BackgroundColor) }}>
<span className="collectionfreeformview-placeholderSpan">{this.props.Document.annotationOn ? '' : this.props.Document.title?.toString()}</span>
</div>
);
@@ -1921,7 +1758,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
brushedView = () => this._brushedView;
gridColor = () =>
DashColor(lightOrDark(this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor)))
- .fade(0.6)
+ .fade(0.5)
.toString();
@computed get backgroundGrid() {
return (
@@ -1943,16 +1780,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
);
}
@computed get pannableContents() {
+ this.incrementalRender();
return (
- <CollectionFreeFormViewPannableContents
+ <CollectionFreeFormPannableContents
rootDoc={this.rootDoc}
brushedView={this.brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
- transform={this.contentTransform}
+ transform={this.PanZoomCenterXf}
transition={this._panZoomTransition ? `transform ${this._panZoomTransition}ms` : Cast(this.layoutDoc._viewTransition, 'string', Cast(this.props.DocumentView?.()?.rootDoc._viewTransition, 'string', null))}
viewDefDivClick={this.props.viewDefDivClick}>
- {this.children}
- </CollectionFreeFormViewPannableContents>
+ {this.underlayViews}
+ {this.contentViews}
+ <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+ </CollectionFreeFormPannableContents>
);
}
@computed get marqueeView() {
@@ -1970,8 +1810,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
selectDocuments={this.selectDocuments}
addDocument={this.addDocument}
addLiveTextDocument={this.addLiveTextBox}
- getContainerTransform={this.getContainerTransform}
- getTransform={this.getTransform}
+ getContainerTransform={this.props.ScreenToLocalTransform}
+ getTransform={this.ScreenToLocalXf}
panXFieldKey={this.panXFieldKey}
panYFieldKey={this.panYFieldKey}
isAnnotationOverlay={this.isAnnotationOverlay}>
@@ -1993,17 +1833,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
nativeDim = () => this.nativeDimScaling;
@action
- brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
+ brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number, holdTime: number = 2500) => {
this._brushtimer1 && clearTimeout(this._brushtimer1);
this._brushtimer && clearTimeout(this._brushtimer);
this._brushedView = undefined;
this._brushtimer1 = setTimeout(
action(() => {
this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2 };
- this._brushtimer = setTimeout(
- action(() => (this._brushedView = undefined)),
- 2500
- );
+ this._brushtimer = setTimeout(action(() => (this._brushedView = undefined)), holdTime); // prettier-ignore
}),
transTime + 1
);
@@ -2080,139 +1917,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
}
-interface CollectionFreeFormOverlayViewProps {
- elements: () => ViewDefResult[];
-}
-
-@observer
-class CollectionFreeFormOverlayView extends React.Component<CollectionFreeFormOverlayViewProps> {
- render() {
- return this.props
- .elements()
- .filter(ele => ele.bounds?.z)
- .map(ele => ele.ele);
- }
-}
-
-interface CollectionFreeFormViewPannableContentsProps {
- rootDoc: Doc;
- viewDefDivClick?: ScriptField;
- children?: React.ReactNode | undefined;
- transition?: string;
- isAnnotationOverlay: boolean | undefined;
- transform: () => string;
- brushedView: () => { panX: number; panY: number; width: number; height: number } | undefined;
-}
-
-@observer
-class CollectionFreeFormViewPannableContents extends React.Component<CollectionFreeFormViewPannableContentsProps> {
- @computed get presPaths() {
- return CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.pathLines(this.props.rootDoc) : null;
- }
- // rectangle highlight used when following trail/link to a region of a collection that isn't a document
- showViewport = (viewport: { panX: number; panY: number; width: number; height: number } | undefined) =>
- !viewport ? null : (
- <div
- className="collectionFreeFormView-brushView"
- style={{
- transform: `translate(${viewport.panX}px, ${viewport.panY}px)`,
- width: viewport.width,
- height: viewport.height,
- border: `orange solid ${viewport.width * 0.005}px`,
- }}
- />
- );
-
- render() {
- return (
- <div
- className={'collectionfreeformview' + (this.props.viewDefDivClick ? '-viewDef' : '-none')}
- onScroll={e => {
- const target = e.target as any;
- if (getComputedStyle(target)?.overflow === 'visible') {
- target.scrollTop = target.scrollLeft = 0; // if collection is visible, scrolling messes things up since there are no scroll bars
- }
- }}
- style={{
- transform: this.props.transform(),
- transition: this.props.transition,
- width: this.props.isAnnotationOverlay ? undefined : 0, // if not an overlay, then this will be the size of the collection, but panning and zooming will move it outside the visible border of the collection and make it selectable. This problem shows up after zooming/panning on a background collection -- you can drag the collection by clicking on apparently empty space outside the collection
- }}>
- {this.props.children}
- {this.presPaths}
- {this.showViewport(this.props.brushedView())}
- </div>
- );
- }
-}
-
-interface CollectionFreeFormViewBackgroundGridProps {
- panX: () => number;
- panY: () => number;
- PanelWidth: () => number;
- PanelHeight: () => number;
- color: () => string;
- isAnnotationOverlay?: boolean;
- nativeDimScaling: () => number;
- zoomScaling: () => number;
- layoutDoc: Doc;
- cachedCenteringShiftX: number;
- cachedCenteringShiftY: number;
-}
@observer
-class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
- chooseGridSpace = (gridSpace: number): number => {
- if (!this.props.zoomScaling()) return gridSpace;
- const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace;
- return divisions < 90 ? gridSpace : this.chooseGridSpace(gridSpace * 2);
- };
+class CollectionFreeFormOverlayView extends React.Component<{ elements: () => ViewDefResult[] }> {
render() {
- const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc['_backgroundGrid-spacing'], 50));
- const shiftX = (this.props.isAnnotationOverlay ? 0 : (-this.props.panX() % gridSpace) - gridSpace) * this.props.zoomScaling();
- const shiftY = (this.props.isAnnotationOverlay ? 0 : (-this.props.panY() % gridSpace) - gridSpace) * this.props.zoomScaling();
- const renderGridSpace = gridSpace * this.props.zoomScaling();
- const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace;
- const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace;
- const strokeStyle = this.props.color();
- return !this.props.nativeDimScaling() ? null : (
- <canvas
- className="collectionFreeFormView-grid"
- width={w}
- height={h}
- style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
- ref={el => {
- const ctx = el?.getContext('2d');
- if (ctx) {
- const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
- const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
- ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
- ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
- ctx.clearRect(0, 0, w, h);
- if (ctx) {
- ctx.strokeStyle = strokeStyle;
- ctx.fillStyle = strokeStyle;
- ctx.beginPath();
- if (this.props.zoomScaling() > 1) {
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
- ctx.moveTo(x, Cy - h);
- ctx.lineTo(x, Cy + h);
- }
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.moveTo(Cx - w, y);
- ctx.lineTo(Cx + w, y);
- }
- } else {
- for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace)
- for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
- ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
- }
- }
- ctx.stroke();
- }
- }
- }}
- />
- );
+ return this.props.elements().filter(ele => ele.bounds?.z).map(ele => ele.ele); // prettier-ignore
}
}
@@ -2224,13 +1932,13 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
if (!focused) {
const selfFfview = !dv.rootDoc._isGroup && dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
let containers = dv.props.docViewPath();
- let parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ let parFfview = dv.CollectionFreeFormView;
for (var cont of containers) {
- parFfview = parFfview ?? cont.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ parFfview = parFfview ?? cont.CollectionFreeFormView;
}
- while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+ while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.DocumentView?.().CollectionFreeFormView;
const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
- ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
+ ffview?.zoomSmoothlyAboutPt(ffview.screenToLocalXf.transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
Doc.linkFollowHighlight(dv?.props.Document, false);
}
});
@@ -2260,8 +1968,8 @@ ScriptingGlobals.add(function pinWithView(pinContent: boolean) {
);
});
ScriptingGlobals.add(function bringToFront() {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc));
+ SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc));
});
ScriptingGlobals.add(function sendToBack(doc: Doc) {
- SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
+ SelectionManager.Views().forEach(view => view.CollectionFreeFormView?.bringToFront(view.rootDoc, true));
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index edcc17afd..2092ecb7a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -22,7 +22,6 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
import { SubCollectionViewProps } from '../CollectionSubView';
-import { TabDocView } from '../TabDocView';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
import React = require('react');
@@ -78,15 +77,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
const bounds: MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
return bounds;
}
- get inkDoc() {
- return this.props.Document;
- }
- get ink() {
- return Cast(this.props.Document.ink, InkField);
- }
- set ink(value: Opt<InkField>) {
- this.props.Document.ink = value;
- }
componentDidMount() {
this.props.setPreviewCursor?.(this.setPreviewCursor);
@@ -218,22 +208,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@action
onPointerDown = (e: React.PointerEvent): void => {
- // if (this.props.pointerEvents?.() === 'none') return;
this._downX = this._lastX = e.clientX;
this._downY = this._lastY = e.clientY;
- if (!(e.nativeEvent as any).marqueeHit) {
- (e.nativeEvent as any).marqueeHit = true;
- // allow marquee if right click OR alt+left click OR in adding presentation slide & left key drag mode
- if (e.button === 2 || (e.button === 0 && (e.altKey || (this.props.isContentActive?.(true) && Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan)))) {
- // if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
- this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
- // (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
- e.preventDefault();
- // }
- // bcz: do we need this? it kills the context menu on the main collection if !altKey
- // e.stopPropagation();
- } else PreviewCursor.Visible = false;
- }
+
+ const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
+ // allow marquee if right drag/meta drag, or pan mode
+ if (e.button === 2 || e.metaKey || scrollMode === freeformScrollMode.Pan) {
+ this.setPreviewCursor(e.clientX, e.clientY, true, false, this.props.Document);
+ e.preventDefault();
+ } else PreviewCursor.Visible = false;
};
@action
@@ -242,7 +225,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._lastY = e.pageY;
this._lassoPts.push([e.clientX, e.clientY]);
if (!e.cancelBubble) {
- if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
+ if (!Utils.isClick(this._lastX, this._lastY, this._downX, this._downY, Date.now())) {
if (!this._commandExecuted) {
this.showMarquee();
}
@@ -262,8 +245,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!e.shiftKey) {
SelectionManager.DeselectAll(mselect.length ? undefined : this.props.Document);
}
- // let inkselect = this.ink ? this.marqueeInkSelect(this.ink.inkData) : new Map();
- // let inks = inkselect.size ? [{ Document: this.inkDoc, Ink: inkselect }] : [];
const docs = mselect.length ? mselect : [this.props.Document];
this.props.selectDocuments(docs);
}
@@ -333,12 +314,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (this.props.pointerEvents?.() === 'none') return;
if (Utils.isClick(e.clientX, e.clientY, this._downX, this._downY, Date.now())) {
if (Doc.ActiveTool === InkTool.None) {
- if (!(e.nativeEvent as any).marqueeHit) {
- (e.nativeEvent as any).marqueeHit = true;
- if (!this.props.trySelectCluster(e.shiftKey)) {
- !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
- } else e.stopPropagation();
- }
+ if (!this.props.trySelectCluster(e.shiftKey)) {
+ !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
+ } else e.stopPropagation();
}
// let the DocumentView stopPropagation of this event when it selects this document
} else {
@@ -409,7 +387,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@undoBatch
@action
pinWithView = () => {
- TabDocView.PinDoc(this.props.Document, { pinViewport: this.Bounds });
+ this.props.pinToPres(this.props.Document, { pinViewport: this.Bounds });
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
};
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index cd8b7a0cc..274012000 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -359,7 +359,7 @@ export class CollectionGridView extends CollectionSubView() {
},
false
);
- if (this.props.isSelected(true)) e.stopPropagation();
+ if (this.props.isSelected()) e.stopPropagation();
}
};
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 9a2c79a22..4267a9059 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -48,7 +48,7 @@ export class CollectionLinearView extends CollectionSubView() {
componentDidMount() {
this._widthDisposer = reaction(
- () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (doc[Width]() || this.dimension()) + tot + 4, 0) : 0),
+ () => 5 + NumCast(this.rootDoc.linearBtnWidth, this.dimension()) + (this.layoutDoc.linearView_IsOpen ? this.childDocs.filter(doc => !doc.hidden).reduce((tot, doc) => (NumCast(doc._width) || this.dimension()) + tot + 4, 0) : 0),
width => this.childDocs.length && (this.layoutDoc._width = width),
{ fireImmediately: true }
);
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 81453c0b8..c2586fb4b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -295,7 +295,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
const aspect = Doc.NativeAspect(layout, undefined, true);
const width = () => this.lookupPixels(layout);
const height = () => PanelHeight() - 2 * NumCast(Document._yMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
- const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(height() * aspect, width()));
+ const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(height() * aspect, width()));
const docheight = () => Math.min(docwidth() / aspect, height());
const dxf = () =>
this.lookupIndividualTransform(layout)
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 04cfc5456..249c3551b 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -292,7 +292,7 @@ export class CollectionMultirowView extends CollectionSubView() {
const height = () => this.lookupPixels(layout);
const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0);
const docheight = () => Math.min(width() / aspect, height());
- const docwidth = () => (layout._layout_forceReflow ? width() : Math.min(width(), docheight() * aspect));
+ const docwidth = () => (layout._layout_reflowHorizontal ? width() : Math.min(width(), docheight() * aspect));
const dxf = () =>
this.lookupIndividualTransform(layout)
.translate(-NumCast(Document._xMargin) - (width() - docwidth()) / 2, -NumCast(Document._yMargin) - (height() - docheight()) / 2)
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f73c037f4..190e4ff2a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,9 +1,8 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, ObservableMap, observe, trace } from 'mobx';
+import { action, computed, observable, ObservableMap, observe } from 'mobx';
import { observer } from 'mobx-react';
-import { computedFn } from 'mobx-utils';
-import { Doc, DocListCast, Field, NumListCast, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
@@ -17,9 +16,9 @@ import { undoable, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { EditableView } from '../../EditableView';
import { Colors } from '../../global/globalEnums';
-import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps } from '../../nodes/DocumentView';
import { KeyValueBox } from '../../nodes/KeyValueBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
@@ -86,7 +85,17 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get _selectedDocs() {
- return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+ const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+ if (!selected.length) {
+ for (const sel of SelectionManager.Docs()) {
+ const contextPath = DocumentManager.GetContextPath(sel, true);
+ if (contextPath.includes(this.rootDoc)) {
+ const parentInd = contextPath.indexOf(this.rootDoc);
+ return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
+ }
+ }
+ }
+ return selected;
}
@computed get documentKeys() {
@@ -236,10 +245,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.layoutDoc.sortDesc = desc;
};
- addRow = (doc: Doc | Doc[]) => {
- const result: boolean = this.addDocument(doc);
- return result;
- };
+ addRow = (doc: Doc | Doc[]) => this.addDocument(doc);
@undoBatch
@action
@@ -550,9 +556,8 @@ export class CollectionSchemaView extends CollectionSubView() {
};
setColumnValues = (key: string, value: string) => {
- let success: boolean = true;
- this.childDocs.forEach(doc => success && KeyValueBox.SetField(doc, key, value));
- return success;
+ this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value));
+ return true;
};
@action
@@ -577,9 +582,7 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeFilterMenu = () => {
- this._filterColumnIndex = undefined;
- };
+ closeFilterMenu = () => (this._filterColumnIndex = undefined);
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
@@ -836,7 +839,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
<div
className="schema-table"
- style={{ width: `calc(100% - ${this.previewWidth + 4}px)` }}
+ style={{ width: `calc(100% - ${this.previewWidth}px)` }}
onWheel={e => this.props.isContentActive() && e.stopPropagation()}
ref={r => {
// prevent wheel events from passively propagating up through containers
@@ -932,51 +935,68 @@ interface CollectionSchemaViewDocsProps {
@observer
class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
- tableWidthFunc = () => this.props.schema.tableWidth;
- childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - index * this.props.rowHeight()));
render() {
return (
<div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
- {this.props.childDocs().docs.map((doc: Doc, index: number) => {
- const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
- return (
- <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
- <DocumentView
- key={doc[Id]}
- {...this.props.schema.props}
- LayoutTemplate={this.props.schema.props.childLayoutTemplate}
- LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
- Document={doc}
- DataDoc={dataDoc}
- yPadding={index}
- renderDepth={this.props.schema.props.renderDepth + 1}
- PanelWidth={this.tableWidthFunc}
- PanelHeight={this.props.rowHeight}
- styleProvider={DefaultStyleProvider}
- waitForDoubleClickToClick={returnNever}
- defaultDoubleClick={returnIgnore}
- dragAction="move"
- onClickScriptDisable="always"
- focus={this.props.schema.focusDocument}
- childFilters={this.props.schema.childDocFilters}
- childFiltersByRanges={this.props.schema.childDocRangeFilters}
- searchFilterDocs={this.props.schema.searchFilterDocs}
- rootSelected={this.props.schema.rootSelected}
- ScreenToLocalTransform={this.childScreenToLocal(index)}
- bringToFront={emptyFunction}
- isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
- isContentActive={emptyFunction}
- whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
- layout_fitWidth={returnTrue}
- />
- </div>
- );
- })}
+ {this.props.childDocs().docs.map((doc: Doc, index: number) => (
+ <div className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
+ <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ </div>
+ ))}
</div>
);
}
}
+
+interface CollectionSchemaViewDocProps {
+ schema: CollectionSchemaView;
+ index: number;
+ doc: Doc;
+ rowHeight: () => number;
+}
+
+@observer
+class CollectionSchemaViewDoc extends React.Component<CollectionSchemaViewDocProps> {
+ tableWidthFunc = () => this.props.schema.tableWidth;
+ screenToLocalXf = () => this.props.schema.props.ScreenToLocalTransform().translate(0, -this.props.rowHeight() - this.props.index * this.props.rowHeight());
+ noOpacityStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => {
+ if (property === StyleProp.Opacity) return 1;
+ return DefaultStyleProvider(doc, props, property);
+ };
+ render() {
+ return (
+ <DocumentView
+ key={this.props.doc[Id]}
+ {...this.props.schema.props}
+ LayoutTemplate={this.props.schema.props.childLayoutTemplate}
+ LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey, this.props.index)}
+ Document={this.props.doc}
+ DataDoc={undefined}
+ renderDepth={this.props.schema.props.renderDepth + 1}
+ PanelWidth={this.tableWidthFunc}
+ PanelHeight={this.props.rowHeight}
+ styleProvider={this.noOpacityStyleProvider}
+ waitForDoubleClickToClick={returnNever}
+ defaultDoubleClick={returnIgnore}
+ dragAction="move"
+ onClickScriptDisable="always"
+ focus={this.props.schema.focusDocument}
+ childFilters={this.props.schema.childDocFilters}
+ childFiltersByRanges={this.props.schema.childDocRangeFilters}
+ searchFilterDocs={this.props.schema.searchFilterDocs}
+ rootSelected={this.props.schema.rootSelected}
+ ScreenToLocalTransform={this.screenToLocalXf}
+ bringToFront={emptyFunction}
+ dragWhenActive={true}
+ isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
+ isContentActive={emptyFunction}
+ whenChildContentsActiveChanged={this.props.schema.props.whenChildContentsActiveChanged}
+ hideDecorations={true}
+ hideTitle={true}
+ hideDocumentButtonBar={true}
+ hideLinkAnchors={true}
+ layout_fitWidth={returnTrue}
+ />
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 4e418728f..7346c4f12 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -1,12 +1,16 @@
import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { IconButton, Size } from 'browndash-components';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
+import { CgClose } from 'react-icons/cg';
+import { FaExternalLinkAlt } from 'react-icons/fa';
import { Doc } from '../../../../fields/Doc';
import { BoolCast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
import { undoable } from '../../../util/UndoManager';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
@@ -15,16 +19,14 @@ import { FieldView, FieldViewProps } from '../../nodes/FieldView';
import { CollectionSchemaView } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaTableCell } from './SchemaTableCell';
-import { Transform } from '../../../util/Transform';
-import { IconButton, Size } from 'browndash-components';
-import { CgClose } from 'react-icons/cg';
-import { FaExternalLinkAlt } from 'react-icons/fa';
-import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
+interface SchemaRowBoxProps {
+ rowIndex: number;
+}
@observer
-export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(SchemaRowBox, fieldKey);
+export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps & SchemaRowBoxProps>() {
+ public static LayoutString(fieldKey: string, rowIndex: number) {
+ return FieldView.LayoutString(SchemaRowBox, fieldKey).replace('fieldKey', `rowIndex={${rowIndex}} fieldKey`);
}
private _ref: HTMLDivElement | null = null;
@@ -170,7 +172,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
transform={() => {
const ind = index === this.schemaView.columnKeys.length - 1 ? this.schemaView.columnKeys.length - 3 : index;
const x = this.schemaView?.displayColumnWidths.reduce((p, c, i) => (i <= ind ? p + c : p), 0);
- const y = (this.props.yPadding ?? 0) * this.props.PanelHeight();
+ const y = (this.props.rowIndex ?? 0) * this.props.PanelHeight();
return new Transform(x + CollectionSchemaView._rowMenuWidth, y, 1);
}}
/>