aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionStackingView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/CollectionStackingView.tsx')
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx165
1 files changed, 66 insertions, 99 deletions
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index be570564b..4a0ddc631 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, makeObservable, observable, Observ
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -35,6 +35,7 @@ import { CollectionStackingViewFieldColumn } from './CollectionStackingViewField
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { computedFn } from 'mobx-utils';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { FieldsDropdown } from '../FieldsDropdown';
export type collectionStackingViewProps = {
sortFunc?: (a: Doc, b: Doc) => number;
@@ -48,15 +49,15 @@ export type collectionStackingViewProps = {
@observer
export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
_disposers: { [key: string]: IReactionDisposer } = {};
+ _addGroupRef = React.createRef<HTMLDivElement>();
_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 }[] = [];
- // Doesn't look like this field is being used anywhere. Obsolete?
- _columnStart: number = 0;
- @observable _refList: HTMLElement[] = [];
+ @observable _colStackRefs: HTMLElement[] = [];
+ @observable _colHdrRefs: HTMLElement[] = [];
// 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
@@ -67,9 +68,24 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@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 colHeaderData() {
- return Cast(this.dataDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ return Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ }
+
+ @computed get Sections() {
+ return this.filteredChildren.reduce(
+ (map, d) => {
+ const docHeader = d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`;
+ const docHeaderString = docHeader !== undefined ? Field.toString(docHeader) : `NO ${this.pivotField.toUpperCase()} VALUE`;
+
+ // find existing header or create
+ const existingHeader = Array.from(map.keys()).find(sh => sh.heading === docHeaderString);
+ if (!existingHeader) map.set(new SchemaHeaderField(docHeaderString), [d]);
+ else map.get(existingHeader)!.push(d);
+ return map;
+ },
+ new ObservableMap<SchemaHeaderField, Doc[]>(this.colHeaderData?.map(hdata => [hdata, []] as [SchemaHeaderField, Doc[]]) ?? [])
+ );
}
// Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
@computed get pivotField() {
@@ -89,7 +105,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
}
@computed get yMargin() {
- return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ return this._props.yMargin || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
}
@computed get gridGap() {
@@ -107,9 +123,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@computed get showAddAGroup() {
return this.pivotField && !this.chromeHidden;
}
+ @computed get availableWidth() {
+ return this._props.PanelWidth() - 2 * this.xMargin - (this.isStackingView ? this.gridGap * ((this.numGroupColumns || 1) - 1) : 0);
+ }
// columnWidth handles the margin on the left and right side of the documents
@computed get columnWidth() {
- const availableWidth = this._props.PanelWidth() - 2 * this.xMargin;
+ const availableWidth = this.availableWidth;
const cwid = availableWidth / (NumCast(this.Document._layout_columnCount) || this._props.PanelWidth() / NumCast(this.Document._layout_columnWidth, this._props.PanelWidth() / 4));
return Math.min(availableWidth, this.isStackingView ? availableWidth / (this.numGroupColumns || 1) : cwid - this.gridGap);
}
@@ -121,28 +140,17 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
- if (this.colHeaderData === undefined) {
- // 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.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
- }
}
+ availableWidthFn = () => this.availableWidth;
columnWidthFn = () => this.columnWidth;
columnDocHeightFn = (doc: Doc) => () => (this.isStackingView ? this.getDocHeight(doc)() : Math.min(this.getDocHeight(doc)(), this._props.PanelHeight()));
- // 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;
- this._renderCount < docs.length &&
- setTimeout(
- action(() => {
- this._renderCount = Math.min(docs.length, this._renderCount + 5);
- })
- );
+ this._renderCount < docs.length &&
+ setTimeout(action(() => (this._renderCount = Math.min(docs.length, this._renderCount + 5)))); // prettier-ignore
return docs.map((d, i) => {
// 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((this.getDocHeight(d)() + this.gridGap) / this.gridGap);
@@ -153,76 +161,28 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
margin: undefined,
transition: this.getDocTransition(d)(),
width: this.columnWidth,
- marginTop: i ? this.gridGap : 0,
height: this.getDocHeight(d)(),
zIndex: DocumentView.getFirstDocumentView(d)?.IsSelected ? 1000 : 0,
}
: { gridRowEnd: `span ${rowSpan}`, zIndex: DocumentView.getFirstDocumentView(d)?.IsSelected ? 1000 : 0 };
// So we're choosing whether we're going to render a column or a masonry doc
return (
- <div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
+ <div className={`collectionStackingView${this.isStackingView ? '-columnDoc' : '-masonryDoc'}`} key={d[Id]} style={style}>
{this.getDisplayDoc(d, this.getDocTransition(d), i)}
</div>
);
});
};
@action
- setDocHeight = (key: string, sectionHeight: number) => {
- this._heightMap.set(key, sectionHeight);
+ setDocHeight = (key: string, sectionHeight: number) => this._heightMap.set(key, sectionHeight);
+
+ setAutoHeight = () => {
+ const maxHeader = this.isStackingView ? this._colHdrRefs.reduce((p, r) => Math.max(p, DivHeight(r)), 0) + (this._colHdrRefs.length ? this.gridGap : 0) : 0;
+ const maxCol = this.isStackingView
+ ? this._colStackRefs.reduce((p, r) => Math.max(p, DivHeight(r)), 0) + this.gridGap
+ : this._colStackRefs.reduce((p, r) => p + DivHeight(r), this._addGroupRef.current ? DivHeight(this._addGroupRef.current) : 0);
+ this._props.setHeight?.(this.headerMargin + 2 * this.yMargin + maxCol + maxHeader);
};
-
- // 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.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
-
- if (this.colHeaderData === undefined) {
- setTimeout(() => {
- this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
- });
- return new Map<SchemaHeaderField, Doc[]>();
- }
- const colHeaderData = Array.from(this.colHeaderData);
- const fields = new Map<SchemaHeaderField, Doc[]>(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []]));
- let changed = false;
- this.filteredChildren.forEach(d => {
- const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.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 = colHeaderData.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
- if (existingHeader) {
- fields.get(existingHeader)!.push(d);
- } else {
- const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
- fields.set(newSchemaHeader, [d]);
- colHeaderData.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)
- .forEach(header => {
- fields.delete(header);
- colHeaderData.splice(colHeaderData.indexOf(header), 1);
- changed = true;
- });
- }
- changed &&
- setTimeout(
- action(() => this.colHeaderData?.splice(0, this.colHeaderData.length, ...colHeaderData)),
- 0
- );
- return fields;
- }
-
- setAutoHeight = () => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : 2 * this.yMargin + this._refList.reduce((p, r) => p + DivHeight(r), 0)));
observer = new ResizeObserver(this.setAutoHeight);
componentDidMount() {
@@ -232,9 +192,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// reset section headers when a new filter is inputted
this._disposers.pivotField = reaction(
() => this.pivotField,
- () => {
- this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List();
- }
+ () => (this.dataDoc[this.fieldKey + '_columnHeaders'] = new List())
);
// reset section headers when a new filter is inputted
this._disposers.width = reaction(
@@ -252,7 +210,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
);
this._disposers.refList = reaction(
- () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
+ () => ({ refList: this._colStackRefs.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
this.observer.disconnect();
if (autoHeight) refList.forEach(r => this.observer.observe(r));
@@ -381,8 +339,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
hideDecorations={this._props.childHideDecorations}
childFiltersByRanges={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- xPadding={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
- yPadding={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
+ xMargin={NumCast(this.layoutDoc._childXPadding, this._props.childXPadding)}
+ yMargin={NumCast(this.layoutDoc._childYPadding, this._props.childYPadding)}
rejectDrop={this._props.childRejectDrop}
addDocument={this._props.addDocument}
moveDocument={this._props.moveDocument}
@@ -409,11 +367,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
- const maxWidth = this.columnWidth / this.numGroupColumns;
if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
- return Math.min(NumCast(d._width), maxWidth);
+ return Math.min(NumCast(d._width), this.columnWidth);
}
- return maxWidth;
+ return this.columnWidth;
});
getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
getDocHeight = computedFn((d?: Doc) => () => {
@@ -424,7 +381,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
if (nw && nh) {
- const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
+ const colWid = this.columnWidth;
const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d)(), colWid);
return Math.min(maxHeight, (docWid * nh) / nw);
}
@@ -571,7 +528,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return (
<CollectionStackingViewFieldColumn
- refList={this._refList}
+ colStackRefs={this._colStackRefs}
+ colHeaderRefs={this._colHdrRefs}
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
@@ -613,8 +571,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
showHandle={first}
Doc={this.Document}
chromeHidden={this.chromeHidden}
+ panelWidth={this.availableWidthFn}
+ columnWidth={this.columnWidthFn}
pivotField={this.pivotField}
- refList={this._refList}
+ sectionRefs={this._colStackRefs}
key={heading ? heading.heading : ''}
rows={rows}
headings={this.headings}
@@ -635,9 +595,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
/// add a new group category (column) to the active set of note categories. (e.g., if the pivot field is 'transportation', groups might be 'car', 'plane', 'bike', etc)
@action
addGroup = (value: string) => {
+ if (!this.colHeaderData) {
+ this.dataDoc[this.fieldKey + '_columnHeaders'] = new List();
+ }
if (value && this.colHeaderData) {
- const schemaHdrField = new SchemaHeaderField(value);
- this.colHeaderData.push(schemaHdrField);
+ this.colHeaderData.push(new SchemaHeaderField(value));
return true;
}
return false;
@@ -745,7 +707,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this.fixWheelEvents(ele, this._props.isContentActive);
}}
style={{
- overflowY: this.isContentActive() ? 'auto' : 'hidden',
+ paddingBottom: this.yMargin,
+ paddingTop: this.yMargin,
+ paddingLeft: this.xMargin,
+ paddingRight: this.xMargin,
+ overflowY: this.isContentActive() && !this.layoutDoc._layout_autoHeight ? 'auto' : 'hidden',
background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string,
pointerEvents: this._props.pointerEvents?.() ?? this.backgroundEvents,
}}
@@ -756,11 +722,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
onContextMenu={this.onContextMenu}
onWheel={e => this.isContentActive() && e.stopPropagation()}>
{this.renderedSections}
- {!this.showAddAGroup ? null : (
- <div key={`${this.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
- <EditableView {...editableViewProps} />
- </div>
- )}
+ <div className="collectionStackingView-addGroupButton" ref={this._addGroupRef} style={{ width: !this.isStackingView ? '100%' : this.columnWidth, display: this.showAddAGroup ? undefined : 'none' }}>
+ <EditableView {...editableViewProps} />
+ </div>
+ <div style={{ right: 0, top: 0, position: 'absolute', display: !this.layoutDoc._pivotField ? 'none' : undefined }}>
+ <FieldsDropdown Doc={this.Document} isInactive={!this._props.isContentActive()} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
+ </div>
</div>
</div>
</>