aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2022-08-24 20:23:17 -0400
committermehekj <mehek.jethani@gmail.com>2022-08-24 20:23:17 -0400
commit69ca9baca6ff1da272a5191187542351bd242ccc (patch)
tree33bca594a876c2f9902841df4ef799f9b35e37f8 /src/client/views/collections
parent31ca85d11a5735f6e74e4a887f99a9c12c35d0a7 (diff)
parentb8ebc0f758d2240af29640d6f8dc490705b42bb9 (diff)
Merge branch 'master' into schema-mehek
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionMenu.tsx44
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx220
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx53
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewDivider.tsx41
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx17
-rw-r--r--src/client/views/collections/TabDocView.tsx39
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx55
9 files changed, 209 insertions, 272 deletions
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index eb55650e4..0dc30e0fd 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -587,49 +587,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
}
@undoBatch
- @action
- pinWithView = (targetDoc: Opt<Doc>) => {
- if (targetDoc) {
- TabDocView.PinDoc(targetDoc);
- const presArray: Doc[] = PresBox.Instance?.sortArray();
- const size: number = PresBox.Instance?._selectedArray.size;
- const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
- const activeDoc = presSelected ? PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.indexOf(presSelected) + 1] : PresBox.Instance?.childDocs[PresBox.Instance?.childDocs.length - 1];
- if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking || targetDoc._viewType === CollectionViewType.NoteTaking) {
- const scroll = targetDoc._scrollTop;
- activeDoc.presPinView = true;
- activeDoc.presPinViewScroll = scroll;
- } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.MAP) {
- const x = targetDoc._panX;
- const y = targetDoc._panY;
- const scale = targetDoc._viewScale;
- activeDoc.presPinView = true;
- activeDoc.presPinViewX = x;
- activeDoc.presPinViewY = y;
- activeDoc.presPinViewScale = scale;
- } else if (targetDoc.type === DocumentType.VID) {
- activeDoc.presPinView = true;
- } else if (targetDoc.type === DocumentType.COMPARISON) {
- const width = targetDoc._clipWidth;
- activeDoc.presPinClipWidth = width;
- activeDoc.presPinView = true;
- }
- }
- };
-
- @computed
- get pinWithViewButton() {
- const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />;
- return !this.selectedDoc ? null : (
- <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top">
- <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}>
- {presPinWithViewIcon}
- </button>
- </Tooltip>
- );
- }
-
- @undoBatch
onAlias = () => {
if (this.selectedDoc && this.selectedDocumentView) {
// const copy = Doc.MakeCopy(this.selectedDocumentView.props.Document, true);
@@ -722,7 +679,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
{this.aliasButton}
{/* {this.pinButton} */}
{this.toggleOverlayButton}
- {this.pinWithViewButton}
<div className="collectionMenu-divider" key="divider2"></div>
{this.subChrome}
<div className="collectionMenu-divider" key="divider3"></div>
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 5c8b10ae1..b359ef420 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,6 +1,6 @@
import React = require('react');
import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+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';
@@ -37,84 +37,82 @@ export type collectionNoteTakingViewProps = {
NativeHeight?: () => number;
};
-//TODO: somehow need to update the mapping and then have everything else rerender. Maybe with a refresh boolean like
-// in Hypermedia?
-
+/**
+ * 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
+ * view is reminiscent of Kanban-style web apps like Trello, or the 'Board' view in Notion. Each column is
+ * headed by a SchemaHeaderField followed by the column's documents. SchemaHeaderFields are NOT present in
+ * the rest of Dash, so it may be worthwhile to transition the headers to simple documents.
+ */
@observer
export class CollectionNoteTakingView extends CollectionSubView<Partial<collectionNoteTakingViewProps>>() {
_disposers: { [key: string]: IReactionDisposer } = {};
_masonryGridRef: HTMLDivElement | null = null;
- _draggerRef = React.createRef<HTMLDivElement>(); // change to relative widths for deleting. change storage from columnStartXCoords to columnHeaders (schemaHeaderFields has a widgth alrady)
- @observable columnStartXCoords: number[] = []; // columnHeaders -- SchemaHeaderField -- widht
+ _draggerRef = React.createRef<HTMLDivElement>();
@observable docsDraggedRowCol: number[] = [];
@observable _cursor: CursorProperty = 'grab';
- @observable _scroll = 0; // used to force the document decoration to update when scrolling
+ @observable _scroll = 0;
@computed get chromeHidden() {
return this.props.chromeHidden || 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 = Array.from(Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null));
+ const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null);
const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset'));
-
- // @#Oberable draggedColIndex = ...
- //@observable cloneDivXYcoords
- // @observable clonedDiv...
-
- // render() {
- // { this.clonedDiv ? <div clone styule={{transform: clonedDivXYCoords}} : null}
- // }
-
- // in NoteatakinView Column code, add a poinerDown handler that calls setupMoveUpEvents() which will clone the column div
- // and re-render it under the cursor during move events.
- // that move move event will update 2 observales -- the draggedColIndex up above, and the location of the clonedDiv so that the render in this view will know where to render the cloned div
- // add observable variable that tells drag column to rnder in a different location than where the schemaHeaderFiel sa y ot.
- // if (col 1 is where col 3) {
- // return 3 2 1 4 56
- // }
if (needsUnsetCategory) {
- columnHeaders.push(new SchemaHeaderField('unset'));
+ setTimeout(() => columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)));
}
return columnHeaders;
}
+ // notetakingCategoryField returns the key to accessing a document's column value
@computed get notetakingCategoryField() {
return 'NotetakingCategory';
}
- @computed get filteredChildren() {
- return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
- }
@computed get headerMargin() {
return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
}
@computed get xMargin() {
return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
}
+ // dividerWidth returns the width of a CollectionNoteTakingViewDivider
+ @computed get dividerWidth() {
+ return 32;
+ }
@computed get yMargin() {
return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
- } // 2 * this.gridGap)); }
+ }
@computed get gridGap() {
return NumCast(this.layoutDoc._gridGap, 10);
}
+ // numGroupColumns returns the number of columns
@computed get numGroupColumns() {
return this.columnHeaders.length;
}
+ // PanelWidth returns the size of the total available space the view occupies
@computed get PanelWidth() {
return this.props.PanelWidth();
}
- @computed get maxColWdith() {
+ // 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;
}
+ // 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;
+ }
- // If the user has not yet created any docs (in another view), this will create a single column. Otherwise,
- // it will adjust according to the
+ // 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')]);
- // add all of the docs that have not been added to a column to this new column
+ this.dataDoc.columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField('New Column', undefined, undefined, 1)]);
}
}
- // passed as a prop to the NoteTakingField, which uses this function
+ // children is passed as a prop to the NoteTakingField, which uses this function
// to render the docs you see within an individual column.
children = (docs: Doc[]) => {
TraceMobx();
@@ -130,39 +128,30 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
});
};
+ // Sections is one of the more important functions in this file, rendering the the documents
+ // for the UI. It properly renders documents being dragged between columns.
// [CAVEATS] (1) keep track of the offsetting
// (2) documentView gets unmounted as you remove it from the list
@computed get Sections() {
TraceMobx();
const columnHeaders = this.columnHeaders;
- let docs = this.childDocs;
+ // filter out the currently dragged docs from the child docs, since we will insert them later
+ const docs = this.childDocs.filter(d => !DragManager.docsBeingDragged.includes(d));
const sections = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
const rowCol = this.docsDraggedRowCol;
-
- // filter out the currently dragged docs from the child docs, since we will insert them later
- if (rowCol.length && DragManager.docsBeingDragged.length) {
- const docIdsToRemove = new Set();
- DragManager.docsBeingDragged.forEach(d => docIdsToRemove.add(d[Id]));
- docs = docs.filter(d => !docIdsToRemove.has(d[Id]));
- }
-
// this will sort the docs into the correct columns (minus the ones you're currently dragging)
docs.map(d => {
const sectionValue = (d[this.notetakingCategoryField] as object) ?? `unset`;
-
// look for if header exists already
const existingHeader = columnHeaders.find(sh => sh.heading === sectionValue.toString());
if (existingHeader) {
sections.get(existingHeader)!.push(d);
}
});
-
// now we add back in the docs that we're dragging
- if (rowCol.length && DragManager.docsBeingDragged.length) {
- const colHeader = columnHeaders[rowCol[1]];
- // TODO: get the actual offset that occurs if the docs were in that column
+ if (rowCol.length) {
const offset = 0;
- sections.get(colHeader)?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
+ sections.get(columnHeaders[rowCol[1]])?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged);
}
return sections;
}
@@ -173,6 +162,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
100
);
};
+
componentDidMount() {
super.componentDidMount?.();
document.addEventListener('pointerup', this.removeDocDragHighlight, true);
@@ -180,11 +170,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
() => this.layoutDoc._autoHeight,
autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), this.headerMargin + Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', ''))))))
);
- this._disposers.headers = reaction(
- () => this.columnHeaders.slice(),
- headers => this.resizeColumns(headers.length),
- { fireImmediately: true }
- );
}
componentWillUnmount() {
@@ -206,6 +191,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get onChildClickHandler() {
return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
}
+
@computed get onChildDoubleClickHandler() {
return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
}
@@ -225,7 +211,6 @@ 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) => {
Doc.BrushDoc(doc);
-
let focusSpeed = 0;
const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
if (found) {
@@ -260,7 +245,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
- // rules for displaying the documents
+ // getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView)
getDisplayDoc(doc: Doc, width: () => number) {
const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
const height = () => this.getDocHeight(doc);
@@ -316,7 +301,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
);
}
- // This is used to get the coordinates of a document when we go from a view like freeform to columns
+ // getDocTransform is used to get the coordinates of a document when we go from a view like freeform to columns
getDocTransform(doc: Doc, dref?: DocumentView) {
const y = this._scroll; // required for document decorations to update when the text box container is scrolled
const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
@@ -329,9 +314,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
getDocWidth(d: Doc) {
const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field);
const existingHeader = this.columnHeaders.find(sh => sh.heading === heading);
- const index = existingHeader ? this.columnHeaders.indexOf(existingHeader) : 0;
- const endColValue = index === this.columnHeaders.length - 1 || index > this.columnStartXCoords.length - 1 ? this.PanelWidth : this.columnStartXCoords[index + 1];
- const maxWidth = index > this.columnStartXCoords.length - 1 ? this.PanelWidth : endColValue - this.columnStartXCoords[index] - 3 * this.xMargin;
+ 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;
}
@@ -358,28 +342,36 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
return Math.min(childHeight, maxHeight, panelHeight);
}
- // called when a column is either added or deleted. This function creates n evenly spaced columns
+ // resizeColumns is called whenever a user adds or removes a column. When removing,
+ // this function renormalizes the column widths to fill the newly available space
+ // in the panel. When adding, this function renormalizes the existing columns to take up
+ // (n - 1)/n space, since the new column will be allocated 1/n of the total space.
+ // Column widths are relative (portion of available space) and stored in the 'width'
+ // field of SchemaHeaderFields.
+ //
+ // Removing example: column widths are [0.5, 0.30, 0.20] --> user deletes the final column --> column widths are [0.625, 0.375].
+ // Adding example: column widths are [0.6, 0.4] --> user adds column at end --> column widths are [0.4, 0.267, 0.33]
@action
- resizeColumns = (n: number) => {
- const totalWidth = this.PanelWidth;
- const dividerWidth = 32;
- const totaldividerWidth = (n - 1) * dividerWidth;
- const colWidth = (totalWidth - totaldividerWidth) / n;
- const newColXCoords: number[] = [];
- let colStart = 0;
- for (let i = 0; i < n; i++) {
- newColXCoords.push(colStart);
- colStart += colWidth + dividerWidth;
+ resizeColumns = (isAdd: boolean, colWidth: number, colIndex: number) => {
+ const n = this.columnHeaders.length;
+ if (n == 1) {
+ this.columnHeaders[0].setWidth(1);
+ return true;
}
- this.columnStartXCoords = newColXCoords;
+ const scaleFactor = isAdd ? 1 - colWidth : 1 / (1 - colWidth);
+ this.columnHeaders.forEach((h, i) => {
+ if (!(isAdd && i == colIndex)) {
+ h.width < 0 ? h.setWidth(1 / n) : h.setWidth(h.width * scaleFactor);
+ }
+ });
+ return true;
};
- // This function is used to preview where a document will drop in a column once a drag is complete.
+ // onPointerMove is used to preview where a document will drop in a column once a drag is complete.
@action
onPointerMove = (force: boolean, ex: number, ey: number) => {
if (this.childDocList && (this.childDocList.includes(DragManager.DocDragData?.draggedDocuments.lastElement()!) || force || this.isContentActive())) {
// get the current docs for the column based on the mouse's x coordinate
- // will use again later, which is why we're saving as local
const xCoord = this.props.ScreenToLocalTransform().transformPoint(ex, ey)[0] - 2 * this.gridGap;
const colDocs = this.getDocsFromXCoord(xCoord);
// get the index for where you need to insert the doc you are currently dragging
@@ -408,10 +400,16 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
};
- // returns the column index for a given x-coordinate
+ // 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 => {
const numColumns = this.columnHeaders.length;
- const coords = this.columnStartXCoords.slice();
+ const coords = [];
+ let colStartXCoord = 0;
+ for (let i = 0; i < numColumns; i++) {
+ coords.push(colStartXCoord);
+ colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth;
+ }
coords.push(this.PanelWidth);
let colIndex = 0;
for (let i = 0; i < numColumns; i++) {
@@ -423,7 +421,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
return colIndex;
};
- // returns the docs of a column based on the x-coordinate provided.
+ // 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);
@@ -455,6 +453,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
};
+ // onInternalDrop is used when dragging and dropping a document within the view, such as dragging
+ // a document to a new column or changing its order within the column.
@undoBatch
@action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -464,14 +464,11 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const rowCol = this.docsDraggedRowCol;
const droppedDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= this.childDocs.length); // if the drop operation adds something to the end of the list, then use that as the new document (may be different than what was dropped e.g., in the case of a button which is dropped but which creates say, a note).
const newDocs = droppedDocs.length ? droppedDocs : de.complete.docDragData.droppedDocuments;
-
- // const docs = this.childDocs
const docs = this.childDocList;
if (docs && newDocs.length) {
// remove the dragged documents from the childDocList
newDocs.filter(d => docs.indexOf(d) !== -1).forEach(d => docs.splice(docs.indexOf(d), 1));
// if the doc starts a columnm (or the drop index is undefined), we can just push it to the front. Otherwise we need to add it to the column properly
- //TODO: you need to update childDocList instead. It seems that childDocs is a copy of the actual array we want to modify
if (rowCol[0] <= 0) {
docs.splice(0, 0, ...newDocs);
} else {
@@ -482,8 +479,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
}
}
- } // it seems like we're creating a link here. Weird. I didn't know that you could establish links by dragging
- else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+ } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
this.props.addDocument?.(source);
de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
@@ -502,7 +498,8 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
return true;
}
- // when dropping outside of the current noteTaking context (like a new tab, freeform view, etc...)
+ // onExternalDrop is used when dragging a document out from a CollectionNoteTakingView
+ // to another tab/view/collection
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
const targInd = this.docsDraggedRowCol?.[0] || 0;
const colInd = this.docsDraggedRowCol?.[1] || 0;
@@ -530,12 +527,14 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
headings = () => Array.from(this.Sections);
refList: any[] = [];
+
editableViewProps = () => ({
GetValue: () => '',
SetValue: this.addGroup,
contents: '+ New Column',
});
+ // sectionNoteTaking returns a CollectionNoteTakingViewColumn (which is an individual column)
sectionNoteTaking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
const type = 'number';
return (
@@ -558,8 +557,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
}}
addDocument={this.addDocument}
- // docsByColumnHeader={this._docsByColumnHeader}
- // setDocsForColHeader={this.setDocsForColHeader}
chromeHidden={this.chromeHidden}
columnHeaders={this.columnHeaders}
Document={this.props.Document}
@@ -569,8 +566,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
numGroupColumns={this.numGroupColumns}
gridGap={this.gridGap}
pivotField={this.notetakingCategoryField}
- columnStartXCoords={this.columnStartXCoords}
- maxColWidth={this.maxColWdith}
+ dividerWidth={this.dividerWidth}
+ maxColWidth={this.maxColWidth}
+ availableWidth={this.availableWidth}
PanelWidth={this.PanelWidth}
key={heading?.heading ?? ''}
headings={this.headings}
@@ -586,19 +584,20 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
);
};
- // called when adding a new columnHeader
+ // addGroup is called when adding a new columnHeader, adding a SchemaHeaderField to our list of
+ // columnHeaders and resizing the existing columns to make room for our new one.
@undoBatch
@action
addGroup = (value: string) => {
+ 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);
- return value && columnHeaders?.push(new SchemaHeaderField(value)) ? true : false;
- };
-
- sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
- const descending = StrCast(this.layoutDoc._columnsSort) === 'descending';
- const firstEntry = descending ? b : a;
- const secondEntry = descending ? a : b;
- return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
+ 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;
};
onContextMenu = (e: React.MouseEvent): void => {
@@ -612,29 +611,29 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
};
- // used to reset column sizes when using the drag handlers
+ // setColumnStartXCoords is used to update column widths when using the drag handlers between columns
@action
- setColumnStartXCoords = (movementX: number, colIndex: number) => {
- const coords = [...this.columnStartXCoords];
- coords[colIndex] += movementX;
- this.columnStartXCoords = coords;
+ setColumnStartXCoords = (movementXScreen: number, colIndex: number) => {
+ const movementX = this.props.ScreenToLocalTransform().transformDirection(movementXScreen, 0)[0];
+ const leftHeader = this.columnHeaders[colIndex];
+ const rightHeader = this.columnHeaders[colIndex + 1];
+ leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
+ rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
};
+ // renderedSections returns a list of all of the JSX elements used (columns and dividers). If the view
+ // has more than one column, those columns will be separated by a CollectionNoteTakingViewDivider that
+ // allows the user to adjust the column widths.
@computed get renderedSections() {
TraceMobx();
- // let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
- // if (this.pivotField) {
- // const entries = Array.from(this.Sections.entries());
- // sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
- // }
const entries = Array.from(this.Sections.entries());
- const sections = entries; //.sort(this.sortFunc);
+ const sections = entries;
const eles: JSX.Element[] = [];
for (let i = 0; i < sections.length; i++) {
const col = this.sectionNoteTaking(sections[i][0], sections[i][1]);
eles.push(col);
if (i < sections.length - 1) {
- eles.push(<CollectionNoteTakingViewDivider key={`divider${i}`} index={i + 1} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />);
+ eles.push(<CollectionNoteTakingViewDivider key={`divider${i}`} index={i} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />);
}
}
return eles;
@@ -642,7 +641,6 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get buttonMenu() {
const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
- // TODO:glr Allow support for multiple buttons
if (menuDoc) {
const width: number = NumCast(menuDoc._width, 30);
const height: number = NumCast(menuDoc._height, 30);
@@ -693,7 +691,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get backgroundEvents() {
return SnappingManager.GetIsDragging();
}
+
observer: any;
+
render() {
TraceMobx();
const buttonMenu = this.rootDoc.buttonMenu;
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 624beca96..4610da4e3 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -5,11 +5,12 @@ import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { ScriptField } from '../../../fields/ScriptField';
+import { Cast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from '../../../Utils';
+import { returnEmptyString } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
@@ -21,13 +22,10 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { EditableView } from '../EditableView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionNoteTakingView.scss';
-import { listSpec } from '../../../fields/Schema';
-import { Cast } from '../../../fields/Types';
const higflyout = require('@hig/flyout');
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-// So this is how we are storing a column
interface CSVFieldColumnProps {
Document: Doc;
DataDoc: Opt<Doc>;
@@ -38,7 +36,6 @@ interface CSVFieldColumnProps {
columnHeaders: SchemaHeaderField[] | undefined;
headingObject: SchemaHeaderField | undefined;
yMargin: number;
- // columnWidth: number;
numGroupColumns: number;
gridGap: number;
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
@@ -49,32 +46,32 @@ interface CSVFieldColumnProps {
screenToLocalTransform: () => Transform;
observeHeight: (myref: any) => void;
unobserveHeight: (myref: any) => void;
- //setDraggedCol:(clonedDiv:any, header:SchemaHeaderField, xycoors: )
editableViewProps: () => any;
- resizeColumns: (n: number) => void;
- columnStartXCoords: number[];
+ resizeColumns: (isAdd: boolean, colWidth: number, colIndex: number) => boolean;
PanelWidth: number;
maxColWidth: number;
- // docsByColumnHeader: Map<string, Doc[]>
- // setDocsForColHeader: (key: string, docs: Doc[]) => void
+ dividerWidth: number;
+ availableWidth: number;
}
+/**
+ * CollectionNoteTakingViewColumn represents an individual column rendered in CollectionNoteTakingView. The
+ * majority of functions here are for rendering styles.
+ */
@observer
export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColumnProps> {
@observable private _background = 'inherit';
+ // columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- // base cases
- if (!this.props.columnHeaders || !this.props.headingObject || this.props.columnHeaders.length == 1) {
+ if (!this.props.columnHeaders || !this.props.headingObject) {
return this.props.maxColWidth;
}
- const i = this.props.columnHeaders.indexOf(this.props.headingObject);
- if (i < 0 || i > this.props.columnStartXCoords.length - 1) {
+ if (this.props.columnHeaders.length == 1) {
return this.props.maxColWidth;
}
- const endColValue = i == this.props.numGroupColumns - 1 ? this.props.PanelWidth : this.props.columnStartXCoords[i + 1];
- // TODO make the math work here. 35 is half of 70, which is the current width of the divider
- return endColValue - this.props.columnStartXCoords[i] - 30;
+ const i = this.props.columnHeaders.indexOf(this.props.headingObject);
+ return this.props.columnHeaders[i].width * this.props.availableWidth;
}
private dropDisposer?: DragManager.DragDropDisposer;
@@ -84,8 +81,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
@observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb';
_ele: HTMLElement | null = null;
- // This is likely similar to what we will be doing. Why do we need to make these refs?
- // is that the only way to have drop targets?
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
if (ele) {
@@ -134,6 +129,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
@action pointerLeave = () => (this._background = 'inherit');
textCallback = (char: string) => this.addNewTextDoc('-typed text-', false, true);
+ // addNewTextDoc is called when a user starts typing in a column to create a new node
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
@@ -146,14 +142,22 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
return this.props.addDocument?.(newDoc) || false;
};
+ // deleteColumn is called when a user deletes a column using the 'trash' icon in the button area.
+ // If the user deletes the first column, the documents get moved to the second column. Otherwise,
+ // all docs are added to the column directly to the left.
@undoBatch
@action
deleteColumn = () => {
const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null);
if (columnHeaders && this.props.headingObject) {
const index = columnHeaders.indexOf(this.props.headingObject);
- this.props.docList.forEach(d => (d[this.props.pivotField] = 'unset'));
+ 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);
+ this.props.resizeColumns(false, colWidth, index);
}
};
@@ -255,7 +259,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
</div>
</div>
) : null;
- // const templatecols = `${this.props.columnWidth / this.props.numGroupColumns}px `;
const templatecols = `${this.columnWidth}px `;
const type = this.props.Document.type;
return (
@@ -280,10 +283,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
</div>
{!this.props.chromeHidden && type !== DocumentType.PRES ? (
- <div
- className="collectionNoteTakingView-DocumentButtons"
- // style={{ width: this.props.columnWidth / this.props.numGroupColumns, marginBottom: 10 }}>
- style={{ width: this.columnWidth - 20, marginBottom: 10 }}>
+ <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>
@@ -311,7 +311,6 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
className={'collectionNoteTakingViewFieldColumn' + (SnappingManager.GetIsDragging() ? 'Dragging' : '')}
key={heading}
style={{
- //TODO: change this so that it's based on the column width
width: this.columnWidth,
background: this._background,
}}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index 7d31b3193..8d659f790 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,5 +1,7 @@
import { action, observable } from 'mobx';
import * as React from 'react';
+import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
+import { UndoManager } from '../../util/UndoManager';
interface DividerProps {
index: number;
@@ -7,34 +9,35 @@ interface DividerProps {
setColumnStartXCoords: (movementX: number, colIndex: number) => void;
}
+/**
+ * CollectionNoteTakingViewDivider are dividers between CollectionNoteTakingViewColumns,
+ * which only appear when there is more than 1 column in CollectionNoteTakingView. Dividers
+ * are two simple vertical lines that allow the user to alter the widths of CollectionNoteTakingViewColumns.
+ */
export class CollectionNoteTakingViewDivider extends React.Component<DividerProps> {
@observable private isHoverActive = false;
@observable private isResizingActive = false;
@action
private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
- e.stopPropagation();
- e.preventDefault();
- window.removeEventListener('pointermove', this.onPointerMove);
- window.removeEventListener('pointerup', this.onPointerUp);
- window.addEventListener('pointermove', this.onPointerMove);
- window.addEventListener('pointerup', this.onPointerUp);
+ const batch = UndoManager.StartBatch('resizing');
+ setupMoveUpEvents(
+ this,
+ e,
+ (e, down, delta) => {
+ this.props.setColumnStartXCoords(delta[0], this.props.index);
+ return false;
+ },
+ action(() => {
+ this.isResizingActive = false;
+ this.isHoverActive = false;
+ batch.end();
+ }),
+ emptyFunction
+ );
this.isResizingActive = true;
};
- @action
- private onPointerUp = () => {
- this.isResizingActive = false;
- this.isHoverActive = false;
- window.removeEventListener('pointermove', this.onPointerMove);
- window.removeEventListener('pointerup', this.onPointerUp);
- };
-
- @action
- onPointerMove = ({ movementX }: PointerEvent) => {
- this.props.setColumnStartXCoords(movementX, this.props.index);
- };
-
render() {
return (
<div
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index d4efef47a..71834607c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -455,8 +455,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
docs.splice(insertInd - offset, 0, ...newDocs);
}
- // reset drag manager docs, because we just dropped
- DragManager.docsBeingDragged.length = 0;
}
} else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
@@ -493,13 +491,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
targInd = i;
}
});
- super.onExternalDrop(e, {}, () => {
- if (targInd !== -1) {
- const newDoc = this.childDocs[this.childDocs.length - 1];
- const docs = this.childDocList;
- if (docs) {
- docs.splice(docs.length - 1, 1);
- docs.splice(targInd, 0, newDoc);
+ super.onExternalDrop(e, {}, (docs: Doc[]) => {
+ if (targInd === -1) {
+ this.addDocument(docs);
+ }
+ else {
+ const childDocs = this.childDocList;
+ if (childDocs) {
+ childDocs.splice(targInd, 0, ...docs);
}
}
});
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e147f34d2..73574bdb3 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -242,43 +242,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
const size: number = PresBox.Instance?._selectedArray.size;
const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
- // If pinWithView option set then update scale and x / y props of slide
- if (pinProps?.pinWithView) {
- const viewProps = pinProps.pinWithView;
- pinDoc.presPinView = true;
- pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
- pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
- pinDoc.presPinViewScale = viewProps.scale;
- pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left + viewProps.bounds.width, viewProps.bounds.top + viewProps.bounds.height]);
- }
- if (pinProps?.pinDocView) {
- const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
- const pannable: boolean = (pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG;
- if (scrollable) {
- const scroll = doc._scrollTop;
- pinDoc.presPinView = true;
- pinDoc.presPinViewScroll = scroll;
- } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
- pinDoc.presPinView = true;
- pinDoc.presStartTime = doc._currentTimecode;
- pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
- } else if (pannable) {
- pinDoc.presPinView = true;
- pinDoc.presPinViewX = pinDoc._panX;
- pinDoc.presPinViewY = pinDoc._panY;
- pinDoc.presPinViewScale = pinDoc._viewScale;
- const pw = NumCast(pinProps.panelWidth);
- const ph = NumCast(pinProps.panelHeight);
- const ps = NumCast(pinDoc._viewScale);
- if (pw && ph && ps) {
- pinDoc.contentBounds = new List<number>([NumCast(pinDoc.panX) - pw / 2 / ps, NumCast(pinDoc.panY) - ph / 2 / ps, NumCast(pinDoc.panX) + pw / 2 / ps, NumCast(pinDoc.panY) + ph / 2 / ps]);
- }
- } else if (doc.type === DocumentType.COMPARISON) {
- const width = doc._clipWidth;
- pinDoc.presPinClipWidth = width;
- pinDoc.presPinView = true;
- }
- }
+
+ PresBox.pinDocView(pinDoc, pinProps);
pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)');
Doc.AddDocToList(curPres, 'data', pinDoc, presSelected);
if (!pinProps?.audioRange && duration !== undefined) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index ee01c341b..89cc22d07 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -34,6 +34,7 @@ export interface PoolData {
zIndex?: number;
width?: number;
height?: number;
+ backgroundColor?: string;
color?: string;
opacity?: number;
transition?: string;
@@ -45,7 +46,7 @@ export interface PoolData {
export interface ViewDefResult {
ele: JSX.Element;
bounds?: ViewDefBounds;
- inkMask?: boolean;
+ inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transprent (hidden), >0 = mask layer and not hidden
}
function toLabel(target: FieldResult<Field>) {
if (typeof target === 'number' || Number(target)) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 010132aa5..d80fcdfc3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -20,15 +20,20 @@
pointer-events: none;
}
+.collectionfreeformview-mask-empty,
.collectionfreeformview-mask {
- mix-blend-mode: multiply;
z-index: 5000;
width: $INK_MASK_SIZE;
height: $INK_MASK_SIZE;
transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF);
- background-color: rgba(0, 0, 0, 0.7);
pointer-events: none;
position: absolute;
+ background-color: transparent;
+ transition: background-color 1s ease 0s;
+}
+.collectionfreeformview-mask {
+ mix-blend-mode: multiply;
+ background-color: rgba(0, 0, 0, 0.7);
}
.collectionfreeformview-viewdef {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 0fd326091..052cbd3bb 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -115,13 +115,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeRef = React.createRef<HTMLDivElement>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
- @observable _keyframeEditing = false;
@observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
@computed get views() {
- const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele);
- const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele);
- if (viewsMask.length) renderableEles.push(<div className="collectionfreeformview-mask">{viewsMask}</div>);
+ 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);
+ if (viewsMask.length) renderableEles.push(<div className={`collectionfreeformview-mask${this._layoutElements.some(ele => (ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}</div>);
return renderableEles;
}
@computed get fitToContentVals() {
@@ -185,8 +184,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.Document.lastFrame = Math.max(NumCast(this.Document._currentFrame), NumCast(this.Document.lastFrame));
}
};
- @action setKeyFrameEditing = (set: boolean) => (this._keyframeEditing = set);
- getKeyFrameEditing = () => this._keyframeEditing;
onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick);
onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
@@ -251,7 +248,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
isCurrent(doc: Doc) {
- if (doc.isInkMask && doc.opacity === 0) return false; // bcz: hacky --- allows inkMasks to be "turned off" in a presentation without removing them from the collection. otherwise, they still render a gray background.. need to come back to this and fix.
const dispTime = NumCast(doc._timecodeToShow, -1);
const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
const curTime = NumCast(this.Document._currentTimecode, -1);
@@ -273,15 +269,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
.sort((doc1, doc2) => NumCast(doc1.zIndex) - NumCast(doc2.zIndex));
zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1));
const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000));
- const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)];
+ const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)];
for (let i = 0; i < docDragData.droppedDocuments.length; i++) {
const d = docDragData.droppedDocuments[i];
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 + (vals.x || 0) - dropPos[0];
- vals.y = y + (vals.y || 0) - dropPos[1];
+ vals.x = x + NumCast(vals.x) - dropPos[0];
+ vals.y = y + NumCast(vals.y) - dropPos[1];
vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
} else {
@@ -1318,22 +1314,23 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
getCalculatedPositions(params: { pair: { layout: Doc; data?: Doc }; index: number; collection: Doc }): PoolData {
- const layoutDoc = Doc.Layout(params.pair.layout);
- const { z, color, zIndex } = params.pair.layout;
- const { x, y, opacity } =
- this.Document._currentFrame === undefined
- ? { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.childOpacity ? this.props.childOpacity() : this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) }
- : CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
+ const curFrame = Cast(this.Document._currentFrame, 'number');
+ const childDoc = params.pair.layout;
+ const childDocLayout = Doc.Layout(childDoc);
+ const { z, zIndex } = childDoc;
+ const { backgroundColor, color } = curFrame === undefined ? { backgroundColor: undefined, color: undefined } : CollectionFreeFormDocumentView.getStringValues(childDoc, curFrame);
+ const { x, y, opacity } = curFrame === undefined ? { x: childDoc.x, y: childDoc.y, opacity: this.props.childOpacity?.() } : CollectionFreeFormDocumentView.getValues(childDoc, curFrame);
return {
x: NumCast(x),
y: NumCast(y),
z: Cast(z, 'number'),
- color: StrCast(color),
+ color: Cast(color, 'string') ? StrCast(color) : this.props.styleProvider?.(childDoc, this.props, StyleProp.Color),
+ backgroundColor: Cast(backgroundColor, 'string') ? StrCast(backgroundColor) : this.props.styleProvider?.(childDoc, this.props, StyleProp.BackgroundColor),
+ opacity: Cast(opacity, 'number') ?? this.props.styleProvider?.(childDoc, this.props, StyleProp.Opacity),
zIndex: Cast(zIndex, 'number'),
- transition: StrCast(layoutDoc.dataTransition),
- opacity: this._keyframeEditing ? 1 : Cast(opacity, 'number', null),
- width: Cast(layoutDoc._width, 'number'),
- height: Cast(layoutDoc._height, 'number'),
+ width: Cast(childDocLayout._width, 'number'),
+ height: Cast(childDocLayout._height, 'number'),
+ transition: StrCast(childDocLayout.dataTransition),
pair: params.pair,
replica: '',
};
@@ -1448,7 +1445,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
for (const entry of array) {
const lastPos = this._cachedPool.get(entry[0]); // last computed pos
const newPos = entry[1];
- if (!lastPos || newPos.opacity !== lastPos.opacity || newPos.x !== lastPos.x || newPos.y !== lastPos.y || newPos.z !== lastPos.z || newPos.zIndex !== lastPos.zIndex) {
+ if (
+ !lastPos ||
+ newPos.color !== lastPos.color ||
+ newPos.backgroundColor !== lastPos.backgroundColor ||
+ newPos.opacity !== lastPos.opacity ||
+ newPos.x !== lastPos.x ||
+ newPos.y !== lastPos.y ||
+ newPos.z !== lastPos.z ||
+ newPos.zIndex !== lastPos.zIndex
+ ) {
this._layoutPoolData.set(entry[0], newPos);
}
if (!lastPos || newPos.height !== lastPos.height || newPos.width !== lastPos.width) {
@@ -1465,7 +1471,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
elements.push({
ele: this.getChildDocView(entry[1]),
bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica),
- inkMask: BoolCast(entry[1].pair.layout.isInkMask),
+ inkMask: BoolCast(entry[1].pair.layout.isInkMask) ? NumCast(entry[1].pair.layout.opacity) : -1,
})
);
@@ -2207,3 +2213,6 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
!readOnly && (SelectionManager.Views()[0].ComponentView as CollectionFreeFormView)?.changeKeyFrame(true);
});
+ScriptingGlobals.add(function pinWithView(readOnly: boolean) {
+ !readOnly && SelectionManager.Views().forEach(view => TabDocView.PinDoc(view.rootDoc, { pinDocView: true, panelWidth: view.props.PanelWidth(), panelHeight: view.props.PanelHeight() }));
+});