aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorljungster <parkerljung@gmail.com>2022-03-22 16:59:32 -0400
committerljungster <parkerljung@gmail.com>2022-03-22 16:59:32 -0400
commitf15aeed47ede0db52720be27e1418cd802eca7bf (patch)
tree48dca6632c110c044423883d4b38a07b52c0a33f
parent4fcad4eb428746ca597598732b3f2946e0eea14a (diff)
haha
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx904
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewFieldColumn.tsx409
2 files changed, 1200 insertions, 113 deletions
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 3fd63d245..0d1f55fed 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -18,47 +18,44 @@ import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
+import { EditableView } from "../EditableView";
import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
import { StyleProp } from "../StyleProvider";
-import { CollectionNoteTakingViewFieldColumn } from "./CollectionNoteTakingViewFieldColumn";
+import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
import "./CollectionStackingView.scss";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
-import internal = require("events");
+import { CollectionNoteTakingViewFieldColumn } from "./CollectionNoteTakingViewFieldColumn";
const _global = (window /* browser */ || global /* node */) as any;
+
export type collectionNoteTakingViewProps = {
chromeHidden?: boolean;
+ // view type is stacking
viewType?: CollectionViewType;
NativeWidth?: () => number;
NativeHeight?: () => number;
};
-//TODO: where am I going to add columns?
-
@observer
export class CollectionNoteTakingView extends CollectionSubView<Partial<collectionNoteTakingViewProps>>() {
- // used in a column dragger, likely due for the masonry grid view. We want to use this
- _draggerRef = React.createRef<HTMLDivElement>();
+ //-------------------------------------------- Delete? --------------------------------------------//
+ // Not sure what a pivot field is. Seems like we cause reaction in MobX get rid of it once we exit this view
+ _pivotFieldDisposer?: IReactionDisposer;
// Seems like we cause reaction in MobX get rid of our height once we exit this view
_autoHeightDisposer?: IReactionDisposer;
+ //------------------------------------------------------------------------------------------------//
+ _masonryGridRef: HTMLDivElement | null = null;
+ // used in a column dragger, likely due for the masonry grid view. We want to use this
+ _draggerRef = React.createRef<HTMLDivElement>();
// keeping track of documents. Updated on internal and external drops. What's the difference?
_docXfs: { height: () => number, width: () => number, stackedDocTransform: () => Transform }[] = [];
-
- //--------------------------------------------------------------------------------------------------------------//
- // TODO: these are things that I added but not sure that they actually belong here
- // We may not need to actually keep track of the numColumns
- _noteTakingRef: HTMLDivElement | null = null;
- // this is the layout doc field that we're splitting on. Replaces pivot field
- _columnIndex: string = "columnIndex";
- @computed get columnIndex() { return this._columnIndex}
- _numColumns: number = 1;
- @computed get numColumns() { return this._numColumns}
- @computed get columnWidth() { return this.props.PanelWidth() - 2 * this.xMargin }
- //--------------------------------------------------------------------------------------------------------------//
-
+ // Doesn't look like this field is being used anywhere. Obsolete?
+ _columnStart: number = 0;
+ // map of node headers to their heights. Used in Masonry
+ @observable _heightMap = new Map<string, number>();
// Assuming that this is the current css cursor style
@observable _cursor: CursorProperty = "grab";
// gets reset whenever we scroll. Not sure what it is
@@ -67,6 +64,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
// it looks like this gets the column headers that Mehek was showing just now
@computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
+ // Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
+ // @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
+ @computed get pivotField() { return "Col" }
// filteredChildren is what you want to work with. It's the list of things that you're currently displaying
@computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
// how much margin we give the header
@@ -74,6 +74,17 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
@computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
@computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
+ // are we stacking or masonry?
+ @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking || (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.NoteTaking; }
+ // this is the number of StackingViewFieldColumns that we have
+ @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
+ // reveals a button to add a group in masonry view
+ @computed get showAddAGroup() { return this.pivotField && !this.chromeHidden; }
+ // columnWidth handles the margin on the left and right side of the documents
+ @computed get columnWidth() {
+ return Math.min(this.props.PanelWidth() - 2 * this.xMargin,
+ this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+ }
@computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
@@ -81,36 +92,41 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
super(props);
if (this.columnHeaders === undefined) {
- this.layoutDoc._columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("0")]);
+ // TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
+ // here we're making an empty list of column headers (again, what Mehek showed us)
+ this.layoutDoc._columnHeaders = new List<SchemaHeaderField>();
}
- console.log(this.layoutDoc._columnHeaders)
}
+ // TODO: plj - these are the children
children = (docs: Doc[]) => {
+ //TODO: can somebody explain me to what exactly TraceMobX is?
TraceMobx();
+ // appears that we are going to reset the _docXfs. TODO: what is Xfs?
this._docXfs.length = 0;
- // Go through each of the documents that are contained
return docs.map((d, i) => {
- if (d._columnIndex && parseInt(d._columnIndex.toString()) + 1 > this.numColumns) {
- this._numColumns = parseInt(d._columnIndex.toString()) + 1;
- } else if (d._columnIndex === undefined) {
- d._columnIndex = 0;
- }
const height = () => this.getDocHeight(d);
const width = () => this.getDocWidth(d);
+ // assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns
+ const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
// just getting the style
- const style = { width: width(), marginTop: i ? this.gridGap : 0, height: height(), margin: this.xMargin };
+ const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
// So we're choosing whether we're going to render a column or a masonry doc
- return <div className={"collectionNoteTakingView-columnDoc"} key={d[Id]} style={style} >
- {this.getDisplayDoc(d, width)}
- </div>
+ return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} style={style} >
+ {this.getDisplayDoc(d, width)}
+ </div>;
});
}
+ @action
+ setDocHeight = (key: string, sectionHeight: number) => {
+ this._heightMap.set(key, sectionHeight);
+ }
- //TODO: this seems important
- get Sections() {
+ // is sections that all collections inherit? I think this is how we show the masonry/columns
+ //TODO: this seems important
+ get Sections() {
// appears that pivot field IS actually for sorting
- if (!this.columnIndex || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+ if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
if (this.columnHeaders === undefined) {
setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
@@ -120,18 +136,21 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
let changed = false;
this.filteredChildren.map(d => {
- const sectionValue = (d[this.columnIndex] ? d[this.columnIndex] : `NO ${this.columnIndex.toUpperCase()} VALUE`) as object;
+ if (!d[this.pivotField]) {
+ d[this.pivotField] = `0`
+ };
+ const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `0`) as object;
// the next five lines ensures that floating point rounding errors don't create more than one section -syip
const parsed = parseInt(sectionValue.toString());
const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
// look for if header exists already
- const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.columnIndex.toUpperCase()} VALUE`));
+ const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `0`));
if (existingHeader) {
fields.get(existingHeader)!.push(d);
}
else {
- const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.columnIndex.toUpperCase()} VALUE`);
+ const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `0`);
fields.set(newSchemaHeader, [d]);
columnHeaders.push(newSchemaHeader);
changed = true;
@@ -149,52 +168,26 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
return fields;
}
- //TODO: this seems important
- // get Sections() {
- // // at the start, we likely will not have column headers
- // if (this.columnHeaders === undefined) {
- // console.log("columns weren't yet defined")
- // setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("0")]));
- // return new Map<SchemaHeaderField, Doc[]>();
- // }
-
- // // on later renders, we should have the column headers
- // const columnHeaders = Array.from(this.columnHeaders);
- // console.log(columnHeaders)
- // const fields = new Map<SchemaHeaderField, Doc[]>();
- // let changed = false;
- // this.filteredChildren.map(d => {
- // const sectionValue = (d._columnIndex ? d._columnIndex : `NO COLUMN VALUE`) as object;
- // // the next five lines ensures that floating point rounding errors don't create more than one section -syip
- // const parsed = parseInt(sectionValue.toString());
- // const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
- // const newSchemaHeader = new SchemaHeaderField(castedSectionValue.toString());
- // const currentDocs = fields.get(newSchemaHeader)
- // if (currentDocs) {
- // currentDocs.push(d)
- // fields.set(newSchemaHeader, currentDocs);
- // }
- // const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO COLUMN VALUE`));
- // if (existingHeader) {
- // columnHeaders.push(newSchemaHeader);
- // changed = true;
- // }
- // });
- // changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
- // return fields;
- // }
componentDidMount() {
super.componentDidMount?.();
+ // reset section headers when a new filter is inputted
+ this._pivotFieldDisposer = reaction(
+ () => this.pivotField,
+ () => this.layoutDoc._columnHeaders = new List()
+ );
+ //TODO: where the heck are we getting filters from?
this._autoHeightDisposer = reaction(() => 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.headerMargin + (this.isStackingView ?
+ Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
+ this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
}
componentWillUnmount() {
super.componentWillUnmount();
+ this._pivotFieldDisposer?.();
this._autoHeightDisposer?.();
}
@@ -203,7 +196,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
}
createRef = (ele: HTMLDivElement | null) => {
- this._noteTakingRef = ele;
+ this._masonryGridRef = ele;
this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
}
@@ -326,7 +319,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
if (this.columnWidth < 150){
margin = 0;
}
- const maxWidth = (this.columnWidth / this.numColumns) - (margin * 2);
+ const maxWidth = (this.columnWidth / this.numGroupColumns) - (margin * 2);
if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d))) {
return Math.min(d[WidthSym](), maxWidth);
}
@@ -340,7 +333,7 @@ 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.numColumns;
+ const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
return Math.min(
maxHeight,
@@ -369,6 +362,35 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
</div>;
}
+ // TODO: plj
+ @action
+ onPointerOver = (e: React.PointerEvent) => {
+ // console.log("hovering over something")
+ if (DragManager.docsBeingDragged.length) {
+ // essentially copying code from onInternalDrop for this:
+ const doc = DragManager.docsBeingDragged[0];
+ // console.log(doc[LayoutSym]())
+
+ console.log(doc[DataSym]);
+ console.log(Doc.IndexOf(doc, this.childDocs));
+
+ }
+
+
+ }
+
+ //used in onPointerOver to swap two nodes in the rendered filtered children list
+ swapNodes = (i: number, j: number) => {
+
+ }
+
+ //plj added this
+ @action
+ onPointerDown = (e: React.PointerEvent) => {
+
+ }
+
+ // TODO: plj - look at this. Start with making changes to db, and then transition to client side
@undoBatch
@action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -385,8 +407,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) {
dropInd = i;
- //TODO: not sure what the axis should actually be. Had a ternary previously with 0/1
- const axis = 1;
+ const axis = this.isStackingView ? 1 : 0;
dropAfter = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0;
}
});
@@ -459,11 +480,13 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
refList: any[] = [];
// what a section looks like if we're in stacking view
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
- const key = "columnIndex";
+ const key = this.pivotField;
let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
- const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
- if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
- type = types[0];
+ if (this.pivotField) {
+ const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
+ if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ type = types[0];
+ }
}
//TODO: I think that we only have one of these atm
return <CollectionNoteTakingViewFieldColumn
@@ -491,9 +514,9 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
DataDoc={this.props.DataDoc}
renderChildren={this.children}
columnWidth={this.columnWidth}
- columnIndex={this._columnIndex}
- numColumns={this.numColumns}
+ numGroupColumns={this.numGroupColumns}
gridGap={this.gridGap}
+ pivotField={this.pivotField}
key={heading?.heading ?? ""}
headings={this.headings}
heading={heading?.heading ?? ""}
@@ -506,13 +529,55 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
/>;
}
+ // what a section looks like if we're in masonry. Shouldn't actually need to use this.
+ sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
+ const key = this.pivotField;
+ let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+ const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
+ if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ type = types[0];
+ }
+ const rows = () => !this.isStackingView ? 1 : Math.max(1, Math.min(docList.length,
+ Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
+ return <CollectionMasonryViewFieldRow
+ showHandle={first}
+ Document={this.props.Document}
+ chromeHidden={this.chromeHidden}
+ pivotField={this.pivotField}
+ unobserveHeight={(ref) => this.refList.splice(this.refList.indexOf(ref), 1)}
+ observeHeight={(ref) => {
+ if (ref) {
+ this.refList.push(ref);
+ this.observer = new _global.ResizeObserver(action((entries: any) => {
+ if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+ const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
+ this.props.setHeight(this.headerMargin + height);
+ }
+ }));
+ this.observer.observe(ref);
+ }
+ }}
+ key={heading ? heading.heading : ""}
+ rows={rows}
+ headings={this.headings}
+ heading={heading ? heading.heading : ""}
+ headingObject={heading}
+ docList={docList}
+ parent={this}
+ type={type}
+ createDropTarget={this.createDashEventsTarget}
+ screenToLocalTransform={this.props.ScreenToLocalTransform}
+ setDocHeight={this.setDocHeight}
+ />;
+ }
+
@action
// What are we adding a group to?
addGroup = (value: string) => {
if (value && this.columnHeaders) {
const schemaHdrField = new SchemaHeaderField(value);
this.columnHeaders.push(schemaHdrField);
- DocUtils.addFieldEnumerations(undefined, this._columnIndex, [{ title: value, _backgroundColor: "schemaHdrField.color" }]);
+ DocUtils.addFieldEnumerations(undefined, this.pivotField, [{ title: value, _backgroundColor: "schemaHdrField.color" }]);
return true;
}
return false;
@@ -540,10 +605,12 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
@computed get renderedSections() {
TraceMobx();
let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
- const entries = Array.from(this.Sections.entries());
- sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
+ if (this.pivotField) {
+ const entries = Array.from(this.Sections.entries());
+ sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
+ }
// a section will have a header and a list of docs. Ok cool.
- return sections.map((section, i) => this.sectionStacking(section[0], section[1]));
+ return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
}
@computed get buttonMenu() {
@@ -617,7 +684,7 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
}
</div> : null}
<div className="collectionStackingMasonry-cont" >
- <div className={"collectionNoteTakingView"}
+ <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
ref={this.createRef}
style={{
overflowY: this.props.isContentActive() ? "auto" : "hidden",
@@ -625,10 +692,29 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
pointerEvents: this.backgroundEvents ? "all" : undefined
}}
onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+ onPointerOver={this.onPointerOver}
+ onPointerDown={this.onPointerDown}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
+ // Todo: what is wheel? Are we talking about a mouse wheel?
onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+ {/* so it appears that we are actually rendering the sections. Maybe this is what we're looking for? */}
{this.renderedSections}
+ {/* I think that showAddGroup must be passed in as false, which is why we can't find what Mehek showed
+ Or it's because we aren't passing a pivot field */}
+ {!this.showAddAGroup ? (null) :
+ <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+ style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+ <EditableView {...editableViewProps} />
+ </div>}
+ {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
+ <Switch
+ onChange={this.onToggle}
+ onClick={this.onToggle}
+ defaultChecked={true}
+ checkedChildren="edit"
+ unCheckedChildren="view"
+ />} */}
</div>
</div>
</>
@@ -636,3 +722,643 @@ export class CollectionNoteTakingView extends CollectionSubView<Partial<collecti
);
}
}
+
+
+// import React = require("react");
+// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+// import { CursorProperty } from "csstype";
+// import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+// import { observer } from "mobx-react";
+// import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+// import { Id } from "../../../fields/FieldSymbols";
+// import { List } from "../../../fields/List";
+// import { listSpec } from "../../../fields/Schema";
+// import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+// import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+// import { TraceMobx } from "../../../fields/util";
+// import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+// import { Docs, DocUtils } from "../../documents/Documents";
+// import { DragManager, dropActionType } from "../../util/DragManager";
+// import { SnappingManager } from "../../util/SnappingManager";
+// import { Transform } from "../../util/Transform";
+// import { undoBatch } from "../../util/UndoManager";
+// import { ContextMenu } from "../ContextMenu";
+// import { ContextMenuProps } from "../ContextMenuItem";
+// import { LightboxView } from "../LightboxView";
+// import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
+// import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
+// import { StyleProp } from "../StyleProvider";
+// import { CollectionNoteTakingViewFieldColumn } from "./CollectionNoteTakingViewFieldColumn";
+// import "./CollectionStackingView.scss";
+// import { CollectionSubView } from "./CollectionSubView";
+// import { CollectionViewType } from "./CollectionView";
+// import internal = require("events");
+// const _global = (window /* browser */ || global /* node */) as any;
+
+// export type collectionNoteTakingViewProps = {
+// chromeHidden?: boolean;
+// viewType?: CollectionViewType;
+// NativeWidth?: () => number;
+// NativeHeight?: () => number;
+// };
+
+// //TODO: where am I going to add columns?
+
+// @observer
+// export class CollectionNoteTakingView extends CollectionSubView<Partial<collectionNoteTakingViewProps>>() {
+// // used in a column dragger, likely due for the masonry grid view. We want to use this
+// _draggerRef = React.createRef<HTMLDivElement>();
+// // Seems like we cause reaction in MobX get rid of our height once we exit this view
+// _autoHeightDisposer?: IReactionDisposer;
+// // keeping track of documents. Updated on internal and external drops. What's the difference?
+// _docXfs: { height: () => number, width: () => number, stackedDocTransform: () => Transform }[] = [];
+
+// //--------------------------------------------------------------------------------------------------------------//
+// // TODO: these are things that I added but not sure that they actually belong here
+// // We may not need to actually keep track of the numColumns
+// _noteTakingRef: HTMLDivElement | null = null;
+// // this is the layout doc field that we're splitting on. Replaces pivot field
+// _columnIndex: string = "columnIndex";
+// @computed get columnIndex() { return this._columnIndex}
+// _numColumns: number = 1;
+// @computed get numColumns() { return this._numColumns}
+// @computed get columnWidth() { return this.props.PanelWidth() - 2 * this.xMargin }
+// //--------------------------------------------------------------------------------------------------------------//
+
+// // Assuming that this is the current css cursor style
+// @observable _cursor: CursorProperty = "grab";
+// // gets reset whenever we scroll. Not sure what it is
+// @observable _scroll = 0; // used to force the document decoration to update when scrolling
+// // does this mean whether the browser is hidden? Or is chrome something else entirely?
+// @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
+// // it looks like this gets the column headers that Mehek was showing just now
+// @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
+// // filteredChildren is what you want to work with. It's the list of things that you're currently displaying
+// @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
+// // how much margin we give the header
+// @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); }
+// @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
+// @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
+// @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
+
+// @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
+
+// constructor(props: any) {
+// super(props);
+
+// if (this.columnHeaders === undefined) {
+// this.layoutDoc._columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("0")]);
+// }
+// console.log(this.layoutDoc._columnHeaders)
+// }
+
+// children = (docs: Doc[]) => {
+// TraceMobx();
+// this._docXfs.length = 0;
+// // Go through each of the documents that are contained
+// return docs.map((d, i) => {
+// if (d._columnIndex && parseInt(d._columnIndex.toString()) + 1 > this.numColumns) {
+// this._numColumns = parseInt(d._columnIndex.toString()) + 1;
+// } else if (d._columnIndex === undefined) {
+// d._columnIndex = 0;
+// }
+// const height = () => this.getDocHeight(d);
+// const width = () => this.getDocWidth(d);
+// // just getting the style
+// const style = { width: width(), marginTop: i ? this.gridGap : 0, height: height(), margin: this.xMargin };
+// // So we're choosing whether we're going to render a column or a masonry doc
+// return <div className={"collectionNoteTakingView-columnDoc"} key={d[Id]} style={style} >
+// {this.getDisplayDoc(d, width)}
+// </div>
+// });
+// }
+
+// //TODO: this seems important
+// get Sections() {
+// // appears that pivot field IS actually for sorting
+// if (!this.columnIndex || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
+
+// if (this.columnHeaders === undefined) {
+// setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
+// return new Map<SchemaHeaderField, Doc[]>();
+// }
+// const columnHeaders = Array.from(this.columnHeaders);
+// const fields = new Map<SchemaHeaderField, Doc[]>(columnHeaders.map(sh => [sh, []] as [SchemaHeaderField, []]));
+// let changed = false;
+// this.filteredChildren.map(d => {
+// const sectionValue = (d[this.columnIndex] ? d[this.columnIndex] : `NO ${this.columnIndex.toUpperCase()} VALUE`) as object;
+// // the next five lines ensures that floating point rounding errors don't create more than one section -syip
+// const parsed = parseInt(sectionValue.toString());
+// const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
+
+// // look for if header exists already
+// const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.columnIndex.toUpperCase()} VALUE`));
+// if (existingHeader) {
+// fields.get(existingHeader)!.push(d);
+// }
+// else {
+// const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.columnIndex.toUpperCase()} VALUE`);
+// fields.set(newSchemaHeader, [d]);
+// columnHeaders.push(newSchemaHeader);
+// changed = true;
+// }
+// });
+// // remove all empty columns if hideHeadings is set
+// // we will want to have something like this, so that we can hide columns and add them back in
+// if (this.layoutDoc._columnsHideIfEmpty) {
+// Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
+// fields.delete(header);
+// columnHeaders.splice(columnHeaders.indexOf(header), 1);
+// changed = true;
+// });
+// }
+// changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
+// return fields;
+// }
+// //TODO: this seems important
+// // get Sections() {
+// // // at the start, we likely will not have column headers
+// // if (this.columnHeaders === undefined) {
+// // console.log("columns weren't yet defined")
+// // setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>([new SchemaHeaderField("0")]));
+// // return new Map<SchemaHeaderField, Doc[]>();
+// // }
+
+// // // on later renders, we should have the column headers
+// // const columnHeaders = Array.from(this.columnHeaders);
+// // console.log(columnHeaders)
+// // const fields = new Map<SchemaHeaderField, Doc[]>();
+// // let changed = false;
+// // this.filteredChildren.map(d => {
+// // const sectionValue = (d._columnIndex ? d._columnIndex : `NO COLUMN VALUE`) as object;
+// // // the next five lines ensures that floating point rounding errors don't create more than one section -syip
+// // const parsed = parseInt(sectionValue.toString());
+// // const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
+// // const newSchemaHeader = new SchemaHeaderField(castedSectionValue.toString());
+// // const currentDocs = fields.get(newSchemaHeader)
+// // if (currentDocs) {
+// // currentDocs.push(d)
+// // fields.set(newSchemaHeader, currentDocs);
+// // }
+// // const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO COLUMN VALUE`));
+// // if (existingHeader) {
+// // columnHeaders.push(newSchemaHeader);
+// // changed = true;
+// // }
+// // });
+// // changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
+// // return fields;
+// // }
+
+// componentDidMount() {
+// super.componentDidMount?.();
+
+// this._autoHeightDisposer = reaction(() => 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", "")))))));
+// }
+
+// componentWillUnmount() {
+// super.componentWillUnmount();
+// this._autoHeightDisposer?.();
+// }
+
+// @action
+// moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
+// return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
+// }
+// createRef = (ele: HTMLDivElement | null) => {
+// this._noteTakingRef = ele;
+// this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
+// }
+
+// @computed get onChildClickHandler() { return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
+// @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
+
+// addDocTab = (doc: Doc, where: string) => {
+// if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+// this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
+// return true;
+// }
+// return this.props.addDocTab(doc, where);
+// }
+
+// scrollToBottom = () => {
+// smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
+// }
+
+// // 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) {
+// 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);
+// }
+// }
+// const endFocus = async (moved: boolean) => options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing;
+// this.props.focus(this.rootDoc, {
+// willZoom: options?.willZoom, scale: options?.scale, afterFocus: (didFocus: boolean) =>
+// new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed))
+// });
+// }
+
+// styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
+// if (property === StyleProp.Opacity && doc) {
+// 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);
+// }
+// isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+
+// // this is what renders the document that you see on the screen
+// // called in Children: this actually adds a document to our children list
+// getDisplayDoc(doc: Doc, width: () => number) {
+// const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
+// const height = () => this.getDocHeight(doc);
+
+// let dref: Opt<DocumentView>;
+// const stackedDocTransform = () => this.getDocTransform(doc, dref);
+// this._docXfs.push({ stackedDocTransform, width, height });
+// //DocumentView is how the node will be rendered
+// return <DocumentView ref={r => dref = r || undefined}
+// Document={doc}
+// DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+// renderDepth={this.props.renderDepth + 1}
+// PanelWidth={width}
+// PanelHeight={height}
+// styleProvider={this.styleProvider}
+// layerProvider={this.props.layerProvider}
+// docViewPath={this.props.docViewPath}
+// fitWidth={this.props.childFitWidth}
+// isContentActive={emptyFunction}
+// isDocumentActive={this.isContentActive}
+// LayoutTemplate={this.props.childLayoutTemplate}
+// LayoutTemplateString={this.props.childLayoutString}
+// freezeDimensions={this.props.childFreezeDimensions}
+// NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+// NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
+// dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
+// dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
+// rootSelected={this.rootSelected}
+// showTitle={this.props.childShowTitle}
+// dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+// onClick={this.onChildClickHandler}
+// onDoubleClick={this.onChildDoubleClickHandler}
+// ScreenToLocalTransform={stackedDocTransform}
+// focus={this.focusDocument}
+// docFilters={this.childDocFilters}
+// hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+// hideResizeHandles={this.props.childHideResizeHandles?.()}
+// hideTitle={this.props.childHideTitle?.()}
+// docRangeFilters={this.childDocRangeFilters}
+// searchFilterDocs={this.searchFilterDocs}
+// ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+// ContainingCollectionView={this.props.CollectionView}
+// addDocument={this.props.addDocument}
+// moveDocument={this.props.moveDocument}
+// removeDocument={this.props.removeDocument}
+// contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+// whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+// addDocTab={this.addDocTab}
+// bringToFront={returnFalse}
+// scriptContext={this.props.scriptContext}
+// pinToPres={this.props.pinToPres}
+// />;
+// }
+
+// getDocTransform(doc: Doc, dref?: DocumentView) {
+// const y = this._scroll; // required for document decorations to update when the text box container is scrolled
+// const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
+// // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off
+// return new Transform(- translateX + (dref?.centeringX || 0), - translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
+// }
+// getDocWidth(d?: Doc) {
+// if (!d) return 0;
+// const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+// // TODO: pj - replace with a better way to calculate the margin
+// let margin = 25;
+// d.margin = 25;
+// if (this.columnWidth < 150){
+// margin = 0;
+// }
+// const maxWidth = (this.columnWidth / this.numColumns) - (margin * 2);
+// if (!this.layoutDoc._columnsFill && !(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d))) {
+// return Math.min(d[WidthSym](), maxWidth);
+// }
+// return maxWidth;
+// }
+// getDocHeight(d?: Doc) {
+// if (!d || d.hidden) return 0;
+// const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
+// const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
+// const maxHeight = (lim => lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim)(NumCast(this.layoutDoc.childLimitHeight, -1));
+// const 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.numColumns;
+// const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
+// return Math.min(
+// maxHeight,
+// docWid * nh / nw);
+// }
+// const childHeight = NumCast(childLayoutDoc._height);
+// const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+// return Math.min(childHeight, maxHeight, panelHeight);
+// }
+
+// // This following three functions must be from the view Mehek showed
+// columnDividerDown = (e: React.PointerEvent) => {
+// runInAction(() => this._cursor = "grabbing");
+// setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
+// }
+// @action
+// onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+// this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
+// return false;
+// }
+
+// @computed get columnDragger() {
+// return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
+// style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
+// <FontAwesomeIcon icon={"arrows-alt-h"} />
+// </div>;
+// }
+
+// @undoBatch
+// @action
+// onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+// // Fairly confident that this is where the swapping of nodes in the various arrays happens
+// console.log('drop')
+// const where = [de.x, de.y];
+// // start at -1 until we're sure we want to add it to the column
+// let dropInd = -1;
+// let dropAfter = 0;
+// if (de.complete.docDragData) {
+// // going to re-add the docs to the _docXFs based on position of where we just dropped
+// this._docXfs.map((cd, i) => {
+// const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+// const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
+// if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) {
+// dropInd = i;
+// //TODO: not sure what the axis should actually be. Had a ternary previously with 0/1
+// const axis = 1;
+// dropAfter = where[axis] > (pos[axis] + pos1[axis]) / 2 ? 1 : 0;
+// }
+// });
+// const oldDocs = this.childDocs.length;
+// if (super.onInternalDrop(e, de)) {
+// // check to see if we actually need anything to the new column of nodes (if droppedDocs != empty)
+// const droppedDocs = this.childDocs.slice().filter((d: Doc, ind: number) => ind >= oldDocs); // 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; // if nothing was added to the end of the list, then presumably the dropped documents were already in the list, but possibly got reordered so we use them.
+
+// const docs = this.childDocList;
+// // reset drag manager docs, because we just dropped
+// DragManager.docsBeingDragged = [];
+// // still figuring out where to add the document
+// if (docs && newDocs.length) {
+// const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter;
+// const offset = newDocs.reduce((off, ndoc) => this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off, 0);
+// newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
+// docs.splice(insertInd - offset, 0, ...newDocs);
+// }
+// }
+// } // 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?.()) {
+// 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
+// e.stopPropagation();
+// }
+// else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+// return false;
+// }
+
+// @undoBatch
+// internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
+// const dropCreator = annoDragData.dropDocCreator;
+// annoDragData.dropDocCreator = (annotationOn: Doc | undefined) => {
+// const dropDoc = dropCreator(annotationOn);
+// return dropDoc || this.rootDoc;
+// };
+// return true;
+// }
+
+// @undoBatch
+// @action
+// //What is the difference between internal and external drop?? Does internal mean we're dropping inside of a collection?
+// // I take it back: external drop means we took it out of column/collection that we were just in
+// onExternalDrop = async (e: React.DragEvent): Promise<void> => {
+// console.log('external drop')
+// const where = [e.clientX, e.clientY];
+// let targInd = -1;
+// this._docXfs.map((cd, i) => {
+// const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+// const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
+// if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && where[1] < pos1[1]) {
+// targInd = i;
+// }
+// });
+// 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);
+// }
+// }
+// });
+// }
+// // sections are important
+// headings = () => Array.from(this.Sections);
+// refList: any[] = [];
+// // what a section looks like if we're in stacking view
+// sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
+// const key = "columnIndex";
+// let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+// const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
+// if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+// type = types[0];
+// }
+// //TODO: I think that we only have one of these atm
+// return <CollectionNoteTakingViewFieldColumn
+// unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
+// observeHeight={ref => {
+// if (ref) {
+// this.refList.push(ref);
+// this.observer = new _global.ResizeObserver(action((entries: any) => {
+// if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+// const height = this.headerMargin +
+// Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+// Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
+// if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+// this.props.setHeight(height);
+// }
+// }
+// }));
+// this.observer.observe(ref);
+// }
+// }}
+// addDocument={this.addDocument}
+// chromeHidden={this.chromeHidden}
+// columnHeaders={this.columnHeaders}
+// Document={this.props.Document}
+// DataDoc={this.props.DataDoc}
+// renderChildren={this.children}
+// columnWidth={this.columnWidth}
+// columnIndex={this._columnIndex}
+// numColumns={this.numColumns}
+// gridGap={this.gridGap}
+// key={heading?.heading ?? ""}
+// headings={this.headings}
+// heading={heading?.heading ?? ""}
+// headingObject={heading}
+// docList={docList}
+// yMargin={this.yMargin}
+// type={type}
+// createDropTarget={this.createDashEventsTarget}
+// screenToLocalTransform={this.props.ScreenToLocalTransform}
+// />;
+// }
+
+// @action
+// // What are we adding a group to?
+// addGroup = (value: string) => {
+// if (value && this.columnHeaders) {
+// const schemaHdrField = new SchemaHeaderField(value);
+// this.columnHeaders.push(schemaHdrField);
+// DocUtils.addFieldEnumerations(undefined, this._columnIndex, [{ title: value, _backgroundColor: "schemaHdrField.color" }]);
+// return true;
+// }
+// return 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;
+// }
+
+// onContextMenu = (e: React.MouseEvent): void => {
+// // need to test if propagation has stopped because GoldenLayout forces a parallel react hierarchy to be created for its top-level layout
+// if (!e.isPropagationStopped()) {
+// const subItems: ContextMenuProps[] = [];
+// subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
+// subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
+// subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" });
+// ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
+// }
+// }
+
+// //
+// @computed get renderedSections() {
+// TraceMobx();
+// let sections = [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]];
+// const entries = Array.from(this.Sections.entries());
+// sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
+// // a section will have a header and a list of docs. Ok cool.
+// return sections.map((section, i) => this.sectionStacking(section[0], section[1]));
+// }
+
+// @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);
+// console.log(menuDoc.title, width, height);
+// return (<div className="buttonMenu-docBtn"
+// style={{ width: width, height: height }}>
+// <DocumentView
+// Document={menuDoc}
+// DataDoc={menuDoc}
+// isContentActive={this.props.isContentActive}
+// isDocumentActive={returnTrue}
+// addDocument={this.props.addDocument}
+// moveDocument={this.props.moveDocument}
+// addDocTab={this.props.addDocTab}
+// pinToPres={emptyFunction}
+// rootSelected={this.props.isSelected}
+// removeDocument={this.props.removeDocument}
+// ScreenToLocalTransform={Transform.Identity}
+// PanelWidth={() => 35}
+// PanelHeight={() => 35}
+// renderDepth={this.props.renderDepth}
+// focus={emptyFunction}
+// styleProvider={this.props.styleProvider}
+// layerProvider={this.props.layerProvider}
+// docViewPath={returnEmptyDoclist}
+// whenChildContentsActiveChanged={emptyFunction}
+// bringToFront={emptyFunction}
+// docFilters={this.props.docFilters}
+// docRangeFilters={this.props.docRangeFilters}
+// searchFilterDocs={this.props.searchFilterDocs}
+// ContainingCollectionView={undefined}
+// ContainingCollectionDoc={undefined}
+// />
+// </div>
+// );
+// }
+// }
+
+
+// @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
+// @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
+
+// @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
+
+// @computed get backgroundEvents() { return SnappingManager.GetIsDragging(); }
+// observer: any;
+// render() {
+// TraceMobx();
+// const editableViewProps = {
+// GetValue: () => "",
+// SetValue: this.addGroup,
+// // I don't recall ever seeing this add a group button
+// contents: "+ ADD A GROUP"
+// };
+// const buttonMenu = this.rootDoc.buttonMenu;
+// const noviceExplainer = this.rootDoc.explainer;
+// return (
+// <>
+// {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
+// {buttonMenu ? this.buttonMenu : null}
+// {Doc.UserDoc().noviceMode && noviceExplainer ?
+// <div className="documentExplanation">
+// {noviceExplainer}
+// </div>
+// : null
+// }
+// </div> : null}
+// <div className="collectionStackingMasonry-cont" >
+// <div className={"collectionNoteTakingView"}
+// ref={this.createRef}
+// style={{
+// overflowY: this.props.isContentActive() ? "auto" : "hidden",
+// background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+// pointerEvents: this.backgroundEvents ? "all" : undefined
+// }}
+// onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+// onDrop={this.onExternalDrop.bind(this)}
+// onContextMenu={this.onContextMenu}
+// onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+// {this.renderedSections}
+// </div>
+// </div>
+// </>
+
+// );
+// }
+// }
diff --git a/src/client/views/collections/CollectionNoteTakingViewFieldColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewFieldColumn.tsx
index 0c3eef36d..5a8256e11 100644
--- a/src/client/views/collections/CollectionNoteTakingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewFieldColumn.tsx
@@ -3,14 +3,13 @@ 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 { RichTextField } from "../../../fields/RichTextField";
import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { ScriptField } from "../../../fields/ScriptField";
-import { NumCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
import { ImageField } from "../../../fields/URLField";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnEmptyString, setupMoveUpEvents } from "../../../Utils";
+import { emptyFunction, setupMoveUpEvents, returnFalse, returnEmptyString } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { DragManager } from "../../util/DragManager";
@@ -20,27 +19,30 @@ import { undoBatch } from "../../util/UndoManager";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import { EditableView } from "../EditableView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
import "./CollectionStackingView.scss";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { Id } from "../../../fields/FieldSymbols";
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>;
docList: Doc[];
heading: string;
- columnIndex: string;
+ pivotField: string;
chromeHidden?: boolean;
columnHeaders: SchemaHeaderField[] | undefined;
headingObject: SchemaHeaderField | undefined;
yMargin: number;
columnWidth: number;
- numColumns: number;
+ numGroupColumns: number;
gridGap: number;
type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined;
headings: () => object[];
+ // I think that stacking view actually has a single column and then supposedly you can add more columns? Unsure
renderChildren: (docs: Doc[]) => JSX.Element[];
addDocument: (doc: Doc | Doc[]) => boolean;
createDropTarget: (ele: HTMLDivElement) => void;
@@ -56,9 +58,9 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
private dropDisposer?: DragManager.DragDropDisposer;
private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
- // @observable _paletteOn = false;
+ @observable _paletteOn = false;
@observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
- // @observable _color = this.props.headingObject ? this.props.headingObject.color : "#f1efeb";
+ @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?
@@ -79,9 +81,8 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
@undoBatch
columnDrop = action((e: Event, de: DragManager.DropEvent) => {
const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
- drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.columnIndex, drop.val, false));
+ drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.pivotField, drop.val, false));
});
-
getValue = (value: string): any => {
const parsed = parseInt(value);
if (!isNaN(parsed)) return parsed;
@@ -91,13 +92,13 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
}
@action
- headingChanged = (value: string) => {
+ headingChanged = (value: string, shiftDown?: boolean) => {
const castedValue = this.getValue(value);
if (castedValue) {
if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
return false;
}
- this.props.docList.forEach(d => d[this.props.columnIndex] = castedValue);
+ this.props.docList.forEach(d => d[this.props.pivotField] = castedValue);
if (this.props.headingObject) {
this.props.headingObject.setHeading(castedValue.toString());
this._heading = this.props.headingObject.heading;
@@ -107,6 +108,12 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
return false;
}
+ @action
+ changeColumnColor = (color: string) => {
+ this.props.headingObject?.setColor(color);
+ this._color = color;
+ }
+
@action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
@action pointerLeave = () => this._background = "inherit";
textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
@@ -114,7 +121,7 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
if (!value && !forceEmptyNote) return false;
- const key = this.props.columnIndex;
+ const key = this.props.pivotField;
const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
newDoc[key] = this.getValue(this.props.heading);
const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
@@ -127,7 +134,7 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
@action
deleteColumn = () => {
- this.props.docList.forEach(d => d[this.props.columnIndex] = undefined);
+ this.props.docList.forEach(d => d[this.props.pivotField] = undefined);
if (this.props.columnHeaders && this.props.headingObject) {
const index = this.props.columnHeaders.indexOf(this.props.headingObject);
this.props.columnHeaders.splice(index, 1);
@@ -150,7 +157,7 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
alias._pivotField = undefined;
let value = this.getValue(this._heading);
value = typeof value === "string" ? `"${value}"` : value;
- alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.columnIndex} === ${value}`, { doc: Doc.name });
+ alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.pivotField} === ${value}`, { doc: Doc.name });
if (alias.viewSpecScript) {
DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
return true;
@@ -158,6 +165,21 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
return false;
}
+ // renderColorPicker = () => {
+ // const gray = "#f1efeb";
+ // const selected = this.props.headingObject ? this.props.headingObject.color : gray;
+ // const colors = ["pink2", "purple4", "bluegreen1", "yellow4", "gray", "red2", "bluegreen7", "bluegreen5", "orange1"];
+ // return <div className="collectionStackingView-colorPicker">
+ // <div className="colorOptions">
+ // {colors.map(col => {
+ // const palette = PastelSchemaPalette.get(col);
+ // return <div className={"colorPicker" + (selected === palette ? " active" : "")}
+ // style={{ backgroundColor: palette }} onClick={() => this.changeColumnColor(palette!)} />;
+ // })}
+ // </div>
+ // </div>;
+ // }
+
renderMenu = () => {
return <div className="collectionStackingView-optionPicker">
<div className="optionOptions">
@@ -224,12 +246,12 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
}
@computed get innards() {
TraceMobx();
- const key = this.props.columnIndex;
+ const key = this.props.pivotField;
const headings = this.props.headings();
const heading = this._heading;
const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin;
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
- const evContents = heading ? heading : this.props?.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+ const evContents = heading ? heading : this.props?.type === "number" ? "0" : `0`;
const headingView = this.props.headingObject ?
<div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{
@@ -241,18 +263,27 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
<div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
- title={evContents === `NO ${key.toUpperCase()} VALUE` ?
- `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}>
+ title={evContents === `0` ?
+ `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
+ style={{ background: evContents !== `0` ? this._color : "inherit" }}>
<EditableView
GetValue={() => evContents}
SetValue={this.headingChanged}
contents={evContents}
oneLine={true}
toggle={this.toggleVisibility} />
+ {evContents === `0` ? (null) :
+ <div className="collectionStackingView-sectionColor">
+ {/* <button className="collectionStackingView-sectionColorButton" onClick={action(e => this._paletteOn = !this._paletteOn)}>
+ <FontAwesomeIcon icon="palette" size="lg" />
+ </button> */}
+ {/* {this._paletteOn ? this.renderColorPicker() : (null)} */}
+ </div>
+ }
{<button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>}
- {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+ {/* {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
<div className="collectionStackingView-sectionOptions">
<Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
<button className="collectionStackingView-sectionOptionButton">
@@ -260,10 +291,10 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
</button>
</Flyout>
</div>
- }
+ } */}
</div>
</div> : (null);
- const templatecols = `${this.props.columnWidth / this.props.numColumns}px `;
+ const templatecols = `${this.props.columnWidth / this.props.numGroupColumns}px `;
const type = this.props.Document.type;
return <>
{this.props.Document._columnsHideIfEmpty ? (null) : headingView}
@@ -284,8 +315,14 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
{this.props.renderChildren(this.props.docList)}
</div>
{!this.props.chromeHidden && type !== DocumentType.PRES ?
+ // TODO: this is the "new" button: see what you can work with here
+ // change cursor to pointer for this, and update dragging cursor
+ //TODO: there is a bug that occurs when adding a freeform document and trying to move it around
+ //TODO: would be great if there was additional space beyond the frame, so that you can actually see your
+ // bottom note
+ //TODO: ok, so we are using a single column, and this is it!
<div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
- style={{ width: this.props.columnWidth / this.props.numColumns, marginBottom: 10, marginLeft: 25 }}>
+ style={{ width: this.props.columnWidth / this.props.numGroupColumns, marginBottom: 10, marginLeft: 25 }}>
<EditableView
GetValue={returnEmptyString}
SetValue={this.addNewTextDoc}
@@ -319,4 +356,328 @@ export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFiel
</div >
);
}
-} \ No newline at end of file
+}
+
+
+// import React = require("react");
+// 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 { RichTextField } from "../../../fields/RichTextField";
+// import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+// import { ScriptField } from "../../../fields/ScriptField";
+// import { NumCast } from "../../../fields/Types";
+// import { ImageField } from "../../../fields/URLField";
+// import { TraceMobx } from "../../../fields/util";
+// import { emptyFunction, returnEmptyString, setupMoveUpEvents } from "../../../Utils";
+// import { Docs, DocUtils } from "../../documents/Documents";
+// import { DocumentType } from "../../documents/DocumentTypes";
+// import { DragManager } from "../../util/DragManager";
+// import { SnappingManager } from "../../util/SnappingManager";
+// import { Transform } from "../../util/Transform";
+// import { undoBatch } from "../../util/UndoManager";
+// import { ContextMenu } from "../ContextMenu";
+// import { ContextMenuProps } from "../ContextMenuItem";
+// import { EditableView } from "../EditableView";
+// import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+// import "./CollectionStackingView.scss";
+// const higflyout = require("@hig/flyout");
+// export const { anchorPoints } = higflyout;
+// export const Flyout = higflyout.default;
+
+// interface CSVFieldColumnProps {
+// Document: Doc;
+// DataDoc: Opt<Doc>;
+// docList: Doc[];
+// heading: string;
+// columnIndex: string;
+// chromeHidden?: boolean;
+// columnHeaders: SchemaHeaderField[] | undefined;
+// headingObject: SchemaHeaderField | undefined;
+// yMargin: number;
+// columnWidth: number;
+// numColumns: number;
+// gridGap: number;
+// type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined;
+// headings: () => object[];
+// renderChildren: (docs: Doc[]) => JSX.Element[];
+// addDocument: (doc: Doc | Doc[]) => boolean;
+// createDropTarget: (ele: HTMLDivElement) => void;
+// screenToLocalTransform: () => Transform;
+// observeHeight: (myref: any) => void;
+// unobserveHeight: (myref: any) => void;
+// }
+
+// @observer
+// export class CollectionNoteTakingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
+// @observable private _background = "inherit";
+
+// private dropDisposer?: DragManager.DragDropDisposer;
+// private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
+
+// // @observable _paletteOn = false;
+// @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;
+
+// // 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) {
+// this._ele = ele;
+// this.props.observeHeight(ele);
+// this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this));
+// }
+// }
+// componentWillUnmount() {
+// this.props.unobserveHeight(this._ele);
+// }
+
+// //TODO: what is scripting? I found it in SetInPlace def but don't know what that is
+// @undoBatch
+// columnDrop = action((e: Event, de: DragManager.DropEvent) => {
+// const drop = { docs: de.complete.docDragData?.droppedDocuments, val: this.getValue(this._heading) };
+// drop.docs?.forEach(d => Doc.SetInPlace(d, this.props.columnIndex, drop.val, false));
+// });
+
+// getValue = (value: string): any => {
+// const parsed = parseInt(value);
+// if (!isNaN(parsed)) return parsed;
+// if (value.toLowerCase().indexOf("true") > -1) return true;
+// if (value.toLowerCase().indexOf("false") > -1) return false;
+// return value;
+// }
+
+// @action
+// headingChanged = (value: string) => {
+// const castedValue = this.getValue(value);
+// if (castedValue) {
+// if (this.props.columnHeaders?.map(i => i.heading).indexOf(castedValue.toString()) !== -1) {
+// return false;
+// }
+// this.props.docList.forEach(d => d[this.props.columnIndex] = castedValue);
+// if (this.props.headingObject) {
+// this.props.headingObject.setHeading(castedValue.toString());
+// this._heading = this.props.headingObject.heading;
+// }
+// return true;
+// }
+// return false;
+// }
+
+// @action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
+// @action pointerLeave = () => this._background = "inherit";
+// textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
+
+// @action
+// addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
+// if (!value && !forceEmptyNote) return false;
+// const key = this.props.columnIndex;
+// const newDoc = Docs.Create.TextDocument(value, { _height: 18, _width: 200, _fitWidth: true, title: value, _autoHeight: true });
+// newDoc[key] = this.getValue(this.props.heading);
+// const maxHeading = this.props.docList.reduce((maxHeading, doc) => NumCast(doc.heading) > maxHeading ? NumCast(doc.heading) : maxHeading, 0);
+// const heading = maxHeading === 0 || this.props.docList.length === 0 ? 1 : maxHeading === 1 ? 2 : 3;
+// newDoc.heading = heading;
+// FormattedTextBox.SelectOnLoad = newDoc[Id];
+// FormattedTextBox.SelectOnLoadChar = forceEmptyNote ? "" : " ";
+// return this.props.addDocument?.(newDoc) || false;
+// }
+
+// @action
+// deleteColumn = () => {
+// this.props.docList.forEach(d => d[this.props.columnIndex] = undefined);
+// if (this.props.columnHeaders && this.props.headingObject) {
+// const index = this.props.columnHeaders.indexOf(this.props.headingObject);
+// this.props.columnHeaders.splice(index, 1);
+// }
+// }
+
+// @action
+// collapseSection = () => {
+// this.props.headingObject?.setCollapsed(!this.props.headingObject.collapsed);
+// this.toggleVisibility();
+// }
+
+// headerDown = (e: React.PointerEvent<HTMLDivElement>) => setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
+
+// //TODO: I think this is where I'm supposed to edit stuff
+// startDrag = (e: PointerEvent, down: number[], delta: number[]) => {
+// // is MakeAlias a way to make a copy of a doc without rendering it?
+// const alias = Doc.MakeAlias(this.props.Document);
+// alias._width = this.props.columnWidth / (this.props.columnHeaders?.length || 1);
+// alias._pivotField = undefined;
+// let value = this.getValue(this._heading);
+// value = typeof value === "string" ? `"${value}"` : value;
+// alias.viewSpecScript = ScriptField.MakeFunction(`doc.${this.props.columnIndex} === ${value}`, { doc: Doc.name });
+// if (alias.viewSpecScript) {
+// DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([alias]), e.clientX, e.clientY);
+// return true;
+// }
+// return false;
+// }
+
+// renderMenu = () => {
+// return <div className="collectionStackingView-optionPicker">
+// <div className="optionOptions">
+// <div className={"optionPicker" + (true ? " active" : "")} onClick={action(() => { })}>Add options here</div>
+// </div>
+// </div >;
+// }
+
+// @observable private collapsed: boolean = false;
+
+// private toggleVisibility = action(() => this.collapsed = !this.collapsed);
+
+// menuCallback = (x: number, y: number) => {
+// ContextMenu.Instance.clearItems();
+// const layoutItems: ContextMenuProps[] = [];
+// const docItems: ContextMenuProps[] = [];
+// const dataDoc = this.props.DataDoc || this.props.Document;
+
+// DocUtils.addDocumentCreatorMenuItems((doc) => {
+// FormattedTextBox.SelectOnLoad = doc[Id];
+// return this.props.addDocument?.(doc);
+// }, this.props.addDocument, x, y, true);
+
+// Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => dataDoc[fieldKey] instanceof RichTextField || dataDoc[fieldKey] instanceof ImageField || typeof (dataDoc[fieldKey]) === "string").map(fieldKey =>
+// docItems.push({
+// description: ":" + fieldKey, event: () => {
+// const created = DocUtils.DocumentFromField(dataDoc, fieldKey, Doc.GetProto(this.props.Document));
+// if (created) {
+// if (this.props.Document.isTemplateDoc) {
+// Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+// }
+// return this.props.addDocument?.(created);
+// }
+// }, icon: "compress-arrows-alt"
+// }));
+// Array.from(Object.keys(Doc.GetProto(dataDoc))).filter(fieldKey => DocListCast(dataDoc[fieldKey]).length).map(fieldKey =>
+// docItems.push({
+// description: ":" + fieldKey, event: () => {
+// const created = Docs.Create.CarouselDocument([], { _width: 400, _height: 200, title: fieldKey });
+// if (created) {
+// const container = this.props.Document.resolvedDataDoc ? Doc.GetProto(this.props.Document) : this.props.Document;
+// if (container.isTemplateDoc) {
+// Doc.MakeMetadataFieldTemplate(created, container);
+// return Doc.AddDocToList(container, Doc.LayoutFieldKey(container), created);
+// }
+// return this.props.addDocument?.(created) || false;
+// }
+// }, icon: "compress-arrows-alt"
+// }));
+// !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
+// !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
+// ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
+// Doc.GetProto(this.props.Document)[name] = "";
+// const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
+// if (created) {
+// if (this.props.Document.isTemplateDoc) {
+// Doc.MakeMetadataFieldTemplate(created, this.props.Document);
+// }
+// this.props.addDocument?.(created);
+// }
+// });
+// const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y);
+// ContextMenu.Instance.displayMenu(x, y, undefined, true);
+// }
+// @computed get innards() {
+// TraceMobx();
+// const key = this.props.columnIndex;
+// const headings = this.props.headings();
+// const heading = this._heading;
+// const columnYMargin = this.props.headingObject ? 0 : this.props.yMargin;
+// const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+// const evContents = heading ? heading : this.props?.type === "number" ? "0" : `NO ${key.toUpperCase()} VALUE`;
+// const headingView = this.props.headingObject ?
+// <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
+// style={{
+// marginTop: this.props.yMargin,
+// width: (this.props.columnWidth) /
+// ((uniqueHeadings.length + (this.props.chromeHidden ? 0 : 1)) || 1)
+// }}>
+// <div className={"collectionStackingView-collapseBar" + (this.props.headingObject.collapsed === true ? " active" : "")} onClick={this.collapseSection}></div>
+// {/* the default bucket (no key value) has a tooltip that describes what it is.
+// Further, it does not have a color and cannot be deleted. */}
+// <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
+// title={evContents === `NO ${key.toUpperCase()} VALUE` ?
+// `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}>
+// <EditableView
+// GetValue={() => evContents}
+// SetValue={this.headingChanged}
+// contents={evContents}
+// oneLine={true}
+// toggle={this.toggleVisibility} />
+// {<button className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
+// <FontAwesomeIcon icon="trash" size="lg" />
+// </button>}
+// {evContents === `NO ${key.toUpperCase()} VALUE` ? (null) :
+// <div className="collectionStackingView-sectionOptions">
+// <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
+// <button className="collectionStackingView-sectionOptionButton">
+// <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
+// </button>
+// </Flyout>
+// </div>
+// }
+// </div>
+// </div> : (null);
+// const templatecols = `${this.props.columnWidth / this.props.numColumns}px `;
+// const type = this.props.Document.type;
+// return <>
+// {this.props.Document._columnsHideIfEmpty ? (null) : headingView}
+// {
+// this.collapsed ? (null) :
+// <div>
+// <div key={`${heading}-stack`} className={`collectionStackingView-masonrySingle`}
+// 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>
+// {!this.props.chromeHidden && type !== DocumentType.PRES ?
+// <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+// style={{ width: this.props.columnWidth / this.props.numColumns, marginBottom: 10, marginLeft: 25 }}>
+// <EditableView
+// GetValue={returnEmptyString}
+// SetValue={this.addNewTextDoc}
+// textCallback={this.textCallback}
+// placeholder={"Type ':' for commands"}
+// contents={<FontAwesomeIcon icon={"plus"}/>}
+// toggle={this.toggleVisibility}
+// menuCallback={this.menuCallback}
+// />
+// </div> : null}
+// </div>
+// }
+// </>;
+// }
+
+
+// render() {
+// TraceMobx();
+// const headings = this.props.headings();
+// const heading = this._heading;
+// const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+// return (
+// <div className={"collectionStackingViewFieldColumn" + (SnappingManager.GetIsDragging() ? "Dragging" : "")} key={heading}
+// style={{
+// width: `${100 / (uniqueHeadings.length + (this.props.chromeHidden ? 0 : 1) || 1)}%`,
+// height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
+// background: this._background
+// }}
+// ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
+// {this.innards}
+// </div >
+// );
+// }
+// } \ No newline at end of file