aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/views/LightboxView.tsx1
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx513
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss623
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx706
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx28
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx97
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx24
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx (renamed from src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx)444
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaHeaders.tsx510
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx (renamed from src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx)0
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx (renamed from src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx)0
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.scss599
-rw-r--r--src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx649
-rw-r--r--src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx (renamed from src/client/views/collections/collectionSchema/SchemaTable.tsx)2
-rw-r--r--src/client/views/nodes/AudioBox.tsx1
15 files changed, 2306 insertions, 1891 deletions
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 99d50b4a2..4fd145f2d 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -180,6 +180,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
})
.filter(m => m)
.map(m => m!);
+ console.log(LightboxView._tourMap);
}
@action public static Previous() {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
deleted file mode 100644
index 9653f2808..000000000
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ /dev/null
@@ -1,513 +0,0 @@
-import React = require("react");
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt, StrListCast } from "../../../../fields/Doc";
-import { listSpec } from "../../../../fields/Schema";
-import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { ScriptField } from "../../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../../fields/Types";
-import { undoBatch } from "../../../util/UndoManager";
-import { CollectionView } from "../CollectionView";
-import { ColumnType } from "./CollectionSchemaView";
-import "./CollectionSchemaView.scss";
-
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-
-export interface AddColumnHeaderProps {
- createColumn: () => void;
-}
-
-@observer
-export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> {
- // the button that allows the user to add a column
- render() {
- return <button className="add-column" onClick={() => this.props.createColumn()}>
- <FontAwesomeIcon icon="plus" size="sm" />
- </button>;
- }
-}
-
-export interface ColumnMenuProps {
- columnField: SchemaHeaderField;
- // keyValue: string;
- possibleKeys: string[];
- existingKeys: string[];
- // keyType: ColumnType;
- typeConst: boolean;
- menuButtonContent: JSX.Element;
- addNew: boolean;
- onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
- setIsEditing: (isEditing: boolean) => void;
- deleteColumn: (column: string) => void;
- onlyShowOptions: boolean;
- setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
- setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
- anchorPoint?: any;
- setColumnColor: (column: SchemaHeaderField, color: string) => void;
-}
-@observer
-export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> {
- @observable private _isOpen: boolean = false;
- @observable private _node: HTMLDivElement | null = null;
-
- componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
-
- componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
-
- @action
- detectClick = (e: PointerEvent) => {
- !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
- }
-
- @action
- toggleIsOpen = (): void => {
- this.props.setIsEditing(this._isOpen = !this._isOpen);
- }
-
- changeColumnType = (type: ColumnType) => {
- this.props.setColumnType(this.props.columnField, type);
- }
-
- changeColumnSort = (desc: boolean | undefined) => {
- this.props.setColumnSort(this.props.columnField, desc);
- }
-
- changeColumnColor = (color: string) => {
- this.props.setColumnColor(this.props.columnField, color);
- }
-
- @action
- setNode = (node: HTMLDivElement): void => {
- if (node) {
- this._node = node;
- }
- }
-
- renderTypes = () => {
- if (this.props.typeConst) return (null);
-
- const type = this.props.columnField.type;
- return (
- <div className="collectionSchema-headerMenu-group">
- <label>Column type:</label>
- <div className="columnMenu-types">
- <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}>
- <FontAwesomeIcon icon={"align-justify"} size="sm" />
- Any
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}>
- <FontAwesomeIcon icon={"hashtag"} size="sm" />
- Number
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}>
- <FontAwesomeIcon icon={"font"} size="sm" />
- Text
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}>
- <FontAwesomeIcon icon={"check-square"} size="sm" />
- Checkbox
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}>
- <FontAwesomeIcon icon={"list-ul"} size="sm" />
- List
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}>
- <FontAwesomeIcon icon={"file"} size="sm" />
- Document
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}>
- <FontAwesomeIcon icon={"image"} size="sm" />
- Image
- </div>
- <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Date)}>
- <FontAwesomeIcon icon={"calendar"} size="sm" />
- Date
- </div>
- </div>
- </div >
- );
- }
-
- renderSorting = () => {
- const sort = this.props.columnField.desc;
- return (
- <div className="collectionSchema-headerMenu-group">
- <label>Sort by:</label>
- <div className="columnMenu-sort">
- <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}>
- <FontAwesomeIcon icon="sort-amount-down" size="sm" />
- Sort descending
- </div>
- <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}>
- <FontAwesomeIcon icon="sort-amount-up" size="sm" />
- Sort ascending
- </div>
- <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}>
- <FontAwesomeIcon icon="times" size="sm" />
- Clear sorting
- </div>
- </div>
- </div>
- );
- }
-
- renderColors = () => {
- const selected = this.props.columnField.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.changeColumnColor(pink!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
- <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
- </div>
- </div>
- );
- }
-
- renderContent = () => {
- return (
- <div className="collectionSchema-header-menuOptions">
- {this.props.onlyShowOptions ? <></> :
- <>
- {this.renderTypes()}
- {this.renderSorting()}
- {this.renderColors()}
- <div className="collectionSchema-headerMenu-group">
- <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Hide Column</button>
- </div>
- </>
- }
- </div>
- );
- }
-
- render() {
- return (
- <div className="collectionSchema-header-menu" ref={this.setNode}>
- <Flyout anchorPoint={this.props.anchorPoint ? this.props.anchorPoint : anchorPoints.TOP_CENTER} content={this.renderContent()}>
- <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div>
- </ Flyout >
- </div>
- );
- }
-}
-
-
-export interface KeysDropdownProps {
- keyValue: string;
- possibleKeys: string[];
- existingKeys: string[];
- canAddNew: boolean;
- addNew: boolean;
- onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void;
- setIsEditing: (isEditing: boolean) => void;
- width?: string;
- docs?: Doc[];
- Document: Doc;
- dataDoc: Doc | undefined;
- fieldKey: string;
- ContainingCollectionDoc: Doc | undefined;
- ContainingCollectionView: Opt<CollectionView>;
- active?: (outsideReaction?: boolean) => boolean | undefined;
- openHeader: (column: any, screenx: number, screeny: number) => void;
- col: SchemaHeaderField;
- icon: IconProp;
-}
-@observer
-export class KeysDropdown extends React.Component<KeysDropdownProps> {
- @observable private _key: string = this.props.keyValue;
- @observable private _searchTerm: string = this.props.keyValue + ":";
- @observable private _isOpen: boolean = false;
- @observable private _node: HTMLDivElement | null = null;
- @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
-
- @action setSearchTerm = (value: string): void => { this._searchTerm = value; };
- @action setKey = (key: string): void => { this._key = key; };
- @action setIsOpen = (isOpen: boolean): void => { this._isOpen = isOpen; };
-
- @action
- onSelect = (key: string): void => {
- this.props.onSelect(this._key, key, this.props.addNew);
- this.setKey(key);
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
-
- @action
- setNode = (node: HTMLDivElement): void => {
- if (node) {
- this._node = node;
- }
- }
-
- componentDidMount() {
- document.addEventListener("pointerdown", this.detectClick);
- const filters = Cast(this.props.Document._docFilters, listSpec("string"));
- if (filters?.some(filter => filter.split(":")[0] === this._key)) {
- runInAction(() => this.closeResultsVisibility = "contents");
- }
- }
-
- @action
- detectClick = (e: PointerEvent): void => {
- if (this._node && this._node.contains(e.target as Node)) {
- } else {
- this._isOpen = false;
- this.props.setIsEditing(false);
- }
- }
-
- private tempfilter: string = "";
- @undoBatch
- onKeyDown = (e: React.KeyboardEvent): void => {
- if (e.key === "Enter") {
- e.stopPropagation();
- if (this._searchTerm.includes(":")) {
- const colpos = this._searchTerm.indexOf(":");
- const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
- if (temp === "") {
- Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
- this.updateFilter();
- }
- else {
- Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
- this.tempfilter = temp;
- Doc.setDocFilter(this.props.Document, this._key, temp, "check");
- this.props.col.setColor("green");
- this.closeResultsVisibility = "contents";
- }
- }
- else {
- Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
- this.updateFilter();
- if (this.showKeys.length) {
- this.onSelect(this.showKeys[0]);
- } else if (this._searchTerm !== "" && this.props.canAddNew) {
- this.setSearchTerm(this._searchTerm || this._key);
- this.onSelect(this._searchTerm);
- }
- }
- }
- }
-
- onChange = (val: string): void => {
- this.setSearchTerm(val);
- }
-
- @action
- onFocus = (e: React.FocusEvent): void => {
- this._isOpen = true;
- this.props.setIsEditing(true);
- }
-
- @computed get showKeys() {
- const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
- const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
- const showKeys = new Set<string>();
- [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode ||
- whitelistKeys.includes(key)
- || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
- return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
- }
-
- @computed get renderOptions() {
- if (!this._isOpen) {
- this.defaultMenuHeight = 0;
- return (null);
- }
- const options = this.showKeys.map(key => {
- return <div key={key} className="key-option" style={{
- border: "1px solid lightgray",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
- }}
- onPointerDown={e => {
- e.stopPropagation();
- }}
- onClick={() => {
- this.onSelect(key);
- this.setSearchTerm("");
- }}>{key}</div>;
- });
-
- // if search term does not already exist as a group type, give option to create new group type
-
- if (this._key !== this._searchTerm.slice(0, this._key.length)) {
- if (this._searchTerm !== "" && this.props.canAddNew) {
- options.push(<div key={""} className="key-option" style={{
- border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
- }}
- onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
- Create "{this._searchTerm}" key</div>);
- }
- }
-
- if (options.length === 0) {
- this.defaultMenuHeight = 0;
- }
- else {
- if (this.props.docs) {
- const panesize = this.props.docs.length * 30;
- options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
- }
- else {
- options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
- }
- }
- return options;
- }
-
- @computed get docSafe() { return DocListCast(this.props.dataDoc?.[this.props.fieldKey]); }
-
- @computed get renderFilterOptions() {
- if (!this._isOpen || !this.props.dataDoc) {
- this.defaultMenuHeight = 0;
- return (null);
- }
- const keyOptions: string[] = [];
- const colpos = this._searchTerm.indexOf(":");
- const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
- this.docSafe.forEach(doc => {
- const key = StrCast(doc[this._key]);
- if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
- keyOptions.push(key);
- }
- });
-
- const filters = StrListCast(this.props.Document._docFilters);
- if (filters.some(filter => filter.split(":")[0] === this._key) === false) {
- this.props.col.setColor("rgb(241, 239, 235)");
- this.closeResultsVisibility = "none";
- }
- for (let i = 0; i < (filters?.length ?? 0) - 1; i++) {
- if (filters[i] === this.props.col.heading && keyOptions.includes(filters[i].split(":")[1]) === false) {
- keyOptions.push(filters[i + 1]);
- }
- }
- const options = keyOptions.map(key => {
- let bool = false;
- if (filters !== undefined) {
- const ind = filters.findIndex(filter => filter.split(":")[1] === key);
- const fields = ind === -1 ? undefined : filters[ind].split(":");
- bool = fields ? fields[2] === "check" : false;
- }
- return <div key={key} className="key-option" style={{
- paddingLeft: 5, textAlign: "left",
- width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
- }}
- >
- <input type="checkbox"
- onPointerDown={e => e.stopPropagation()}
- onClick={e => e.stopPropagation()}
- onChange={action(e => {
- if (e.target.checked) {
- Doc.setDocFilter(this.props.Document, this._key, key, "check");
- this.closeResultsVisibility = "contents";
- this.props.col.setColor("green");
- } else {
- Doc.setDocFilter(this.props.Document, this._key, key, "remove");
- this.updateFilter();
- }
- })}
- checked={bool}
- />
- <span style={{ paddingLeft: 4 }}>
- {key}
- </span>
-
- </div>;
- });
- if (options.length === 0) {
- this.defaultMenuHeight = 0;
- }
- else {
- if (this.props.docs) {
- const panesize = this.props.docs.length * 30;
- options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
- }
- else {
- options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
- }
-
- }
- return options;
- }
-
- @observable defaultMenuHeight = 0;
-
-
- updateFilter() {
- const filters = Cast(this.props.Document._docFilters, listSpec("string"));
- if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
- this.props.col.setColor("rgb(241, 239, 235)");
- this.closeResultsVisibility = "none";
- }
- }
-
- @computed get scriptField() {
- const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
- const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
- return script ? () => script : undefined;
- }
- filterBackground = () => "rgba(105, 105, 105, 0.432)";
- @observable filterOpen: boolean | undefined = undefined;
- closeResultsVisibility: string = "none";
-
- removeFilters = (e: React.PointerEvent): void => {
- const keyOptions: string[] = [];
- this.docSafe.forEach(doc => {
- const key = StrCast(doc[this._key]);
- if (keyOptions.includes(key) === false) {
- keyOptions.push(key);
- }
- });
-
- Doc.setDocFilter(this.props.Document, this._key, "", "remove");
- this.props.col.setColor("rgb(241, 239, 235)");
- this.closeResultsVisibility = "none";
- }
- render() {
- return (
- <div style={{ display: "flex", width: '100%', alignContent: 'center', alignItems: 'center' }} ref={this.setNode}>
- <div className="schema-icon" onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }}>
- <FontAwesomeIcon icon={this.props.icon} size="lg" style={{ display: "inline" }} />
- </div>
-
- <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
- <input className="keys-search" style={{ width: "100%" }}
- ref={this._inputRef} type="text"
- value={this._searchTerm} placeholder="Column key"
- onKeyDown={this.onKeyDown}
- onChange={e => this.onChange(e.target.value)}
- onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
- onFocus={this.onFocus} ></input>
- <div style={{ display: this.closeResultsVisibility }}>
- <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
- style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
- </div>
- {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
- width: this.props.width, maxWidth: this.props.width, height: "auto",
- }}>
- {this._searchTerm.includes(":") ? this.renderFilterOptions : this.renderOptions}
- </div>}
- </div >
- </div>
- );
- }
-}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 19401c7f0..0f4053127 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,599 +1,58 @@
@import '../../global/globalCssVariables.scss';
-@import '../../../../../node_modules/react-table/react-table.css';
-.collectionSchemaView-container {
- border-width: $COLLECTION_BORDER_WIDTH;
- border-color: $medium-gray;
- border-style: solid;
- border-radius: $border-radius;
- box-sizing: border-box;
- position: relative;
- top: 0;
- width: 100%;
- height: 100%;
- margin-top: 0;
- transition: top 0.5s;
- display: flex;
- justify-content: space-between;
- flex-wrap: nowrap;
- touch-action: none;
- div {
- touch-action: none;
- }
- .collectionSchemaView-tableContainer {
- width: 100%;
- height: 100%;
- }
- .collectionSchemaView-dividerDragger {
- position: relative;
- height: 100%;
- width: $SCHEMA_DIVIDER_WIDTH;
- z-index: 20;
- right: 0;
- top: 0;
- background: gray;
- cursor: col-resize;
- }
- // .documentView-node:first-child {
- // background: $white;
- // }
-}
-
-.collectionSchemaView-searchContainer {
- border-width: $COLLECTION_BORDER_WIDTH;
- border-color: $medium-gray;
- border-style: solid;
- border-radius: $border-radius;
- box-sizing: border-box;
- position: relative;
- top: 0;
- width: 100%;
- height: 100%;
- margin-top: 0;
- transition: top 0.5s;
- display: flex;
- justify-content: space-between;
- flex-wrap: nowrap;
- touch-action: none;
- padding: 2px;
- div {
- touch-action: none;
- }
- .collectionSchemaView-tableContainer {
- width: 100%;
- height: 100%;
- }
- .collectionSchemaView-dividerDragger {
- position: relative;
- height: 100%;
- width: 20px;
- z-index: 20;
- right: 0;
- top: 0;
- background: gray;
- cursor: col-resize;
- }
- // .documentView-node:first-child {
- // background: $white;
- // }
-}
-
-.ReactTable {
- width: 100%;
- background: white;
- box-sizing: border-box;
- border: none !important;
- float: none !important;
- .rt-table {
- height: 100%;
- display: -webkit-inline-box;
- direction: ltr;
- overflow: visible;
- }
- .rt-noData {
- display: none;
- }
- .rt-thead {
- width: 100%;
- z-index: 100;
- overflow-y: visible;
- &.-header {
- font-size: 12px;
- height: 30px;
- box-shadow: none;
- z-index: 100;
- overflow-y: visible;
- }
- .rt-resizable-header-content {
- height: 100%;
- overflow: visible;
- }
- .rt-th {
- padding: 0;
- border-left: solid 1px $light-gray;
- }
- }
- .rt-th {
- font-size: 13px;
- text-align: center;
- &:last-child {
- overflow: visible;
- }
- }
- .rt-tbody {
- width: 100%;
- direction: rtl;
- overflow: visible;
- .rt-td {
- border-right: 1px solid rgba(0, 0, 0, 0.2);
- }
- }
- .rt-tr-group {
- direction: ltr;
- flex: 0 1 auto;
- min-height: 30px;
- border: 0 !important;
- }
- .rt-tr-group:nth-of-type(even) {
- direction: ltr;
- flex: 0 1 auto;
- min-height: 30px;
- border: 0 !important;
- background-color: red;
- }
- .rt-tr {
- width: 100%;
- min-height: 30px;
- }
- .rt-td {
- padding: 0;
- font-size: 13px;
- text-align: center;
- white-space: nowrap;
- display: flex;
- align-items: center;
- .imageBox-cont {
- position: relative;
- max-height: 100%;
- }
- .imageBox-cont img {
- object-fit: contain;
- max-width: 100%;
- height: 100%;
- }
- .videoBox-cont {
- object-fit: contain;
- width: auto;
- height: 100%;
- }
- }
- .rt-td.rt-expandable {
- display: flex;
- align-items: center;
- height: inherit;
- }
- .rt-resizer {
- width: 8px;
- right: -4px;
- }
- .rt-resizable-header {
- padding: 0;
- height: 30px;
- }
- .rt-resizable-header:last-child {
- overflow: visible;
- .rt-resizer {
- width: 5px !important;
- }
- }
-}
-
-.documentView-node-topmost {
- text-align: left;
- transform-origin: center top;
- display: inline-block;
-}
-
-.collectionSchema-col {
- height: 100%;
-}
-
-.collectionSchema-header-menu {
- height: auto;
- z-index: 100;
- position: absolute;
- background: white;
- padding: 5px;
- position: fixed;
- background: white;
- border: black 1px solid;
- .collectionSchema-header-toggler {
- z-index: 100;
- width: 100%;
- height: 100%;
- padding: 4px;
- letter-spacing: 2px;
- text-transform: uppercase;
- svg {
- margin-right: 4px;
- }
- }
-}
-
-.collectionSchemaView-header {
- height: 100%;
- color: gray;
- z-index: 100;
- overflow-y: visible;
- display: flex;
- justify-content: space-between;
- flex-wrap: wrap;
-}
-
-button.add-column {
- width: 28px;
-}
-.collectionSchemaView-menuOptions-wrapper {
- background: rgb(241, 239, 235);
- display: flex;
+.collectionSchemaView {
cursor: default;
- height: 100%;
- align-content: center;
- align-items: center;
-}
-
-.collectionSchema-header-menuOptions {
- color: black;
- width: 180px;
- text-align: left;
- .collectionSchema-headerMenu-group {
- padding: 7px 0;
- border-bottom: 1px solid lightgray;
- cursor: pointer;
- &:first-child {
- padding-top: 0;
- }
- &:last-child {
- border: none;
- text-align: center;
- padding: 12px 0 0 0;
- }
- }
- label {
- color: $medium-gray;
- font-weight: normal;
- letter-spacing: 2px;
- text-transform: uppercase;
- }
- input {
- color: black;
- width: 100%;
- }
- .columnMenu-option {
- cursor: pointer;
- padding: 3px;
- background-color: white;
- transition: background-color 0.2s;
- &:hover {
- background-color: $light-gray;
- }
- &.active {
- font-weight: bold;
- border: 2px solid $light-gray;
- }
- svg {
- color: gray;
- margin-right: 5px;
- width: 10px;
- }
- }
-
- .keys-dropdown {
- position: relative;
- //width: 100%;
- background-color: white;
- input {
- border: 2px solid $light-gray;
- padding: 3px;
- height: 28px;
- font-weight: bold;
- letter-spacing: '2px';
- text-transform: 'uppercase';
- &:focus {
- font-weight: normal;
- }
- }
- }
- .columnMenu-colors {
- display: flex;
- justify-content: space-between;
- flex-wrap: wrap;
- .columnMenu-colorPicker {
- cursor: pointer;
- width: 20px;
- height: 20px;
- border-radius: 10px;
- &.active {
- border: 2px solid white;
- box-shadow: 0 0 0 2px lightgray;
- }
- }
- }
-}
-.schema-icon {
- cursor: pointer;
- width: 25px;
- height: 25px;
- display: flex;
- align-items: center;
- justify-content: center;
- align-content: center;
- background-color: $medium-blue;
- color: white;
- margin-right: 5px;
- font-size: 10px;
- border-radius: 3px;
-}
+ .schema-table {
+ background-color: $white;
-.keys-options-wrapper {
- position: absolute;
- text-align: left;
- height: fit-content;
- top: 100%;
- z-index: 21;
- background-color: #ffffff;
- box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%);
- padding: 1px;
- .key-option {
- cursor: pointer;
- color: #000000;
- width: 100%;
- height: 25px;
- font-weight: 400;
- display: flex;
- justify-content: left;
- align-items: center;
- padding-left: 5px;
- &:hover {
- background-color: $light-gray;
- }
- }
-}
-
-.collectionSchema-row {
- height: 100%;
- background-color: white;
- &.row-focused .rt-td {
- background-color: $light-blue; //$light-gray;
- overflow: visible;
- }
- &.row-wrapped {
- .rt-td {
- white-space: normal;
- }
- }
- .row-dragger {
- display: flex;
- justify-content: space-evenly;
- width: 58px;
- position: absolute;
- /* max-width: 50px; */
- min-height: 30px;
- align-items: center;
- color: lightgray;
- background-color: white;
- transition: color 0.1s ease;
- .row-option {
- color: black;
- cursor: pointer;
- position: relative;
- transition: color 0.1s ease;
+ .schema-header-row,
+ .schema-row {
display: flex;
- flex-direction: column;
- justify-content: center;
- z-index: 2;
- border-radius: 3px;
- padding: 3px;
- &:hover {
- background-color: $light-gray;
+ flex-direction: row;
+
+ .schema-column-header,
+ .schema-table-cell,
+ .row-menu {
+ min-width: 50px;
+ border: 1px solid $medium-gray;
+ padding: 5px;
+ overflow: hidden;
}
}
- }
- .collectionSchema-row-wrapper {
- &.row-above {
- border-top: 1px solid $medium-blue;
- }
- &.row-below {
- border-bottom: 1px solid $medium-blue;
- }
- &.row-inside {
- border: 2px dashed $medium-blue;
- }
- .row-dragging {
- background-color: blue;
- }
- }
-}
-.collectionSchemaView-cellContainer {
- width: 100%;
- height: unset;
-}
-
-.collectionSchemaView-cellContents {
- width: 100%;
-}
+ .schema-header-row {
+ justify-content: flex-end;
-.collectionSchemaView-cellWrapper {
- display: flex;
- height: 100%;
- text-align: left;
- padding-left: 19px;
- position: relative;
- align-items: center;
- align-content: center;
- &:focus {
- outline: none;
- }
- &.editing {
- padding: 0;
- box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
- transform: scale(1.1);
- z-index: 40;
- input {
- outline: 0;
- border: none;
- background-color: $white;
- width: 100%;
- height: fit-content;
- min-height: 26px;
- }
- }
- &.focused {
- overflow: hidden;
- &.inactive {
- border: none;
- }
- }
- p {
- width: 100%;
- height: 100%;
- }
- &:hover .collectionSchemaView-cellContents-docExpander {
- display: block;
- }
- .collectionSchemaView-cellContents-document {
- display: inline-block;
- }
- .collectionSchemaView-cellContents-docButton {
- float: right;
- width: '15px';
- height: '15px';
- }
- .collectionSchemaView-dropdownWrapper {
- border: grey;
- border-style: solid;
- border-width: 1px;
- height: 30px;
- .collectionSchemaView-dropdownButton {
- //display: inline-block;
- float: left;
- height: 100%;
- }
- .collectionSchemaView-dropdownText {
- display: inline-block;
- //float: right;
- height: 100%;
- display: 'flex';
- font-size: 13;
- justify-content: 'center';
- align-items: 'center';
- }
- }
- .collectionSchemaView-dropdownContainer {
- position: absolute;
- border: 1px solid rgba(0, 0, 0, 0.04);
- box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14);
- .collectionSchemaView-dropdownOption:hover {
- background-color: rgba(0, 0, 0, 0.14);
- cursor: pointer;
+ .schema-column-header {
+ font-weight: bold;
+ }
}
- }
-}
-
-.collectionSchemaView-cellContents-docExpander {
- height: 30px;
- width: 30px;
- display: none;
- position: absolute;
- top: 0;
- right: 0;
- background-color: lightgray;
-}
-
-.doc-drag-over {
- background-color: red;
-}
-
-.collectionSchemaView-toolbar {
- z-index: 100;
-}
-
-.collectionSchemaView-toolbar {
- height: 30px;
- display: flex;
- justify-content: flex-end;
- padding: 0 10px;
- border-bottom: 2px solid gray;
- .collectionSchemaView-toolbar-item {
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
-}
-
-#preview-schema-checkbox-div {
- margin-left: 20px;
- font-size: 12px;
-}
-.collectionSchemaView-table {
- width: 100%;
- height: 100%;
- overflow: auto;
- padding: 3px;
-}
+ .schema-row {
+ justify-content: space-evenly;
-.rt-td.rt-expandable {
- overflow: visible;
- position: relative;
- height: 100%;
- z-index: 1;
-}
+ .row-menu {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
-.reactTable-sub {
- background-color: rgb(252, 252, 252);
- width: 100%;
- .rt-thead {
- display: none;
- }
- .row-dragger {
- background-color: rgb(252, 252, 252);
- }
- .rt-table {
- background-color: rgb(252, 252, 252);
- }
- .collectionSchemaView-table {
- width: 100%;
- border: solid 1px;
- overflow: visible;
- padding: 0px;
- }
-}
+ .row-button {
+ width: 20px;
+ height: 20px;
+ border-radius: 100%;
+ background-color: $dark-gray;
+ color: white;
+ padding: 5px;
+ cursor: pointer;
+ }
+ }
-.collectionSchemaView-expander {
- height: 100%;
- min-height: 30px;
- position: absolute;
- color: gray;
- width: 20;
- height: auto;
- left: 55;
- svg {
- position: absolute;
- top: 50%;
- left: 10;
- transform: translate(-50%, -50%);
+ .row-cells {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+ }
+ }
}
}
-
-.collectionSchemaView-addRow {
- color: gray;
- letter-spacing: 2px;
- text-transform: uppercase;
- cursor: pointer;
- font-size: 10.5px;
- margin-left: 50px;
- margin-top: 10px;
-}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index c4ee1805f..9ca1644e3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -1,646 +1,164 @@
import React = require('react');
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, untracked } from 'mobx';
+import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx';
import { observer } from 'mobx-react';
-import Measure from 'react-measure';
-import { Resize } from 'react-table';
-import { Doc, Opt } from '../../../../fields/Doc';
+import { Doc } 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, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { SelectionManager } from '../../../util/SelectionManager';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { Transform } from '../../../util/Transform';
-import { undoBatch } from '../../../util/UndoManager';
-import { ContextMenu } from '../../ContextMenu';
-import { ContextMenuProps } from '../../ContextMenuItem';
-import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
-import { DocumentView } from '../../nodes/DocumentView';
-import { DefaultStyleProvider } from '../../StyleProvider';
+import { Cast } from '../../../../fields/Types';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
-import { SchemaTable } from './SchemaTable';
-// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
+import { SchemaColumnHeader } from './SchemaColumnHeader';
+import { SchemaRowBox } from './SchemaRowBox';
+import { DragManager } from '../../../util/DragManager';
+import { PresBox } from '../../nodes/trails/PresBox';
+import { UndoManager } from '../../../util/UndoManager';
+import { returnFalse } from '../../../../Utils';
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],
-]);
+
+const defaultColumnKeys: string[] = ['title', 'type', 'author', 'text', 'data', 'tags'];
@observer
export class CollectionSchemaView extends CollectionSubView() {
- private _previewCont?: HTMLDivElement;
+ private _lastSelectedRow: number | undefined;
- @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;
+ @observable _rowMenuWidth: number = 60;
+ @observable _selectedDocs: ObservableSet = new ObservableSet<SchemaRowBox>();
+ @observable _isDragging: 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 columnKeys() {
+ return Cast(this.props.Document.columnKeys, listSpec('string'), defaultColumnKeys);
}
- @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));
+ @computed get columnWidths() {
+ return Cast(
+ this.props.Document.columnWidths,
+ listSpec('number'),
+ this.columnKeys.map(() => (this.props.PanelWidth() - this._rowMenuWidth) / this.columnKeys.length)
+ );
}
- @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;
+ changeColumnKey = (index: number, newKey: string) => {
+ let currKeys = this.columnKeys;
+ currKeys[index] = newKey;
+ this.layoutDoc.columnKeys = new List<string>(currKeys);
+ return true;
};
- 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;
+ selectRow = (e: React.PointerEvent, doc: Doc) => {
+ const ctrl = e.ctrlKey || e.metaKey;
+ const shift = e.shiftKey;
+ if (shift && this._lastSelectedRow !== undefined) {
+ const currRowIndex = this.childDocs.indexOf(doc);
+ const startRow = Math.min(this._lastSelectedRow, currRowIndex);
+ const endRow = Math.max(this._lastSelectedRow, currRowIndex);
+ for (let i = startRow; i <= endRow; i++) {
+ const currDoc: Doc = this.childDocs[i];
+ if (!this._selectedDocs.has(currDoc)) this._selectedDocs.add(currDoc);
+ }
+ this._lastSelectedRow = endRow;
+ } else if (ctrl) {
+ if (!this._selectedDocs.has(doc)) {
+ this._selectedDocs.add(doc);
+ this._lastSelectedRow = this.childDocs.indexOf(doc);
} 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;
- }
- }
+ this._selectedDocs.delete(doc);
}
- }
- };
-
- @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._selectedDocs.clear();
+ this._selectedDocs.add(doc);
+ this._lastSelectedRow = this.childDocs.indexOf(doc);
}
- 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);
- }}>
- Hide 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.SelectSchemaViewDoc(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;
+ sortedSelectedDocs = (): Doc[] => {
+ return this.childDocs.filter(doc => this._selectedDocs.has(doc));
};
- 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);
+ @action.bound
+ moveDocument = (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => {
+ console.log('hello');
+ console.log(targetCollection?.title);
+ if (Doc.AreProtosEqual(this.props.Document, targetCollection)) {
+ console.log('hi');
+ return true;
}
- };
-
- @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}
- fitContentsToBox={returnTrue}
- 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.childDocFilters}
- docRangeFilters={this.childDocRangeFilters}
- searchFilterDocs={this.searchFilterDocs}
- styleProvider={DefaultStyleProvider}
- 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();
+ const first = document instanceof Doc ? document : document[0];
+ if (!first?._stayInCollection && addDocument !== returnFalse) {
+ UndoManager.RunInTempBatch(() => this.props.removeDocument?.(document) && addDocument(document));
+ return true;
}
+ return false;
};
@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;
+ startDrag = (e: React.PointerEvent, doc: Doc) => {
+ if (this._selectedDocs.size === 0) this._selectedDocs.add(doc);
+ this._isDragging = true;
+ const dragData = new DragManager.DocumentDragData(this.sortedSelectedDocs(), 'move');
+ dragData.moveDocument = this.moveDocument;
+ const dragItem: HTMLElement[] = [];
+ const dragDiv = document.createElement('div');
+ dragDiv.className = 'presItem-multiDrag';
+ dragDiv.innerText = 'Move ' + this._selectedDocs.size + ' rows';
+ dragDiv.style.position = 'absolute';
+ dragDiv.style.top = e.clientY + 'px';
+ dragDiv.style.left = e.clientX - 50 + 'px';
+ dragItem.push(dragDiv);
+
+ DragManager.StartDocumentDrag(
+ dragItem.map(ele => ele),
+ dragData,
+ e.clientX,
+ e.clientY,
+ undefined
+ );
+ this._isDragging = false;
+ return true;
};
@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;
+ endDrag = (e: React.PointerEvent) => {
+ // this._isDragging = false;
};
- 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: 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 className="collectionSchemaView">
+ <div className="schema-table">
+ <div className="schema-header-row">
+ {this.columnKeys.map((key, index) => (
+ <SchemaColumnHeader columnIndex={index} columnKeys={this.columnKeys} columnWidths={this.columnWidths} changeColumnKey={this.changeColumnKey} />
+ ))}
+ </div>
+ <div className="schema-table-content">
+ {this.childDocs.map((doc: Doc) => (
+ <SchemaRowBox
+ {...this.props}
+ Document={doc}
+ ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+ ContainingCollectionView={this.props.CollectionView}
+ columnKeys={this.columnKeys}
+ columnWidths={this.columnWidths}
+ rowMenuWidth={this._rowMenuWidth}
+ selectedRows={this._selectedDocs}
+ selectRow={this.selectRow}
+ startDrag={this.startDrag}
+ endDrag={this.endDrag}
+ dragging={this._isDragging}
+ />
+ ))}
+ </div>
</div>
- {this.dividerDragger}
- {!this.previewWidth() ? null : this.previewPanel}
- {this._headerOpen && this.props.isContentActive() ? menu : null}
</div>
);
}
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
new file mode 100644
index 000000000..9bd1b843f
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -0,0 +1,28 @@
+import React = require('react');
+import { observer } from 'mobx-react';
+import './CollectionSchemaView.scss';
+import { EditableView } from '../../EditableView';
+import { emptyFunction } from '../../../../Utils';
+import { action, computed } from 'mobx';
+
+export interface SchemaColumnHeaderProps {
+ columnKeys: string[];
+ columnWidths: number[];
+ columnIndex: number;
+ changeColumnKey: (index: number, newKey: string) => boolean;
+}
+
+@observer
+export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
+ @computed get fieldKey() {
+ return this.props.columnKeys[this.props.columnIndex];
+ }
+
+ render() {
+ return (
+ <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}>
+ <EditableView SetValue={(newKey: string) => this.props.changeColumnKey(this.props.columnIndex, newKey)} GetValue={() => this.fieldKey} contents={this.fieldKey} />
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
new file mode 100644
index 000000000..0065a0938
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -0,0 +1,97 @@
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, ObservableMap, ObservableSet } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../../fields/Doc';
+import { undoBatch } from '../../../util/UndoManager';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
+import { FieldViewProps } from '../../nodes/FieldView';
+import './CollectionSchemaView.scss';
+import { SchemaTableCell } from './SchemaTableCell';
+import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
+
+export interface SchemaRowBoxProps extends FieldViewProps {
+ columnKeys: string[];
+ columnWidths: number[];
+ rowMenuWidth: number;
+ selectedRows: ObservableSet<Doc>;
+ selectRow: (e: any, doc: Doc) => void;
+ startDrag: (e: any, doc: Doc) => boolean;
+ endDrag: (e: any) => void
+ dragging: boolean;
+}
+
+@observer
+export class SchemaRowBox extends ViewBoxBaseComponent<SchemaRowBoxProps>() {
+ private _ref: HTMLDivElement | null = null;
+
+ isSelected = () => this.props.selectedRows.has(this.props.Document);
+ bounds = () => this._ref?.getBoundingClientRect();
+
+ @action
+ onRowPointerDown = (e: React.PointerEvent) => {
+ e.stopPropagation();
+
+ setupMoveUpEvents(
+ this, e,
+ (e) => this.props.startDrag(e, this.props.Document),
+ emptyFunction,
+ (e) => this.props.selectRow(e, this.props.Document)
+ )
+ }
+
+ onPointerEnter = (e: any) => {
+ document.removeEventListener('pointermove', this.onPointerMove);
+ document.addEventListener('pointermove', this.onPointerMove);
+ };
+
+ onPointerMove = (e: any) => {
+ let dragIsRow: boolean = true;
+ DragManager.docsBeingDragged.forEach(doc => {
+ dragIsRow = this.props.selectedRows.has(doc);
+ })
+ if (this._ref && dragIsRow) {
+ const rect = this._ref.getBoundingClientRect();
+ const y = e.clientY - rect.top; //y position within the element.
+ const height = this._ref.clientHeight;
+ const halfLine = height / 2;
+ if (y <= halfLine) {
+ this._ref.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
+ this._ref.style.borderBottom = '0px';
+ } else if (y > halfLine) {
+ this._ref.style.borderTop = '0px';
+ this._ref.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
+ }
+ }
+ };
+
+ onPointerLeave = (e: any) => {
+ if (this._ref) {
+ this._ref.style.borderTop = '0px';
+ this._ref.style.borderBottom = '0px';
+ }
+ document.removeEventListener('pointermove', this.onPointerMove);
+ };
+
+ render() {
+ return (
+ <div className="schema-row" style={this.isSelected() ? { backgroundColor: Colors.LIGHT_BLUE, opacity: this.props.dragging ? 0.5 : 1} : {}} onPointerDown={this.onRowPointerDown} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave} ref={(row: HTMLDivElement | null) => (this._ref = row)}>
+ <div className="row-menu" style={{width: this.props.rowMenuWidth}}>
+ <div className="row-button" onPointerDown={undoBatch((e) => {e.stopPropagation(); this.props.removeDocument?.(this.props.Document)})}>
+ <FontAwesomeIcon icon="times" />
+ </div>
+ <div className="row-button" onPointerDown={(e) => {e.stopPropagation(); this.props.addDocTab(this.props.Document, 'add:right')}}>
+ <FontAwesomeIcon icon="external-link-alt" />
+ </div>
+ </div>
+ <div className="row-cells">
+ {this.props.columnKeys.map((key, index) => (
+ <SchemaTableCell Document={this.props.Document} key={key} fieldKey={key} columnWidth={this.props.columnWidths[index]} />
+ ))}
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
new file mode 100644
index 000000000..1eb5e0e01
--- /dev/null
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -0,0 +1,24 @@
+import React = require('react');
+import { observer } from 'mobx-react';
+import { Doc, Field } from '../../../../fields/Doc';
+import { StrCast } from '../../../../fields/Types';
+import './CollectionSchemaView.scss';
+
+export interface SchemaTableCellProps {
+ Document: Doc;
+ fieldKey: string;
+ columnWidth: number;
+}
+
+@observer
+export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
+ render() {
+ return (
+ <div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
+ {/* {StrCast(this.props.Document[this.props.fieldKey])} */}
+ {/* Field.toKeyValueString(this.props.Document, this.props.fieldKey) */}
+ {Field.toString(this.props.Document[this.props.fieldKey] as Field)}
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
index adcd9e1e3..fb93d8b8e 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaCells.tsx
@@ -1,35 +1,35 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { extname } from "path";
-import DatePicker from "react-datepicker";
-import { CellInfo } from "react-table";
-import { DateField } from "../../../../fields/DateField";
-import { Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { ComputedField } from "../../../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, FieldValue, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { emptyFunction, Utils } from "../../../../Utils";
-import { Docs } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager } from "../../../util/DragManager";
-import { KeyCodes } from "../../../util/KeyCodes";
-import { CompileScript } from "../../../util/Scripting";
-import { SearchUtil } from "../../../util/SearchUtil";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { undoBatch } from "../../../util/UndoManager";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { extname } from 'path';
+import DatePicker from 'react-datepicker';
+import { CellInfo } from 'react-table';
+import { DateField } from '../../../../fields/DateField';
+import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { ComputedField } from '../../../../fields/ScriptField';
+import { BoolCast, Cast, DateCast, FieldValue, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { emptyFunction, Utils } from '../../../../Utils';
+import { Docs } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager } from '../../../util/DragManager';
+import { KeyCodes } from '../../../util/KeyCodes';
+import { CompileScript } from '../../../util/Scripting';
+import { SearchUtil } from '../../../util/SearchUtil';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { undoBatch } from '../../../util/UndoManager';
import '../../../views/DocumentDecorations.scss';
-import { EditableView } from "../../EditableView";
+import { EditableView } from '../../EditableView';
import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
-import { DocumentIconContainer } from "../../nodes/DocumentIcon";
-import { OverlayView } from "../../OverlayView";
-import { CollectionView } from "../CollectionView";
-import "./CollectionSchemaView.scss";
+import { DocumentIconContainer } from '../../nodes/DocumentIcon';
+import { OverlayView } from '../../OverlayView';
+import { CollectionView } from '../CollectionView';
+import './CollectionSchemaView.scss';
// intialize cell properties
export interface CellProps {
@@ -48,8 +48,7 @@ export interface CellProps {
// called when a button is pressed on the node itself
addDocTab: (document: Doc, where: string) => boolean;
pinToPres: (document: Doc) => void;
- moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined,
- addDocument: (document: Doc | Doc[]) => boolean) => boolean;
+ moveDocument?: (document: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
isFocused: boolean;
changeFocusedCellByIndex: (row: number, col: number) => void;
// set whether the cell is in the isEditing mode
@@ -67,7 +66,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// return a field key that is corrected for whether it COMMENT
public static resolvedFieldKey(column: string, rowDoc: Doc) {
const fieldKey = column;
- if (fieldKey.startsWith("*")) {
+ if (fieldKey.startsWith('*')) {
const rootKey = fieldKey.substring(1);
const allKeys = [...Array.from(Object.keys(rowDoc)), ...Array.from(Object.keys(Doc.GetProto(rowDoc)))];
const matchedKeys = allKeys.filter(key => key.includes(rootKey));
@@ -82,33 +81,37 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original);
// methods for dragging and dropping
protected _dropDisposer?: DragManager.DragDropDisposer;
- @observable contents: string = "";
+ @observable contents: string = '';
- componentDidMount() { document.addEventListener("keydown", this.onKeyDown); }
- componentWillUnmount() { document.removeEventListener("keydown", this.onKeyDown); }
+ componentDidMount() {
+ document.addEventListener('keydown', this.onKeyDown);
+ }
+ componentWillUnmount() {
+ document.removeEventListener('keydown', this.onKeyDown);
+ }
@action
onKeyDown = (e: KeyboardEvent): void => {
// If a cell is editable and clicked, hitting enter shoudl allow the user to edit it
if (this.props.isFocused && this.props.isEditable && e.keyCode === KeyCodes.ENTER) {
- document.removeEventListener("keydown", this.onKeyDown);
+ document.removeEventListener('keydown', this.onKeyDown);
this._isEditing = true;
this.props.setIsEditing(true);
}
- }
+ };
@action
isEditingCallback = (isEditing: boolean): void => {
// a general method that takes a boolean that determines whether the cell should be in
// is-editing mode
// remove the event listener if it's there
- document.removeEventListener("keydown", this.onKeyDown);
+ document.removeEventListener('keydown', this.onKeyDown);
// it's not already in is-editing mode, re-add the event listener
- isEditing && document.addEventListener("keydown", this.onKeyDown);
+ isEditing && document.addEventListener('keydown', this.onKeyDown);
this._isEditing = isEditing;
this.props.setIsEditing(isEditing);
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
- }
+ };
@action
onPointerDown = async (e: React.PointerEvent): Promise<void> => {
@@ -119,19 +122,19 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
this.props.setPreviewDoc(this.props.rowProps.original);
let url: string;
- if (url = StrCast(this.props.rowProps.row.href)) {
+ if ((url = StrCast(this.props.rowProps.row.href))) {
// opens up the the doc in a new window, blurring the old one
try {
new URL(url);
const temp = window.open(url)!;
temp.blur();
window.focus();
- } catch { }
+ } catch {}
}
const doc = Cast(this._rowDoc[this.renderFieldKey], Doc, null);
doc && this.props.setPreviewDoc(doc);
- }
+ };
@undoBatch
applyToDoc = (doc: Doc, row: number, col: number, run: (args?: { [name: string]: any }) => any) => {
@@ -142,7 +145,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
doc[this.renderFieldKey] = res.result;
return true;
// return whether the change was successful
- }
+ };
private drop = (e: Event, de: DragManager.DropEvent) => {
// if the drag has data at its completion
@@ -151,41 +154,51 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
if (de.complete.docDragData.draggedDocuments.length === 1) {
// update the renderFieldKey
this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0];
- }
- else {
+ } else {
// create schema document reflecting the new column arrangement
- const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {});
+ const coll = Docs.Create.SchemaDocument([new SchemaHeaderField('title', '#f1efeb')], de.complete.docDragData.draggedDocuments, {});
this._rowDataDoc[this.renderFieldKey] = coll;
}
e.stopPropagation();
}
- }
+ };
protected dropRef = (ele: HTMLElement | null) => {
// if the drop disposer is not undefined, run its function
this._dropDisposer?.();
// if ele is not null, give ele a non-undefined drop disposer
ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)));
- }
+ };
returnHighlights(contents: string, positions?: number[]) {
if (positions) {
const results = [];
StrCast(this.props.Document._searchString);
const length = StrCast(this.props.Document._searchString).length;
- const color = contents ? "black" : "grey";
+ const color = contents ? 'black' : 'grey';
- results.push(<span key="-1" style={{ color }}>{contents?.slice(0, positions[0])}</span>);
+ results.push(
+ <span key="-1" style={{ color }}>
+ {contents?.slice(0, positions[0])}
+ </span>
+ );
positions.forEach((num, cur) => {
- results.push(<span key={"start" + cur} style={{ backgroundColor: "#FFFF00", color }}>{contents?.slice(num, num + length)}</span>);
+ results.push(
+ <span key={'start' + cur} style={{ backgroundColor: '#FFFF00', color }}>
+ {contents?.slice(num, num + length)}
+ </span>
+ );
let end = 0;
- cur === positions.length - 1 ? end = contents.length : end = positions[cur + 1];
- results.push(<span key={"end" + cur} style={{ color }}>{contents?.slice(num + length, end)}</span>);
- }
- );
+ cur === positions.length - 1 ? (end = contents.length) : (end = positions[cur + 1]);
+ results.push(
+ <span key={'end' + cur} style={{ color }}>
+ {contents?.slice(num + length, end)}
+ </span>
+ );
+ });
return results;
}
- return <span style={{ color: contents ? "black" : "grey" }}>{contents ? contents?.valueOf() : "undefined"}</span>;
+ return <span style={{ color: contents ? 'black' : 'grey' }}>{contents ? contents?.valueOf() : 'undefined'}</span>;
}
@computed get renderFieldKey() {
@@ -199,10 +212,9 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
// Jump to the this document
- DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [],
- undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
+ DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [], undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
}
- }
+ };
renderCellWithType(type: string | undefined) {
const dragRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -214,29 +226,29 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const onPointerEnter = (e: React.PointerEvent): void => {
// e.buttons === 1 means the left moue pointer is down
- if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === "document" || type === undefined)) {
- dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over";
+ if (e.buttons === 1 && SnappingManager.GetIsDragging() && (type === 'document' || type === undefined)) {
+ dragRef.current!.className = 'collectionSchemaView-cellContainer doc-drag-over';
}
};
const onPointerLeave = (e: React.PointerEvent): void => {
// change the class name to indicate that the cell is no longer being dragged
- dragRef.current!.className = "collectionSchemaView-cellContainer";
+ dragRef.current!.className = 'collectionSchemaView-cellContainer';
};
let contents = Field.toString(field as Field);
// display 2 hyphens instead of a blank box for empty cells
- contents = contents === "" ? "--" : contents;
+ contents = contents === '' ? '--' : contents;
// classname reflects the tatus of the cell
- let className = "collectionSchemaView-cellWrapper";
- if (this._isEditing) className += " editing";
- if (this.props.isFocused && this.props.isEditable) className += " focused";
- if (this.props.isFocused && !this.props.isEditable) className += " inactive";
+ let className = 'collectionSchemaView-cellWrapper';
+ if (this._isEditing) className += ' editing';
+ if (this.props.isFocused && this.props.isEditable) className += ' focused';
+ if (this.props.isFocused && !this.props.isEditable) className += ' inactive';
const positions = [];
- if (StrCast(this.props.Document._searchString).toLowerCase() !== "") {
+ if (StrCast(this.props.Document._searchString).toLowerCase() !== '') {
// term is ...promise pending... if the field is a Promise, otherwise it is the cell's contents
- let term = (field instanceof Promise) ? "...promise pending..." : contents.toLowerCase();
+ let term = field instanceof Promise ? '...promise pending...' : contents.toLowerCase();
const search = StrCast(this.props.Document._searchString).toLowerCase();
let start = term.indexOf(search);
let tally = 0;
@@ -256,56 +268,60 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
positions.pop();
}
}
- const placeholder = type === "number" ? "0" : contents === "" ? "--" : "undefined";
+ const placeholder = type === 'number' ? '0' : contents === '' ? '--' : 'undefined';
return (
- <div className="collectionSchemaView-cellContainer" style={{ cursor: field instanceof Doc ? "grab" : "auto" }}
- ref={dragRef} onPointerDown={this.onPointerDown} onClick={action(e => this._isEditing = true)} onPointerEnter={onPointerEnter} onPointerLeave={onPointerLeave}>
+ <div
+ className="collectionSchemaView-cellContainer"
+ style={{ cursor: field instanceof Doc ? 'grab' : 'auto' }}
+ ref={dragRef}
+ onPointerDown={this.onPointerDown}
+ onClick={action(e => (this._isEditing = true))}
+ onPointerEnter={onPointerEnter}
+ onPointerLeave={onPointerLeave}>
<div className={className} ref={this._focusRef} tabIndex={-1}>
- <div className="collectionSchemaView-cellContents" ref={type === undefined || type === "document" ? this.dropRef : null}>
- {!this.props.Document._searchDoc ?
+ <div className="collectionSchemaView-cellContents" ref={type === undefined || type === 'document' ? this.dropRef : null}>
+ {!this.props.Document._searchDoc ? (
<EditableView
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
- display={"inline"}
+ display={'inline'}
contents={contents}
- height={"auto"}
+ height={'auto'}
maxHeight={Number(MAX_ROW_HEIGHT)}
placeholder={placeholder}
GetValue={() => {
const cfield = ComputedField.WithoutComputed(() => FieldValue(field));
const cscript = cfield instanceof ComputedField ? cfield.script.originalScript : undefined;
- const cfinalScript = cscript?.split("return")[cscript.split("return").length - 1];
- return cscript ? (cfinalScript?.endsWith(";") ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) :
- Field.IsField(cfield) ? Field.toScriptString(cfield) : "";
+ const cfinalScript = cscript?.split('return')[cscript.split('return').length - 1];
+ return cscript ? (cfinalScript?.endsWith(';') ? `:=${cfinalScript?.substring(0, cfinalScript.length - 2)}` : cfinalScript) : Field.IsField(cfield) ? Field.toScriptString(cfield) : '';
}}
SetValue={action((value: string) => {
// sets what is displayed after the user makes an input
let retVal = false;
- if (value.startsWith(":=") || value.startsWith("=:=")) {
+ if (value.startsWith(':=') || value.startsWith('=:=')) {
// decides how to compute a value when given either of the above strings
- const script = value.substring(value.startsWith("=:=") ? 3 : 2);
- retVal = this.props.setComputed(script, value.startsWith(":=") ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
+ const script = value.substring(value.startsWith('=:=') ? 3 : 2);
+ retVal = this.props.setComputed(script, value.startsWith(':=') ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col);
} else {
// check if the input is a number
let inputIsNum = true;
for (const s of value) {
- if (isNaN(parseInt(s)) && !(s === ".") && !(s === ",")) {
+ if (isNaN(parseInt(s)) && !(s === '.') && !(s === ',')) {
inputIsNum = false;
}
}
// check if the input is a boolean
- const inputIsBool: boolean = value === "false" || value === "true";
- // what to do in the case
- if (!inputIsNum && !inputIsBool && !value.startsWith("=")) {
+ const inputIsBool: boolean = value === 'false' || value === 'true';
+ // what to do in the case
+ if (!inputIsNum && !inputIsBool && !value.startsWith('=')) {
// if it's not a number, it's a string, and should be processed as such
- // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
+ // strips the string of quotes when it is edited to prevent quotes form being added to the text automatically
// after each edit
let valueSansQuotes = value;
if (this._isEditing) {
const vsqLength = valueSansQuotes.length;
// get rid of outer quotes
- valueSansQuotes = valueSansQuotes.substring(value.startsWith("\"") ? 1 : 0,
- valueSansQuotes.charAt(vsqLength - 1) === "\"" ? vsqLength - 1 : vsqLength);
+ valueSansQuotes = valueSansQuotes.substring(value.startsWith('"') ? 1 : 0, valueSansQuotes.charAt(vsqLength - 1) === '"' ? vsqLength - 1 : vsqLength);
}
let inputAsString = '"';
// escape any quotes in the string
@@ -319,27 +335,27 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
// add a closing quote
inputAsString += '"';
//two options here: we can strip off outer quotes or we can figure out what's going on with the script
- const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+ const script = CompileScript(inputAsString, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
const changeMade = inputAsString.length !== value.length || inputAsString.length - 2 !== value.length;
// change it if a change is made, otherwise, just compile using the old cell conetnts
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
// handle numbers and expressions
- } else if (inputIsNum || value.startsWith("=")) {
+ } else if (inputIsNum || value.startsWith('=')) {
//TODO: make accept numbers
- const inputscript = value.substring(value.startsWith("=") ? 1 : 0);
+ const inputscript = value.substring(value.startsWith('=') ? 1 : 0);
// if commas are not stripped, the parser only considers the numbers after the last comma
- let inputSansCommas = "";
+ let inputSansCommas = '';
for (const s of inputscript) {
- if (!(s === ",")) {
+ if (!(s === ',')) {
inputSansCommas += s;
}
}
- const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+ const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
const changeMade = value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
// handle booleans
} else if (inputIsBool) {
- const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
+ const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
const changeMade = value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
}
@@ -352,33 +368,47 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
})}
OnFillDown={async (value: string) => {
// computes all of the value preceded by :=
- const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- script.compiled && DocListCast(this.props.Document[this.props.fieldKey]).
- forEach((doc, i) => value.startsWith(":=") ?
- this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) :
- this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run));
+ const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: 'number', $c: 'number', $: 'any' } });
+ script.compiled &&
+ DocListCast(this.props.Document[this.props.fieldKey]).forEach((doc, i) =>
+ value.startsWith(':=') ? this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)
+ );
}}
/>
- :
+ ) : (
this.returnHighlights(contents, positions)
- }
- </div >
+ )}
+ </div>
</div>
</div>
);
}
- render() { return this.renderCellWithType(undefined); }
+ render() {
+ return this.renderCellWithType(undefined);
+ }
}
@observer
-export class CollectionSchemaNumberCell extends CollectionSchemaCell { render() { return this.renderCellWithType("number"); } }
+export class CollectionSchemaNumberCell extends CollectionSchemaCell {
+ render() {
+ return this.renderCellWithType('number');
+ }
+}
@observer
-export class CollectionSchemaBooleanCell extends CollectionSchemaCell { render() { return this.renderCellWithType("boolean"); } }
+export class CollectionSchemaBooleanCell extends CollectionSchemaCell {
+ render() {
+ return this.renderCellWithType('boolean');
+ }
+}
@observer
-export class CollectionSchemaStringCell extends CollectionSchemaCell { render() { return this.renderCellWithType("string"); } }
+export class CollectionSchemaStringCell extends CollectionSchemaCell {
+ render() {
+ return this.renderCellWithType('string');
+ }
+}
@observer
export class CollectionSchemaDateCell extends CollectionSchemaCell {
@@ -396,24 +426,24 @@ export class CollectionSchemaDateCell extends CollectionSchemaCell {
// ^ DateCast is always undefined for some reason, but that is what the field should be set to
this._rowDoc[this.renderFieldKey] = new DateField(date as Date);
//}
- }
+ };
render() {
- return !this.props.isFocused ? <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : "--"}</span> :
- <DatePicker
- selected={this._date?.date || new Date}
- onSelect={date => this.handleChange(date)}
- onChange={date => this.handleChange(date)}
- />;
+ return !this.props.isFocused ? (
+ <span onPointerDown={this.onPointerDown}>{this._date ? Field.toString(this._date as Field) : '--'}</span>
+ ) : (
+ <DatePicker selected={this._date?.date || new Date()} onSelect={date => this.handleChange(date)} onChange={date => this.handleChange(date)} />
+ );
}
}
@observer
export class CollectionSchemaDocCell extends CollectionSchemaCell {
-
_overlayDisposer?: () => void;
- @computed get _doc() { return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc)); }
+ @computed get _doc() {
+ return FieldValue(Cast(this._rowDoc[this.renderFieldKey], Doc));
+ }
@action
onSetValue = (value: string) => {
@@ -422,7 +452,7 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
const script = CompileScript(value, {
addReturn: true,
typecheck: true,
- transformer: DocumentIconContainer.getTransformer()
+ transformer: DocumentIconContainer.getTransformer(),
});
// compile the script
const results = script.compiled && script.run();
@@ -432,44 +462,43 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
return true;
}
return false;
- }
+ };
- componentWillUnmount() { this.onBlur(); }
+ componentWillUnmount() {
+ this.onBlur();
+ }
- onBlur = () => { this._overlayDisposer?.(); };
+ onBlur = () => {
+ this._overlayDisposer?.();
+ };
onFocus = () => {
this.onBlur();
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
- }
+ };
@action
isEditingCallback = (isEditing: boolean): void => {
// the isEditingCallback from a general CollectionSchemaCell
- document.removeEventListener("keydown", this.onKeyDown);
- isEditing && document.addEventListener("keydown", this.onKeyDown);
+ document.removeEventListener('keydown', this.onKeyDown);
+ isEditing && document.addEventListener('keydown', this.onKeyDown);
this._isEditing = isEditing;
this.props.setIsEditing(isEditing);
this.props.changeFocusedCellByIndex(this.props.row, this.props.col);
- }
+ };
render() {
// if there's a doc, render it
- return !this._doc ? this.renderCellWithType("document") :
- <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1}
- onPointerDown={this.onPointerDown}
- >
- <div className="collectionSchemaView-cellContents-document"
- style={{ padding: "5.9px" }}
- ref={this.dropRef}
- onFocus={this.onFocus}
- onBlur={this.onBlur}
- >
+ return !this._doc ? (
+ this.renderCellWithType('document')
+ ) : (
+ <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+ <div className="collectionSchemaView-cellContents-document" style={{ padding: '5.9px' }} ref={this.dropRef} onFocus={this.onFocus} onBlur={this.onBlur}>
<EditableView
editing={this._isEditing}
isEditingCallback={this.isEditingCallback}
- display={"inline"}
- contents={this._doc.title || "--"}
- height={"auto"}
+ display={'inline'}
+ contents={this._doc.title || '--'}
+ height={'auto'}
maxHeight={Number(MAX_ROW_HEIGHT)}
GetValue={() => StrCast(this._doc?.title)}
SetValue={action((value: string) => {
@@ -477,33 +506,36 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
return true;
})}
/>
- </div >
- <div onClick={() => this._doc && this.props.addDocTab(this._doc, "add:right")} className="collectionSchemaView-cellContents-docButton">
+ </div>
+ <div onClick={() => this._doc && this.props.addDocTab(this._doc, 'add:right')} className="collectionSchemaView-cellContents-docButton">
<FontAwesomeIcon icon="external-link-alt" size="lg" />
</div>
- </div>;
+ </div>
+ );
}
}
@observer
export class CollectionSchemaImageCell extends CollectionSchemaCell {
-
choosePath(url: URL) {
- if (url.protocol === "data") return url.href; // if the url ises the data protocol, just return the href
+ if (url.protocol === 'data') return url.href; // if the url ises the data protocol, just return the href
if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
- if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here — good question
+ if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href; //Why is this here — good question
const ext = extname(url.href);
- return url.href.replace(ext, "_o" + ext);
+ return url.href.replace(ext, '_o' + ext);
}
render() {
const field = Cast(this._rowDoc[this.renderFieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
- const alts = DocListCast(this._rowDoc[this.renderFieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
- const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+ const alts = DocListCast(this._rowDoc[this.renderFieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+ const altpaths = alts
+ .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+ .filter(url => url)
+ .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
// If there is a path, follow it; otherwise, follow a link to a default image icon
- const url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ const url = paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
const aspect = Doc.NativeAspect(this._rowDoc); // aspect ratio
let width = Math.min(75, this.props.rowProps.width); // get a with that is no smaller than 75px
@@ -511,25 +543,28 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
width = height * aspect; // increase the width of the image if necessary to maintain proportionality
const reference = React.createRef<HTMLDivElement>();
- return <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
- <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
- <img src={url[0]}
- width={paths.length ? width : "20px"}
- height={paths.length ? height : "20px"} />
- </div >
- </div>;
+ return (
+ <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+ <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
+ <img src={url[0]} width={paths.length ? width : '20px'} height={paths.length ? height : '20px'} />
+ </div>
+ </div>
+ );
}
}
-
@observer
export class CollectionSchemaListCell extends CollectionSchemaCell {
_overlayDisposer?: () => void;
- @computed get _field() { return this._rowDoc[this.renderFieldKey]; }
- @computed get _optionsList() { return this._field as List<any>; }
+ @computed get _field() {
+ return this._rowDoc[this.renderFieldKey];
+ }
+ @computed get _optionsList() {
+ return this._field as List<any>;
+ }
@observable private _opened = false; // whether the list is opened
- @observable private _text = "select an item";
+ @observable private _text = 'select an item';
@observable private _selectedNum = 0; // the index of the list item selected
@action
@@ -538,102 +573,109 @@ export class CollectionSchemaListCell extends CollectionSchemaCell {
this._optionsList[this._selectedNum] = this._text = value;
(this._field as List<any>).splice(this._selectedNum, 1, value);
- }
+ };
@action
onSelected = (element: string, index: number) => {
// if an item is selected, the private variables should update to reflect this
this._text = element;
this._selectedNum = index;
- }
+ };
onFocus = () => {
this._overlayDisposer?.();
this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
- }
+ };
render() {
const link = false;
const reference = React.createRef<HTMLDivElement>();
- // if the list is not opened, don't display it; otherwise, do.
+ // if the list is not opened, don't display it; otherwise, do.
if (this._optionsList?.length) {
- const options = !this._opened ? (null) :
+ const options = !this._opened ? null : (
<div>
{this._optionsList.map((element, index) => {
const val = Field.toString(element);
- return <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: "6px" }} onPointerDown={(e) => this.onSelected(StrCast(element), index)} >
- {val}
- </div>;
+ return (
+ <div className="collectionSchemaView-dropdownOption" key={index} style={{ padding: '6px' }} onPointerDown={e => this.onSelected(StrCast(element), index)}>
+ {val}
+ </div>
+ );
})}
- </div>;
-
- const plainText = <div style={{ padding: "5.9px" }}>{this._text}</div>;
- const textarea = <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: "5.9px" }} ref={this.dropRef} >
- <EditableView
- editing={this._isEditing}
- isEditingCallback={this.isEditingCallback}
- display={"inline"}
- contents={this._text}
- height={"auto"}
- maxHeight={Number(MAX_ROW_HEIGHT)}
- GetValue={() => this._text}
- SetValue={action((value: string) => {
- // add special for params
- this.onSetValue(value);
- return true;
- })}
- />
- </div >;
+ </div>
+ );
+
+ const plainText = <div style={{ padding: '5.9px' }}>{this._text}</div>;
+ const textarea = (
+ <div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} style={{ padding: '5.9px' }} ref={this.dropRef}>
+ <EditableView
+ editing={this._isEditing}
+ isEditingCallback={this.isEditingCallback}
+ display={'inline'}
+ contents={this._text}
+ height={'auto'}
+ maxHeight={Number(MAX_ROW_HEIGHT)}
+ GetValue={() => this._text}
+ SetValue={action((value: string) => {
+ // add special for params
+ this.onSetValue(value);
+ return true;
+ })}
+ />
+ </div>
+ );
//☰
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
<div className="collectionSchemaView-cellContents" key={this._rowDoc[Id]} ref={reference}>
<div className="collectionSchemaView-dropDownWrapper">
- <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: "length", position: "relative" }}
- onClick={action(e => this._opened = !this._opened)} >
- <FontAwesomeIcon icon={this._opened ? "caret-up" : "caret-down"} size="sm" />
+ <button type="button" className="collectionSchemaView-dropdownButton" style={{ right: 'length', position: 'relative' }} onClick={action(e => (this._opened = !this._opened))}>
+ <FontAwesomeIcon icon={this._opened ? 'caret-up' : 'caret-down'} size="sm" />
</button>
<div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
</div>
{options}
- </div >
+ </div>
</div>
);
}
- return this.renderCellWithType("list");
+ return this.renderCellWithType('list');
}
}
-
@observer
export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
- @computed get _isChecked() { return BoolCast(this._rowDoc[this.renderFieldKey]); }
+ @computed get _isChecked() {
+ return BoolCast(this._rowDoc[this.renderFieldKey]);
+ }
render() {
const reference = React.createRef<HTMLDivElement>();
return (
<div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
- <input type="checkbox" checked={this._isChecked} onChange={e => this._rowDoc[this.renderFieldKey] = e.target.checked} />
+ <input type="checkbox" checked={this._isChecked} onChange={e => (this._rowDoc[this.renderFieldKey] = e.target.checked)} />
</div>
);
}
}
-
@observer
export class CollectionSchemaButtons extends CollectionSchemaCell {
// the navigation buttons for schema view when it is used for search.
render() {
- return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? <></> :
- <div style={{ paddingTop: 8, paddingLeft: 3 }} >
+ return !this.props.Document._searchDoc || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this._rowDoc.type) as DocumentType) ? (
+ <></>
+ ) : (
+ <div style={{ paddingTop: 8, paddingLeft: 3 }}>
<button style={{ padding: 2, left: 77 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, true)}>
<FontAwesomeIcon icon="arrow-up" size="sm" />
</button>
- <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)} >
+ <button style={{ padding: 2 }} onClick={() => Doc.SearchMatchNext(this._rowDoc, false)}>
<FontAwesomeIcon icon="arrow-down" size="sm" />
</button>
- </div>;
+ </div>
+ );
}
}
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaHeaders.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaHeaders.tsx
new file mode 100644
index 000000000..32283d76c
--- /dev/null
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaHeaders.tsx
@@ -0,0 +1,510 @@
+// import React = require("react");
+// import { IconProp } from "@fortawesome/fontawesome-svg-core";
+// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+// import { action, computed, observable, runInAction, trace } from "mobx";
+// import { observer } from "mobx-react";
+// import { Doc, DocListCast, Opt, StrListCast } from "../../../../fields/Doc";
+// import { listSpec } from "../../../../fields/Schema";
+// import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
+// import { ScriptField } from "../../../../fields/ScriptField";
+// import { Cast, StrCast } from "../../../../fields/Types";
+// import { undoBatch } from "../../../util/UndoManager";
+// import { CollectionView } from "../CollectionView";
+// import { ColumnType } from "./CollectionSchemaView";
+// import "./CollectionSchemaView.scss";
+
+// const higflyout = require("@hig/flyout");
+// export const { anchorPoints } = higflyout;
+// export const Flyout = higflyout.default;
+
+// export interface AddColumnHeaderProps {
+// createColumn: () => void;
+// }
+
+// @observer
+// export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> {
+// // the button that allows the user to add a column
+// render() {
+// return <button className="add-column" onClick={() => this.props.createColumn()}>
+// <FontAwesomeIcon icon="plus" size="sm" />
+// </button>;
+// }
+// }
+
+// export interface ColumnMenuProps {
+// columnField: SchemaHeaderField;
+// // keyValue: string;
+// possibleKeys: string[];
+// existingKeys: string[];
+// // keyType: ColumnType;
+// typeConst: boolean;
+// menuButtonContent: JSX.Element;
+// addNew: boolean;
+// onSelect: (oldKey: string, newKey: string, addnew: boolean) => void;
+// setIsEditing: (isEditing: boolean) => void;
+// deleteColumn: (column: string) => void;
+// onlyShowOptions: boolean;
+// setColumnType: (column: SchemaHeaderField, type: ColumnType) => void;
+// setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void;
+// anchorPoint?: any;
+// setColumnColor: (column: SchemaHeaderField, color: string) => void;
+// }
+// @observer
+// export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> {
+// @observable private _isOpen: boolean = false;
+// @observable private _node: HTMLDivElement | null = null;
+
+// componentDidMount() { document.addEventListener("pointerdown", this.detectClick); }
+
+// componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); }
+
+// @action
+// detectClick = (e: PointerEvent) => {
+// !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false);
+// }
+
+// @action
+// toggleIsOpen = (): void => {
+// this.props.setIsEditing(this._isOpen = !this._isOpen);
+// }
+
+// changeColumnType = (type: ColumnType) => {
+// this.props.setColumnType(this.props.columnField, type);
+// }
+
+// changeColumnSort = (desc: boolean | undefined) => {
+// this.props.setColumnSort(this.props.columnField, desc);
+// }
+
+// changeColumnColor = (color: string) => {
+// this.props.setColumnColor(this.props.columnField, color);
+// }
+
+// @action
+// setNode = (node: HTMLDivElement): void => {
+// if (node) {
+// this._node = node;
+// }
+// }
+
+// renderTypes = () => {
+// if (this.props.typeConst) return (null);
+
+// const type = this.props.columnField.type;
+// return (
+// <div className="collectionSchema-headerMenu-group">
+// <label>Column type:</label>
+// <div className="columnMenu-types">
+// <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}>
+// <FontAwesomeIcon icon={"align-justify"} size="sm" />
+// Any
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}>
+// <FontAwesomeIcon icon={"hashtag"} size="sm" />
+// Number
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}>
+// <FontAwesomeIcon icon={"font"} size="sm" />
+// Text
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}>
+// <FontAwesomeIcon icon={"check-square"} size="sm" />
+// Checkbox
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}>
+// <FontAwesomeIcon icon={"list-ul"} size="sm" />
+// List
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}>
+// <FontAwesomeIcon icon={"file"} size="sm" />
+// Document
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}>
+// <FontAwesomeIcon icon={"image"} size="sm" />
+// Image
+// </div>
+// <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Date)}>
+// <FontAwesomeIcon icon={"calendar"} size="sm" />
+// Date
+// </div>
+// </div>
+// </div >
+// );
+// }
+
+// renderSorting = () => {
+// const sort = this.props.columnField.desc;
+// return (
+// <div className="collectionSchema-headerMenu-group">
+// <label>Sort by:</label>
+// <div className="columnMenu-sort">
+// <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}>
+// <FontAwesomeIcon icon="sort-amount-down" size="sm" />
+// Sort descending
+// </div>
+// <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}>
+// <FontAwesomeIcon icon="sort-amount-up" size="sm" />
+// Sort ascending
+// </div>
+// <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}>
+// <FontAwesomeIcon icon="times" size="sm" />
+// Clear sorting
+// </div>
+// </div>
+// </div>
+// );
+// }
+
+// renderColors = () => {
+// const selected = this.props.columnField.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.changeColumnColor(pink!)}></div>
+// <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div>
+// <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div>
+// <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div>
+// <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div>
+// <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div>
+// </div>
+// </div>
+// );
+// }
+
+// renderContent = () => {
+// return (
+// <div className="collectionSchema-header-menuOptions">
+// {this.props.onlyShowOptions ? <></> :
+// <>
+// {this.renderTypes()}
+// {this.renderSorting()}
+// {this.renderColors()}
+// <div className="collectionSchema-headerMenu-group">
+// <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Hide Column</button>
+// </div>
+// </>
+// }
+// </div>
+// );
+// }
+
+// render() {
+// return (
+// <div className="collectionSchema-header-menu" ref={this.setNode}>
+// <Flyout anchorPoint={this.props.anchorPoint ? this.props.anchorPoint : anchorPoints.TOP_CENTER} content={this.renderContent()}>
+// <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div>
+// </ Flyout >
+// </div>
+// );
+// }
+// }
+
+// export interface KeysDropdownProps {
+// keyValue: string;
+// possibleKeys: string[];
+// existingKeys: string[];
+// canAddNew: boolean;
+// addNew: boolean;
+// onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void;
+// setIsEditing: (isEditing: boolean) => void;
+// width?: string;
+// docs?: Doc[];
+// Document: Doc;
+// dataDoc: Doc | undefined;
+// fieldKey: string;
+// ContainingCollectionDoc: Doc | undefined;
+// ContainingCollectionView: Opt<CollectionView>;
+// active?: (outsideReaction?: boolean) => boolean | undefined;
+// openHeader: (column: any, screenx: number, screeny: number) => void;
+// col: SchemaHeaderField;
+// icon: IconProp;
+// }
+// @observer
+// export class KeysDropdown extends React.Component<KeysDropdownProps> {
+// @observable private _key: string = this.props.keyValue;
+// @observable private _searchTerm: string = this.props.keyValue + ":";
+// @observable private _isOpen: boolean = false;
+// @observable private _node: HTMLDivElement | null = null;
+// @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef();
+
+// @action setSearchTerm = (value: string): void => { this._searchTerm = value; };
+// @action setKey = (key: string): void => { this._key = key; };
+// @action setIsOpen = (isOpen: boolean): void => { this._isOpen = isOpen; };
+
+// @action
+// onSelect = (key: string): void => {
+// this.props.onSelect(this._key, key, this.props.addNew);
+// this.setKey(key);
+// this._isOpen = false;
+// this.props.setIsEditing(false);
+// }
+
+// @action
+// setNode = (node: HTMLDivElement): void => {
+// if (node) {
+// this._node = node;
+// }
+// }
+
+// componentDidMount() {
+// document.addEventListener("pointerdown", this.detectClick);
+// const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+// if (filters?.some(filter => filter.split(":")[0] === this._key)) {
+// runInAction(() => this.closeResultsVisibility = "contents");
+// }
+// }
+
+// @action
+// detectClick = (e: PointerEvent): void => {
+// if (this._node && this._node.contains(e.target as Node)) {
+// } else {
+// this._isOpen = false;
+// this.props.setIsEditing(false);
+// }
+// }
+
+// private tempfilter: string = "";
+// @undoBatch
+// onKeyDown = (e: React.KeyboardEvent): void => {
+// if (e.key === "Enter") {
+// e.stopPropagation();
+// if (this._searchTerm.includes(":")) {
+// const colpos = this._searchTerm.indexOf(":");
+// const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
+// if (temp === "") {
+// Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+// this.updateFilter();
+// }
+// else {
+// Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+// this.tempfilter = temp;
+// Doc.setDocFilter(this.props.Document, this._key, temp, "check");
+// this.props.col.setColor("green");
+// this.closeResultsVisibility = "contents";
+// }
+// }
+// else {
+// Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove");
+// this.updateFilter();
+// if (this.showKeys.length) {
+// this.onSelect(this.showKeys[0]);
+// } else if (this._searchTerm !== "" && this.props.canAddNew) {
+// this.setSearchTerm(this._searchTerm || this._key);
+// this.onSelect(this._searchTerm);
+// }
+// }
+// }
+// }
+
+// onChange = (val: string): void => {
+// this.setSearchTerm(val);
+// }
+
+// @action
+// onFocus = (e: React.FocusEvent): void => {
+// this._isOpen = true;
+// this.props.setIsEditing(true);
+// }
+
+// @computed get showKeys() {
+// const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
+// const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+// const showKeys = new Set<string>();
+// [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode ||
+// whitelistKeys.includes(key)
+// || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
+// return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
+// }
+
+// @computed get renderOptions() {
+// if (!this._isOpen) {
+// this.defaultMenuHeight = 0;
+// return (null);
+// }
+// const options = this.showKeys.map(key => {
+// return <div key={key} className="key-option" style={{
+// border: "1px solid lightgray",
+// width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
+// }}
+// onPointerDown={e => {
+// e.stopPropagation();
+// }}
+// onClick={() => {
+// this.onSelect(key);
+// this.setSearchTerm("");
+// }}>{key}</div>;
+// });
+
+// // if search term does not already exist as a group type, give option to create new group type
+
+// if (this._key !== this._searchTerm.slice(0, this._key.length)) {
+// if (this._searchTerm !== "" && this.props.canAddNew) {
+// options.push(<div key={""} className="key-option" style={{
+// border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white",
+// }}
+// onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}>
+// Create "{this._searchTerm}" key</div>);
+// }
+// }
+
+// if (options.length === 0) {
+// this.defaultMenuHeight = 0;
+// }
+// else {
+// if (this.props.docs) {
+// const panesize = this.props.docs.length * 30;
+// options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+// }
+// else {
+// options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+// }
+// }
+// return options;
+// }
+
+// @computed get docSafe() { return DocListCast(this.props.dataDoc?.[this.props.fieldKey]); }
+
+// @computed get renderFilterOptions() {
+// if (!this._isOpen || !this.props.dataDoc) {
+// this.defaultMenuHeight = 0;
+// return (null);
+// }
+// const keyOptions: string[] = [];
+// const colpos = this._searchTerm.indexOf(":");
+// const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length);
+// this.docSafe.forEach(doc => {
+// const key = StrCast(doc[this._key]);
+// if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") {
+// keyOptions.push(key);
+// }
+// });
+
+// const filters = StrListCast(this.props.Document._docFilters);
+// if (filters.some(filter => filter.split(":")[0] === this._key) === false) {
+// this.props.col.setColor("rgb(241, 239, 235)");
+// this.closeResultsVisibility = "none";
+// }
+// for (let i = 0; i < (filters?.length ?? 0) - 1; i++) {
+// if (filters[i] === this.props.col.heading && keyOptions.includes(filters[i].split(":")[1]) === false) {
+// keyOptions.push(filters[i + 1]);
+// }
+// }
+// const options = keyOptions.map(key => {
+// let bool = false;
+// if (filters !== undefined) {
+// const ind = filters.findIndex(filter => filter.split(":")[1] === key);
+// const fields = ind === -1 ? undefined : filters[ind].split(":");
+// bool = fields ? fields[2] === "check" : false;
+// }
+// return <div key={key} className="key-option" style={{
+// paddingLeft: 5, textAlign: "left",
+// width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
+// }}
+// >
+// <input type="checkbox"
+// onPointerDown={e => e.stopPropagation()}
+// onClick={e => e.stopPropagation()}
+// onChange={action(e => {
+// if (e.target.checked) {
+// Doc.setDocFilter(this.props.Document, this._key, key, "check");
+// this.closeResultsVisibility = "contents";
+// this.props.col.setColor("green");
+// } else {
+// Doc.setDocFilter(this.props.Document, this._key, key, "remove");
+// this.updateFilter();
+// }
+// })}
+// checked={bool}
+// />
+// <span style={{ paddingLeft: 4 }}>
+// {key}
+// </span>
+
+// </div>;
+// });
+// if (options.length === 0) {
+// this.defaultMenuHeight = 0;
+// }
+// else {
+// if (this.props.docs) {
+// const panesize = this.props.docs.length * 30;
+// options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8;
+// }
+// else {
+// options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8;
+// }
+
+// }
+// return options;
+// }
+
+// @observable defaultMenuHeight = 0;
+
+// updateFilter() {
+// const filters = Cast(this.props.Document._docFilters, listSpec("string"));
+// if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) {
+// this.props.col.setColor("rgb(241, 239, 235)");
+// this.closeResultsVisibility = "none";
+// }
+// }
+
+// @computed get scriptField() {
+// const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)";
+// const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+// return script ? () => script : undefined;
+// }
+// filterBackground = () => "rgba(105, 105, 105, 0.432)";
+// @observable filterOpen: boolean | undefined = undefined;
+// closeResultsVisibility: string = "none";
+
+// removeFilters = (e: React.PointerEvent): void => {
+// const keyOptions: string[] = [];
+// this.docSafe.forEach(doc => {
+// const key = StrCast(doc[this._key]);
+// if (keyOptions.includes(key) === false) {
+// keyOptions.push(key);
+// }
+// });
+
+// Doc.setDocFilter(this.props.Document, this._key, "", "remove");
+// this.props.col.setColor("rgb(241, 239, 235)");
+// this.closeResultsVisibility = "none";
+// }
+// render() {
+// return (
+// <div style={{ display: "flex", width: '100%', alignContent: 'center', alignItems: 'center' }} ref={this.setNode}>
+// <div className="schema-icon" onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }}>
+// <FontAwesomeIcon icon={this.props.icon} size="lg" style={{ display: "inline" }} />
+// </div>
+
+// <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}>
+// <input className="keys-search" style={{ width: "100%" }}
+// ref={this._inputRef} type="text"
+// value={this._searchTerm} placeholder="Column key"
+// onKeyDown={this.onKeyDown}
+// onChange={e => this.onChange(e.target.value)}
+// onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }}
+// onFocus={this.onFocus} ></input>
+// <div style={{ display: this.closeResultsVisibility }}>
+// <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg"
+// style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} />
+// </div>
+// {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{
+// width: this.props.width, maxWidth: this.props.width, height: "auto",
+// }}>
+// {this._searchTerm.includes(":") ? this.renderFilterOptions : this.renderOptions}
+// </div>}
+// </div >
+// </div>
+// );
+// }
+// }
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
index 28d2e6ab1..28d2e6ab1 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableColumn.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableColumn.tsx
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
index f872637e5..f872637e5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaMovableRow.tsx
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.scss b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.scss
new file mode 100644
index 000000000..22ce8c8f2
--- /dev/null
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.scss
@@ -0,0 +1,599 @@
+@import '../../global/globalCssVariables.scss';
+// @import '../../../../../node_modules/react-table/react-table.css';
+.collectionSchemaView-container {
+ border-width: $COLLECTION_BORDER_WIDTH;
+ border-color: $medium-gray;
+ border-style: solid;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position: relative;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ margin-top: 0;
+ transition: top 0.5s;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ touch-action: none;
+ div {
+ touch-action: none;
+ }
+ .collectionSchemaView-tableContainer {
+ width: 100%;
+ height: 100%;
+ }
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ height: 100%;
+ width: $SCHEMA_DIVIDER_WIDTH;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: gray;
+ cursor: col-resize;
+ }
+ // .documentView-node:first-child {
+ // background: $white;
+ // }
+}
+
+.collectionSchemaView-searchContainer {
+ border-width: $COLLECTION_BORDER_WIDTH;
+ border-color: $medium-gray;
+ border-style: solid;
+ border-radius: $border-radius;
+ box-sizing: border-box;
+ position: relative;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ margin-top: 0;
+ transition: top 0.5s;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: nowrap;
+ touch-action: none;
+ padding: 2px;
+ div {
+ touch-action: none;
+ }
+ .collectionSchemaView-tableContainer {
+ width: 100%;
+ height: 100%;
+ }
+ .collectionSchemaView-dividerDragger {
+ position: relative;
+ height: 100%;
+ width: 20px;
+ z-index: 20;
+ right: 0;
+ top: 0;
+ background: gray;
+ cursor: col-resize;
+ }
+ // .documentView-node:first-child {
+ // background: $white;
+ // }
+}
+
+.ReactTable {
+ width: 100%;
+ background: white;
+ box-sizing: border-box;
+ border: none !important;
+ float: none !important;
+ .rt-table {
+ height: 100%;
+ display: -webkit-inline-box;
+ direction: ltr;
+ overflow: visible;
+ }
+ .rt-noData {
+ display: none;
+ }
+ .rt-thead {
+ width: 100%;
+ z-index: 100;
+ overflow-y: visible;
+ &.-header {
+ font-size: 12px;
+ height: 30px;
+ box-shadow: none;
+ z-index: 100;
+ overflow-y: visible;
+ }
+ .rt-resizable-header-content {
+ height: 100%;
+ overflow: visible;
+ }
+ .rt-th {
+ padding: 0;
+ border-left: solid 1px $light-gray;
+ }
+ }
+ .rt-th {
+ font-size: 13px;
+ text-align: center;
+ &:last-child {
+ overflow: visible;
+ }
+ }
+ .rt-tbody {
+ width: 100%;
+ direction: rtl;
+ overflow: visible;
+ .rt-td {
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+ }
+ }
+ .rt-tr-group {
+ direction: ltr;
+ flex: 0 1 auto;
+ min-height: 30px;
+ border: 0 !important;
+ }
+ .rt-tr-group:nth-of-type(even) {
+ direction: ltr;
+ flex: 0 1 auto;
+ min-height: 30px;
+ border: 0 !important;
+ background-color: red;
+ }
+ .rt-tr {
+ width: 100%;
+ min-height: 30px;
+ }
+ .rt-td {
+ padding: 0;
+ font-size: 13px;
+ text-align: center;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+ .imageBox-cont {
+ position: relative;
+ max-height: 100%;
+ }
+ .imageBox-cont img {
+ object-fit: contain;
+ max-width: 100%;
+ height: 100%;
+ }
+ .videoBox-cont {
+ object-fit: contain;
+ width: auto;
+ height: 100%;
+ }
+ }
+ .rt-td.rt-expandable {
+ display: flex;
+ align-items: center;
+ height: inherit;
+ }
+ .rt-resizer {
+ width: 8px;
+ right: -4px;
+ }
+ .rt-resizable-header {
+ padding: 0;
+ height: 30px;
+ }
+ .rt-resizable-header:last-child {
+ overflow: visible;
+ .rt-resizer {
+ width: 5px !important;
+ }
+ }
+}
+
+.documentView-node-topmost {
+ text-align: left;
+ transform-origin: center top;
+ display: inline-block;
+}
+
+.collectionSchema-col {
+ height: 100%;
+}
+
+.collectionSchema-header-menu {
+ height: auto;
+ z-index: 100;
+ position: absolute;
+ background: white;
+ padding: 5px;
+ position: fixed;
+ background: white;
+ border: black 1px solid;
+ .collectionSchema-header-toggler {
+ z-index: 100;
+ width: 100%;
+ height: 100%;
+ padding: 4px;
+ letter-spacing: 2px;
+ text-transform: uppercase;
+ svg {
+ margin-right: 4px;
+ }
+ }
+}
+
+.collectionSchemaView-header {
+ height: 100%;
+ color: gray;
+ z-index: 100;
+ overflow-y: visible;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+button.add-column {
+ width: 28px;
+}
+
+.collectionSchemaView-menuOptions-wrapper {
+ background: rgb(241, 239, 235);
+ display: flex;
+ cursor: default;
+ height: 100%;
+ align-content: center;
+ align-items: center;
+}
+
+.collectionSchema-header-menuOptions {
+ color: black;
+ width: 180px;
+ text-align: left;
+ .collectionSchema-headerMenu-group {
+ padding: 7px 0;
+ border-bottom: 1px solid lightgray;
+ cursor: pointer;
+ &:first-child {
+ padding-top: 0;
+ }
+ &:last-child {
+ border: none;
+ text-align: center;
+ padding: 12px 0 0 0;
+ }
+ }
+ label {
+ color: $medium-gray;
+ font-weight: normal;
+ letter-spacing: 2px;
+ text-transform: uppercase;
+ }
+ input {
+ color: black;
+ width: 100%;
+ }
+ .columnMenu-option {
+ cursor: pointer;
+ padding: 3px;
+ background-color: white;
+ transition: background-color 0.2s;
+ &:hover {
+ background-color: $light-gray;
+ }
+ &.active {
+ font-weight: bold;
+ border: 2px solid $light-gray;
+ }
+ svg {
+ color: gray;
+ margin-right: 5px;
+ width: 10px;
+ }
+ }
+
+ .keys-dropdown {
+ position: relative;
+ //width: 100%;
+ background-color: white;
+ input {
+ border: 2px solid $light-gray;
+ padding: 3px;
+ height: 28px;
+ font-weight: bold;
+ letter-spacing: '2px';
+ text-transform: 'uppercase';
+ &:focus {
+ font-weight: normal;
+ }
+ }
+ }
+ .columnMenu-colors {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ .columnMenu-colorPicker {
+ cursor: pointer;
+ width: 20px;
+ height: 20px;
+ border-radius: 10px;
+ &.active {
+ border: 2px solid white;
+ box-shadow: 0 0 0 2px lightgray;
+ }
+ }
+ }
+}
+
+.schema-icon {
+ cursor: pointer;
+ width: 25px;
+ height: 25px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ align-content: center;
+ background-color: $medium-blue;
+ color: white;
+ margin-right: 5px;
+ font-size: 10px;
+ border-radius: 3px;
+}
+
+.keys-options-wrapper {
+ position: absolute;
+ text-align: left;
+ height: fit-content;
+ top: 100%;
+ z-index: 21;
+ background-color: #ffffff;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%);
+ padding: 1px;
+ .key-option {
+ cursor: pointer;
+ color: #000000;
+ width: 100%;
+ height: 25px;
+ font-weight: 400;
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ padding-left: 5px;
+ &:hover {
+ background-color: $light-gray;
+ }
+ }
+}
+
+.collectionSchema-row {
+ height: 100%;
+ background-color: white;
+ &.row-focused .rt-td {
+ background-color: $light-blue; //$light-gray;
+ overflow: visible;
+ }
+ &.row-wrapped {
+ .rt-td {
+ white-space: normal;
+ }
+ }
+ .row-dragger {
+ display: flex;
+ justify-content: space-evenly;
+ width: 58px;
+ position: absolute;
+ /* max-width: 50px; */
+ min-height: 30px;
+ align-items: center;
+ color: lightgray;
+ background-color: white;
+ transition: color 0.1s ease;
+ .row-option {
+ color: black;
+ cursor: pointer;
+ position: relative;
+ transition: color 0.1s ease;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ z-index: 2;
+ border-radius: 3px;
+ padding: 3px;
+ &:hover {
+ background-color: $light-gray;
+ }
+ }
+ }
+ .collectionSchema-row-wrapper {
+ &.row-above {
+ border-top: 1px solid $medium-blue;
+ }
+ &.row-below {
+ border-bottom: 1px solid $medium-blue;
+ }
+ &.row-inside {
+ border: 2px dashed $medium-blue;
+ }
+ .row-dragging {
+ background-color: blue;
+ }
+ }
+}
+
+.collectionSchemaView-cellContainer {
+ width: 100%;
+ height: unset;
+}
+
+.collectionSchemaView-cellContents {
+ width: 100%;
+}
+
+.collectionSchemaView-cellWrapper {
+ display: flex;
+ height: 100%;
+ text-align: left;
+ padding-left: 19px;
+ position: relative;
+ align-items: center;
+ align-content: center;
+ &:focus {
+ outline: none;
+ }
+ &.editing {
+ padding: 0;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ transform: scale(1.1);
+ z-index: 40;
+ input {
+ outline: 0;
+ border: none;
+ background-color: $white;
+ width: 100%;
+ height: fit-content;
+ min-height: 26px;
+ }
+ }
+ &.focused {
+ overflow: hidden;
+ &.inactive {
+ border: none;
+ }
+ }
+ p {
+ width: 100%;
+ height: 100%;
+ }
+ &:hover .collectionSchemaView-cellContents-docExpander {
+ display: block;
+ }
+ .collectionSchemaView-cellContents-document {
+ display: inline-block;
+ }
+ .collectionSchemaView-cellContents-docButton {
+ float: right;
+ width: '15px';
+ height: '15px';
+ }
+ .collectionSchemaView-dropdownWrapper {
+ border: grey;
+ border-style: solid;
+ border-width: 1px;
+ height: 30px;
+ .collectionSchemaView-dropdownButton {
+ //display: inline-block;
+ float: left;
+ height: 100%;
+ }
+ .collectionSchemaView-dropdownText {
+ display: inline-block;
+ //float: right;
+ height: 100%;
+ display: 'flex';
+ font-size: 13;
+ justify-content: 'center';
+ align-items: 'center';
+ }
+ }
+ .collectionSchemaView-dropdownContainer {
+ position: absolute;
+ border: 1px solid rgba(0, 0, 0, 0.04);
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14);
+ .collectionSchemaView-dropdownOption:hover {
+ background-color: rgba(0, 0, 0, 0.14);
+ cursor: pointer;
+ }
+ }
+}
+
+.collectionSchemaView-cellContents-docExpander {
+ height: 30px;
+ width: 30px;
+ display: none;
+ position: absolute;
+ top: 0;
+ right: 0;
+ background-color: lightgray;
+}
+
+.doc-drag-over {
+ background-color: red;
+}
+
+.collectionSchemaView-toolbar {
+ z-index: 100;
+}
+
+.collectionSchemaView-toolbar {
+ height: 30px;
+ display: flex;
+ justify-content: flex-end;
+ padding: 0 10px;
+ border-bottom: 2px solid gray;
+ .collectionSchemaView-toolbar-item {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+}
+
+#preview-schema-checkbox-div {
+ margin-left: 20px;
+ font-size: 12px;
+}
+
+.collectionSchemaView-table {
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ padding: 3px;
+}
+
+.rt-td.rt-expandable {
+ overflow: visible;
+ position: relative;
+ height: 100%;
+ z-index: 1;
+}
+
+.reactTable-sub {
+ background-color: rgb(252, 252, 252);
+ width: 100%;
+ .rt-thead {
+ display: none;
+ }
+ .row-dragger {
+ background-color: rgb(252, 252, 252);
+ }
+ .rt-table {
+ background-color: rgb(252, 252, 252);
+ }
+ .collectionSchemaView-table {
+ width: 100%;
+ border: solid 1px;
+ overflow: visible;
+ padding: 0px;
+ }
+}
+
+.collectionSchemaView-expander {
+ height: 100%;
+ min-height: 30px;
+ position: absolute;
+ color: gray;
+ width: 20;
+ height: auto;
+ left: 55;
+ svg {
+ position: absolute;
+ top: 50%;
+ left: 10;
+ transform: translate(-50%, -50%);
+ }
+}
+
+.collectionSchemaView-addRow {
+ color: gray;
+ letter-spacing: 2px;
+ text-transform: uppercase;
+ cursor: pointer;
+ font-size: 10.5px;
+ margin-left: 50px;
+ margin-top: 10px;
+}
diff --git a/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
new file mode 100644
index 000000000..260db4b88
--- /dev/null
+++ b/src/client/views/collections/old_collectionSchema/OldCollectionSchemaView.tsx
@@ -0,0 +1,649 @@
+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 { 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, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { DocUtils } from '../../../documents/Documents';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { ContextMenuProps } from '../../ContextMenuItem';
+import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss';
+import { DocumentView } from '../../nodes/DocumentView';
+import { DefaultStyleProvider } from '../../StyleProvider';
+import { CollectionSubView } from '../CollectionSubView';
+import './CollectionSchemaView.scss';
+// import { SchemaTable } from './SchemaTable';
+// 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() {
+ 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);
+ }}>
+ Hide 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.SelectSchemaViewDoc(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}
+ fitContentsToBox={returnTrue}
+ 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.childDocFilters}
+ docRangeFilters={this.childDocRangeFilters}
+ searchFilterDocs={this.searchFilterDocs}
+ styleProvider={DefaultStyleProvider}
+ 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: 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>
+ );
+ TraceMobx();
+ return <div>HELLO</div>;
+ }
+}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
index fafea5ce3..dfeee3173 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/old_collectionSchema/OldSchemaTable.tsx
@@ -39,7 +39,7 @@ import {
CollectionSchemaStringCell,
} from './CollectionSchemaCells';
import { CollectionSchemaAddColumnHeader, KeysDropdown } from './CollectionSchemaHeaders';
-import { MovableColumn } from './CollectionSchemaMovableColumn';
+import { MovableColumn } from './OldCollectionSchemaMovableColumn';
import { MovableRow } from './CollectionSchemaMovableRow';
import './CollectionSchemaView.scss';
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 8437736ae..7bbd7c055 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -18,6 +18,7 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import './AudioBox.scss';
import { FieldView, FieldViewProps } from './FieldView';
+import { SelectionManager } from '../../util/SelectionManager';
/**
* AudioBox