aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2022-10-12 13:21:07 -0400
committermehekj <mehek.jethani@gmail.com>2022-10-12 13:21:07 -0400
commit0b3a83acd4f75b7f6ff4b9bb7daf4377dede51a1 (patch)
tree438789f7e7f50e5eb9829e1f301b4d043d8d4906 /src/client/views/collections
parent69ca9baca6ff1da272a5191187542351bd242ccc (diff)
parenteb5f75785fd28acb50f1b30434e89223fff00185 (diff)
Merge branch 'master' into schema-mehek
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.tsx21
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.scss41
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx145
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx88
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewDivider.tsx1
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss5
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx35
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx16
-rw-r--r--src/client/views/collections/CollectionSubView.tsx27
-rw-r--r--src/client/views/collections/CollectionTreeView.scss5
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx73
-rw-r--r--src/client/views/collections/CollectionView.tsx10
-rw-r--r--src/client/views/collections/TabDocView.tsx172
-rw-r--r--src/client/views/collections/TreeView.scss19
-rw-r--r--src/client/views/collections/TreeView.tsx15
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx144
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss20
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx16
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx20
21 files changed, 530 insertions, 397 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 0dc30e0fd..46e8494ab 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -34,7 +34,6 @@ 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';
@@ -726,18 +725,16 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
}
public static gotoKeyFrame(doc: Doc, newFrame: number) {
- if (!doc) {
- return;
- }
- const dataField = doc[Doc.LayoutFieldKey(doc)];
- const childDocs = DocListCast(dataField);
- const currentFrame = Cast(doc._currentFrame, 'number', null);
- if (currentFrame === undefined) {
- doc._currentFrame = 0;
- CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
+ if (doc) {
+ const childDocs = DocListCast(doc[Doc.LayoutFieldKey(doc)]);
+ const currentFrame = Cast(doc._currentFrame, 'number', null);
+ if (currentFrame === undefined) {
+ doc._currentFrame = 0;
+ CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
+ }
+ CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
+ doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
}
- CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
- doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
}
@undoBatch
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index fe98f307e..4d1f18e54 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -1,7 +1,7 @@
@import '../global/globalCssVariables';
.collectionNoteTakingView-DocumentButtons {
- display: flex;
+ display: none;
justify-content: space-between;
margin: auto;
}
@@ -51,6 +51,16 @@
display: flex;
}
+.collectionNoteTakingViewFieldColumn {
+ display: flex;
+ overflow: auto;
+}
+.collectionNoteTakingViewFieldColumn:hover {
+ .collectionNoteTakingView-DocumentButtons {
+ display: flex;
+ }
+}
+
// TODO:glr Turn this into a seperate class
.documentButtonMenu {
position: relative;
@@ -82,7 +92,6 @@
top: 0;
overflow-y: auto;
overflow-x: hidden;
- flex-wrap: wrap;
transition: top 0.5s;
> div {
@@ -102,6 +111,11 @@
height: auto;
}
+ .collectionNoteTakingView-columnStack {
+ height: 100%;
+ width: 100%;
+ display: inline-block;
+ }
.collectionNoteTakingView-Nodes {
width: 100%;
height: 100%;
@@ -113,6 +127,11 @@
left: 0;
width: 100%;
position: absolute;
+ margin: auto;
+ width: max-content;
+ height: max-content;
+ position: relative;
+ grid-auto-rows: 0px;
}
.collectionNoteTakingView-description {
@@ -133,6 +152,16 @@
margin-left: -5;
}
+ .collectionNoteTakingView-sectionDelete {
+ display: none;
+ position: absolute;
+ right: 0;
+ width: max-content;
+ height: max-content;
+ top: 10;
+ padding: 2;
+ }
+
// Documents in NoteTaking view
.collectionNoteTakingView-columnDoc {
display: flex;
@@ -347,14 +376,6 @@
}
}
}
-
- .collectionNoteTakingView-sectionDelete {
- position: absolute;
- right: 25px;
- top: 0;
- height: 100%;
- display: none;
- }
}
.collectionNoteTakingView-sectionHeader:hover {
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index b359ef420..b0f64ed60 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -3,7 +3,7 @@ import { CursorProperty } from 'csstype';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -11,7 +11,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
-import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager, dropActionType } from '../../util/DragManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
@@ -19,7 +19,6 @@ import { undoBatch } from '../../util/UndoManager';
import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { LightboxView } from '../LightboxView';
-import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -30,13 +29,6 @@ import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivid
import { CollectionSubView } from './CollectionSubView';
const _global = (window /* browser */ || global) /* node */ as any;
-export type collectionNoteTakingViewProps = {
- chromeHidden?: boolean;
- viewType?: CollectionViewType;
- NativeWidth?: () => number;
- NativeHeight?: () => number;
-};
-
/**
* CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1)
* add and remove columns (2) change column sizes and (3) move documents within and between columns. This
@@ -45,41 +37,42 @@ export type collectionNoteTakingViewProps = {
* the rest of Dash, so it may be worthwhile to transition the headers to simple documents.
*/
@observer
-export class CollectionNoteTakingView extends CollectionSubView<Partial<collectionNoteTakingViewProps>>() {
+export class CollectionNoteTakingView extends CollectionSubView() {
_disposers: { [key: string]: IReactionDisposer } = {};
_masonryGridRef: HTMLDivElement | null = null;
_draggerRef = React.createRef<HTMLDivElement>();
+ notetakingCategoryField = 'NotetakingCategory';
+ public DividerWidth = 16;
@observable docsDraggedRowCol: number[] = [];
@observable _cursor: CursorProperty = 'grab';
@observable _scroll = 0;
@computed get chromeHidden() {
- return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+ return BoolCast(this.layoutDoc.chromeHidden);
}
// columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns
@computed get columnHeaders() {
const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
- const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset'));
- if (needsUnsetCategory) {
- setTimeout(() => columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)));
+ const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
+ if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
+ setTimeout(() => {
+ const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
+ const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset'));
+ if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) {
+ if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1));
+ else this.dataDoc.columnHeaders = new List<SchemaHeaderField>();
+ }
+ });
}
- return columnHeaders;
- }
- // notetakingCategoryField returns the key to accessing a document's column value
- @computed get notetakingCategoryField() {
- return 'NotetakingCategory';
+ return columnHeaders ?? ([] as SchemaHeaderField[]);
}
@computed get headerMargin() {
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
}
@computed get xMargin() {
- return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
- }
- // dividerWidth returns the width of a CollectionNoteTakingViewDivider
- @computed get dividerWidth() {
- return 32;
+ return NumCast(this.layoutDoc._xMargin, 5);
}
@computed get yMargin() {
- return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
+ return NumCast(this.layoutDoc._yMargin, 5);
}
@computed get gridGap() {
return NumCast(this.layoutDoc._gridGap, 10);
@@ -94,22 +87,13 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
// maxColWidth returns the maximum column width, which is slightly less than the total available space.
@computed get maxColWidth() {
- return this.props.PanelWidth() - 2 * this.xMargin;
+ return this.props.PanelWidth();
}
// availableWidth is the total amount of non-divider width. Since widths are stored relatively,
// we use availableWidth to convert from a percentage to a pixel count.
@computed get availableWidth() {
- const numDividers = this.columnHeaders.length - 1;
- return this.maxColWidth - numDividers * this.dividerWidth;
- }
-
- // Documents should NOT have column category fields until entering this view, so the contructor creates the 'New Column'
- // category for the user to then edit later.
- constructor(props: any) {
- super(props);
- if (this.columnHeaders === undefined) {
- this.dataDoc.columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField('New Column', undefined, undefined, 1)]);
- }
+ const numDividers = this.numGroupColumns - 1;
+ return this.maxColWidth - numDividers * this.DividerWidth;
}
// children is passed as a prop to the NoteTakingField, which uses this function
@@ -149,7 +133,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
});
// now we add back in the docs that we're dragging
- if (rowCol.length) {
+ if (rowCol.length && columnHeaders.length > rowCol[1]) {
const offset = 0;
sections.get(columnHeaders[rowCol[1]])?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
}
@@ -209,7 +193,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
};
// 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]);
@@ -217,7 +201,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const top = found.getBoundingClientRect().top;
const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
if (Math.floor(localTop[1]) !== 0) {
- smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+ smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
}
}
const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing);
@@ -236,9 +220,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
if (this.props.childOpacity) {
return this.props.childOpacity();
}
- if (this.Document._currentFrame !== undefined) {
- return CollectionFreeFormDocumentView.getValues(doc, NumCast(this.Document._currentFrame))?.opacity;
- }
}
return this.props.styleProvider?.(doc, props, property);
};
@@ -315,12 +296,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
const existingWidth = existingHeader?.width ? existingHeader.width : 0;
- const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth - 2 * this.xMargin : this.maxColWidth - 2 * this.xMargin;
- if (d.type === DocumentType.RTF) {
- return maxWidth;
- }
- const width = d[WidthSym]();
- return width < maxWidth ? width : maxWidth;
+ const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth;
+ const width = d.fitWidth ? maxWidth : d[WidthSym]();
+ return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth);
}
// how to get the height of a document. Nothing special here.
@@ -332,8 +310,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
if (nw && nh) {
- // const colWid = this.columnWidth / this.numGroupColumns;
- // const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
const docWid = this.getDocWidth(d);
return Math.min(maxHeight, (docWid * nh) / nw);
}
@@ -392,26 +368,27 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
});
// we alter the pivot fields of the docs in case they are moved to a new column.
const colIndex = this.getColumnFromXCoord(xCoord);
- const colHeader = StrCast(this.columnHeaders[colIndex].heading);
+ const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
DragManager.docsBeingDragged.forEach(d => (d[this.notetakingCategoryField] = colHeader));
// used to notify sections to re-render
this.docsDraggedRowCol.length = 0;
- this.docsDraggedRowCol.push(dropInd, this.getColumnFromXCoord(xCoord));
+ const columnFromCoord = this.getColumnFromXCoord(xCoord);
+ columnFromCoord !== undefined && this.docsDraggedRowCol.push(dropInd, columnFromCoord);
}
};
// getColumnFromXCoord returns the column index for a given x-coordinate (currently always the client's mouse coordinate).
// This function is used to know which document a column SHOULD be in while it is being dragged.
- getColumnFromXCoord = (xCoord: number): number => {
+ getColumnFromXCoord = (xCoord: number): number | undefined => {
+ let colIndex: number | undefined = undefined;
const numColumns = this.columnHeaders.length;
const coords = [];
let colStartXCoord = 0;
for (let i = 0; i < numColumns; i++) {
coords.push(colStartXCoord);
- colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth;
+ colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth;
}
coords.push(this.PanelWidth);
- let colIndex = 0;
for (let i = 0; i < numColumns; i++) {
if (xCoord > coords[i] && xCoord < coords[i + 1]) {
colIndex = i;
@@ -423,20 +400,16 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
// getDocsFromXCoord returns the docs of a column based on the x-coordinate provided.
getDocsFromXCoord = (xCoord: number): Doc[] => {
- const colIndex = this.getColumnFromXCoord(xCoord);
- const colHeader = StrCast(this.columnHeaders[colIndex].heading);
- // const docs = this.childDocList
- const docs = this.childDocs;
const docsMatchingHeader: Doc[] = [];
- if (docs) {
- docs.map(d => {
- if (d instanceof Promise) return;
- const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
- if (sectionValue.toString() == colHeader) {
- docsMatchingHeader.push(d);
- }
- });
- }
+ const colIndex = this.getColumnFromXCoord(xCoord);
+ const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading);
+ this.childDocs?.map(d => {
+ if (d instanceof Promise) return;
+ const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset';
+ if (sectionValue.toString() == colHeader) {
+ docsMatchingHeader.push(d);
+ }
+ });
return docsMatchingHeader;
};
@@ -511,7 +484,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
this.onPointerMove(true, e.clientX, e.clientY);
docus?.map((doc: Doc) => this.addDocument(doc));
const newDoc = this.childDocs.lastElement();
- const colHeader = StrCast(this.columnHeaders[colInd].heading);
+ const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading);
newDoc[this.notetakingCategoryField] = colHeader;
const docs = this.childDocList;
if (docs && targInd !== -1) {
@@ -566,13 +539,13 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
numGroupColumns={this.numGroupColumns}
gridGap={this.gridGap}
pivotField={this.notetakingCategoryField}
- dividerWidth={this.dividerWidth}
+ dividerWidth={this.DividerWidth}
maxColWidth={this.maxColWidth}
availableWidth={this.availableWidth}
PanelWidth={this.PanelWidth}
- key={heading?.heading ?? ''}
+ key={heading?.heading ?? 'unset'}
headings={this.headings}
- heading={heading?.heading ?? ''}
+ heading={heading?.heading ?? 'unset'}
headingObject={heading}
docList={docList}
yMargin={this.yMargin}
@@ -589,15 +562,19 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@undoBatch
@action
addGroup = (value: string) => {
- for (const header of this.columnHeaders) {
- if (header.heading == value) {
- alert('You cannot use an existing column name. Please try a new column name');
- return value;
+ if (this.columnHeaders) {
+ for (const header of this.columnHeaders) {
+ if (header.heading == value) {
+ alert('You cannot use an existing column name. Please try a new column name');
+ return value;
+ }
}
}
const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
const newColWidth = 1 / (this.numGroupColumns + 1);
- return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1) ? true : false;
+ value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1);
+ this.dataDoc.columnHeaders = new List<SchemaHeaderField>(columnHeaders.map(header => header[Copy]()));
+ return true;
};
onContextMenu = (e: React.MouseEvent): void => {
@@ -642,10 +619,10 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get buttonMenu() {
const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
if (menuDoc) {
- const width: number = NumCast(menuDoc._width, 30);
- const height: number = NumCast(menuDoc._height, 30);
+ const width = NumCast(menuDoc._width, 30);
+ const height = NumCast(menuDoc._height, 30);
return (
- <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
+ <div className="buttonMenu-docBtn" style={{ width, height }}>
<DocumentView
Document={menuDoc}
DataDoc={menuDoc}
@@ -678,10 +655,10 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
@computed get nativeWidth() {
- return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+ return Doc.NativeWidth(this.layoutDoc);
}
@computed get nativeHeight() {
- return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+ return Doc.NativeHeight(this.layoutDoc);
}
@computed get scaling() {
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 4610da4e3..84d1c0205 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -22,9 +22,6 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionNoteTakingView.scss';
-const higflyout = require('@hig/flyout');
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
interface CSVFieldColumnProps {
Document: Doc;
@@ -64,19 +61,15 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
// columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- if (!this.props.columnHeaders || !this.props.headingObject) {
- return this.props.maxColWidth;
- }
- if (this.props.columnHeaders.length == 1) {
- return this.props.maxColWidth;
- }
+ if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length === 1) return '100%';
const i = this.props.columnHeaders.indexOf(this.props.headingObject);
- return this.props.columnHeaders[i].width * this.props.availableWidth;
+ return this.props.columnHeaders[i].width * 100 + '%';
}
private dropDisposer?: DragManager.DragDropDisposer;
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
+ public static ColumnMargin = 10;
@observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
@observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
_ele: HTMLElement | null = null;
@@ -148,15 +141,17 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
@undoBatch
@action
deleteColumn = () => {
- const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
- if (columnHeaders && this.props.headingObject) {
- const index = columnHeaders.indexOf(this.props.headingObject);
+ const acolumnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
+ if (acolumnHeaders && this.props.headingObject) {
+ const index = acolumnHeaders.indexOf(this.props.headingObject);
+ const columnHeaders = acolumnHeaders; // new List<SchemaHeaderField>(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly. otherwise we end up changing field values in the undo stack since they are shared by reference
const newColIndex = index > 0 ? index - 1 : 1;
const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined;
const newHeading = newColHeader ? newColHeader.heading : 'unset';
this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading));
const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0;
columnHeaders.splice(index, 1);
+ //Doc.GetProto(this.props.Document).columnHeaders = columnHeaders;
this.props.resizeColumns(false, colWidth, index);
}
};
@@ -247,9 +242,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
ref={this._headerRef}
style={{
marginTop: 2 * this.props.yMargin,
- // width: (this.props.columnWidth) /
- // ((uniqueHeadings.length) || 1)
- width: this.columnWidth - 20,
+ width: 'calc(100% - 5px)',
}}>
<div
className="collectionNoteTakingView-sectionHeader-subCont"
@@ -257,48 +250,41 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
style={{ background: evContents !== `No Value` ? this._color : 'inherit' }}>
<EditableView GetValue={() => evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
</div>
+ {(this.props.columnHeaders?.length ?? 0) > 1 && (
+ <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
+ <FontAwesomeIcon icon="trash" size="lg" />
+ </button>
+ )}
</div>
) : null;
- const templatecols = `${this.columnWidth}px `;
+ const templatecols = this.columnWidth;
const type = this.props.Document.type;
return (
<>
{headingView}
- {
- <div style={{ height: '100%' }}>
- <div
- key={`${heading}-stack`}
- className={`collectionNoteTakingView-Nodes`}
- style={{
- padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
- margin: 'auto',
- width: 'max-content', //singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
- height: 'max-content',
- position: 'relative',
- gridGap: this.props.gridGap,
- gridTemplateColumns: templatecols,
- gridAutoRows: '0px',
- }}>
- {this.props.renderChildren(this.props.docList)}
- </div>
+ <div className="collectionNoteTakingView-columnStack">
+ <div
+ key={`${heading}-stack`}
+ className={`collectionNoteTakingView-Nodes`}
+ style={{
+ padding: `${columnYMargin}px ${0}px ${this.props.yMargin}px ${0}px`,
+ gridGap: this.props.gridGap,
+ gridTemplateColumns: templatecols,
+ }}>
+ {this.props.renderChildren(this.props.docList)}
+ </div>
- {!this.props.chromeHidden && type !== DocumentType.PRES ? (
- <div className="collectionNoteTakingView-DocumentButtons" style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
- <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
- <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
- </div>
- <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
- <EditableView {...this.props.editableViewProps()} />
- </div>
- {this.props.columnHeaders?.length && this.props.columnHeaders.length > 1 && (
- <button className="collectionNoteTakingView-sectionDelete" onClick={this.deleteColumn}>
- <FontAwesomeIcon icon="trash" size="lg" />
- </button>
- )}
+ {!this.props.chromeHidden && type !== DocumentType.PRES ? (
+ <div className="collectionNoteTakingView-DocumentButtons" style={{ marginBottom: 10 }}>
+ <div key={`${heading}-add-document`} className="collectionNoteTakingView-addDocumentButton">
+ <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} textCallback={this.textCallback} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
</div>
- ) : null}
- </div>
- }
+ <div key={`${this.props.Document[Id]}-addGroup`} className="collectionNoteTakingView-addDocumentButton">
+ <EditableView {...this.props.editableViewProps()} />
+ </div>
+ </div>
+ ) : null}
+ </div>
</>
);
}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 8d659f790..a1309b11f 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -57,7 +57,6 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
width: 12,
borderRight: '4px solid #282828',
borderLeft: '4px solid #282828',
- margin: '0px 10px',
}}
/>
</div>
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index c296e1172..5a107d2ca 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -10,6 +10,7 @@
border-width: 0 2px 0 2px;
&:hover {
+ cursor: default;
.collectionStackedTimeline-hover {
display: block;
}
@@ -109,14 +110,15 @@
height: 100%;
width: 10px;
pointer-events: all;
- cursor: ew-resize;
z-index: 100;
}
.collectionStackedTimeline-resizer {
right: 0;
+ cursor: e-resize;
}
.collectionStackedTimeline-left-resizer {
left: 0;
+ cursor: w-resize;
}
}
@@ -143,5 +145,6 @@
transform: translate(0, -100%);
font-weight: bold;
+ pointer-events: none;
}
}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 2543624d3..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;
@@ -244,6 +246,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
if (!wasSelecting) {
this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
wasSelecting = true;
+ this._timelineWrapper && (this._timelineWrapper.style.cursor = 'ew-resize');
}
this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
return false;
@@ -260,6 +263,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
}
(!isClick || !wasSelecting) && (this._markerEnd = undefined);
+ this._timelineWrapper && (this._timelineWrapper.style.cursor = '');
}),
(e, doubleTap) => {
if (e.button !== 2) {
@@ -561,7 +565,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
<div
className="collectionStackedTimeline-timelineContainer"
- style={{ width: this.props.PanelWidth() }}
+ style={{ width: this.props.PanelWidth(), cursor: SnappingManager.GetIsDragging() ? 'grab' : '' }}
onWheel={e => e.stopPropagation()}
onScroll={this.setScroll}
onMouseMove={e => this.isContentActive() && this.onHover(e)}
@@ -691,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;
@@ -801,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,
@@ -817,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 71834607c..ba29b1d6f 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -80,13 +80,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
}
@computed get xMargin() {
- return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
+ return NumCast(this.layoutDoc._xMargin, Math.min(3, 0.05 * this.props.PanelWidth()));
}
@computed get yMargin() {
- return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
- } // 2 * this.gridGap)); }
+ return this.props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this.props.PanelWidth()));
+ }
+
@computed get gridGap() {
- return NumCast(this.layoutDoc._gridGap, 10);
+ return NumCast(this.layoutDoc._gridGap, 5);
}
// are we stacking or masonry?
@computed get isStackingView() {
@@ -250,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;
@@ -259,7 +260,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const top = found.getBoundingClientRect().top;
const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
if (Math.floor(localTop[1]) !== 0) {
- smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+ smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
}
}
const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
@@ -494,8 +495,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
super.onExternalDrop(e, {}, (docs: Doc[]) => {
if (targInd === -1) {
this.addDocument(docs);
- }
- else {
+ } else {
const childDocs = this.childDocList;
if (childDocs) {
childDocs.splice(targInd, 0, ...docs);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e33bb77de..30759b766 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -212,7 +212,7 @@ 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);
@@ -446,6 +446,7 @@ export function CollectionSubView<X>(moreProps?: X) {
}
this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
}
+
slowLoadDocuments = async (
files: File[] | string,
options: DocumentOptions,
@@ -456,11 +457,24 @@ export function CollectionSubView<X>(moreProps?: X) {
clientY: number,
addDocument: (doc: Doc | Doc[]) => boolean
) => {
- const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
+ // create placeholder docs
+ // inside placeholder docs have some func that
+
+ let pileUpDoc = undefined;
if (typeof files === 'string') {
- generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
+ const loading = Docs.Create.LoadingDocument(files, options);
+ generatedDocuments.push(loading);
+ Doc.addCurrentlyLoading(loading);
+ DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
- generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options)));
+ generatedDocuments.push(
+ ...files.map(file => {
+ const loading = Docs.Create.LoadingDocument(file, options);
+ Doc.addCurrentlyLoading(loading);
+ DocUtils.uploadFileToDoc(file, {}, loading);
+ return loading;
+ })
+ );
}
if (generatedDocuments.length) {
// Creating a dash document
@@ -476,7 +490,8 @@ export function CollectionSubView<X>(moreProps?: X) {
if (completed) completed(set);
else {
if (isFreeformView && generatedDocuments.length > 1) {
- addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!);
+ pileUpDoc = DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!;
+ addDocument(pileUpDoc);
} else {
generatedDocuments.forEach(addDocument);
}
@@ -488,7 +503,6 @@ export function CollectionSubView<X>(moreProps?: X) {
alert('Document upload failed - possibly an unsupported file type.');
}
}
- disposer();
};
}
@@ -501,5 +515,4 @@ import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'
import { DragManager, dropActionType } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
-import { OverlayView } from '../OverlayView';
import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index c0561e42c..1aef29b2f 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;
@@ -62,7 +61,6 @@
.editableView-container-editing {
display: block;
text-overflow: ellipsis;
- font-size: 1vw;
white-space: nowrap;
}
}
@@ -80,9 +78,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 +100,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..fe5dc17f5 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -83,7 +83,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;
@@ -111,7 +111,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);
}
@@ -298,7 +298,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 ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}>
{this.outlineMode ? this.documentTitle : this.editableTitle}
</div>
);
@@ -378,40 +382,45 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
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">
+ {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 ? { paddingLeft: 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(),
+ transform: this.outlineMode ? `scale(${this.nativeDimScaling})` : '',
+ paddingLeft: `${this.marginX()}px`,
+ width: this.outlineMode ? `calc(${100 / this.nativeDimScaling}%)` : '',
+ 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>,
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1ee77d4ce..dcaad5632 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)
@@ -116,6 +116,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 +154,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 +188,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 +205,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 73574bdb3..9938245ea 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,75 @@ 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);
- 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 = pinProps?.pinDocView && !pinProps?.pinWithView ? 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.treeViewGrowsHorizontally = true; // the document expands horizontally when displayed as a tree view header
+ pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header
+ const presArray: Doc[] = PresBox.Instance?.sortArray();
+ const size: number = PresBox.Instance?.selectedArray.size;
+ const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
+ const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+
+ if (!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,10 +381,10 @@ 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) {
- const focusSpeed = 1000;
+ if (options?.willZoom !== false && shrinkwrap && this._document) {
+ const focusSpeed = NumCast(this._document.focusSpeed, 500);
shrinkwrap();
this._document._viewTransition = `transform ${focusSpeed}ms`;
setTimeout(
@@ -398,7 +416,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..57bb5274d 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -53,13 +53,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 +115,15 @@
.treeView-header-editing,
.treeView-header {
+ display: flex; // needed for PresBox's treeView
border: transparent 1px solid;
- display: flex;
+ align-items: center;
+ width: 100%;
+ border-radius: 5px;
+
+ &:hover {
+ background-color: #bdddf5;
+ }
//align-items: center;
::-webkit-scrollbar {
@@ -140,6 +150,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..14563f990 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -8,7 +8,7 @@ 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 { Docs, DocUtils } from '../../documents/Documents';
@@ -575,7 +575,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;
@@ -692,7 +692,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 +736,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 +782,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();
@@ -953,7 +953,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}
@@ -1038,7 +1037,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 +1057,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
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 052cbd3bb..0d061a325 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,6 @@ import { PresBox } from '../../nodes/trails/PresBox';
import { VideoBox } from '../../nodes/VideoBox';
import { CreateImage } from '../../nodes/WebBoxRenderer';
import { StyleProp } from '../../StyleProvider';
-import { CollectionDockingView } from '../CollectionDockingView';
import { CollectionSubView } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeView';
import { TabDocView } from '../TabDocView';
@@ -53,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
-import e = require('connect-flash');
export type collectionFreeformViewProps = {
annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -90,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[] = [];
@@ -116,10 +116,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _marqueeRef = React.createRef<HTMLDivElement>();
@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;
}
@@ -130,7 +131,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
}
@computed get fitContentsToBox() {
- return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay;
+ return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox || this.Document.isGroup) && !this.isAnnotationOverlay;
}
@computed get contentBounds() {
const cb = Cast(this.rootDoc.contentBounds, listSpec('number'));
@@ -154,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()
@@ -189,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;
@@ -233,11 +238,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const newBoxes = newBox instanceof Doc ? [newBox] : newBox;
for (const newBox of newBoxes) {
if (newBox.activeFrame !== undefined) {
- const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field]);
- CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]);
- CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]);
+ const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]);
+ CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}-indexed`]);
+ CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]);
delete newBox.activeFrame;
- CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== 'opacity' && (newBox[field] = vals[i]));
+ CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i]));
}
}
if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
@@ -275,10 +280,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const layoutDoc = Doc.Layout(d);
if (this.Document._currentFrame !== undefined) {
CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
- const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
- vals.x = x + NumCast(vals.x) - dropPos[0];
- vals.y = y + NumCast(vals.y) - dropPos[1];
- vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
+ const pvals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); // get filled in values (uses defaults when not value is specified) for position
+ const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000), false); // get non-default values for everything else
+ vals.x = x + NumCast(pvals.x) - dropPos[0];
+ vals.y = y + NumCast(pvals.y) - dropPos[1];
CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
} else {
d.x = x + NumCast(d.x) - dropPos[0];
@@ -1100,10 +1105,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]);
@@ -1111,9 +1113,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
@@ -1132,22 +1135,22 @@ 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
- newState.initializers![this.Document[Id]] = { panX: panX, panY: panY };
+ newState.initializers![this.Document[Id]] = { panX, panY };
HistoryUtil.pushState(newState);
}
// focus on the document in the collection
const didMove = !cantTransform && !doc.z && (panX !== savedState.panX || panY !== savedState.panY || scale !== savedState.scale);
- const focusSpeed = options?.instant ? 0 : didMove ? (doc.focusSpeed !== undefined ? Number(doc.focusSpeed) : 500) : 0;
+ const focusSpeed = options?.instant ? 0 : didMove ? NumCast(doc.focusSpeed, 500) : 0;
// glr: freeform transform speed can be set by adjusting presTransition field - needs a way of knowing when presentation is not active...
if (didMove) {
scale && (this.Document[this.scaleFieldKey] = scale);
@@ -1166,8 +1169,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document._panY = restoreState.panY;
this.Document[this.scaleFieldKey] = restoreState.scale;
}
- runInAction(() => (this._viewTransition = 0));
}
+ runInAction(() => (this._viewTransition = 0));
return resetView;
};
const xf = !cantTransform
@@ -1176,7 +1179,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)))),
@@ -1199,10 +1202,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
scale: newScale,
};
}
- const pw = this.props.PanelWidth() / NumCast(this.layoutDoc._viewScale, 1);
- const ph = this.props.PanelHeight() / NumCast(this.layoutDoc._viewScale, 1);
- const cx = NumCast(this.layoutDoc._panX);
- const cy = NumCast(this.layoutDoc._panY);
+
+ const panelWidth = this.props.isAnnotationOverlay ? this.nativeWidth : this.props.PanelWidth();
+ const panelHeight = this.props.isAnnotationOverlay ? this.nativeHeight : this.props.PanelHeight();
+ const pw = panelWidth / NumCast(this.layoutDoc._viewScale, 1);
+ const ph = panelHeight / NumCast(this.layoutDoc._viewScale, 1);
+ const cx = NumCast(this.layoutDoc._panX) + (this.props.isAnnotationOverlay ? pw / 2 : 0);
+ const cy = NumCast(this.layoutDoc._panY) + (this.props.isAnnotationOverlay ? ph / 2 : 0);
const screen = { left: cx - pw / 2, right: cx + pw / 2, top: cy - ph / 2, bot: cy + ph / 2 };
if (screen.right - screen.left < bounds.right - bounds.left || screen.bot - screen.top < bounds.bot - bounds.top) {
return {
@@ -1212,8 +1218,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
}
return {
- panX: cx + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
- panY: cy + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
+ panX: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panX) : cx) + Math.min(0, bounds.left - pw / 10 - screen.left) + Math.max(0, bounds.right + pw / 10 - screen.right),
+ panY: (this.props.isAnnotationOverlay ? NumCast(this.layoutDoc._panY) : cy) + Math.min(0, bounds.top - ph / 10 - screen.top) + Math.max(0, bounds.bot + ph / 10 - screen.bot),
};
};
@@ -1424,15 +1430,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) };
}
@@ -1453,7 +1456,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
newPos.x !== lastPos.x ||
newPos.y !== lastPos.y ||
newPos.z !== lastPos.z ||
- newPos.zIndex !== lastPos.zIndex
+ newPos.zIndex !== lastPos.zIndex ||
+ newPos.transition !== lastPos.transition
) {
this._layoutPoolData.set(entry[0], newPos);
}
@@ -1521,14 +1525,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._disposers.layoutComputation = reaction(
- () => this.doLayoutComputation,
- elements => (this._layoutElements = elements || []),
- { fireImmediately: true, name: 'doLayout' }
- );
this._marqueeRef.current?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any);
@@ -1545,10 +1545,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
const pbounds = {
- x: (cbounds.x - p[0]) * this.zoomScaling() + c[0],
- y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
- r: (cbounds.r - p[0]) * this.zoomScaling() + c[0],
- b: (cbounds.b - p[1]) * this.zoomScaling() + c[1],
+ x: cbounds.x - p[0] + c[0],
+ y: cbounds.y - p[1] + c[1],
+ r: cbounds.r - p[0] + c[0],
+ b: cbounds.b - p[1] + c[1],
};
this.layoutDoc._width = pbounds.r - pbounds.x;
this.layoutDoc._height = pbounds.b - pbounds.y;
@@ -1560,6 +1560,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
},
{ fireImmediately: true }
);
+
+ this._disposers.layoutComputation = reaction(
+ () => this.doLayoutComputation,
+ elements => (this._layoutElements = elements || []),
+ { fireImmediately: true, name: 'doLayout' }
+ );
})
);
}
@@ -1841,7 +1847,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@computed get placeholder() {
return (
<div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
- <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
+ <span className="collectionfreeformview-placeholderSpan">{this.props.Document.annotationOn ? '' : this.props.Document.title?.toString()}</span>
</div>
);
}
@@ -1855,6 +1861,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this.props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
+ slowLoadDocuments={this.slowLoadDocuments}
trySelectCluster={this.trySelectCluster}
activeDocuments={this.getActiveDocuments}
selectDocuments={this.selectDocuments}
@@ -1880,6 +1887,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
</div>
) : null}
<CollectionFreeFormViewPannableContents
+ brushView={this._brushedView}
isAnnotationOverlay={this.isAnnotationOverlay}
isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
transform={this.contentTransform}
@@ -1915,9 +1923,25 @@ 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'}
@@ -2003,6 +2027,7 @@ interface CollectionFreeFormViewPannableContentsProps {
presPinView?: boolean;
isAnnotationOverlay: boolean | undefined;
isAnnotationOverlayScrollable: boolean | undefined;
+ brushView: { panX: number; panY: number; width: number; height: number; opacity: number };
}
@observer
@@ -2122,6 +2147,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}
@@ -2199,7 +2239,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;
},
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 41e4d6b6a..e0f5cbe5b 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -1,16 +1,14 @@
-
.marqueeView {
position: inherit;
- top:0;
- left:0;
- width:100%;
- height:100%;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
overflow: hidden;
border-radius: inherit;
user-select: none;
}
-
.marqueeView:focus-within {
overflow: hidden;
}
@@ -22,13 +20,13 @@
border-color: black;
pointer-events: none;
.marquee-legend {
- bottom:-18px;
- left:0;
+ bottom: -18px;
+ left: 0;
position: absolute;
font-size: 9;
- white-space:nowrap;
+ white-space: nowrap;
}
.marquee-legend::after {
- content: "Press <space> for lasso"
+ content: 'Press <space> for lasso';
}
-} \ 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 65a11cbcb..584c9690f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -40,6 +40,16 @@ interface MarqueeViewProps {
nudge?: (x: number, y: number, nudgeTime?: number) => boolean;
ungroup?: () => void;
setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
+ slowLoadDocuments: (
+ files: File[] | string,
+ options: DocumentOptions,
+ generatedDocuments: Doc[],
+ text: string,
+ completed: ((doc: Doc[]) => void) | undefined,
+ clientX: number,
+ clientY: number,
+ addDocument: (doc: Doc | Doc[]) => boolean
+ ) => Promise<void>;
}
export interface MarqueeViewBounds {
@@ -330,7 +340,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
this._downY = y;
const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
if ([AclAdmin, AclEdit, AclAugment].includes(effectiveAcl)) {
- PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge);
+ PreviewCursor.Show(x, y, this.onKeyPress, this.props.addLiveTextDocument, this.props.getTransform, this.props.addDocument, this.props.nudge, this.props.slowLoadDocuments);
}
this.clearSelection();
}
@@ -415,13 +425,11 @@ 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, { pinWithView: viewOptions, pinDocView: true });
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