aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ClientUtils.ts3
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx42
-rw-r--r--src/client/views/collections/CollectionStackingView.scss5
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx154
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx30
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx25
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts14
7 files changed, 111 insertions, 162 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts
index cc8b715b4..8eb1a39ec 100644
--- a/src/ClientUtils.ts
+++ b/src/ClientUtils.ts
@@ -670,7 +670,8 @@ export function setupMoveUpEvents(
}
export function DivHeight(ele: HTMLElement | null): number {
- return ele ? Number(getComputedStyle(ele).height.replace('px', '')) : 0;
+ const hgt = ele ? Number(getComputedStyle(ele).height.replace('px', '')) : 0;
+ return Number.isNaN(hgt) ? 0 : hgt;
}
export function DivWidth(ele: HTMLElement | null): number {
return ele ? Number(getComputedStyle(ele).width.replace('px', '')) : 0;
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 89ccf5a0f..d1f7971d4 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -28,12 +28,14 @@ interface CMVFieldRowProps {
headingObject: SchemaHeaderField | undefined;
docList: Doc[];
parent: CollectionStackingView;
+ panelWidth: () => number;
+ columnWidth: () => number;
pivotField: string;
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
createDropTarget: (ele: HTMLDivElement) => void;
screenToLocalTransform: () => Transform;
setDocHeight: (key: string, thisHeight: number) => void;
- refList: Element[];
+ sectionRefs: Element[];
showHandle: boolean;
}
@@ -74,7 +76,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
createRowDropRef = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Doc);
- else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ else if (this._ele) this.props.sectionRefs.splice(this.props.sectionRefs.indexOf(this._ele), 1);
this._ele = ele;
};
@action
@@ -82,10 +84,10 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
this.heading = this._props.headingObject?.heading || '';
this.color = this._props.headingObject?.color || '#f1efeb';
this.collapsed = this._props.headingObject?.collapsed || false;
- this._ele && this.props.refList.push(this._ele);
+ this._ele && this.props.sectionRefs.push(this._ele);
}
componentWillUnmount() {
- this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ this._ele && this.props.sectionRefs.splice(this.props.sectionRefs.indexOf(this._ele), 1);
this._ele = null;
}
@@ -128,10 +130,8 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const key = this._props.pivotField;
const castedValue = this.getValue(value);
if (castedValue) {
- if (this._props.parent.colHeaderData) {
- if (this._props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
- return false;
- }
+ if (this._props.parent.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) || 0 > -1) {
+ return false;
}
key && this._props.docList.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
this._heading = castedValue.toString();
@@ -251,20 +251,11 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
textCallback = (/* char: string */) => this.addDocument('', false);
@computed get contentLayout() {
- const rows = Math.max(1, Math.min(this._props.docList.length, Math.floor((this._props.parent._props.PanelWidth() - 2 * this._props.parent.xMargin) / (this._props.parent.columnWidth + this._props.parent.gridGap))));
- const showChrome = !this._props.chromeHidden;
- const stackPad = showChrome ? `0px ${this._props.parent.xMargin}px` : `${this._props.parent.yMargin}px ${this._props.parent.xMargin}px 0px ${this._props.parent.xMargin}px `;
+ const rows = Math.max(1, Math.min(this._props.docList.length, Math.floor(this._props.panelWidth() / this._props.columnWidth())));
return this.collapsed ? null : (
<div style={{ position: 'relative' }}>
- {showChrome ? (
- <div
- className="collectionStackingView-addDocumentButton"
- style={
- {
- // width: style.columnWidth / style.numGroupColumns,
- // padding: `${NumCast(this._props.parent.layoutDoc._yPadding, this._props.parent.yMargin)}px 0px 0px 0px`,
- }
- }>
+ {!this._props.chromeHidden ? (
+ <div className="collectionStackingView-addDocumentButton">
<EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents="+ NEW" />
</div>
) : null}
@@ -272,11 +263,9 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
className="collectionStackingView-masonryGrid"
ref={this._contRef}
style={{
- padding: stackPad,
minHeight: this._props.showHandle && this._props.parent._props.isContentActive() ? '10px' : undefined,
- width: this._props.parent.NodeWidth,
gridGap: this._props.parent.gridGap,
- gridTemplateColumns: numberRange(rows).reduce(list => list + ` ${this._props.parent.columnWidth}px`, ''),
+ gridTemplateColumns: numberRange(rows).reduce(list => list + ` ${this._props.columnWidth()}px`, ''),
}}>
{this._props.parent.children(this._props.docList)}
</div>
@@ -339,7 +328,12 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
render() {
const background = this._background;
return (
- <div className="collectionStackingView-masonrySection" style={{ width: this._props.parent.NodeWidth, background }} ref={this.createRowDropRef} onPointerEnter={this.pointerEnteredRow} onPointerLeave={this.pointerLeaveRow}>
+ <div
+ className="collectionStackingView-masonrySection"
+ style={{ width: this._props.pivotField ? this._props.panelWidth() : '100%', background }}
+ ref={this.createRowDropRef}
+ onPointerEnter={this.pointerEnteredRow}
+ onPointerLeave={this.pointerLeaveRow}>
{this.headingView}
{this.contentLayout}
</div>
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 05ac52ff9..d6e4943ff 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -2,10 +2,12 @@
.collectionMasonryView {
display: inline;
+ flex-wrap: wrap;
}
.collectionStackingView {
display: flex;
+ justify-content: space-between;
}
.collectionStackingMasonry-cont {
@@ -56,7 +58,6 @@
top: 0;
overflow-y: auto;
overflow-x: hidden;
- flex-wrap: wrap;
transition: top 0.5s;
> div {
@@ -210,7 +211,6 @@
.collectionStackingView-sectionHeader {
text-align: center;
margin: auto;
- margin-bottom: 10px;
background: global.$medium-gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
@@ -367,7 +367,6 @@
.collectionStackingView-addGroupButton {
display: flex;
overflow: hidden;
- margin: auto;
width: 90%;
overflow: ellipses;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 25a222cbb..cc514f262 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,10 +68,25 @@ 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);
}
+
+ @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() {
return StrCast(this.layoutDoc._pivotField);
@@ -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));
@@ -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}
@@ -636,8 +596,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@action
addGroup = (value: string) => {
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 +704,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 +719,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} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
+ </div>
</div>
</div>
</>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 345f60e75..8c7cb8276 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,5 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DivHeight, DivWidth, returnEmptyString, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
@@ -50,14 +50,14 @@ interface CSVFieldColumnProps {
addDocument: (doc: Doc | Doc[]) => boolean;
createDropTarget: (ele: HTMLDivElement) => void;
screenToLocalTransform: () => Transform;
- refList: HTMLElement[];
+ colStackRefs: HTMLElement[];
+ colHeaderRefs: HTMLElement[];
}
@observer
export class CollectionStackingViewFieldColumn extends ObservableReactComponent<CSVFieldColumnProps> {
private dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
- private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
@observable _background = 'inherit';
@observable _paletteOn = false;
@observable _heading = '';
@@ -72,6 +72,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
_ele: HTMLElement | null = null;
_eleMasonrySingle = React.createRef<HTMLDivElement>();
+ _headerRef: HTMLDivElement | null = null;
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetDropAction: dropActionType) => {
const dragData = de.complete.docDragData;
@@ -93,13 +94,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
- else if (this._eleMasonrySingle.current) this.props.refList.splice(this.props.refList.indexOf(this._eleMasonrySingle.current), 1);
+ else if (this._eleMasonrySingle.current) runInAction(() => this.props.colStackRefs.splice(this.props.colStackRefs.indexOf(this._eleMasonrySingle.current!), 1));
this._ele = ele;
};
@action
componentDidMount() {
- this._eleMasonrySingle.current && this.props.refList.push(this._eleMasonrySingle.current);
+ this._eleMasonrySingle.current && this.props.colStackRefs.push(this._eleMasonrySingle.current);
this._disposers.collapser = reaction(
() => this._props.headingObject?.collapsed,
collapsed => { this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false; }, // prettier-ignore
@@ -108,7 +109,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
}
componentWillUnmount() {
this._disposers.collapser?.();
- this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ this._ele && runInAction(() => this.props.colStackRefs.splice(this.props.colStackRefs.indexOf(this._ele!), 1));
this._ele = null;
}
@@ -192,7 +193,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
value = typeof value === 'string' ? `"${value}"` : value;
embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this._props.pivotField} === ${value}`, { doc: Doc.name });
if (embedding.viewSpecScript) {
- DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
+ DragManager.StartDocumentDrag([this._headerRef!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
return true;
}
return false;
@@ -314,9 +315,14 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
<div
key={heading}
className="collectionStackingView-sectionHeader"
- ref={this._headerRef}
+ ref={r => {
+ if (this._headerRef && this._props.colHeaderRefs.includes(this._headerRef)) this._props.colHeaderRefs.splice(this._props.colStackRefs.indexOf(this._headerRef), 1);
+ r && this._props.colHeaderRefs.push(r);
+ this._headerRef = r;
+ }}
style={{
- marginTop: this._props.yMargin,
+ marginTop: 0,
+ marginBottom: this._props.gridGap,
width: this._props.columnWidth,
}}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
@@ -367,7 +373,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ref={this._eleMasonrySingle}
className="collectionStackingView-masonrySingle"
style={{
- padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
+ padding: `${columnYMargin}px ${0}px ${0}px ${0}px`,
margin: this._props.dontCenter.includes('x') ? undefined : 'auto',
height: 'max-content',
position: 'relative',
@@ -400,15 +406,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
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"
key={heading}
style={{
- width: `${100 / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1)}%`,
+ width: this._props.columnWidth,
height: undefined,
background: this._background,
}}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 4189fcc0c..fea2986c7 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1522,7 +1522,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
const { $from } = this.EditorView.state.selection;
- const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
+ const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() });
const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
if (selLoadChar === 'Enter') {
@@ -1549,7 +1549,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const { text, paragraph } = schema.nodes;
const selNode = this.EditorView.state.selection.$anchor.node();
if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
- const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
+ const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() })];
this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
}
}
@@ -1685,7 +1685,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (e.clientX > boundsRect.left && e.clientX < boundsRect.right && e.clientY > boundsRect.bottom) {
// if we clicked below the last prosemirror div, then set the selection to be the end of the document
editorView.focus();
- editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
+ // editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
}
} else if (node && [editorView.state.schema.nodes.ordered_list, editorView.state.schema.nodes.listItem].includes(node.type) && node !== (editorView.state.selection as NodeSelection)?.node && pcords) {
editorView.dispatch(editorView.state.tr.setSelection(NodeSelection.create(editorView.state.doc, pcords.pos)));
@@ -1814,7 +1814,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return;
}
if (this._enteringStyle && 'tix!'.includes(e.key)) {
- const tag = e.key === 't' ? 'todo' : e.key === 'i' ? 'ignore' : e.key === 'x' ? 'disagree' : e.key === '!' ? 'important' : '??';
const node = state.selection.$from.nodeAfter;
const start = state.selection.from;
const end = state.selection.to;
@@ -1823,9 +1822,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
StopEvent(e);
_editorView.dispatch(
state.tr
- .removeMark(start, end, schema.marks.user_mark)
- .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) }))
- .addMark(start, end, schema.marks.user_tag.create({ userid: ClientUtils.CurrentUserEmail(), tag, modified: Math.round(Date.now() / 1000 / 60) }))
+ .removeMark(start, end, schema.marks.user_mark) //
+ .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
);
return;
}
@@ -1858,9 +1856,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
break;
default:
if ([AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.Document))) {
- const modified = Math.floor(Date.now() / 1000);
- const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark && m.attrs.modified === modified);
- _editorView.dispatch(state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified })));
+ const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark);
+ _editorView.dispatch(
+ state.tr
+ .removeStoredMark(schema.marks.user_mark) //
+ .addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
+ );
}
break;
}
@@ -1884,14 +1885,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yMargin || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (this.EditorView && children && !SnappingManager.IsDragging) {
- const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
+ const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), 0) ?? 0;
const toNum = (val: string) => Number(val.replace('px', ''));
const toHgt = (node: Element): number => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
const childHeight = height === 'auto' ? getChildrenHeights(Array.from(node.children)) : toNum(height);
return childHeight + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
};
- const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children);
+ const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children) + margins;
const scrollHeight = this.ProseRef && proseHeight;
if (this._props.setHeight && !this._props.suppressSetHeight && scrollHeight && !this._props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index b7dae1ca3..333ee6be8 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -330,20 +330,6 @@ export const marks: { [index: string]: MarkSpec } = {
return ['span', { class: 'UM-' + uid + remote + ' UM-min-' + min + ' UM-hr-' + hr + ' UM-day-' + day }, 0];
},
},
- // the id of the user who entered the text
- user_tag: {
- attrs: {
- userid: { default: '' },
- modified: { default: 'when?' }, // 1 second intervals since 1970
- tag: { default: '' },
- },
- group: 'inline',
- inclusive: false,
- toDOM: node => {
- const uid = node.attrs.userid.replace('.', '').replace('@', '');
- return ['span', { class: 'UT-' + uid + ' UT-' + node.attrs.tag }, 0];
- },
- },
// :: MarkSpec Code font mark. Represented as a `<code>` element.
code: {