aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgeireann <geireann_lindfield_roberts@brown.edu>2021-07-12 13:54:23 -0400
committergeireann <geireann_lindfield_roberts@brown.edu>2021-07-12 13:54:23 -0400
commite48f447f66f7f50f39be385e0eb09df552f5f503 (patch)
treee678a44ff8d0f6b8da65cea855893fdb83584e69
parenta5c099cb5ae455064f65989dc977870ce2c3f7fc (diff)
Changed "schemaView" :arrow_right: "collectionSchema"
So style is consistent with other folders
-rw-r--r--src/client/util/SelectionManager.ts2
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx575
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx (renamed from src/client/views/collections/schemaView/CollectionSchemaCells.tsx)0
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx (renamed from src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx)0
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx (renamed from src/client/views/collections/schemaView/CollectionSchemaMovableColumn.tsx)0
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx (renamed from src/client/views/collections/schemaView/CollectionSchemaMovableRow.tsx)0
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss (renamed from src/client/views/collections/schemaView/CollectionSchemaView.scss)0
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx (renamed from src/client/views/collections/schemaView/CollectionSchemaView.tsx)0
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx (renamed from src/client/views/collections/schemaView/SchemaTable.tsx)0
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx2
-rw-r--r--src/client/views/search/SearchBox.tsx8
-rw-r--r--src/fields/SchemaHeaderField.ts2
13 files changed, 583 insertions, 8 deletions
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index a624d5b7c..00f0894c7 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,7 +1,7 @@
import { action, observable, ObservableMap } from "mobx";
import { computedFn } from "mobx-utils";
import { Doc, Opt } from "../../fields/Doc";
-import { CollectionSchemaView } from "../views/collections/schemaView/CollectionSchemaView";
+import { CollectionSchemaView } from "../views/collections/collectionSchema/CollectionSchemaView";
import { CollectionViewType } from "../views/collections/CollectionView";
import { DocumentView } from "../views/nodes/DocumentView";
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
new file mode 100644
index 000000000..8f2847139
--- /dev/null
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -0,0 +1,575 @@
+import React = require("react");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, untracked } from "mobx";
+import { observer } from "mobx-react";
+import Measure from "react-measure";
+import { Resize } from "react-table";
+import "react-table/react-table.css";
+import { Doc, Opt } from "../../../fields/Doc";
+import { List } from "../../../fields/List";
+import { listSpec } from "../../../fields/Schema";
+import { PastelSchemaPalette, SchemaHeaderField } from "../../../fields/SchemaHeaderField";
+import { Cast, NumCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
+import { emptyFunction, emptyPath, returnFalse, setupMoveUpEvents, returnEmptyDoclist, returnTrue } from "../../../Utils";
+import { SelectionManager } from "../../util/SelectionManager";
+import { SnappingManager } from "../../util/SnappingManager";
+import { Transform } from "../../util/Transform";
+import { undoBatch } from "../../util/UndoManager";
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/globalCssVariables.scss';
+import { ContextMenu } from "../ContextMenu";
+import { ContextMenuProps } from "../ContextMenuItem";
+import '../DocumentDecorations.scss';
+import { DocumentView } from "../nodes/DocumentView";
+import { DefaultStyleProvider } from "../StyleProvider";
+import "./CollectionSchemaView.scss";
+import { CollectionSubView } from "./CollectionSubView";
+import { SchemaTable } from "./SchemaTable";
+import { DocUtils } from "../../documents/Documents";
+// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
+
+export enum ColumnType {
+ Any,
+ Number,
+ String,
+ Boolean,
+ Doc,
+ Image,
+ List,
+ Date
+}
+// this map should be used for keys that should have a const type of value
+const columnTypes: Map<string, ColumnType> = new Map([
+ ["title", ColumnType.String],
+ ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number],
+ ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean],
+ ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number]
+]);
+
+@observer
+export class CollectionSchemaView extends CollectionSubView(doc => doc) {
+ private _previewCont?: HTMLDivElement;
+
+ @observable _previewDoc: Doc | undefined = undefined;
+ @observable _focusedTable: Doc = this.props.Document;
+ @observable _col: any = "";
+ @observable _menuWidth = 0;
+ @observable _headerOpen = false;
+ @observable _headerIsEditing = false;
+ @observable _menuHeight = 0;
+ @observable _pointerX = 0;
+ @observable _pointerY = 0;
+ @observable _openTypes: boolean = false;
+
+ @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); }
+ @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; }
+ @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); }
+ @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); }
+ @computed get scale() { return this.props.ScreenToLocalTransform().Scale; }
+ @computed get columns() { return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); }
+ set columns(columns: SchemaHeaderField[]) { this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); }
+
+ @computed get menuCoordinates() {
+ let searchx = 0;
+ let searchy = 0;
+ if (this.props.Document._searchDoc) {
+ const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0];
+ if (el !== undefined) {
+ const rect = el.getBoundingClientRect();
+ searchx = rect.x;
+ searchy = rect.y;
+ }
+ }
+ const x = Math.max(0, Math.min(document.body.clientWidth - this._menuWidth, this._pointerX)) - searchx;
+ const y = Math.max(0, Math.min(document.body.clientHeight - this._menuHeight, this._pointerY)) - searchy;
+ return this.props.ScreenToLocalTransform().transformPoint(x, y);
+ }
+
+ get documentKeys() {
+ const docs = this.childDocs;
+ const keys: { [key: string]: boolean } = {};
+ // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields.
+ // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be
+ // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked.
+ // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu
+ // is displayed (unlikely) it won't show up until something else changes.
+ //TODO Types
+ untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false))));
+
+ this.columns.forEach(key => keys[key.heading] = true);
+ return Array.from(Object.keys(keys));
+ }
+
+ @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing;
+
+ @undoBatch
+ setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => {
+ this._openTypes = false;
+ if (columnTypes.get(columnField.heading)) return;
+
+ const columns = this.columns;
+ const index = columns.indexOf(columnField);
+ if (index > -1) {
+ columnField.setType(NumCast(type));
+ columns[index] = columnField;
+ this.columns = columns;
+ }
+ });
+
+ @undoBatch
+ setColumnColor = (columnField: SchemaHeaderField, color: string): void => {
+ const columns = this.columns;
+ const index = columns.indexOf(columnField);
+ if (index > -1) {
+ columnField.setColor(color);
+ columns[index] = columnField;
+ this.columns = columns; // need to set the columns to trigger rerender
+ }
+ }
+
+ @undoBatch
+ @action
+ setColumnSort = (columnField: SchemaHeaderField, descending: boolean | undefined) => {
+ const columns = this.columns;
+ columns.forEach(col => col.setDesc(undefined));
+
+ const index = columns.findIndex(c => c.heading === columnField.heading);
+ const column = columns[index];
+ column.setDesc(descending);
+ columns[index] = column;
+ this.columns = columns;
+ }
+
+ renderTypes = (col: any) => {
+ if (columnTypes.get(col.heading)) return (null);
+
+ const type = col.type;
+
+ const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}>
+ <FontAwesomeIcon icon={"align-justify"} size="sm" />
+ Any
+ </div>;
+
+ const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}>
+ <FontAwesomeIcon icon={"hashtag"} size="sm" />
+ Number
+ </div>;
+
+ const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}>
+ <FontAwesomeIcon icon={"font"} size="sm" />
+ Text
+ </div>;
+
+ const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
+ <FontAwesomeIcon icon={"check-square"} size="sm" />
+ Checkbox
+ </div>;
+
+ const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}>
+ <FontAwesomeIcon icon={"list-ul"} size="sm" />
+ List
+ </div>;
+
+ const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
+ <FontAwesomeIcon icon={"file"} size="sm" />
+ Document
+ </div>;
+
+ const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}>
+ <FontAwesomeIcon icon={"image"} size="sm" />
+ Image
+ </div>;
+
+ const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}>
+ <FontAwesomeIcon icon={"calendar"} size="sm" />
+ Date
+ </div>;
+
+
+ const allColumnTypes = <div className="columnMenu-types">
+ {anyType}
+ {numType}
+ {textType}
+ {boolType}
+ {listType}
+ {docType}
+ {imageType}
+ {dateType}
+ </div>;
+
+ const justColType = type === ColumnType.Any ? anyType : type === ColumnType.Number ? numType :
+ type === ColumnType.String ? textType : type === ColumnType.Boolean ? boolType :
+ type === ColumnType.List ? listType : type === ColumnType.Doc ? docType :
+ type === ColumnType.Date ? dateType : imageType;
+
+ return (
+ <div className="collectionSchema-headerMenu-group" onClick={action(() => this._openTypes = !this._openTypes)}>
+ <div>
+ <label style={{ cursor: "pointer" }}>Column type:</label>
+ <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right", transform: `rotate(${this._openTypes ? "180deg" : 0})`, transition: "0.2s all ease" }} />
+ </div>
+ {this._openTypes ? allColumnTypes : justColType}
+ </div >
+ );
+ }
+
+ renderSorting = (col: any) => {
+ const sort = col.desc;
+ return (
+ <div className="collectionSchema-headerMenu-group">
+ <label>Sort by:</label>
+ <div className="columnMenu-sort">
+ <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.setColumnSort(col, true)}>
+ <FontAwesomeIcon icon="sort-amount-down" size="sm" />
+ Sort descending
+ </div>
+ <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.setColumnSort(col, false)}>
+ <FontAwesomeIcon icon="sort-amount-up" size="sm" />
+ Sort ascending
+ </div>
+ <div className="columnMenu-option" onClick={() => this.setColumnSort(col, undefined)}>
+ <FontAwesomeIcon icon="times" size="sm" />
+ Clear sorting
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+ renderColors = (col: any) => {
+ const selected = col.color;
+
+ const pink = PastelSchemaPalette.get("pink2");
+ const purple = PastelSchemaPalette.get("purple2");
+ const blue = PastelSchemaPalette.get("bluegreen1");
+ const yellow = PastelSchemaPalette.get("yellow4");
+ const red = PastelSchemaPalette.get("red2");
+ const gray = "#f1efeb";
+
+ return (
+ <div className="collectionSchema-headerMenu-group">
+ <label>Color:</label>
+ <div className="columnMenu-colors">
+ <div className={"columnMenu-colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div>
+ <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div>
+ <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div>
+ <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div>
+ <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div>
+ <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div>
+ </div>
+ </div>
+ );
+ }
+
+ @undoBatch
+ @action
+ changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => {
+ const columns = this.columns;
+ if (columns === undefined) {
+ this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]);
+ } else {
+ if (addNew) {
+ columns.push(new SchemaHeaderField(newKey, "f1efeb"));
+ this.columns = columns;
+ } else {
+ const index = columns.map(c => c.heading).indexOf(oldKey);
+ if (index > -1) {
+ const column = columns[index];
+ column.setHeading(newKey);
+ columns[index] = column;
+ this.columns = columns;
+ if (filter) {
+ Doc.setDocFilter(this.props.Document, newKey, filter, "match");
+ }
+ else {
+ this.props.Document._docFilters = undefined;
+ }
+ }
+ }
+ }
+ }
+
+ @action
+ openHeader = (col: any, screenx: number, screeny: number) => {
+ this._col = col;
+ this._headerOpen = true;
+ this._pointerX = screenx;
+ this._pointerY = screeny;
+ }
+
+ @action
+ closeHeader = () => { this._headerOpen = false; }
+
+ @undoBatch
+ @action
+ deleteColumn = (key: string) => {
+ const columns = this.columns;
+ if (columns === undefined) {
+ this.columns = new List<SchemaHeaderField>([]);
+ } else {
+ const index = columns.map(c => c.heading).indexOf(key);
+ if (index > -1) {
+ columns.splice(index, 1);
+ this.columns = columns;
+ }
+ }
+ this.closeHeader();
+ }
+
+ getPreviewTransform = (): Transform => {
+ return this.props.ScreenToLocalTransform().translate(- this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, - this.borderWidth);
+ }
+
+ @action
+ onHeaderClick = (e: React.PointerEvent) => {
+ e.stopPropagation();
+ }
+
+ @action
+ onWheel(e: React.WheelEvent) {
+ const scale = this.props.ScreenToLocalTransform().Scale;
+ this.props.isContentActive(true) && e.stopPropagation();
+ }
+
+ @computed get renderMenuContent() {
+ TraceMobx();
+ return <div className="collectionSchema-header-menuOptions">
+ {this.renderTypes(this._col)}
+ {this.renderColors(this._col)}
+ <div className="collectionSchema-headerMenu-group">
+ <button onClick={() => { this.deleteColumn(this._col.heading); }}
+ >Delete Column</button>
+ </div>
+ </div>;
+ }
+
+ private createTarget = (ele: HTMLDivElement) => {
+ this._previewCont = ele;
+ super.CreateDropTarget(ele);
+ }
+
+ isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable;
+
+ @action setFocused = (doc: Doc) => this._focusedTable = doc;
+
+ @action setPreviewDoc = (doc: Opt<Doc>) => {
+ SelectionManager.SelectSchemaView(this, doc);
+ this._previewDoc = doc;
+ }
+
+ //toggles preview side-panel of schema
+ @action
+ toggleExpander = () => {
+ this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0;
+ }
+
+ onDividerDown = (e: React.PointerEvent) => {
+ setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander);
+ }
+ @action
+ onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ const nativeWidth = this._previewCont!.getBoundingClientRect();
+ const minWidth = 40;
+ const maxWidth = 1000;
+ const movedWidth = this.props.ScreenToLocalTransform().transformDirection(nativeWidth.right - e.clientX, 0)[0];
+ const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth;
+ this.props.Document.schemaPreviewWidth = width;
+ return false;
+ }
+
+ onPointerDown = (e: React.PointerEvent): void => {
+ if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) {
+ if (this.props.isSelected(true)) e.stopPropagation();
+ else this.props.select(false);
+ }
+ }
+
+ @computed
+ get previewDocument(): Doc | undefined { return this._previewDoc; }
+
+ @computed
+ get dividerDragger() {
+ return this.previewWidth() === 0 ? (null) :
+ <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} >
+ <div className="collectionSchemaView-dividerDragger" />
+ </div>;
+ }
+
+ @computed
+ get previewPanel() {
+ return <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}>
+ {!this.previewDocument ? (null) :
+ <DocumentView
+ Document={this.previewDocument}
+ DataDoc={undefined}
+ fitContentsToDoc={returnTrue}
+ freezeDimensions={true}
+ dontCenter={"y"}
+ focus={DocUtils.DefaultFocus}
+ renderDepth={this.props.renderDepth}
+ rootSelected={this.rootSelected}
+ PanelWidth={this.previewWidth}
+ PanelHeight={this.previewHeight}
+ isContentActive={returnTrue}
+ isDocumentActive={returnFalse}
+ ScreenToLocalTransform={this.getPreviewTransform}
+ docFilters={this.docFilters}
+ docRangeFilters={this.docRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ styleProvider={DefaultStyleProvider}
+ layerProvider={undefined}
+ docViewPath={returnEmptyDoclist}
+ ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+ ContainingCollectionView={this.props.CollectionView}
+ moveDocument={this.props.moveDocument}
+ addDocument={this.props.addDocument}
+ removeDocument={this.props.removeDocument}
+ whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ bringToFront={returnFalse}
+ />}
+ </div>;
+ }
+
+ @computed
+ get schemaTable() {
+ return <SchemaTable
+ Document={this.props.Document}
+ PanelHeight={this.props.PanelHeight}
+ PanelWidth={this.props.PanelWidth}
+ childDocs={this.childDocs}
+ CollectionView={this.props.CollectionView}
+ ContainingCollectionView={this.props.ContainingCollectionView}
+ ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+ fieldKey={this.props.fieldKey}
+ renderDepth={this.props.renderDepth}
+ moveDocument={this.props.moveDocument}
+ ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+ active={this.props.isContentActive}
+ onDrop={this.onExternalDrop}
+ addDocTab={this.props.addDocTab}
+ pinToPres={this.props.pinToPres}
+ isSelected={this.props.isSelected}
+ isFocused={this.isFocused}
+ setFocused={this.setFocused}
+ setPreviewDoc={this.setPreviewDoc}
+ deleteDocument={this.props.removeDocument}
+ addDocument={this.props.addDocument}
+ dataDoc={this.props.DataDoc}
+ columns={this.columns}
+ documentKeys={this.documentKeys}
+ headerIsEditing={this._headerIsEditing}
+ openHeader={this.openHeader}
+ onClick={this.onTableClick}
+ onPointerDown={emptyFunction}
+ onResizedChange={this.onResizedChange}
+ setColumns={this.setColumns}
+ reorderColumns={this.reorderColumns}
+ changeColumns={this.changeColumns}
+ setHeaderIsEditing={this.setHeaderIsEditing}
+ changeColumnSort={this.setColumnSort}
+ />;
+ }
+
+ @computed
+ public get schemaToolbar() {
+ return <div className="collectionSchemaView-toolbar">
+ <div className="collectionSchemaView-toolbar-item">
+ <div id="preview-schema-checkbox-div">
+ <input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} />
+ Show Preview
+ </div>
+ </div>
+ </div>;
+ }
+
+ onSpecificMenu = (e: React.MouseEvent) => {
+ if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) {
+ const cm = ContextMenu.Instance;
+ const options = cm.findByDescription("Options...");
+ const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
+ optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" });
+ !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
+ cm.displayMenu(e.clientX, e.clientY);
+ (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this.
+ e.stopPropagation();
+ }
+ }
+
+ @action
+ onTableClick = (e: React.MouseEvent): void => {
+ if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) {
+ this.setPreviewDoc(undefined);
+ } else {
+ e.stopPropagation();
+ }
+ this.setFocused(this.props.Document);
+ this.closeHeader();
+ }
+
+ onResizedChange = (newResized: Resize[], event: any) => {
+ const columns = this.columns;
+ newResized.forEach(resized => {
+ const index = columns.findIndex(c => c.heading === resized.id);
+ const column = columns[index];
+ column.setWidth(resized.value);
+ columns[index] = column;
+ });
+ this.columns = columns;
+ }
+
+ @action
+ setColumns = (columns: SchemaHeaderField[]) => this.columns = columns
+
+ @undoBatch
+ reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => {
+ const columns = [...columnsValues];
+ const oldIndex = columns.indexOf(toMove);
+ const relIndex = columns.indexOf(relativeTo);
+ const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex;
+
+ if (oldIndex === newIndex) return;
+
+ columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]);
+ this.columns = columns;
+ }
+
+ onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation();
+
+ render() {
+ TraceMobx();
+ if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0);
+ const menuContent = this.renderMenuContent;
+ const menu = <div className="collectionSchema-header-menu"
+ onWheel={e => this.onZoomMenu(e)}
+ onPointerDown={e => this.onHeaderClick(e)}
+ style={{ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)` }}>
+ <Measure offset onResize={action((r: any) => {
+ const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height);
+ this._menuWidth = dim[0]; this._menuHeight = dim[1];
+ })}>
+ {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>}
+ </Measure>
+ </div>;
+ return <div className={"collectionSchemaView" + (this.props.Document._searchDoc ? "-searchContainer" : "-container")}
+ style={{
+ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
+ pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
+ width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+ }} >
+ <div className="collectionSchemaView-tableContainer"
+ style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
+ onContextMenu={this.onSpecificMenu}
+ onPointerDown={this.onPointerDown}
+ onWheel={e => this.props.isContentActive(true) && e.stopPropagation()}
+ onDrop={e => this.onExternalDrop(e, {})}
+ ref={this.createTarget}>
+ {this.schemaTable}
+ </div>
+ {this.dividerDragger}
+ {!this.previewWidth() ? (null) : this.previewPanel}
+ {this._headerOpen && this.props.isContentActive() ? menu : null}
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index e5b1721f9..e225c4a11 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -29,7 +29,7 @@ import CollectionMapView from './CollectionMapView';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from "./schemaView/CollectionSchemaView";
+import { CollectionSchemaView } from "./collectionSchema/CollectionSchemaView";
import { CollectionStackingView } from './CollectionStackingView';
import { SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
diff --git a/src/client/views/collections/schemaView/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index f75179cea..f75179cea 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
diff --git a/src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index b2115b22e..b2115b22e 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
diff --git a/src/client/views/collections/schemaView/CollectionSchemaMovableColumn.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
index 456c38c68..456c38c68 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaMovableColumn.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
diff --git a/src/client/views/collections/schemaView/CollectionSchemaMovableRow.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
index f48906ba5..f48906ba5 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
diff --git a/src/client/views/collections/schemaView/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index b57fee0e4..b57fee0e4 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
diff --git a/src/client/views/collections/schemaView/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index ef28f75c8..ef28f75c8 100644
--- a/src/client/views/collections/schemaView/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
diff --git a/src/client/views/collections/schemaView/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 0d5c9e077..0d5c9e077 100644
--- a/src/client/views/collections/schemaView/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index ecf4c0901..34488ffbe 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -8,7 +8,7 @@ import { emptyPath, OmitKeys, Without } from "../../../Utils";
import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
import { CollectionDockingView } from "../collections/CollectionDockingView";
import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionSchemaView } from "../collections/schemaView/CollectionSchemaView";
+import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView";
import { CollectionView } from "../collections/CollectionView";
import { InkingStroke } from "../InkingStroke";
import { PresElementBox } from "../presentationview/PresElementBox";
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index a671c955d..6a2325342 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -18,7 +18,7 @@ import { SetupDrag } from '../../util/DragManager';
import { SearchUtil } from '../../util/SearchUtil';
import { Transform } from '../../util/Transform';
import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionSchemaView, ColumnType } from "../collections/schemaView/CollectionSchemaView";
+import { CollectionSchemaView, ColumnType } from "../collections/collectionSchema/CollectionSchemaView";
import { CollectionViewType } from '../collections/CollectionView';
import { ViewBoxBaseComponent } from "../DocComponent";
import { FieldView, FieldViewProps } from '../nodes/FieldView';
@@ -522,7 +522,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
{`${Doc.CurrentUserEmail}`}
<div className="searchBox-logoff" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
Logoff
- </div>
+ </div>
</div>
<div className="searchBox-lozenge" onClick={() => DocServer.UPDATE_SERVER_CACHE()}>
{`UI project`}
@@ -534,10 +534,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc
</select>
<div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}>
New
- </div>
+ </div>
<div className="searchBox-dashboards" onClick={undoBatch(() => CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}>
Snapshot
- </div>
+ </div>
</div>
</div>
<div className="searchBox-query" >
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index 74cf934f2..a53fa542e 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -3,7 +3,7 @@ import { serializable, primitive } from "serializr";
import { ObjectField } from "./ObjectField";
import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols";
import { scriptingGlobal } from "../client/util/Scripting";
-import { ColumnType } from "../client/views/collections/schemaView/CollectionSchemaView";
+import { ColumnType } from "../client/views/collections/collectionSchema/CollectionSchemaView";
export const PastelSchemaPalette = new Map<string, string>([
// ["pink1", "#FFB4E8"],