aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2022-10-28 10:10:04 -0400
committerbrynnchernosky <56202540+brynnchernosky@users.noreply.github.com>2022-10-28 10:10:04 -0400
commitceb338752aacc383c97a0e3a9b608365a1cf39b6 (patch)
treed2f355b726a9b21950f332c0f65931d7d6eef515 /src/client/views/collections
parent5d6a0458b9d4f35e0c568a4d76d4fcab4e22f698 (diff)
parent2fc88a931cb2fc3408297b000208990633445585 (diff)
merge
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.scss8
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx46
-rw-r--r--src/client/views/collections/CollectionMenu.tsx8
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.scss3
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx2
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx31
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx4
-rw-r--r--src/client/views/collections/CollectionSubView.tsx13
-rw-r--r--src/client/views/collections/CollectionTreeView.scss7
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx130
-rw-r--r--src/client/views/collections/CollectionView.tsx12
-rw-r--r--src/client/views/collections/TabDocView.tsx169
-rw-r--r--src/client/views/collections/TreeView.scss20
-rw-r--r--src/client/views/collections/TreeView.tsx209
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx225
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx42
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx14
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx20
18 files changed, 542 insertions, 421 deletions
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 091ba8e74..ac3541f2c 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -154,7 +154,15 @@
background: $white;
}
+ .lm_controls {
+ height: 27px;
+ display: flex;
+ align-content: center;
+ justify-content: center;
+ }
+
.lm_controls > li {
+ height: 27px !important;
opacity: 1;
transform: scale(1);
}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 6d70cc0d2..e9b41de25 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { inheritParentAcls } from '../../../fields/util';
import { emptyFunction, incrementTitleCopy } from '../../../Utils';
@@ -56,11 +56,12 @@ export class CollectionDockingView extends CollectionSubView() {
constructor(props: SubCollectionViewProps) {
super(props);
- runInAction(() => (CollectionDockingView.Instance = this));
+ if (this.props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
//Why is this here?
(window as any).React = React;
(window as any).ReactDOM = ReactDOM;
DragManager.StartWindowDrag = this.StartOtherDrag;
+ this.rootDoc.myTrails; // this is equivalent to having a prefetchProxy for myTrails which is needed for the My Trails button in the UI which assumes that Doc.ActiveDashboard.myTrails is legit...
}
/**
@@ -73,7 +74,7 @@ export class CollectionDockingView extends CollectionSubView() {
public StartOtherDrag = (e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => {
this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag');
const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) };
- const dragSource = this._goldenLayout.createDragSource(document.createElement('div'), config);
+ 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 });
};
@@ -280,12 +281,13 @@ export class CollectionDockingView extends CollectionSubView() {
this.stateChanged();
return true;
}
-
setupGoldenLayout = async () => {
+ //const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document)));
const config = StrCast(this.props.Document.dockingConfig);
if (config) {
const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? [];
+
await Promise.all(docids.map(id => DocServer.GetRefField(id)));
if (this._goldenLayout) {
@@ -311,6 +313,8 @@ export class CollectionDockingView extends CollectionSubView() {
glay.root.layoutManager.on('itemDropped', this.tabItemDropped);
glay.root.layoutManager.on('dragStart', this.tabDragStart);
glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
+ } else {
+ console.log('ERROR: no config for dashboard!!');
}
};
@@ -395,10 +399,10 @@ export class CollectionDockingView extends CollectionSubView() {
const _height = Number(getComputedStyle(content).height.replace('px', ''));
return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => {
const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title + '-icon', _width, _height, _nativeWidth, _nativeHeight });
- const proto = Cast(img.proto, Doc, null)!;
- proto['data-nativeWidth'] = _width;
- proto['data-nativeHeight'] = _height;
- this.dataDoc.thumb = img;
+ const proto = this.dataDoc; // Cast(img.proto, Doc, null)!;
+ proto['thumb-nativeWidth'] = _width;
+ proto['thumb-nativeHeight'] = _height;
+ this.dataDoc.thumb = new ImageField(iconFile);
});
}
}
@@ -429,7 +433,8 @@ export class CollectionDockingView extends CollectionSubView() {
return newtab;
});
const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
- return DashboardView.openDashboard(await copy);
+ DashboardView.SetupDashboardTrails(copy);
+ return DashboardView.openDashboard(copy);
}
@action
@@ -455,7 +460,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
tabDestroyed = (tab: any) => {
- if (tab.DashDoc?.type !== DocumentType.KVP) {
+ if (![DocumentType.KVP, DocumentType.PRES].includes(tab.DashDoc?.type)) {
Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
}
@@ -475,6 +480,7 @@ export class CollectionDockingView extends CollectionSubView() {
};
stackCreated = (stack: any) => {
+ stack = stack.header ? stack : stack.origin;
stack.header?.element.on('mousedown', (e: any) => {
const dashboard = Doc.ActiveDashboard;
if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
@@ -497,7 +503,7 @@ export class CollectionDockingView extends CollectionSubView() {
.click(
action(() => {
//if (confirm('really close this?')) {
- if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
+ if ((!stack.parent.isRoot && !stack.parent.parent.isRoot) || stack.parent.contentItems.length > 1) {
stack.remove();
} else {
alert('cant delete the last stack');
@@ -532,7 +538,23 @@ export class CollectionDockingView extends CollectionSubView() {
};
render() {
- return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />;
+ const href = ImageCast(this.rootDoc.thumb)?.url.href;
+ return this.props.renderDepth > -1 ? (
+ <div>
+ {href ? (
+ <img
+ style={{ background: 'white', top: 0, position: 'absolute' }}
+ src={href} // + '?d=' + (new Date()).getTime()}
+ width={this.props.PanelWidth()}
+ height={this.props.PanelHeight()}
+ />
+ ) : (
+ <p>nested dashboards has no thumbnail</p>
+ )}
+ </div>
+ ) : (
+ <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />
+ );
}
}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 6a0f69359..db81f28f6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -34,13 +34,13 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum
import { DocumentView } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
-import { PresBox } from '../nodes/trails/PresBox';
import { DefaultStyleProvider } from '../StyleProvider';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionLinearView } from './collectionLinear';
import './CollectionMenu.scss';
import { COLLECTION_BORDER_WIDTH } from './CollectionView';
import { TabDocView } from './TabDocView';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
interface CollectionMenuProps {
panelHeight: () => number;
@@ -768,7 +768,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
private _head = ['', '', '', '', 'arrow', '', ''];
private _end = ['', '', '', 'arrow', 'arrow', '', ''];
- private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'];
+ private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'] as GestureUtils.Gestures[];
private _title = ['pen', 'highlighter', 'line', 'line with arrow', 'line with double arrows', 'square', 'circle'];
private _faName = ['pen-fancy', 'highlighter', 'minus', 'long-arrow-alt-right', 'arrows-alt-h', 'square', 'circle'];
@observable _selectedPrimitive = this._shapePrims.length;
@@ -849,13 +849,13 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
SetActiveArrowEnd(this._end[i]);
SetActiveBezierApprox('300');
- GestureOverlay.Instance.InkShape = this._shapePrims[i];
+ if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = this._shapePrims[i];
} else {
this._selectedPrimitive = this._shapePrims.length;
Doc.ActiveTool = InkTool.None;
SetActiveArrowStart('');
SetActiveArrowEnd('');
- GestureOverlay.Instance.InkShape = '';
+ if (GestureOverlay.Instance) GestureOverlay.Instance.InkShape = undefined;
SetActiveBezierApprox('0');
}
e.stopPropagation();
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 08b13fd50..4d1f18e54 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -52,9 +52,8 @@
}
.collectionNoteTakingViewFieldColumn {
- height: 100%;
display: flex;
- overflow: hidden;
+ overflow: auto;
}
.collectionNoteTakingViewFieldColumn:hover {
.collectionNoteTakingView-DocumentButtons {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 92c0bc341..b0f64ed60 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -193,7 +193,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
};
// let's dive in and get the actual document we want to drag/move around
- focusDocument = (doc: Doc, options?: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: DocFocusOptions) => {
Doc.BrushDoc(doc);
let focusSpeed = 0;
const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b29abf083..7bf798656 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -2,7 +2,7 @@ import React = require('react');
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
-import { Doc, DocListCast, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -23,11 +23,13 @@ import { AudioWaveform } from '../AudioWaveform';
import { CollectionSubView } from '../collections/CollectionSubView';
import { Colors } from '../global/globalEnums';
import { LightboxView } from '../LightboxView';
-import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, DocumentViewSharedProps } from '../nodes/DocumentView';
import { LabelBox } from '../nodes/LabelBox';
import './CollectionStackedTimeline.scss';
import { VideoBox } from '../nodes/VideoBox';
import { ImageField } from '../../../fields/URLField';
+import { StyleProp } from '../StyleProvider';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
export type CollectionStackedTimelineProps = {
Play: () => void;
@@ -693,6 +695,7 @@ interface StackedTimelineAnchorProps {
width: number;
height: number;
toTimeline: (screen_delta: number, width: number) => number;
+ styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
playLink: (linkDoc: Doc) => void;
setTime: (time: number) => void;
startTag: string;
@@ -803,12 +806,31 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
return [resetTitle];
};
+ innerStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
+ if (property === StyleProp.Decorations && doc && NumCast(doc.timecodeToHide) - NumCast(doc.timecodeToShow) < 0.0002) {
+ return (
+ <div className="styleProvider-lock">
+ <FontAwesomeIcon
+ icon={'camera'}
+ style={{ color: 'red' }}
+ onClick={e => {
+ LinkFollower.FollowLink(undefined, doc, props as DocumentViewSharedProps, e.altKey);
+ e.stopPropagation();
+ }}
+ size="lg"
+ />
+ </div>
+ );
+ }
+ return this.props.styleProvider?.(doc, props, property);
+ };
+
// renders anchor LabelBox
renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
const anchor = observable({ view: undefined as any });
- const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
+ const focusFunc = (doc: Doc, options: DocFocusOptions) => {
this.props.playLink(mark);
- this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
+ this.props.focus(doc, options);
};
return {
anchor,
@@ -819,6 +841,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
ref={action((r: DocumentView | null) => (anchor.view = r))}
Document={mark}
DataDoc={undefined}
+ styleProvider={this.innerStyleProvider}
renderDepth={this.props.renderDepth + 1}
LayoutTemplate={undefined}
LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index cc006c734..77b47ed82 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -251,7 +251,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
};
// let's dive in and get the actual document we want to drag/move around
- focusDocument = (doc: Doc, options?: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: DocFocusOptions) => {
Doc.BrushDoc(doc);
let focusSpeed = 0;
@@ -350,6 +350,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
searchFilterDocs={this.searchFilterDocs}
ContainingCollectionDoc={this.props.CollectionView?.props.Document}
ContainingCollectionView={this.props.CollectionView}
+ xPadding={NumCast(this.layoutDoc._childXPadding, this.props.childXPadding)}
+ yPadding={NumCast(this.layoutDoc._childYPadding, this.props.childYPadding)}
addDocument={this.props.addDocument}
moveDocument={this.props.moveDocument}
removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 4227955ef..7bc273d7d 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -212,18 +212,13 @@ export function CollectionSubView<X>(moreProps?: X) {
let added = false;
const dropAction = docDragData.dropAction || docDragData.userDropAction;
const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
- const someMoved = !docDragData.userDropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
+ const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
if (movedDocs.length) {
- const canAdd =
- this.props.Document._viewType === CollectionViewType.Pile ||
- de.embedKey ||
- !this.props.isAnnotationOverlay ||
- this.props.Document.allowOverlayDrop ||
- Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
+ const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || this.props.Document.allowOverlayDrop || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
} else {
ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
@@ -464,13 +459,13 @@ export function CollectionSubView<X>(moreProps?: X) {
if (typeof files === 'string') {
const loading = Docs.Create.LoadingDocument(files, options);
generatedDocuments.push(loading);
- loading.isLoading = true;
+ Doc.addCurrentlyLoading(loading);
DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
generatedDocuments.push(
...files.map(file => {
const loading = Docs.Create.LoadingDocument(file, options);
- loading.isLoading = true;
+ Doc.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 c0561e42c..3785b7d61 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -2,7 +2,6 @@
.collectionTreeView-container {
transform-origin: top left;
- height: 100%;
}
.collectionTreeView-dropTarget {
border-width: $COLLECTION_BORDER_WIDTH;
@@ -14,8 +13,6 @@
width: 100%;
position: relative;
top: 0;
- padding-left: 10px;
- padding-right: 10px;
background: $light-gray;
font-size: 13px;
overflow: auto;
@@ -62,7 +59,6 @@
.editableView-container-editing {
display: block;
text-overflow: ellipsis;
- font-size: 1vw;
white-space: nowrap;
}
}
@@ -80,9 +76,7 @@
}
.collectionTreeView-titleBar {
- display: inline-block;
width: 100%;
- height: max-content;
.contentFittingDocumentView {
display: block; // makes titleBar take up full width of the treeView (flex doesn't for some reason)
}
@@ -104,6 +98,7 @@
text-overflow: ellipsis;
white-space: pre-wrap;
min-width: 10px;
+ grid-column: 2;
}
.docContainer-system {
font-variant: all-small-caps;
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index dce792d19..0ff89c5a7 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -58,7 +58,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
private _isDisposing = false; // notes that instance is in process of being disposed
private refList: Set<any> = new Set(); // list of tree view items to monitor for height changes
private observer: any; // observer for monitoring tree view items.
- private static expandViewLabelSize = 20;
@computed get doc() {
return this.props.Document;
@@ -83,7 +82,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
return this.doc === Doc.MyDashboards;
}
- @observable _explainerHeight = 0; // height of the description of the tree view
+ @observable _titleHeight = 0; // height of the title bar
MainEle = () => this._mainEle;
@@ -100,7 +99,10 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
Object.values(this._disposers).forEach(disposer => disposer?.());
}
+ shrinkWrap = () => {}; // placeholder to allow setContentView to work
+
componentDidMount() {
+ //this.props.setContentView?.(this);
this._disposers.autoheight = reaction(
() => this.rootDoc.autoHeight,
auto => auto && this.computeHeight(),
@@ -111,7 +113,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
computeHeight = () => {
if (!this._isDisposing) {
const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
- const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot());
+ const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6;
this.layoutDoc._autoHeightMargins = bodyHeight;
this.props.setHeight?.(bodyHeight + titleHeight);
}
@@ -147,6 +149,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
};
+ screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this._headerHeight);
+
@action
remove = (doc: Doc | Doc[]): boolean => {
const docs = doc instanceof Doc ? [doc] : doc;
@@ -195,9 +199,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
onTreeDrop = (e: React.DragEvent, addDocs?: (docs: Doc[]) => void) => this.onExternalDrop(e, {}, addDocs);
@undoBatch
- makeTextCollection = (childDocs: Doc[]) => {
- this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
- };
+ makeTextCollection = (childDocs: Doc[]) => this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
get editableTitle() {
return (
@@ -256,6 +258,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
const icons = StrListCast(this.doc.childContextMenuIcons);
return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
};
+ headerFields = () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields);
@computed get treeViewElements() {
TraceMobx();
const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -275,11 +278,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
dropAction,
this.props.addDocTab,
this.props.styleProvider,
- this.props.ScreenToLocalTransform,
+ this.screenToLocalTransform,
this.isContentActive,
this.panelWidth,
this.props.renderDepth,
- () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields),
+ this.headerFields,
[],
this.props.onCheckedClick,
this.onChildClick,
@@ -298,7 +301,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
}
@computed get titleBar() {
return this.dataDoc === null ? null : (
- <div className="collectionTreeView-titleBar" key={this.doc[Id]} style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} ref={r => (this._titleRef = r)}>
+ <div
+ className="collectionTreeView-titleBar"
+ ref={action((r: any) => (this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.props.ScreenToLocalTransform().Scale))}
+ key={this.doc[Id]}
+ style={!this.outlineMode ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}}>
{this.outlineMode ? this.documentTitle : this.editableTitle}
</div>
);
@@ -366,52 +373,56 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
- panelWidth = () => Math.max(0, this.props.PanelWidth() - this.marginX() - CollectionTreeView.expandViewLabelSize) * (this.props.NativeDimScaling?.() || 1);
+ panelWidth = () => Math.max(0, this.props.PanelWidth() - 2 * this.marginX() * (this.props.NativeDimScaling?.() || 1));
addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
+ @observable _headerHeight = 0;
contentFunc = () => {
const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
return [
- <div
- className="collectionTreeView-contents"
- key="tree"
- style={{
- ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
- overflow: 'auto',
- height: '100%', //this.layoutDoc._autoHeight ? "max-content" : "100%"
- }}>
- {titleBar}
+ <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
+ {!this.buttonMenu && !this.noviceExplainer ? null : (
+ <div className="documentButtonMenu" ref={action((r: HTMLDivElement | null) => r && (this._headerHeight = Number(getComputedStyle(r).height.replace(/px/, ''))))}>
+ {this.buttonMenu}
+ {this.noviceExplainer}
+ </div>
+ )}
<div
- className="collectionTreeView-container"
+ className="collectionTreeView-contents"
+ key="tree"
style={{
- transform: this.outlineMode ? `scale(${this.nativeDimScaling})` : '',
- paddingLeft: `${this.marginX()}px`,
- width: this.outlineMode ? `calc(${100 / this.nativeDimScaling}%)` : '',
- }}
- onContextMenu={this.onContextMenu}>
- {!this.buttonMenu && !this.noviceExplainer ? null : (
- <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
- {this.buttonMenu}
- {this.noviceExplainer}
- </div>
- )}
+ ...(!titleBar ? { marginLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+ overflow: 'auto',
+ width: '100%',
+ height: '100%',
+ }}>
+ {titleBar}
<div
- className="collectionTreeView-dropTarget"
+ className="collectionTreeView-container"
style={{
- background: background(),
- height: `calc(100% - ${this._explainerHeight}px)`,
- pointerEvents: pointerEvents(),
+ marginLeft: `${this.marginX()}px`,
+ minHeight: `calc(100% - ${this._titleHeight}px)`,
}}
- onWheel={e => e.stopPropagation()}
- onDrop={this.onTreeDrop}
- ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
- <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+ onContextMenu={this.onContextMenu}>
+ <div
+ className="collectionTreeView-dropTarget"
+ style={{
+ background: background(),
+ pointerEvents: pointerEvents(),
+ height: `max-content`,
+ minHeight: '100%',
+ }}
+ onWheel={e => e.stopPropagation()}
+ onDrop={this.onTreeDrop}
+ ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
+ <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+ </div>
</div>
</div>
</div>,
@@ -420,24 +431,29 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
render() {
TraceMobx();
- return !(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
- <CollectionFreeFormView
- {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
- isAnnotationOverlay={true}
- isAnnotationOverlayScrollable={true}
- childDocumentsActive={this.props.isDocumentActive}
- fieldKey={this.props.fieldKey + '-annotations'}
- dropAction={'move'}
- select={emptyFunction}
- addDocument={this.addAnnotationDocument}
- removeDocument={this.remAnnotationDocument}
- moveDocument={this.moveAnnotationDocument}
- bringToFront={emptyFunction}
- renderDepth={this.props.renderDepth + 1}>
- {this.contentFunc}
- </CollectionFreeFormView>
- ) : (
- this.contentFunc()
+ const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1) || 1;
+ return (
+ <div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
+ {!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+ <CollectionFreeFormView
+ {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+ isAnnotationOverlay={true}
+ isAnnotationOverlayScrollable={true}
+ childDocumentsActive={this.props.isDocumentActive}
+ fieldKey={this.props.fieldKey + '-annotations'}
+ dropAction={'move'}
+ select={emptyFunction}
+ addDocument={this.addAnnotationDocument}
+ removeDocument={this.remAnnotationDocument}
+ moveDocument={this.moveAnnotationDocument}
+ bringToFront={emptyFunction}
+ renderDepth={this.props.renderDepth + 1}>
+ {this.contentFunc}
+ </CollectionFreeFormView>
+ ) : (
+ this.contentFunc()
+ )}
+ </div>
);
}
}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1ee77d4ce..9f63a11aa 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -15,7 +15,6 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils';
import { InteractionUtils } from '../../util/InteractionUtils';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
-import { DashboardView } from '../DashboardView';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { FieldView, FieldViewProps } from '../nodes/FieldView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
@@ -42,6 +41,7 @@ interface CollectionViewProps_ extends FieldViewProps {
isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
layoutEngine?: () => string;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
+ setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }) => void) => void;
// property overrides for child documents
childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
@@ -54,6 +54,8 @@ interface CollectionViewProps_ extends FieldViewProps {
childHideDecorationTitle?: () => boolean;
childHideResizeHandles?: () => boolean;
childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
+ childXPadding?: number;
+ childYPadding?: number;
childLayoutString?: string;
childIgnoreNativeSize?: boolean;
childClickScript?: ScriptField;
@@ -116,6 +118,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
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} />;
@@ -153,7 +156,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
!Doc.noviceMode && subItems.push({ description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' });
subItems.push({ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' });
- if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
+ if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
const existingVm = ContextMenu.Instance.findByDescription(category);
const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
@@ -187,7 +190,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
}
!Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' });
- if (!Doc.noviceMode) {
+ if (!Doc.noviceMode && false) {
optionItems.push({
description: 'Create Branch',
event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), 'add:right'),
@@ -204,9 +207,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
icon: 'project-diagram',
});
}
- if (this.Document._viewType === CollectionViewType.Docking) {
- optionItems.push({ description: 'Create Dashboard', event: () => DashboardView.createNewDashboard(), icon: 'project-diagram' });
- }
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index bead5825c..cde5132a3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -56,7 +56,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return this._document && Doc.Layout(this._document);
}
@computed get tabColor() {
- return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor)));
+ let tabColor = StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor)));
+ if (tabColor === 'transparent') return 'black';
+ return tabColor;
}
@computed get tabTextColor() {
return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color)));
@@ -94,6 +96,18 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const iconWrap = document.createElement('div');
const closeWrap = document.createElement('div');
+ const getChild = () => {
+ let child = this.view?.ContentDiv?.children[0];
+ while (child?.children.length) {
+ const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
+ if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
+ if (next?.className?.toString().includes(DashFieldView.name)) break;
+ if (next) child = next;
+ else break;
+ }
+ return child;
+ };
+
titleEle.size = StrCast(doc.title).length + 3;
titleEle.value = doc.title;
titleEle.onkeydown = (e: KeyboardEvent) => {
@@ -117,14 +131,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
action(e => {
if (this.view) {
SelectionManager.SelectView(this.view, false);
- let child = this.view.ContentDiv!.children[0];
- while (child.children.length) {
- const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
- if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
- if (next?.className?.toString().includes(DashFieldView.name)) break;
- if (next) child = next;
- else break;
- }
+ const child = getChild();
simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
} else {
this._activated = true;
@@ -163,6 +170,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
};
+ tab.element[0].oncontextmenu = (e: MouseEvent) => {
+ let child = getChild();
+ if (child) {
+ simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ };
+
// select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected
titleEle.onpointerdown = action((e: any) => {
if (e.target.className !== 'lm_iconWrap') {
@@ -213,73 +229,74 @@ export class TabDocView extends React.Component<TabDocViewProps> {
public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) {
const docList = docs instanceof Doc ? [docs] : docs;
- // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
- const curPres = Doc.ActivePresentation;
- if (curPres) {
- const batch = UndoManager.StartBatch('pinning doc');
- docList.forEach(doc => {
- // Edge Case 1: Cannot pin document to itself
- if (doc === curPres) {
- alert('Cannot pin presentation document to itself');
- return;
- }
- const pinDoc = Doc.MakeAlias(doc);
- pinDoc.presentationTargetDoc = doc;
- pinDoc.title = doc.title + ' - Slide';
- pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
- pinDoc.presMovement = PresMovement.Zoom;
- pinDoc.groupWithUp = false;
- pinDoc.context = curPres;
- // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
- pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
- pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
- pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
- pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
- pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
- pinDoc.treeViewGrowsHorizontally = true; // the document expands horizontally when displayed as a tree view header
- pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
- const presArray: Doc[] = PresBox.Instance?.sortArray();
- const size: number = PresBox.Instance?.selectedArray.size;
- const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
- const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+ const batch = UndoManager.StartBatch('pinning doc');
+ const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true);
- PresBox.pinDocView(pinDoc, pinProps, doc);
- pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
- Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
- if (!pinProps?.audioRange && duration !== undefined) {
- pinDoc.mediaStart = 'manual';
- pinDoc.mediaStop = 'manual';
- pinDoc.presStartTime = NumCast(doc.clipStart);
- pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
- }
- //save position
- if (pinProps?.activeFrame !== undefined) {
- pinDoc.presActiveFrame = pinProps?.activeFrame;
- pinDoc.title = doc.title + ' (move)';
- pinDoc.presMovement = PresMovement.Pan;
- }
- if (pinDoc.isInkMask) {
- pinDoc.presHideAfter = true;
- pinDoc.presHideBefore = true;
- pinDoc.presMovement = PresMovement.None;
- }
- if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
- PresBox.Instance?.clearSelectedArray();
- pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
- });
- if (
- CollectionDockingView.Instance &&
- !Array.from(CollectionDockingView.Instance.tabMap)
- .map(d => d.DashDoc)
- .includes(curPres)
- ) {
- const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []);
- if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
- CollectionDockingView.AddSplit(curPres, 'right');
- setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
+ if (!Doc.ActivePresentation) {
+ Doc.AddDocToList(Doc.MyTrails, 'data', curPres);
+ Doc.ActivePresentation = curPres;
+ }
+
+ docList.forEach(doc => {
+ // Edge Case 1: Cannot pin document to itself
+ if (doc === curPres) {
+ alert('Cannot pin presentation document to itself');
+ return;
+ }
+ const pinDoc = Doc.MakeAlias(doc);
+ pinDoc.presentationTargetDoc = doc;
+ pinDoc.title = doc.title + ' - Slide';
+ pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data
+ pinDoc.presMovement = doc.type === DocumentType.SCRIPTING || pinProps?.pinDocLayout ? PresMovement.None : PresMovement.Zoom;
+ pinDoc.groupWithUp = false;
+ pinDoc.context = curPres;
+ // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time
+ pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+ pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling.
+ pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+ pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+ pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+ pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
+ const presArray: Doc[] = PresBox.Instance?.sortArray();
+ const size: number = PresBox.Instance?.selectedArray.size;
+ const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
+ const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+
+ if (!pinProps?.audioRange && duration !== undefined) {
+ pinDoc.mediaStart = 'manual';
+ pinDoc.mediaStop = 'manual';
+ pinDoc.presStartTime = NumCast(doc.clipStart);
+ pinDoc.presEndTime = NumCast(doc.clipEnd, duration);
+ }
+ PresBox.pinDocView(pinDoc, pinProps, doc);
+ pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
+ Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
+ //save position
+ if (pinProps?.activeFrame !== undefined) {
+ pinDoc.presActiveFrame = pinProps?.activeFrame;
+ pinDoc.title = doc.title + ' (move)';
+ pinDoc.presMovement = PresMovement.Pan;
+ }
+ if (pinDoc.isInkMask) {
+ pinDoc.presHideAfter = true;
+ pinDoc.presHideBefore = true;
+ pinDoc.presMovement = PresMovement.None;
}
- setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
+ if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+ PresBox.Instance?.clearSelectedArray();
+ pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array
+ });
+ if (
+ !Array.from(CollectionDockingView.Instance?.tabMap ?? [])
+ .map(d => d.DashDoc)
+ .includes(curPres)
+ ) {
+ const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []);
+ if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
+ CollectionDockingView.AddSplit(curPres, 'right');
+ setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
}
+ setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
}
componentDidMount() {
@@ -363,9 +380,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
};
@action
- focusFunc = (doc: Doc, options?: DocFocusOptions) => {
+ focusFunc = (doc: Doc, options: DocFocusOptions) => {
const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap;
- if (shrinkwrap && this._document) {
+ if (options?.willZoom !== false && shrinkwrap && this._document) {
const focusSpeed = NumCast(this._document.focusSpeed, 500);
shrinkwrap();
this._document._viewTransition = `transform ${focusSpeed}ms`;
@@ -398,7 +415,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
@computed get docView() {
- return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? null : (
+ return !this._activated || !this._document ? null : (
<>
<DocumentView
key={this._document[Id]}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index ce87e6f89..83fee013a 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -24,6 +24,7 @@
// width: $TREE_BULLET_WIDTH;
width: 100%;
height: 100%;
+ position: absolute;
.treeView-expandIcon {
display: none;
@@ -53,13 +54,16 @@
}
.bullet {
+ grid-column: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
position: relative;
width: $TREE_BULLET_WIDTH;
+ min-height: 20px;
color: $medium-gray;
- margin-top: 3px;
- // transform: scale(1.3, 1.3); // bcz: why was this here? It makes displaying images in the treeView for documents that have icons harder.
border: #80808030 1px solid;
- border-radius: 4px;
+ border-radius: 5px;
}
}
@@ -112,8 +116,15 @@
.treeView-header-editing,
.treeView-header {
+ display: flex; // needed for PresBox's treeView
border: transparent 1px solid;
- display: flex;
+ align-items: center;
+ width: max-content;
+ border-radius: 5px;
+
+ &:hover {
+ background-color: #bdddf5;
+ }
//align-items: center;
::-webkit-scrollbar {
@@ -140,6 +151,7 @@
.treeView-rightButtons {
display: flex;
+ grid-column: 3;
align-items: center;
margin-left: 0.25rem;
opacity: 0.75;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index aa1330762..1e97eee37 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -8,13 +8,14 @@ import { List } from '../../../fields/List';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
@@ -31,7 +32,6 @@ import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
import { CollectionView } from './CollectionView';
import './TreeView.scss';
import React = require('react');
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -321,6 +321,7 @@ export class TreeView extends React.Component<TreeViewProps> {
_viewType: CollectionViewType.Tree,
hideLinkButton: true,
_showSidebar: true,
+ _fitWidth: true,
treeViewType: TreeViewType.outline,
x: 0,
y: 0,
@@ -398,29 +399,22 @@ export class TreeView extends React.Component<TreeViewProps> {
};
docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
getTransform = () => this.refTransform(this._tref.current);
- docWidth = () => {
- const layoutDoc = this.layoutDoc;
- const aspect = Doc.NativeAspect(layoutDoc);
- if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
- if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
- return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
- };
- docHeight = () => {
- const layoutDoc = this.layoutDoc;
- return Math.max(
- 70,
- Math.min(
- this.MAX_EMBED_HEIGHT,
- (() => {
- const aspect = Doc.NativeAspect(layoutDoc);
- if (aspect) return this.docWidth() / (aspect || 1);
- return layoutDoc._fitWidth
- ? !Doc.NativeHeight(this.doc)
- ? NumCast(this.props.containerCollection._height)
- : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
- : layoutDoc[HeightSym]() || 50;
- })()
- )
+ embeddedPanelWidth = () => this.props.panelWidth() / (this.props.treeView.props.NativeDimScaling?.() || 1);
+ embeddedPanelHeight = () => {
+ console.log(this.props.treeView.rootDoc.title);
+ const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+ return Math.min(
+ layoutDoc[HeightSym](),
+ this.MAX_EMBED_HEIGHT,
+ (() => {
+ const aspect = Doc.NativeAspect(layoutDoc);
+ if (aspect) return this.embeddedPanelWidth() / (aspect || 1);
+ return layoutDoc._fitWidth
+ ? !Doc.NativeHeight(layoutDoc)
+ ? NumCast(layoutDoc._height) //this.props.containerCollection._height)
+ : Math.min((this.embeddedPanelWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
+ : (this.embeddedPanelWidth() * layoutDoc[HeightSym]()) / layoutDoc[WidthSym]();
+ })()
);
};
@@ -512,28 +506,6 @@ export class TreeView extends React.Component<TreeViewProps> {
return rows;
}
- rtfWidth = () => {
- const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
- return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.NativeDimScaling?.() || 1);
- };
- rtfHeight = () => {
- const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
- return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
- };
- rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
- expandPanelHeight = () => {
- if (this.layoutDoc._fitWidth) return this.docHeight();
- const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
- const docAspect = this.docWidth() / this.docHeight();
- return docAspect < aspect ? this.docWidth() / aspect : this.docHeight();
- };
- expandPanelWidth = () => {
- if (this.layoutDoc._fitWidth) return this.docWidth();
- const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
- const docAspect = this.docWidth() / this.docHeight();
- return docAspect > aspect ? this.docHeight() * aspect : this.docWidth();
- };
-
@computed get renderContent() {
TraceMobx();
const expandKey = this.treeViewExpandedView;
@@ -575,7 +547,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<ul
key={expandKey + 'more'}
title="click to change sort order"
- className={this.doc.treeViewHideTitle ? 'no-indent' : ''}
+ className={''} //this.doc.treeViewHideTitle ? 'no-indent' : ''}
onPointerDown={e => {
downX = e.clientX;
downY = e.clientY;
@@ -629,7 +601,7 @@ export class TreeView extends React.Component<TreeViewProps> {
} else if (this.treeViewExpandedView === 'fields') {
return (
<ul key={this.doc[Id] + this.doc.title}>
- <div style={{ display: 'inline-block' }}>{this.expandedField}</div>
+ <div>{this.expandedField}</div>
</ul>
);
}
@@ -692,7 +664,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />
)
) : (
- <div className="treeView-bulletIcons">
+ <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}>
<div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />
</div>
@@ -736,7 +708,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}}
/>
)}
- {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
+ {Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
<span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
{this.treeViewExpandedView}
</span>
@@ -782,7 +754,7 @@ export class TreeView extends React.Component<TreeViewProps> {
onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
- refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
+ refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document, {});
ignoreEvent = (e: any) => {
if (this.props.isContentActive(true)) {
e.stopPropagation();
@@ -792,29 +764,39 @@ export class TreeView extends React.Component<TreeViewProps> {
titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
+ const treeView = this.props.treeView;
+ // prettier-ignore
switch (property.split(':')[0]) {
- case StyleProp.Opacity:
- return this.props.treeView.outlineMode ? undefined : 1;
- case StyleProp.BackgroundColor:
- return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+ case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
+ case StyleProp.BackgroundColor: return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+ case StyleProp.Highlighting: if (this.props.treeView.outlineMode) return undefined;
+ case StyleProp.Hidden: return false;
+ case StyleProp.BoxShadow: return undefined;
case StyleProp.DocContents:
- return this.props.treeView.outlineMode ? null : (
+ const highlightIndex = this.props.treeView.outlineMode ? Doc.DocBrushStatus.unbrushed : Doc.isBrushedHighlightedDegree(doc);
+ const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
+ return treeView.outlineMode ? null : (
<div
className="treeView-label"
style={{
// just render a title for a tree view label (identified by treeViewDoc being set in 'props')
maxWidth: props?.PanelWidth() || undefined,
background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
+ outline: `solid ${highlightColor} ${highlightIndex}px`,
+ paddingLeft: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+ paddingRight: NumCast(treeView.rootDoc.childXPadding, NumCast(treeView.props.childXPadding, Doc.IsComicStyle(doc)?20:0)),
+ paddingTop: treeView.props.childYPadding,
+ paddingBottom: treeView.props.childYPadding,
}}>
{StrCast(doc?.title)}
</div>
);
- default:
- return this.props?.treeView?.props.styleProvider?.(doc, props, property);
}
+ return treeView.props.styleProvider?.(doc, props, property);
};
embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (property.startsWith(StyleProp.Decorations)) return null;
+ if (property.startsWith(StyleProp.Hidden)) return false;
return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
};
onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
@@ -841,7 +823,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
return false;
};
- titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
+ titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth())) / (this.props.treeView.props.NativeDimScaling?.() || 1) - 3 * treeBulletWidth();
return18 = () => 18;
/**
@@ -849,6 +831,7 @@ export class TreeView extends React.Component<TreeViewProps> {
*/
@computed
get renderTitle() {
+ //
TraceMobx();
const view = this._editTitle ? (
<EditableView
@@ -885,6 +868,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
})}
Document={this.doc}
+ fitWidth={(doc: Doc) => true}
DataDoc={undefined}
scriptContext={this}
hideDecorationTitle={this.props.treeView.outlineMode}
@@ -903,7 +887,7 @@ export class TreeView extends React.Component<TreeViewProps> {
removeDocument={this.props.removeDoc}
ScreenToLocalTransform={this.getTransform}
NativeHeight={this.return18}
- NativeWidth={this.titleWidth}
+ NativeWidth={returnZero}
PanelWidth={this.titleWidth}
PanelHeight={this.return18}
contextMenuItems={this.contextMenuItems}
@@ -916,10 +900,12 @@ export class TreeView extends React.Component<TreeViewProps> {
disableDocBrushing={this.props.treeView.props.disableDocBrushing}
hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
+ xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)}
+ yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionView={undefined}
+ ContainingCollectionView={this.props.treeView.props.CollectionView}
ContainingCollectionDoc={this.props.treeView.props.Document}
/>
);
@@ -953,7 +939,6 @@ export class TreeView extends React.Component<TreeViewProps> {
<div
className={`treeView-header` + (editing ? '-editing' : '')}
key="titleheader"
- style={{ width: StrCast(this.doc.treeViewHeaderWidth, 'max-content') }}
ref={this._header}
onClick={this.ignoreEvent}
onPointerDown={this.ignoreEvent}
@@ -967,52 +952,52 @@ export class TreeView extends React.Component<TreeViewProps> {
};
renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
- const isExpandable = this.doc._treeViewGrowsHorizontally;
- const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
- const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
return (
- <DocumentView
- key={this.doc[Id]}
- ref={action((r: DocumentView | null) => (this._dref = r))}
- Document={this.doc}
- DataDoc={undefined}
- PanelWidth={panelWidth}
- PanelHeight={panelHeight}
- NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
- NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
- LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
- LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
- isContentActive={isActive}
- isDocumentActive={isActive}
- styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
- hideTitle={asText}
- fitContentsToBox={returnTrue}
- hideDecorationTitle={this.props.treeView.outlineMode}
- hideResizeHandles={this.props.treeView.outlineMode}
- onClick={this.onChildClick}
- focus={this.refocus}
- onKey={this.onKeyDown}
- hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
- dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
- ScreenToLocalTransform={this.docTransform}
- renderDepth={this.props.renderDepth + 1}
- treeViewDoc={this.props.treeView?.props.Document}
- rootSelected={returnTrue}
- docViewPath={this.props.treeView.props.docViewPath}
- docFilters={returnEmptyFilter}
- docRangeFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- ContainingCollectionDoc={this.props.containerCollection}
- ContainingCollectionView={undefined}
- addDocument={this.props.addDocument}
- moveDocument={this.move}
- removeDocument={this.props.removeDoc}
- whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
- addDocTab={this.props.addDocTab}
- pinToPres={this.props.treeView.props.pinToPres}
- disableDocBrushing={this.props.treeView.props.disableDocBrushing}
- bringToFront={returnFalse}
- />
+ <div style={{ height: this.embeddedPanelHeight(), width: this.embeddedPanelWidth() }}>
+ <DocumentView
+ key={this.doc[Id]}
+ ref={action((r: DocumentView | null) => (this._dref = r))}
+ Document={this.doc}
+ DataDoc={undefined}
+ PanelWidth={this.embeddedPanelWidth}
+ PanelHeight={this.embeddedPanelHeight}
+ LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
+ LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
+ isContentActive={isActive}
+ isDocumentActive={isActive}
+ styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+ hideTitle={asText}
+ //fitContentsToBox={returnTrue}
+ hideDecorationTitle={this.props.treeView.outlineMode}
+ hideResizeHandles={this.props.treeView.outlineMode}
+ onClick={this.onChildClick}
+ focus={this.refocus}
+ onKey={this.onKeyDown}
+ hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+ dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
+ ScreenToLocalTransform={this.docTransform}
+ renderDepth={this.props.renderDepth + 1}
+ treeViewDoc={this.props.treeView?.props.Document}
+ rootSelected={returnTrue}
+ docViewPath={this.props.treeView.props.docViewPath}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.containerCollection}
+ ContainingCollectionView={undefined}
+ addDocument={this.props.addDocument}
+ moveDocument={this.move}
+ removeDocument={this.props.removeDoc}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ xPadding={NumCast(this.props.treeView.props.Document.childXPadding, this.props.treeView.props.childXPadding)}
+ yPadding={NumCast(this.props.treeView.props.Document.childYPadding, this.props.treeView.props.childYPadding)}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.treeView.props.pinToPres}
+ disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+ bringToFront={returnFalse}
+ scriptContext={this}
+ />
+ </div>
);
};
@@ -1038,7 +1023,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderBorder() {
const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
- const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+ const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } };
return (
<div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
{!this.treeViewOpen ? null : this.renderContent}
@@ -1058,7 +1043,7 @@ export class TreeView extends React.Component<TreeViewProps> {
render() {
TraceMobx();
const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
- return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (
+ return this.props.renderedIds?.indexOf(this.doc[Id]) !== -1 ? (
'<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
) : (
<div
@@ -1146,7 +1131,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None));
- const rowWidth = () => panelWidth() - treeBulletWidth();
+ const rowWidth = () => panelWidth() - treeBulletWidth() * (treeView.props.NativeDimScaling?.() || 1);
const treeViewRefs = new Map<Doc, TreeView | undefined>();
return docs
.filter(child => child instanceof Doc)
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 039407e77..2f246e74f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -26,7 +26,7 @@ import { InteractionUtils } from '../../../util/InteractionUtils';
import { ReplayMovements } from '../../../util/ReplayMovements';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { SelectionManager } from '../../../util/SelectionManager';
-import { ColorScheme } from '../../../util/SettingsManager';
+import { ColorScheme, freeformScrollMode } from '../../../util/SettingsManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
@@ -88,6 +88,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _cachedPool: Map<string, PoolData> = new Map();
private _lastTap = 0;
private _batch: UndoManager.Batch | undefined = undefined;
+ private _brushtimer: any;
+ private _brushtimer1: any;
// private isWritingMode: boolean = true;
// private writingModeDocs: Doc[] = [];
@@ -111,13 +113,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _clusterSets: Doc[][] = [];
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
- @observable _marqueeRef = React.createRef<HTMLDivElement>();
+ @observable _marqueeRef: HTMLDivElement | null = null;
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
+ @observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region
@computed get views() {
- const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1).map(ele => ele.ele);
- const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask === -1).map(ele => ele.ele);
+ const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1 && ele.inkMask !== undefined).map(ele => ele.ele);
+ const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && (ele.inkMask === -1 || ele.inkMask === undefined)).map(ele => ele.ele);
if (viewsMask.length) renderableEles.push(<div className={`collectionfreeformview-mask${this._layoutElements.some(ele => (ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}</div>);
return renderableEles;
}
@@ -152,8 +155,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
}
@computed get cachedCenteringShiftY(): number {
+ const dv = this.props.DocumentView?.();
const scaling = this.fitContentsToBox || !this.nativeDimScaling ? 1 : this.nativeDimScaling;
- return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling; // shift so pan position is at center of window for non-overlay collections
+ // if freeform has a native aspect, then the panel height needs to be adjusted to match it
+ const aspect = dv?.nativeWidth && dv?.nativeHeight && !dv.layoutDoc.fitWidth ? dv.nativeHeight / dv.nativeWidth : this.props.PanelHeight() / this.props.PanelWidth();
+ return this.props.isAnnotationOverlay ? 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()
@@ -187,6 +193,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
elementFunc = () => this._layoutElements;
shrinkWrap = () => {
+ if (this.props.DocumentView?.().nativeWidth) return;
const vals = this.fitToContentVals;
this.layoutDoc._panX = vals.bounds.cx;
this.layoutDoc._panY = vals.bounds.cy;
@@ -561,6 +568,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@undoBatch
onGesture = (e: Event, ge: GestureUtils.GestureEvent) => {
switch (ge.gesture) {
+ default:
+ case GestureUtils.Gestures.Line:
+ case GestureUtils.Gestures.Circle:
+ case GestureUtils.Gestures.Rectangle:
+ case GestureUtils.Gestures.Triangle:
case GestureUtils.Gestures.Stroke:
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
@@ -590,34 +602,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.addDocument(inkDoc);
e.stopPropagation();
break;
- case GestureUtils.Gestures.Box:
- const lt = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y)));
- const rb = this.getTransform().transformPoint(Math.max(...ge.points.map(p => p.X)), Math.max(...ge.points.map(p => p.Y)));
- const bounds = { x: lt[0], r: rb[0], y: lt[1], b: rb[1] };
- const bWidth = bounds.r - bounds.x;
- const bHeight = bounds.b - bounds.y;
- const sel = this.getActiveDocuments().filter(doc => {
- const l = NumCast(doc.x);
- const r = l + doc[WidthSym]();
- const t = NumCast(doc.y);
- const b = t + doc[HeightSym]();
- const pass = !(bounds.x > r || bounds.r < l || bounds.y > b || bounds.b < t);
- if (pass) {
- doc.x = l - bounds.x - bWidth / 2;
- doc.y = t - bounds.y - bHeight / 2;
- }
- return pass;
- });
- this.addDocument(Docs.Create.FreeformDocument(sel, { title: 'nested collection', x: bounds.x, y: bounds.y, _width: bWidth, _height: bHeight, _panX: 0, _panY: 0 }));
- sel.forEach(d => this.props.removeDocument?.(d));
- e.stopPropagation();
- break;
- case GestureUtils.Gestures.StartBracket:
- const start = this.getTransform().transformPoint(Math.min(...ge.points.map(p => p.X)), Math.min(...ge.points.map(p => p.Y)));
- this._inkToTextStartX = start[0];
- this._inkToTextStartY = start[1];
- break;
- case GestureUtils.Gestures.EndBracket:
+ 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 => s.proto?.type === 'rtf' && s.color);
@@ -716,6 +701,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
+ scrollPan = (e: WheelEvent | { deltaX: number; deltaY: number }): void => {
+ const dx = e.deltaX;
+ const dy = e.deltaY;
+ this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
+ };
+
+ @action
pan = (e: PointerEvent | React.Touch | { clientX: number; clientY: number }): void => {
const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
@@ -794,15 +786,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const prevPointInkSpace = inkStroke.ptFromScreen(lastPoint);
const currPointInkSpace = inkStroke.ptFromScreen(currPoint);
for (var i = 0; i < inkData.length - 3; i += 4) {
- const intersects = Array.from(
- new Set(
- InkField.Segment(inkData, i).intersects({
- // compute all unique intersections
- p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
- p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
- }) as (number | string)[]
- )
- ); // convert to more manageable union array type
+ const rawIntersects = InkField.Segment(inkData, i).intersects({
+ // compute all unique intersections
+ p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+ p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y },
+ });
+ const intersects = Array.from(new Set(rawIntersects as (number | string)[])); // convert to more manageable union array type
+ if (intersects.length) {
+ console.log();
+ }
// return tuples of the inkingStroke intersected, and the t value of the intersection
intersections.push(...intersects.map(t => ({ inkView, t: +t + Math.floor(i / 4) }))); // convert string t's to numbers and add start of curve segment to convert from local t value to t value along complete curve
}
@@ -851,6 +843,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return segments;
};
+ // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection
+ // call in a test for linearity
+ bintersects = (curve: Bezier, otherCurve: Bezier) => {
+ if ((otherCurve as any)._linear) {
+ return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] });
+ }
+ return curve.intersects(otherCurve);
+ };
+
/**
* Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all
* ink strokes in the current collection.
@@ -875,7 +876,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (ink?.Document === otherInk.props.Document && neighboringSegment) continue;
const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
- curve.intersects(otherCurve).forEach((val: string | number, i: number) => {
+ this.bintersects(curve, otherCurve).forEach((val: string | number, i: number) => {
// Converting the Bezier.js Split type to a t-value number.
const t = +val.toString().split('/')[0];
if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
@@ -1005,13 +1006,38 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerWheel = (e: React.WheelEvent): void => {
if (this.layoutDoc._Transform || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
- if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
- // things that can scroll vertically should do that instead of zooming
- e.stopPropagation();
- } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
- e.stopPropagation();
- e.preventDefault();
- !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ e.stopPropagation();
+ e.preventDefault();
+ switch (Doc.UserDoc().freeformScrollMode) {
+ case freeformScrollMode.Pan:
+ // if shift is selected then zoom
+ if (e.ctrlKey) {
+ if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
+ // things that can scroll vertically should do that instead of zooming
+ } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ }
+ // otherwise pan
+ } else if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
+ // things that can scroll vertically should do that instead of zooming
+ } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ const dx = e.deltaX;
+ const dy = e.deltaY;
+ if (e.shiftKey) {
+ !this.props.isAnnotationOverlayScrollable && this.scrollPan({ deltaX: dy, deltaY: 0 });
+ } else {
+ !this.props.isAnnotationOverlayScrollable && this.scrollPan({ deltaX: dx, deltaY: dy });
+ }
+ }
+ break;
+ default:
+ case freeformScrollMode.Zoom:
+ if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
+ // things that can scroll vertically should do that instead of zooming
+ } else if (this.props.isContentActive(true) && !this.Document._isGroup) {
+ !this.props.isAnnotationOverlayScrollable && this.zoom(e.clientX, e.clientY, e.deltaY); // if (!this.props.isAnnotationOverlay) // bcz: do we want to zoom in on images/videos/etc?
+ }
+ break;
}
};
@@ -1098,10 +1124,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
if (this.Document._isGroup) return;
- setTimeout(
- action(() => (this._viewTransition = 0)),
- (this._viewTransition = transitionTime)
- ); // set transition to be smooth, then reset
+ this._viewTransition = transitionTime;
const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]);
@@ -1109,9 +1132,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newpan = this.getTransform().transformDirection(scrDelta.x, scrDelta.y);
this.layoutDoc._panX = NumCast(this.layoutDoc._panX) - newpan[0];
this.layoutDoc._panY = NumCast(this.layoutDoc._panY) - newpan[1];
+ return new Promise<number>(res => setTimeout(() => res(runInAction(() => (this._viewTransition = 0))), this._viewTransition)); // set transition to be smooth, then reset
}
- focusDocument = (doc: Doc, options?: DocFocusOptions) => {
+ focusDocument = (doc: Doc, options: DocFocusOptions) => {
const state = HistoryUtil.getState();
// TODO This technically isn't correct if type !== "doc", as
@@ -1130,13 +1154,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
// SelectionManager.DeselectAll();
// }
- if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined) {
+ if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined || this.props.Document.currentTimecode !== undefined) {
this.props.focus(doc, options);
} else {
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY), scale: options?.willZoom ? this.Document[this.scaleFieldKey] : undefined };
const newState = HistoryUtil.getState();
- const cantTransform = /*this.props.isAnnotationOverlay ||*/ (this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc;
+ const cantTransform = (this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc;
const { panX, panY, scale } = cantTransform ? savedState : this.calculatePanIntoView(doc, xfToCollection, options?.willZoom ? options?.scale || 0.75 : undefined);
if (!cantTransform) {
// only pan and zoom to focus on a document if the document is not an annotation in an annotation overlay collection
@@ -1174,7 +1198,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
? new Transform(NumCast(this.rootDoc.x), NumCast(this.rootDoc.y), this.rootDoc[WidthSym]() / Doc.NativeWidth(this.rootDoc))
: new Transform(NumCast(this.rootDoc.x) + this.rootDoc[WidthSym]() / 2 - NumCast(this.rootDoc._panX), NumCast(this.rootDoc.y) + this.rootDoc[HeightSym]() / 2 - NumCast(this.rootDoc._panY), 1);
- this.props.focus(cantTransform ? doc : this.rootDoc, {
+ this.props.focus(!cantTransform ? this.rootDoc : doc, {
...options,
docTransform: xf,
afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))),
@@ -1293,7 +1317,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
dontScaleFilter={this.props.dontScaleFilter}
dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
pointerEvents={this.pointerEvents}
- jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
+ rotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
//fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
/>
);
@@ -1322,8 +1346,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const { backgroundColor, color } = curFrame === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, curFrame);
const { x, y, opacity } = curFrame === undefined ? { x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, curFrame);
return {
- x: NumCast(x),
- y: NumCast(y),
+ x: Number.isNaN(NumCast(x)) ? 0 : NumCast(x),
+ y: Number.isNaN(NumCast(y)) ? 0 : NumCast(y),
z: Cast(z, 'number'),
color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.props.styleProvider?.(childDoc, this.props, StyleProp.BackgroundColor),
@@ -1425,15 +1449,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@computed get doInternalLayoutComputation() {
TraceMobx();
const newPool = new Map<string, PoolData>();
+ // prettier-ignore
switch (this.layoutEngine) {
- case 'pass':
- return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
- case 'timeline':
- return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
- case 'pivot':
- return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
- case 'starburst':
- return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
+ case 'pass': return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
+ case 'timeline': return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
+ case 'pivot': return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
+ case 'starburst': return { newPool, computedElementData: this.doEngineLayout(newPool, computerStarburstLayout) };
}
return { newPool, computedElementData: this.doFreeformLayout(newPool) };
}
@@ -1523,12 +1544,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
componentDidMount() {
super.componentDidMount?.();
this.props.setContentView?.(this);
+ this.props.setBrushViewer?.(this.brushView);
setTimeout(
action(() => {
this._firstRender = false;
-
- this._marqueeRef.current?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
-
this._disposers.groupBounds = reaction(
() => {
if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
@@ -1646,7 +1665,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
componentWillUnmount() {
Object.values(this._disposers).forEach(disposer => disposer?.());
- this._marqueeRef.current?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+ this._marqueeRef?.removeEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
}
@action
@@ -1659,10 +1678,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if ((e as any).handlePan || this.props.isAnnotationOverlay) return;
(e as any).handlePan = true;
- if (!this.layoutDoc._noAutoscroll && !this.props.renderDepth && this._marqueeRef?.current) {
+ if (!this.layoutDoc._noAutoscroll && !this.props.renderDepth && this._marqueeRef) {
const dragX = e.detail.clientX;
const dragY = e.detail.clientY;
- const bounds = this._marqueeRef.current?.getBoundingClientRect();
+ const bounds = this._marqueeRef?.getBoundingClientRect();
const deltaX = dragX - bounds.left < 25 ? -(25 + (bounds.left - dragX)) : bounds.right - dragX < 25 ? 25 - (bounds.right - dragX) : 0;
const deltaY = dragY - bounds.top < 25 ? -(25 + (bounds.top - dragY)) : bounds.bottom - dragY < 25 ? 25 - (bounds.bottom - dragY) : 0;
@@ -1720,7 +1739,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
event: () => (this.Document._fitContentsToBox = !this.fitContentsToBox),
icon: !this.fitContentsToBox ? 'expand-arrows-alt' : 'compress-arrows-alt',
});
- appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinDocView: true, panelWidth: this.props.PanelWidth(), panelHeight: this.props.PanelHeight() }), icon: 'map-pin' });
+ appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, { pinViewport: MarqueeView.CurViewBounds(this.rootDoc, this.props.PanelWidth(), this.props.PanelHeight()) }), icon: 'map-pin' });
//appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
this.props.ContainingCollectionView && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
@@ -1820,10 +1839,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
DragManager.SetSnapLines(horizLines, vertLines);
};
- onPointerOver = (e: React.PointerEvent) => {
- e.stopPropagation();
- };
-
incrementalRender = action(() => {
if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
@@ -1867,7 +1882,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
getContainerTransform={this.getContainerTransform}
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
- <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
+ <div
+ className="marqueeView-div"
+ ref={r => {
+ this._marqueeRef = r;
+ r?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
+ }}
+ style={{ opacity: this.props.dontRenderDocuments ? 0.7 : undefined }}>
{this.layoutDoc._backgroundGridShow ? (
<div>
<CollectionFreeFormBackgroundGrid // bcz : UGHH don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
@@ -1884,6 +1905,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
) : null}
<CollectionFreeFormViewPannableContents
+ brushView={this._brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
transform={this.contentTransform}
@@ -1919,14 +1941,29 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
};
+ @action
+ brushView = (viewport: { width: number; height: number; panX: number; panY: number }) => {
+ this._brushedView = { ...viewport, panX: viewport.panX - viewport.width / 2, panY: viewport.panY - viewport.height / 2, opacity: 1 };
+ this._brushtimer1 && clearTimeout(this._brushtimer1);
+ this._brushtimer && clearTimeout(this._brushtimer);
+ this._brushtimer1 = setTimeout(
+ action(() => {
+ this._brushedView.opacity = 0;
+ this._brushtimer = setTimeout(
+ action(() => (this._brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 })),
+ 500
+ );
+ }),
+ 1000
+ );
+ };
+
render() {
TraceMobx();
- const clientRect = this._mainCont?.getBoundingClientRect();
return (
<div
className={'collectionfreeformview-container'}
ref={this.createDashEventsTarget}
- onPointerOver={this.onPointerOver}
onWheel={this.onPointerWheel}
onClick={this.onClick}
onPointerDown={this.onPointerDown}
@@ -2007,6 +2044,7 @@ interface CollectionFreeFormViewPannableContentsProps {
presPinView?: boolean;
isAnnotationOverlay: boolean | undefined;
isAnnotationOverlayScrollable: boolean | undefined;
+ brushView: { panX: number; panY: number; width: number; height: number; opacity: number };
}
@observer
@@ -2126,6 +2164,21 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
//willChange: "transform"
}}>
{this.props.children()}
+ {!this.props.brushView.width ? null : (
+ <div
+ className="collectionFreeFormView-brushView"
+ style={{
+ zIndex: 1000,
+ opacity: this.props.brushView.opacity,
+ border: 'orange solid 2px',
+ position: 'absolute',
+ transform: `translate(${this.props.brushView.panX}px, ${this.props.brushView.panY}px)`,
+ width: this.props.brushView.width,
+ height: this.props.brushView.height,
+ transition: 'opacity 2s',
+ }}
+ />
+ )}
{this.presPaths}
{this.progressivize}
{this.zoomProgressivize}
@@ -2203,7 +2256,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined;
const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
- ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5);
+ await ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5);
}
return ViewAdjustment.doNothing;
},
@@ -2217,6 +2270,6 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
!readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
-ScriptingGlobals.add(function pinWithView(readOnly: boolean) {
- !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocView: true, panelWidth: view.props.PanelWidth(), panelHeight: view.props.PanelHeight() }));
+ScriptingGlobals.add(function pinWithView(readOnly: boolean, pinDocContent: boolean) {
+ !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocContent, pinViewport: MarqueeView.CurViewBounds(view.rootDoc, view.props.PanelWidth(), view.props.PanelHeight()) }));
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 8a8b528f6..488f51d77 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -1,11 +1,11 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { observer } from "mobx-react";
-import { unimplementedFunction } from "../../../../Utils";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { observer } from 'mobx-react';
+import { unimplementedFunction } from '../../../../Utils';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@observer
export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -25,44 +25,34 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
}
render() {
- const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 19, transform: 'translate(-2px, -2px)' }} />;
+ const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 19, transform: 'translate(-2px, -2px)' }} />;
const buttons = [
<Tooltip key="collect" title={<div className="dash-tooltip">Create a Collection</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={this.createCollection}>
+ <button className="antimodeMenu-button" onPointerDown={this.createCollection}>
<FontAwesomeIcon icon="object-group" size="lg" />
</button>
</Tooltip>,
<Tooltip key="group" title={<div className="dash-tooltip">Create a Grouping</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={e => this.createCollection(e, true)}>
+ <button className="antimodeMenu-button" onPointerDown={e => this.createCollection(e, true)}>
<FontAwesomeIcon icon="layer-group" size="lg" />
</button>
</Tooltip>,
<Tooltip key="summarize" title={<div className="dash-tooltip">Summarize Documents</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={this.summarize}>
+ <button className="antimodeMenu-button" onPointerDown={this.summarize}>
<FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
</button>
</Tooltip>,
<Tooltip key="delete" title={<div className="dash-tooltip">Delete Documents</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={this.delete}>
+ <button className="antimodeMenu-button" onPointerDown={this.delete}>
<FontAwesomeIcon icon="trash-alt" size="lg" />
</button>
</Tooltip>,
- <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin with selected region</div>} placement="bottom">
- <button
- className="antimodeMenu-button"
- onPointerDown={this.pinWithView}>
+ <Tooltip key="pinWithView" title={<div className="dash-tooltip">Pin selected region to trail</div>} placement="bottom">
+ <button className="antimodeMenu-button" onPointerDown={this.pinWithView}>
{presPinWithViewIcon}
</button>
</Tooltip>,
];
return this.getElement(buttons);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 58a00bbac..a020b67cd 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -18,7 +18,7 @@ import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
-import { PinViewProps, PresBox } from '../../nodes/trails/PresBox';
+import { PresBox } from '../../nodes/trails/PresBox';
import { VideoBox } from '../../nodes/VideoBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
@@ -61,6 +61,11 @@ export interface MarqueeViewBounds {
@observer
export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
+ public static CurViewBounds(pinDoc: Doc, panelWidth: number, panelHeight: number) {
+ const ps = NumCast(pinDoc._viewScale, 1);
+ return { left: NumCast(pinDoc._panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
+ }
+
private _commandExecuted = false;
@observable _lastX: number = 0;
@observable _lastY: number = 0;
@@ -425,13 +430,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
@undoBatch
@action
pinWithView = async () => {
- const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
const doc = this.props.Document;
- const viewOptions: PinViewProps = {
- bounds: this.Bounds,
- scale: scale,
- };
- TabDocView.PinDoc(doc, { pinWithView: viewOptions });
+ TabDocView.PinDoc(doc, { pinViewport: this.Bounds });
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
};
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 0d7d67dd8..92f6bbb64 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -198,13 +198,7 @@ export class CollectionLinearView extends CollectionSubView() {
<div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : 'transparent' }}>
<div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu}>
{!expandable ? null : (
- <Tooltip
- title={
- <>
- <div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}</div>
- </>
- }
- placement="top">
+ <Tooltip title={<div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}</div>} placement="top">
{menuOpener}
</Tooltip>
)}
@@ -213,7 +207,17 @@ export class CollectionLinearView extends CollectionSubView() {
type="checkbox"
checked={BoolCast(this.props.Document.linearViewIsExpanded)}
ref={this.addMenuToggle}
- onChange={action(() => (this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked))}
+ onChange={action(e => {
+ ScriptCast(this.Document.onClick)?.script.run({
+ this: this.layoutDoc,
+ self: this.rootDoc,
+ _readOnly_: false,
+ scriptContext: this.props.scriptContext,
+ thisContainer: this.props.ContainingCollectionDoc,
+ documentView: this.props.docViewPath().lastElement(),
+ });
+ this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked;
+ })}
/>
<div