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.tsx318
1 files changed, 130 insertions, 188 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 151e6cbef..ad31113a2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,15 +1,17 @@
import React = require('react');
-import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, ObservableMap, trace, untracked } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { listSpec } from '../../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTransparent, returnTrue, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
import { Docs, DocUtils } from '../../../documents/Documents';
+import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { SelectionManager } from '../../../util/SelectionManager';
import { Transform } from '../../../util/Transform';
@@ -17,18 +19,14 @@ import { undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { EditableView } from '../../EditableView';
-import { DocComponentView, DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../../nodes/DocumentView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { KeyValueBox } from '../../nodes/KeyValueBox';
+import { DefaultStyleProvider } from '../../StyleProvider';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
-import { DefaultStyleProvider } from '../../StyleProvider';
-import { DocumentManager } from '../../../util/DocumentManager';
-import { ScriptField } from '../../../../fields/ScriptField';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { IconButton } from 'browndash-components';
-import { KeyValueBox } from '../../nodes/KeyValueBox';
export enum ColumnType {
Number,
@@ -43,7 +41,6 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate',
@observer
export class CollectionSchemaView extends CollectionSubView() {
private _ref: HTMLDivElement | null = null;
- private _selectedDocSortedArray: Doc[] = [];
private _closestDropIndex: number = 0;
private _previewRef: HTMLDivElement | null = null;
private _makeNewColumn: boolean = false;
@@ -53,13 +50,11 @@ export class CollectionSchemaView extends CollectionSubView() {
public static _rowMenuWidth: number = 100;
public static _previewDividerWidth: number = 4;
- @observable _lastSelectedRow: number | undefined;
@computed get _selectedDocs() {
- return new Set(SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document)));
+ return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document));
}
@observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>();
@observable _colEles: HTMLDivElement[] = [];
- @observable _isDragging: boolean = false;
@observable _displayColumnWidths: number[] | undefined;
@observable _columnMenuIndex: number | undefined;
@observable _menuOptions: string[] = [];
@@ -133,7 +128,7 @@ export class CollectionSchemaView extends CollectionSubView() {
}
componentDidMount() {
- this.props.setContentView?.(this as DocComponentView);
+ this.props.setContentView?.(this);
document.addEventListener('keydown', this.onKeyDown);
}
@@ -143,24 +138,36 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
onKeyDown = (e: KeyboardEvent) => {
- if (this._selectedDocs.size > 0) {
- if (e.key == 'ArrowDown') {
- const lastDoc = Array.from(this._selectedDocs.values()).lastElement();
- const lastIndex = this.rowIndex(lastDoc);
- if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
- !e.shiftKey && this.clearSelection();
- const newDoc = this.childDocs[lastIndex + 1];
- this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
- }
- }
- if (e.key == 'ArrowUp') {
- const firstDoc = Array.from(this._selectedDocs.values())[0];
- const firstIndex = this.rowIndex(firstDoc);
- if (firstIndex > 0 && firstIndex < this.childDocs.length) {
- !e.shiftKey && this.clearSelection();
- const newDoc = this.childDocs[firstIndex - 1];
- this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
- }
+ if (this._selectedDocs.length > 0) {
+ switch (e.key) {
+ case 'ArrowDown':
+ {
+ const lastDoc = this._selectedDocs.lastElement();
+ const lastIndex = this.rowIndex(lastDoc);
+ const curDoc = this.childDocs[lastIndex];
+ if (lastIndex >= 0 && lastIndex < this.childDocs.length - 1) {
+ !e.shiftKey && this.clearSelection();
+ const newDoc = this.childDocs[lastIndex + 1];
+ if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+ else this.addDocToSelection(newDoc, e.shiftKey, lastIndex + 1);
+ }
+ e.stopPropagation();
+ }
+ break;
+ case 'ArrowUp':
+ {
+ const firstDoc = this._selectedDocs.lastElement();
+ const firstIndex = this.rowIndex(firstDoc);
+ const curDoc = this.childDocs[firstIndex];
+ if (firstIndex > 0 && firstIndex < this.childDocs.length) {
+ !e.shiftKey && this.clearSelection();
+ const newDoc = this.childDocs[firstIndex - 1];
+ if (this._selectedDocs.includes(newDoc)) SelectionManager.DeselectView(DocumentManager.Instance.getFirstDocumentView(curDoc));
+ else this.addDocToSelection(newDoc, e.shiftKey, firstIndex - 1);
+ }
+ e.stopPropagation();
+ }
+ break;
}
}
};
@@ -326,60 +333,25 @@ export class CollectionSchemaView extends CollectionSubView() {
addDocToSelection = (doc: Doc, extendSelection: boolean, index: number) => {
const rowDocView = DocumentManager.Instance.getDocumentView(doc);
if (rowDocView) SelectionManager.SelectView(rowDocView, extendSelection);
- this._lastSelectedRow = index;
- };
-
- @action
- removeDocFromSelection = (doc: Doc) => {
- const rowDocView = DocumentManager.Instance.getDocumentView(doc);
- if (rowDocView) SelectionManager.DeselectView(rowDocView);
- if (this._selectedDocs.size === 0) {
- this._lastSelectedRow = undefined;
- }
};
@action
- clearSelection = () => {
- SelectionManager.DeselectAll();
- this._lastSelectedRow = undefined;
- };
-
- rowOnClickScript = ScriptField.MakeFunction('scriptContext.selectRow(self, shiftKey, ctrlKey || metaKey)', { scriptContext: 'any', shiftKey: 'boolean', ctrlKey: 'boolean', metaKey: 'boolean' })!;
-
- @action
- selectRow = (doc: Doc, shift: boolean, ctrl: boolean) => {
- const index = this.childDocs.indexOf(doc);
- if (index < 0) return;
- if (shift && this._lastSelectedRow !== undefined) {
- const startRow = Math.min(this._lastSelectedRow, index);
- const endRow = Math.max(this._lastSelectedRow, index);
- for (let i = startRow; i <= endRow; i++) {
- const currDoc = this.childDocs[i];
- if (!this._selectedDocs.has(currDoc)) this.addDocToSelection(currDoc, true, i);
- }
- this._lastSelectedRow = index;
- } else if (ctrl) {
- if (!this._selectedDocs.has(doc)) {
- this.addDocToSelection(doc, true, index);
- } else {
- this.removeDocFromSelection(doc);
- }
- } else {
- if (!this._selectedDocs.has(doc)) {
- this.clearSelection();
- this.addDocToSelection(doc, false, index);
- }
+ clearSelection = () => SelectionManager.DeselectAll();
+
+ selectRows = (rootDoc: Doc, lastSelected: Doc) => {
+ const index = this.childDocs.indexOf(rootDoc);
+ const lastSelectedRow = this.childDocs.indexOf(lastSelected);
+ const startRow = Math.min(lastSelectedRow, index);
+ const endRow = Math.max(lastSelectedRow, index);
+ for (let i = startRow; i <= endRow; i++) {
+ const currDoc = this.childDocs[i];
+ if (!this._selectedDocs.includes(currDoc)) this.addDocToSelection(currDoc, true, i);
}
};
- @action
- sortedSelectedDocs = (): Doc[] => {
- return this.childDocs.filter(doc => this._selectedDocs.has(doc));
- };
+ sortedSelectedDocs = () => this.childDocs.filter(doc => this._selectedDocs.includes(doc));
- setDropIndex = (index: number) => {
- this._closestDropIndex = index;
- };
+ setDropIndex = (index: number) => (this._closestDropIndex = index);
@action
onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -394,17 +366,20 @@ export class CollectionSchemaView extends CollectionSubView() {
return total + curr;
}, CollectionSchemaView._rowMenuWidth);
this.swapColumns(de.complete.columnDragData.colIndex, i);
+ e.stopPropagation();
return true;
}
- if (super.onInternalDrop(e, de)) {
- this._isDragging = false;
- const pushedDocs: Doc[] = this.childDocs.filter((doc: Doc, index: number) => index >= this._closestDropIndex && !this._selectedDocs.has(doc));
+ const draggedDocs = de.complete.docDragData?.draggedDocuments;
+ if (draggedDocs && super.onInternalDrop(e, de)) {
+ const pushedDocs = this.childDocs.filter((doc, index) => index >= this._closestDropIndex && !draggedDocs.includes(doc));
this.props.removeDocument?.(pushedDocs);
- this.props.removeDocument?.(this._selectedDocSortedArray);
- this.addDocument(this._selectedDocSortedArray);
+ this.props.removeDocument?.(draggedDocs);
+ this.addDocument(draggedDocs);
this.addDocument(pushedDocs);
this.setSort(undefined);
- this.clearSelection();
+ SelectionManager.DeselectAll();
+ setTimeout(() => draggedDocs.forEach(doc => DocumentManager.Instance.AddViewRenderedCb(doc, dv => dv.select(true))), 100);
+ e.stopPropagation();
return true;
}
return false;
@@ -412,46 +387,11 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- super.onExternalDrop(
- e,
- {},
- undoBatch(
- action(docus => {
- this._isDragging = false;
- docus.map((doc: Doc) => {
- this.addDocument(doc);
- });
- })
- )
- );
+ super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
this.setSort(undefined);
};
- @action
- startDrag = (e: PointerEvent, doc: Doc, index: number) => {
- if (!this._selectedDocs.has(doc)) {
- this.clearSelection();
- this.addDocToSelection(doc, false, index);
- }
- this._isDragging = true;
- this._selectedDocSortedArray = this.sortedSelectedDocs();
- const dragData = new DragManager.DocumentDragData(this._selectedDocSortedArray, 'move');
- dragData.moveDocument = this.props.moveDocument;
- const dragItem: HTMLElement[] = Array.from(this._selectedDocs.values()).map((doc: Doc) => this._rowEles.get(doc));
-
- DragManager.StartDocumentDrag(
- dragItem.map(ele => ele),
- dragData,
- e.clientX,
- e.clientY,
- undefined
- );
- return true;
- };
-
- onDividerDown = (e: React.PointerEvent) => {
- setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
- };
+ onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
@action
onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
@@ -557,9 +497,6 @@ export class CollectionSchemaView extends CollectionSubView() {
return undefined;
};
- isChildContentActive = () =>
- this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined;
-
@computed get fieldDefaultInput() {
switch (this._newFieldType) {
case ColumnType.Number:
@@ -644,23 +581,17 @@ export class CollectionSchemaView extends CollectionSubView() {
ContextMenu.Instance.clearItems();
ContextMenu.Instance.addItem({
description: 'Change field',
- event: () => {
- this.openColumnMenu(index, false);
- },
+ event: () => this.openColumnMenu(index, false),
icon: 'pencil-alt',
});
ContextMenu.Instance.addItem({
description: 'Filter field',
- event: () => {
- this.openFilterMenu(index);
- },
+ event: () => this.openFilterMenu(index),
icon: 'filter',
});
ContextMenu.Instance.addItem({
description: 'Delete column',
- event: () => {
- this.removeColumn(index);
- },
+ event: () => this.removeColumn(index),
icon: 'trash',
});
ContextMenu.Instance.displayMenu(x, y, undefined, false);
@@ -672,9 +603,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
};
- getFieldFilters = (field: string) => {
- return StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
- };
+ getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
removeFieldFilters = (field: string) => {
this.getFieldFilters(field).forEach(filter => {
@@ -694,9 +623,7 @@ export class CollectionSchemaView extends CollectionSubView() {
};
@action
- updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
- this._filterValue = e.target.value;
- };
+ updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterValue = e.target.value);
@computed get newFieldMenu() {
return (
@@ -705,7 +632,6 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- id=""
checked={this._newFieldType == ColumnType.Number}
onChange={action(() => {
this._newFieldType = ColumnType.Number;
@@ -718,7 +644,6 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- id=""
checked={this._newFieldType == ColumnType.Boolean}
onChange={action(() => {
this._newFieldType = ColumnType.Boolean;
@@ -731,7 +656,6 @@ export class CollectionSchemaView extends CollectionSubView() {
<input
type="radio"
name="newFieldType"
- id=""
checked={this._newFieldType == ColumnType.String}
onChange={action(() => {
this._newFieldType = ColumnType.String;
@@ -880,13 +804,11 @@ export class CollectionSchemaView extends CollectionSubView() {
);
}
- tableWidthFunc = () => this.tableWidth;
- rowHeightFunc = () => CollectionSchemaView._rowHeight;
- rowClickScriptFunc = () => this.rowOnClickScript;
isContentActive = () => this.props.isSelected() || this.props.isContentActive();
screenToLocal = () => this.props.ScreenToLocalTransform().translate(-this.tableWidth, 0);
previewWidthFunc = () => this.previewWidth;
render() {
+ trace();
return (
<div
className="collectionSchemaView"
@@ -894,6 +816,12 @@ export class CollectionSchemaView extends CollectionSubView() {
this._ref = ele;
this.createDashEventsTarget(ele);
}}
+ onPointerDown={e => {
+ // this is analogous to the panning code for a freeform view.
+ // however, schema views don't pan so it does nothing. but it does eat the pointerDown event
+ // if the content is active to prevent the schema from being dragged
+ this.isContentActive() && setupMoveUpEvents(this, e, returnFalse, emptyFunction, emptyFunction, false);
+ }}
onDrop={this.onExternalDrop.bind(this)}>
<div className="schema-table">
<div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}>
@@ -927,53 +855,16 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
{this._columnMenuIndex !== undefined && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
- <div className="schema-table-content">
- {this.childDocs.map((doc: Doc, index: number) => {
- const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
- let dref: Opt<DocumentView>;
- return (
- <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
- <DocumentView
- {...this.props}
- ref={r => (dref = r || undefined)}
- LayoutTemplate={this.props.childLayoutTemplate}
- LayoutTemplateString={SchemaRowBox.LayoutString(this.props.fieldKey)}
- Document={doc}
- DataDoc={dataDoc}
- ContainingCollectionView={this.props.CollectionView}
- ContainingCollectionDoc={this.Document}
- PanelWidth={this.tableWidthFunc}
- PanelHeight={this.rowHeightFunc}
- styleProvider={DefaultStyleProvider}
- focus={this.focusDocument}
- docFilters={this.childDocFilters}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- rootSelected={this.rootSelected}
- ScreenToLocalTransform={Transform.Identity}
- bringToFront={emptyFunction}
- isDocumentActive={this.isContentActive}
- isContentActive={emptyFunction}
- hideDecorations={true}
- hideTitle={true}
- hideDocumentButtonBar={true}
- hideLinkAnchors={true}
- fitWidth={returnTrue}
- onClick={this.rowClickScriptFunc}
- scriptContext={this}
- />
- </div>
- );
- })}
- </div>
+ <CollectionSchemaViewDocs schema={this} />
+
<EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
</div>
{this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
{this.previewWidth > 0 && (
<div style={{ width: `${this.previewWidth}px` }} ref={ref => (this._previewRef = ref)}>
- {this._lastSelectedRow !== undefined && (
+ {Array.from(this._selectedDocs).lastElement() && (
<DocumentView
- Document={this.childDocs[this._lastSelectedRow]}
+ Document={Array.from(this._selectedDocs).lastElement()}
DataDoc={undefined}
fitContentsToBox={returnTrue}
dontCenter={'y'}
@@ -1007,3 +898,54 @@ export class CollectionSchemaView extends CollectionSubView() {
);
}
}
+
+interface CollectionSchemaViewDocsProps {
+ schema: CollectionSchemaView;
+}
+
+@observer
+class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsProps> {
+ tableWidthFunc = () => this.props.schema.tableWidth;
+ rowHeightFunc = () => CollectionSchemaView._rowHeight;
+ render() {
+ return (
+ <div className="schema-table-content">
+ {this.props.schema.childDocs.map((doc: Doc, index: number) => {
+ const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.schema.props.DataDoc;
+ return (
+ <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
+ <DocumentView
+ {...this.props.schema.props}
+ LayoutTemplate={this.props.schema.props.childLayoutTemplate}
+ LayoutTemplateString={SchemaRowBox.LayoutString(this.props.schema.props.fieldKey)}
+ Document={doc}
+ DataDoc={dataDoc}
+ ContainingCollectionView={this.props.schema.props.CollectionView}
+ ContainingCollectionDoc={this.props.schema.Document}
+ PanelWidth={this.tableWidthFunc}
+ PanelHeight={this.rowHeightFunc}
+ styleProvider={DefaultStyleProvider}
+ focus={this.props.schema.focusDocument}
+ docFilters={this.props.schema.childDocFilters}
+ docRangeFilters={this.props.schema.childDocRangeFilters}
+ searchFilterDocs={this.props.schema.searchFilterDocs}
+ rootSelected={this.props.schema.rootSelected}
+ ScreenToLocalTransform={Transform.Identity}
+ bringToFront={emptyFunction}
+ isDocumentActive={this.props.schema.props.childDocumentsActive?.() ? this.props.schema.props.isDocumentActive : this.props.schema.isContentActive}
+ isContentActive={emptyFunction}
+ whenChildContentsActiveChanged={active => this.props.schema.props.whenChildContentsActiveChanged(active)}
+ hideDecorations={true}
+ hideTitle={true}
+ hideDocumentButtonBar={true}
+ hideLinkAnchors={true}
+ fitWidth={returnTrue}
+ scriptContext={this}
+ />
+ </div>
+ );
+ })}
+ </div>
+ );
+ }
+}