aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/collectionSchema/CollectionSchemaView.tsx')
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx549
1 files changed, 369 insertions, 180 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 6a956f2ac..7c2cfd15f 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,45 +1,38 @@
+/* eslint-disable no-restricted-syntax */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Popup, PopupTrigger, Type } from 'browndash-components';
-import { ObservableMap, action, computed, makeObservable, observable, observe } from 'mobx';
+import { ObservableMap, action, computed, makeObservable, observable, observe, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
-import { Doc, DocListCast, Field, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
+import { returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
+import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
-import { DocUtils, Docs, DocumentOptions, FInfo } from '../../../documents/Documents';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { DragManager, dropActionType } from '../../../util/DragManager';
-import { SelectionManager } from '../../../util/SelectionManager';
+import { ColumnType } from '../../../../fields/SchemaHeaderField';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { DocUtils } from '../../../documents/DocUtils';
+import { Docs, DocumentOptions, FInfo } from '../../../documents/Documents';
+import { DragManager } from '../../../util/DragManager';
+import { dropActionType } from '../../../util/DropActionTypes';
import { SettingsManager } from '../../../util/SettingsManager';
import { undoBatch, undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { EditableView } from '../../EditableView';
import { ObservableReactComponent } from '../../ObservableReactComponent';
-import { DefaultStyleProvider, StyleProp } from '../../StyleProvider';
+import { StyleProp } from '../../StyleProp';
+import { DefaultStyleProvider } from '../../StyleProvider';
import { Colors } from '../../global/globalEnums';
import { DocumentView } from '../../nodes/DocumentView';
-import { FieldViewProps, FocusViewOptions } from '../../nodes/FieldView';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { FieldViewProps } from '../../nodes/FieldView';
+import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
-const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
-export enum ColumnType {
- Number,
- String,
- Boolean,
- Date,
- Image,
- RTF,
- Enumeration,
- Any,
-}
+const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
export const FInfotoColType: { [key: string]: ColumnType } = {
string: ColumnType.String,
@@ -56,7 +49,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', '
@observer
export class CollectionSchemaView extends CollectionSubView() {
private _keysDisposer: any;
- private _closestDropIndex: number = 0;
private _previewRef: HTMLDivElement | null = null;
private _makeNewColumn: boolean = false;
private _documentOptions: DocumentOptions = new DocumentOptions();
@@ -88,7 +80,13 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _menuValue: string = '';
@observable _filterColumnIndex: number | undefined = undefined;
@observable _filterSearchValue: string = '';
- @observable _selectedCell: [Doc, number] | undefined = undefined;
+ @observable _selectedCol: number = 0;
+ @observable _selectedCells: Array<Doc> = [];
+ @observable _mouseCoordinates = { x: 0, y: 0 };
+ @observable _lowestSelectedIndex = -1; // lowest index among selected rows; used to properly sync dragged docs with cursor position
+ @observable _relCursorIndex = -1; // cursor index relative to the current selected cells
+ @observable _draggedColIndex = 0;
+ @observable _colBeingDragged = false;
// target HTMLelement portal for showing a popup menu to edit cell values.
public get MenuTarget() {
@@ -96,14 +94,14 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get _selectedDocs() {
- const selected = SelectionManager.Docs.filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.Document));
+ // get all selected documents then filter out any whose parent is not this schema document
+ const selected = DocumentView.SelectedDocs().filter(doc => this.childDocs.includes(doc));
if (!selected.length) {
- for (const sel of SelectionManager.Docs) {
- const contextPath = DocumentManager.GetContextPath(sel, true);
- if (contextPath.includes(this.Document)) {
- const parentInd = contextPath.indexOf(this.Document);
- return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
- }
+ // if no schema doc is directly selected, test if a child of a schema doc is selected (such as in the preview window)
+ const childOfSchemaDoc = DocumentView.SelectedDocs().find(sel => DocumentView.getContextPath(sel, true).includes(this.Document));
+ if (childOfSchemaDoc) {
+ const contextPath = DocumentView.getContextPath(childOfSchemaDoc, true);
+ return [contextPath[contextPath.indexOf(childOfSchemaDoc) - 1]]; // the schema doc that is "selected" by virtue of one of its children being selected
}
}
return selected;
@@ -122,7 +120,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get columnKeys() {
- return Cast(this.layoutDoc.schema_columnKeys, listSpec('string'), defaultColumnKeys);
+ return StrListCast(this.layoutDoc.schema_columnKeys, defaultColumnKeys);
}
@computed get storedColumnWidths() {
@@ -132,12 +130,17 @@ export class CollectionSchemaView extends CollectionSubView() {
);
const totalWidth = widths.reduce((sum, width) => sum + width, 0);
+ // If the total width of all columns is not the width of the schema table minus the width of the row menu, resize them appropriately
if (totalWidth !== this.tableWidth - CollectionSchemaView._rowMenuWidth) {
return widths.map(w => (w / totalWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth));
}
return widths;
}
+ @computed get rowHeights() {
+ return this.childDocs.map(() => this.rowHeightFunc());
+ }
+
@computed get displayColumnWidths() {
return this._displayColumnWidths ?? this.storedColumnWidths;
}
@@ -157,8 +160,8 @@ export class CollectionSchemaView extends CollectionSubView() {
Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1]));
this._keysDisposer = observe(
this.dataDoc[this.fieldKey ?? 'data'] as List<Doc>,
- change => {
- switch (change.type as any) {
+ (change: any) => {
+ switch (change.type) {
case 'splice':
// prettier-ignore
(change as any).added.forEach((doc: Doc) => // for each document added
@@ -166,7 +169,9 @@ export class CollectionSchemaView extends CollectionSubView() {
Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them
!this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author'))))));
break;
- case 'update': //let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ case 'update': // let oldValue = change.oldValue; // fill this in if the entire child list will ever be reassigned with a new list
+ break;
+ default:
}
},
true
@@ -178,6 +183,9 @@ export class CollectionSchemaView extends CollectionSubView() {
document.removeEventListener('keydown', this.onKeyDown);
}
+ // ViewBoxInterface overrides
+ override isUnstyledView = returnTrue; // used by style provider : turns off opacity, animation effects, scaling
+
rowIndex = (doc: Doc) => this.sortedDocs.docs.indexOf(doc);
@action
@@ -190,13 +198,12 @@ export class CollectionSchemaView extends CollectionSubView() {
const lastIndex = this.rowIndex(lastDoc);
const curDoc = this.sortedDocs.docs[lastIndex];
if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
- !e.shiftKey && this.clearSelection();
const newDoc = this.sortedDocs.docs[lastIndex + 1];
if (this._selectedDocs.includes(newDoc)) {
- SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc));
+ this.deselectCell(curDoc);
} else {
- this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
- this._selectedCell && (this._selectedCell[0] = newDoc);
+ this.selectCell(newDoc, this._selectedCol, e.shiftKey, e.ctrlKey);
this.scrollToDoc(newDoc, {});
}
}
@@ -210,12 +217,12 @@ export class CollectionSchemaView extends CollectionSubView() {
const firstIndex = this.rowIndex(firstDoc);
const curDoc = this.sortedDocs.docs[firstIndex];
if (firstIndex > 0 && firstIndex < this.childDocs.length) {
- !e.shiftKey && this.clearSelection();
const newDoc = this.sortedDocs.docs[firstIndex - 1];
- if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
- else {
- this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
- this._selectedCell && (this._selectedCell[0] = newDoc);
+ if (this._selectedDocs.includes(newDoc)) {
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(curDoc));
+ this.deselectCell(curDoc);
+ } else {
+ this.selectCell(newDoc, this._selectedCol, e.shiftKey, e.ctrlKey);
this.scrollToDoc(newDoc, {});
}
}
@@ -224,17 +231,17 @@ export class CollectionSchemaView extends CollectionSubView() {
}
break;
case 'ArrowRight':
- if (this._selectedCell) {
- this._selectedCell[1] = Math.min(this._selectedCell[1] + 1, this.columnKeys.length - 1);
+ if (this._selectedCells) {
+ this._selectedCol = Math.min(this._colEles.length - 1, this._selectedCol + 1);
} else if (this._selectedDocs.length > 0) {
- this.selectCell(this._selectedDocs[0], 0);
+ this.selectCell(this._selectedDocs[0], 0, false, false);
}
break;
case 'ArrowLeft':
- if (this._selectedCell) {
- this._selectedCell[1] = Math.max(this._selectedCell[1] - 1, 0);
+ if (this._selectedCells) {
+ this._selectedCol = Math.max(0, this._selectedCol - 1);
} else if (this._selectedDocs.length > 0) {
- this.selectCell(this._selectedDocs[0], 0);
+ this.selectCell(this._selectedDocs[0], 0, false, false);
}
break;
case 'Backspace': {
@@ -242,12 +249,17 @@ export class CollectionSchemaView extends CollectionSubView() {
break;
}
case 'Escape': {
- this.deselectCell();
+ this.deselectAllCells();
+ break;
}
+ default:
}
}
};
+ @action
+ changeSelectedCellColumn = () => {};
+
@undoBatch
setColumnSort = (field: string | undefined, desc: boolean = false) => {
this.layoutDoc.sortField = field;
@@ -262,7 +274,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this.addNewKey(newKey, defaultVal);
}
- let currKeys = [...this.columnKeys];
+ const currKeys = this.columnKeys.slice(); // copy the column key array first, then change it.
currKeys[index] = newKey;
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -279,13 +291,16 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(0, 0, key);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@action
- addNewKey = (key: string, defaultVal: any) => this.childDocs.forEach(doc => (doc[DocData][key] = defaultVal));
+ addNewKey = (key: string, defaultVal: any) =>
+ this.childDocs.forEach(doc => {
+ doc[DocData][key] = defaultVal;
+ });
@undoBatch
removeColumn = (index: number) => {
@@ -295,7 +310,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const newDesiredTableWidth = currWidths.reduce((w, cw) => w + cw, 0);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths.map(w => (w / newDesiredTableWidth) * (this.tableWidth - CollectionSchemaView._rowMenuWidth)));
- let currKeys = this.columnKeys.slice();
+ const currKeys = this.columnKeys.slice();
currKeys.splice(index, 1);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
};
@@ -303,7 +318,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
startResize = (e: any, index: number) => {
this._displayColumnWidths = this.storedColumnWidths;
- setupMoveUpEvents(this, e, (e, delta) => this.resizeColumn(e, index), this.finishResize, emptyFunction);
+ setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction);
};
@action
@@ -342,34 +357,32 @@ export class CollectionSchemaView extends CollectionSubView() {
@undoBatch
moveColumn = (fromIndex: number, toIndex: number) => {
- let currKeys = this.columnKeys.slice();
+ if (this._selectedCol === fromIndex) this._selectedCol = toIndex;
+ else if (toIndex === this._selectedCol) this._selectedCol = fromIndex; // keeps selected cell consistent
+
+ const currKeys = this.columnKeys.slice();
currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnKeys = new List<string>(currKeys);
- let currWidths = this.storedColumnWidths.slice();
+ const currWidths = this.storedColumnWidths.slice();
currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
this.layoutDoc.schema_columnWidths = new List<number>(currWidths);
+
+ this._draggedColIndex = toIndex;
};
@action
dragColumn = (e: PointerEvent, index: number) => {
+ this._draggedColIndex = index;
+ this._colBeingDragged = true;
const dragData = new DragManager.ColumnDragData(index);
const dragEles = [this._colEles[index]];
this.childDocs.forEach(doc => dragEles.push(this._rowEles.get(doc).children[1].children[index]));
DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y);
-
- document.removeEventListener('pointermove', this.highlightDropColumn);
- document.addEventListener('pointermove', this.highlightDropColumn);
- let stopHighlight = (e: PointerEvent) => {
- document.removeEventListener('pointermove', this.highlightDropColumn);
- document.removeEventListener('pointerup', stopHighlight);
- };
- document.addEventListener('pointerup', stopHighlight);
-
return true;
};
- findDropIndex = (mouseX: number) => {
+ findColDropIndex = (mouseX: number) => {
let index: number | undefined;
this.displayColumnWidths.reduce((total, curr, i) => {
if (total <= mouseX && total + curr >= mouseX) {
@@ -377,28 +390,72 @@ export class CollectionSchemaView extends CollectionSubView() {
else index = i + 1;
}
return total + curr;
- }, CollectionSchemaView._rowMenuWidth);
+ }, 2 * CollectionSchemaView._rowMenuWidth); // probably prone to issues; find better implementation (!!!)
return index;
};
+ /**
+ * Calculates the relative index of the cursor in the group of selected rows, ie.
+ * if five rows are selected and the cursor is in the middle row, its relative index would be 2.
+ * Used to align actively dragged documents properly with the cursor.
+ * @param mouseY the initial Y position of the cursor on drag
+ */
@action
- highlightDropColumn = (e: PointerEvent) => {
- e.stopPropagation();
- const mouseX = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY)[0];
- const index = this.findDropIndex(mouseX);
+ setRelCursorIndex = (mouseY: number) => {
+ this._mouseCoordinates.y = mouseY; // updates this.rowDropIndex computed value to overwrite the old cached value
+
+ const rowHeight = CollectionSchemaView._rowHeight;
+ const adjInitMouseY = mouseY - rowHeight - 100; // rowHeight: height of the column menu cells | 100: height of the top menu
+ const yOffset = this._lowestSelectedIndex * rowHeight;
+
+ const heights = this._selectedDocs.map(() => this.rowHeightFunc());
+ let index: number = 0;
+ heights.reduce((total, curr, i) => {
+ if (total <= adjInitMouseY && total + curr >= adjInitMouseY) {
+ if (adjInitMouseY <= total + curr) index = i;
+ else index = i + 1;
+ }
+ return total + curr;
+ }, yOffset);
+ this._relCursorIndex = index;
+ };
+
+ findRowDropIndex = (mouseY: number) => {
+ const rowHeight = CollectionSchemaView._rowHeight;
+ let index: number = 0;
+ this.rowHeights.reduce((total, curr, i) => {
+ if (total <= mouseY && total + curr >= mouseY) {
+ if (mouseY <= total + curr) index = i;
+ else index = i + 1;
+ }
+ return total + curr;
+ }, rowHeight);
+
+ // fix index if selected rows are dragged out of bounds
+ let adjIndex = index - this._relCursorIndex;
+ const maxY = this.rowHeights.reduce((total, curr) => total + curr, 0) + rowHeight;
+ if (mouseY > maxY) adjIndex = this.childDocs.length - 1;
+ else if (adjIndex <= 0) adjIndex = 0;
+
+ return adjIndex;
+ };
+
+ highlightDraggedColumn = (index: number) =>
this._colEles.forEach((colRef, i) => {
- let leftStyle = '';
- let rightStyle = '';
- if (i + 1 === index) rightStyle = `solid 12px ${Colors.MEDIUM_BLUE}`;
- if (i === index && i === 0) leftStyle = `solid 12px ${Colors.MEDIUM_BLUE}`;
- colRef.style.borderLeft = leftStyle;
- colRef.style.borderRight = rightStyle;
- this.childDocs.forEach(doc => {
- this._rowEles.get(doc).children[1].children[i].style.borderLeft = leftStyle;
- this._rowEles.get(doc).children[1].children[i].style.borderRight = rightStyle;
+ const edgeStyle = i === index ? `solid 2px ${Colors.MEDIUM_BLUE}` : '';
+ const cellEles = [
+ colRef,
+ ...this.childDocs //
+ .filter(doc => i !== this._selectedCol || !this._selectedDocs.includes(doc))
+ .map(doc => this._rowEles.get(doc).children[1].children[i]),
+ ];
+ cellEles[0].style.borderTop = edgeStyle;
+ cellEles.forEach(ele => {
+ ele.style.borderLeft = edgeStyle;
+ ele.style.borderRight = edgeStyle;
});
+ cellEles.slice(-1)[0].style.borderBottom = edgeStyle;
});
- };
@action
addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref);
@@ -413,13 +470,16 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
- const rowDocView = DocumentManager.Instance.getDocumentView(doc);
- if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
+ addDocToSelection = (doc: Doc, extendSelection: boolean) => {
+ const rowDocView = DocumentView.getDocumentView(doc);
+ if (rowDocView) DocumentView.SelectView(rowDocView, extendSelection);
};
@action
- clearSelection = () => SelectionManager.DeselectAll();
+ clearSelection = () => {
+ DocumentView.DeselectAll();
+ this.deselectAllCells();
+ };
selectRows = (doc: Doc, lastSelected: Doc) => {
const index = this.rowIndex(doc);
@@ -428,63 +488,97 @@ export class CollectionSchemaView extends CollectionSubView() {
const endRow = Math.max(lastSelectedRow, index);
for (let i = startRow; i <= endRow; i++) {
const currDoc = this.sortedDocs.docs[i];
- if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true, i);
+ if (!this._selectedDocs.includes(currDoc)) {
+ this.selectCell(currDoc, this._selectedCol, false, true);
+ }
}
};
@action
- selectCell = (doc: Doc, index: number) => (this._selectedCell = [doc, index]);
+ selectCell = (doc: Doc, col: number, shiftKey: boolean, ctrlKey: boolean) => {
+ if (!shiftKey && !ctrlKey) this.clearSelection();
+ !this._selectedCells && (this._selectedCells = []);
+ !shiftKey && this._selectedCells && this._selectedCells.push(doc);
+ const index = this.rowIndex(doc);
+
+ if (!this) return;
+ const lastSelected = Array.from(this._selectedDocs).lastElement();
+ if (shiftKey && lastSelected && !this._selectedDocs.includes(doc)) this.selectRows(doc, lastSelected);
+ else if (ctrlKey) {
+ if (lastSelected && this._selectedDocs.includes(doc)) {
+ DocumentView.DeselectView(DocumentView.getFirstDocumentView(doc));
+ this.deselectCell(doc);
+ } else this.addDocToSelection(doc, true);
+ } else this.addDocToSelection(doc, false);
+ this._selectedCol = col;
+
+ if (this._lowestSelectedIndex === -1 || index < this._lowestSelectedIndex) this._lowestSelectedIndex = index;
+
+ // let selectedIndexes: Array<Number> = this._selectedCells.map(doc => this.rowIndex(doc));
+ };
@action
- deselectCell = () => (this._selectedCell = undefined);
+ deselectCell = (doc: Doc) => {
+ this._selectedCells && (this._selectedCells = this._selectedCells.filter(d => d !== doc));
+ if (this.rowIndex(doc) === this._lowestSelectedIndex) this._lowestSelectedIndex = Math.min(...this._selectedDocs.map(d => this.rowIndex(d)));
+ };
+
+ @action
+ deselectAllCells = () => {
+ this._selectedCells = [];
+ this._lowestSelectedIndex = -1;
+ };
sortedSelectedDocs = () => this.sortedDocs.docs.filter(doc => this._selectedDocs.includes(doc));
- setDropIndex = (index: number) => (this._closestDropIndex = index);
+ @computed
+ get rowDropIndex() {
+ const mouseY = this.ScreenToLocalBoxXf().transformPoint(this._mouseCoordinates.x, this._mouseCoordinates.y)[1];
+ return this.findRowDropIndex(mouseY);
+ }
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
if (de.complete.columnDragData) {
- const mouseX = this.ScreenToLocalBoxXf().transformPoint(de.x, de.y)[0];
- const index = this.findDropIndex(mouseX);
- this.moveColumn(de.complete.columnDragData.colIndex, index ?? de.complete.columnDragData.colIndex);
+ this._colBeingDragged = false;
+ e.stopPropagation();
this._colEles.forEach((colRef, i) => {
+ // style for menu cell
colRef.style.borderLeft = '';
colRef.style.borderRight = '';
+ colRef.style.borderTop = '';
+
this.childDocs.forEach(doc => {
- this._rowEles.get(doc).children[1].children[i].style.borderLeft = '';
- this._rowEles.get(doc).children[1].children[i].style.borderRight = '';
+ if (!(this._selectedDocs.includes(doc) && i === this._selectedCol)) {
+ this._rowEles.get(doc).children[1].children[i].style.borderLeft = '';
+ this._rowEles.get(doc).children[1].children[i].style.borderRight = '';
+ this._rowEles.get(doc).children[1].children[i].style.borderBottom = '';
+ }
});
});
-
- e.stopPropagation();
return true;
}
+
const draggedDocs = de.complete.docDragData?.draggedDocuments;
if (draggedDocs && super.onInternalDrop(e, de) && !this.sortField) {
- const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc));
- const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs];
- const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc));
- this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]);
+ const map = draggedDocs?.map(doc => this.rowIndex(doc));
+ console.log(map);
+ this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...this.sortedDocs.docs]);
this.clearSelection();
draggedDocs.forEach(doc => {
- const draggedView = DocumentManager.Instance.getFirstDocumentView(doc);
- if (draggedView) DocumentManager.Instance.RemoveView(draggedView);
- DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true));
+ DocumentView.addViewRenderedCb(doc, dv => dv.select(true));
});
+ this._lowestSelectedIndex = Math.min(...(draggedDocs?.map(doc => this.rowIndex(doc)) ?? []));
return true;
}
return false;
};
- onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
- };
-
+ onExternalDrop = (e: React.DragEvent) => super.onExternalDrop(e, {}, docs => docs.map(doc => this.addDocument(doc)));
onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
@action
- onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ onDividerMove = (e: PointerEvent) => {
const nativeWidth = this._previewRef!.getBoundingClientRect();
const minWidth = 40;
const maxWidth = 1000;
@@ -514,37 +608,76 @@ export class CollectionSchemaView extends CollectionSubView() {
const rect = found.getBoundingClientRect();
const localRect = this.ScreenToLocalBoxXf().transformBounds(rect.left, rect.top, rect.width, rect.height);
if (localRect.y < this.rowHeightFunc() || localRect.y + localRect.height > this._props.PanelHeight()) {
- let focusSpeed = options.zoomTime ?? 50;
+ const focusSpeed = options.zoomTime ?? 50;
smoothScroll(focusSpeed, this._tableContentRef!, localRect.y + this._tableContentRef!.scrollTop - this.rowHeightFunc(), options.easeFunc);
return focusSpeed;
}
}
+ return undefined;
};
@computed get fieldDefaultInput() {
switch (this._newFieldType) {
case ColumnType.Number:
- return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="number"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? 0}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
case ColumnType.Boolean:
return (
<>
- <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} />
+ <input
+ type="checkbox"
+ name=""
+ id=""
+ value={this._newFieldDefault}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.checked;
+ })}
+ />
{this._newFieldDefault ? 'true' : 'false'}
</>
);
case ColumnType.String:
- return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />;
+ return (
+ <input
+ type="text"
+ name=""
+ id=""
+ value={this._newFieldDefault ?? ''}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={action((e: any) => {
+ this._newFieldDefault = e.target.value;
+ })}
+ />
+ );
+ default:
+ return undefined;
}
}
onSearchKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'Enter':
- this._menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuKeys[0]) : action(() => (this._makeNewField = true))();
+ this._menuKeys.length > 0 && this._menuValue.length > 0
+ ? this.setKey(this._menuKeys[0])
+ : runInAction(() => {
+ this._makeNewField = true;
+ });
break;
case 'Escape':
this.closeColumnMenu();
break;
+ default:
}
};
@@ -559,7 +692,28 @@ export class CollectionSchemaView extends CollectionSubView() {
};
setColumnValues = (key: string, value: string) => {
- this.childDocs.forEach(doc => KeyValueBox.SetField(doc, key, value));
+ const selectedDocs: Doc[] = [];
+ this.childDocs.forEach(doc => {
+ const docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0);
+ if (docIsSelected) {
+ selectedDocs.push(doc);
+ }
+ });
+ if (selectedDocs.length === 1) {
+ this.childDocs.forEach(doc => Doc.SetField(doc, key, value));
+ } else {
+ selectedDocs.forEach(doc => Doc.SetField(doc, key, value));
+ }
+ return true;
+ };
+
+ setSelectedColumnValues = (key: string, value: string) => {
+ this.childDocs.forEach(doc => {
+ const docIsSelected = this._selectedCells && !(this._selectedCells?.filter(d => d === doc).length === 0);
+ if (docIsSelected) {
+ Doc.SetField(doc, key, value);
+ }
+ });
return true;
};
@@ -576,7 +730,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeColumnMenu = () => (this._columnMenuIndex = undefined);
+ closeColumnMenu = () => {
+ this._columnMenuIndex = undefined;
+ };
@action
openFilterMenu = (index: number) => {
@@ -585,7 +741,9 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- closeFilterMenu = () => (this._filterColumnIndex = undefined);
+ closeFilterMenu = () => {
+ this._filterColumnIndex = undefined;
+ };
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
@@ -615,7 +773,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
};
- getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field);
+ getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] === field);
removeFieldFilters = (field: string) => {
this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove'));
@@ -627,11 +785,14 @@ export class CollectionSchemaView extends CollectionSubView() {
case 'Escape':
this.closeFilterMenu();
break;
+ default:
}
};
@action
- updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterSearchValue = e.target.value);
+ updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
+ this._filterSearchValue = e.target.value;
+ };
@computed get newFieldMenu() {
return (
@@ -640,7 +801,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Number}
+ checked={this._newFieldType === ColumnType.Number}
onChange={action(() => {
this._newFieldType = ColumnType.Number;
this._newFieldDefault = 0;
@@ -652,7 +813,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.Boolean}
+ checked={this._newFieldType === ColumnType.Boolean}
onChange={action(() => {
this._newFieldType = ColumnType.Boolean;
this._newFieldDefault = false;
@@ -664,7 +825,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- checked={this._newFieldType == ColumnType.String}
+ checked={this._newFieldType === ColumnType.String}
onChange={action(() => {
this._newFieldType = ColumnType.String;
this._newFieldDefault = '';
@@ -676,7 +837,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="schema-key-warning">{this._newFieldWarning}</div>
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action(() => {
if (this.documentKeys.includes(this._menuValue)) {
this._newFieldWarning = 'Field already exists';
} else if (this._menuValue.length === 0) {
@@ -703,7 +864,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div className="schema-key-search">
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action((e: any) => {
e.stopPropagation();
this._makeNewField = true;
})}>
@@ -741,7 +902,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get renderColumnMenu() {
- const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
+ const x = this._columnMenuIndex! === -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
return (
<div className="schema-column-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
<input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
@@ -750,7 +911,6 @@ export class CollectionSchemaView extends CollectionSubView() {
);
}
get renderKeysMenu() {
- console.log('RNDERMENUT:' + this._columnMenuIndex);
return (
<div className="schema-column-menu" style={{ left: 0, minWidth: CollectionSchemaView._minColWidth }}>
<input className="schema-key-search-input" type="text" onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
@@ -780,17 +940,11 @@ export class CollectionSchemaView extends CollectionSubView() {
}
return (
<div key={key} className="schema-filter-option">
- <input
+ <input //
type="checkbox"
onPointerDown={e => e.stopPropagation()}
onClick={e => e.stopPropagation()}
- onChange={action(e => {
- if (e.target.checked) {
- Doc.setDocFilter(this.Document, columnKey, key, 'check');
- } else {
- Doc.setDocFilter(this.Document, columnKey, key, 'remove');
- }
- })}
+ onChange={e => Doc.setDocFilter(this.Document, columnKey, key, e.target.checked ? 'check' : 'remove')}
checked={bool}
/>
<span style={{ paddingLeft: 4 }}>{key}</span>
@@ -807,7 +961,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this.renderFilterOptions}
<div
className="schema-column-menu-button"
- onPointerDown={action(e => {
+ onPointerDown={action((e: any) => {
e.stopPropagation();
this.closeFilterMenu();
})}>
@@ -817,22 +971,41 @@ export class CollectionSchemaView extends CollectionSubView() {
);
}
+ @action
+ onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
+ if (DragManager.docsBeingDragged.length) {
+ this._mouseCoordinates = { x: e.clientX, y: e.clientY };
+ }
+ if (this._colBeingDragged) {
+ const newIndex = this.findColDropIndex(e.clientX);
+ if (newIndex !== this._draggedColIndex) this.moveColumn(this._draggedColIndex, newIndex ?? this._draggedColIndex);
+ this._draggedColIndex = newIndex || this._draggedColIndex;
+ this.highlightDraggedColumn(newIndex ?? this._draggedColIndex);
+ }
+ };
+
@computed get sortedDocs() {
+ const draggedDocs = this.isContentActive() ? DragManager.docsBeingDragged : [];
const field = StrCast(this.layoutDoc.sortField);
- const desc = BoolCast(this.layoutDoc.sortDesc);
+ const desc = BoolCast(this.layoutDoc.sortDesc); // is this an ascending or descending sort
+ const staticDocs = this.childDocs.filter(d => !draggedDocs.includes(d));
const docs = !field
- ? this.childDocs
- : [...this.childDocs].sort((docA, docB) => {
- const aStr = Field.toString(docA[field] as Field);
- const bStr = Field.toString(docB[field] as Field);
- var out = 0;
+ ? staticDocs
+ : [...staticDocs].sort((docA, docB) => {
+ // this sorts the documents based on the selected field. returning -1 for a before b, 0 for a = b, 1 for a > b
+ const aStr = Field.toString(docA[field] as FieldType);
+ const bStr = Field.toString(docB[field] as FieldType);
+ let out = 0;
if (aStr < bStr) out = -1;
if (aStr > bStr) out = 1;
if (desc) out *= -1;
return out;
});
+
+ docs.splice(this.rowDropIndex, 0, ...draggedDocs);
return { docs };
}
+
rowHeightFunc = () => (BoolCast(this.layoutDoc._schema_singleLine) ? CollectionSchemaView._rowSingleLineHeight : CollectionSchemaView._rowHeight);
sortedDocsFunc = () => this.sortedDocs;
isContentActive = () => this._props.isSelected() || this._props.isContentActive();
@@ -842,8 +1015,8 @@ export class CollectionSchemaView extends CollectionSubView() {
_oldWheel: any;
render() {
return (
- <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)}>
- <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
+ <div className="collectionSchemaView" ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)} onPointerMove={e => this.onPointerMove(e)}>
+ <div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }} />
<div
className="schema-table"
style={{ width: `calc(100% - ${this.previewWidth}px)` }}
@@ -859,7 +1032,7 @@ export class CollectionSchemaView extends CollectionSubView() {
placement="right"
background={SettingsManager.userBackgroundColor}
color={SettingsManager.userColor}
- toggle={<FontAwesomeIcon onPointerDown={e => this.openColumnMenu(-1, true)} icon="plus" />}
+ toggle={<FontAwesomeIcon onPointerDown={() => this.openColumnMenu(-1, true)} icon="plus" />}
trigger={PopupTrigger.CLICK}
type={Type.TERT}
isOpen={this._columnMenuIndex !== -1 ? false : undefined}
@@ -868,6 +1041,7 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this.columnKeys.map((key, index) => (
<SchemaColumnHeader
+ // eslint-disable-next-line react/no-array-index-key
key={index}
columnIndex={index}
columnKeys={this.columnKeys}
@@ -887,28 +1061,42 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this._columnMenuIndex !== undefined && this._columnMenuIndex !== -1 && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
- <CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
+ {
+ // eslint-disable-next-line no-use-before-define
+ <CollectionSchemaViewDocs
+ schema={this}
+ childDocs={this.sortedDocsFunc}
+ rowHeight={this.rowHeightFunc}
+ setRef={(ref: HTMLDivElement | null) => {
+ this._tableContentRef = ref;
+ }}
+ />
+ }
{this.layoutDoc.chromeHidden ? null : (
<div className="schema-add">
<EditableView
GetValue={returnEmptyString}
SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
placeholder={"Type text to create note or ':' to create specific type"}
- contents={'+ New Node'}
+ contents="+ New Node"
menuCallback={this.menuCallback}
height={CollectionSchemaView._newNodeInputHeight}
/>
</div>
)}
</div>
- {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
+ {this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown} />}
{this.previewWidth > 0 && (
- <div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
+ <div
+ style={{ width: `${this.previewWidth}px` }}
+ ref={ref => {
+ this._previewRef = ref;
+ }}>
{Array.from(this._selectedDocs).lastElement() && (
<DocumentView
Document={Array.from(this._selectedDocs).lastElement()}
fitContentsToBox={returnTrue}
- dontCenter={'y'}
+ dontCenter="y"
onClickScriptDisable="always"
focus={emptyFunction}
defaultDoubleClick={returnIgnore}
@@ -939,28 +1127,6 @@ export class CollectionSchemaView extends CollectionSubView() {
}
}
-interface CollectionSchemaViewDocsProps {
- schema: CollectionSchemaView;
- setRef: (ref: HTMLDivElement | null) => void;
- childDocs: () => { docs: Doc[] };
- rowHeight: () => number;
-}
-
-@observer
-class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
- render() {
- return (
- <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
- {this.props.childDocs().docs.map((doc: Doc, index: number) => (
- <div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
- <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
- </div>
- ))}
- </div>
- );
- }
-}
-
interface CollectionSchemaViewDocProps {
schema: CollectionSchemaView;
index: number;
@@ -981,10 +1147,12 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
if (property === StyleProp.Opacity) return 1;
return DefaultStyleProvider(doc, props, property);
};
+ isRowContentActive = () => this._props.schema.isContentActive() || this._props.schema._props.isSelected() || this._props.schema._props.isAnyChildContentActive();
render() {
return (
<DocumentView
key={this._props.doc[Id]}
+ // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props.schema._props}
containerViewPath={this._props.schema.childContainerViewPath}
LayoutTemplate={this._props.schema._props.childLayoutTemplate}
@@ -1004,16 +1172,37 @@ class CollectionSchemaViewDoc extends ObservableReactComponent<CollectionSchemaV
searchFilterDocs={this._props.schema.searchFilterDocs}
rootSelected={this._props.schema.rootSelected}
ScreenToLocalTransform={this.screenToLocalXf}
- dragWhenActive={true}
+ dragWhenActive
isDocumentActive={this._props.schema._props.childDocumentsActive?.() ? this._props.schema._props.isDocumentActive : this._props.schema.isContentActive}
- isContentActive={emptyFunction}
+ isContentActive={this.isRowContentActive}
whenChildContentsActiveChanged={this._props.schema._props.whenChildContentsActiveChanged}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
- layout_fitWidth={returnTrue}
+ hideDecorations
+ hideTitle
+ hideDocumentButtonBar
+ hideLinkAnchors
+ fitWidth={returnTrue}
/>
);
}
}
+interface CollectionSchemaViewDocsProps {
+ schema: CollectionSchemaView;
+ setRef: (ref: HTMLDivElement | null) => void;
+ childDocs: () => { docs: Doc[] };
+ rowHeight: () => number;
+}
+
+@observer
+class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
+ render() {
+ return (
+ <div className="schema-table-content" ref={this.props.setRef} style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + this.props.rowHeight()}px)` }}>
+ {this.props.childDocs().docs.map((doc: Doc, index: number) => (
+ <div key={doc[Id]} className="schema-row-wrapper" style={{ height: this.props.rowHeight() }}>
+ <CollectionSchemaViewDoc doc={doc} schema={this.props.schema} index={index} rowHeight={this.props.rowHeight} />
+ </div>
+ ))}
+ </div>
+ );
+ }
+}