aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json46
-rw-r--r--src/client/documents/Documents.ts41
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss17
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx183
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx27
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx1
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx8
7 files changed, 210 insertions, 113 deletions
diff --git a/package-lock.json b/package-lock.json
index da9a57d41..5f81e1f0f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2452,7 +2452,7 @@
"@types/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
+ "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
"dev": true
},
"@types/strip-json-comments": {
@@ -2803,7 +2803,7 @@
"textarea-caret": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
- "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
+ "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
}
}
},
@@ -2902,7 +2902,7 @@
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
- "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"agent-base": {
"version": "6.0.2",
@@ -3799,7 +3799,7 @@
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
- "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"bail": {
"version": "2.0.2",
@@ -3869,7 +3869,7 @@
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
- "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
+ "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64-js": {
"version": "1.5.1",
@@ -4842,7 +4842,7 @@
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
- "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.3.0",
@@ -4852,7 +4852,7 @@
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
- "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"compress-commons": {
"version": "2.1.1",
@@ -5572,7 +5572,7 @@
"custom-event": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
- "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
},
"cyclist": {
"version": "1.0.1",
@@ -6646,7 +6646,7 @@
"dynamic-dedupe": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
- "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
+ "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
"dev": true,
"requires": {
"xtend": "^4.0.0"
@@ -6776,7 +6776,7 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"ws": {
"version": "7.4.6",
@@ -9283,7 +9283,7 @@
"fs-extra": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
- "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+ "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^2.1.0",
@@ -9943,14 +9943,14 @@
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
}
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
- "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-flag": {
"version": "3.0.0",
@@ -10526,7 +10526,7 @@
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inflight": {
"version": "1.0.6",
@@ -11637,7 +11637,7 @@
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
"requires": {
"graceful-fs": "^4.1.6"
}
@@ -12060,7 +12060,7 @@
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lodash.isplainobject": {
"version": "4.0.6",
@@ -12321,7 +12321,7 @@
"mathquill": {
"version": "0.10.1-a",
"resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
- "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
+ "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
"requires": {
"jquery": "^1.12.3"
},
@@ -12329,7 +12329,7 @@
"jquery": {
"version": "1.12.4",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
- "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
+ "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
}
}
},
@@ -22135,7 +22135,7 @@
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
- "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"to-fast-properties": {
"version": "2.0.0",
@@ -22507,7 +22507,7 @@
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true
}
}
@@ -23758,7 +23758,7 @@
"resolve-cwd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
- "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
"dev": true,
"requires": {
"resolve-from": "^3.0.0"
@@ -23767,7 +23767,7 @@
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
"dev": true
},
"semver": {
@@ -24319,7 +24319,7 @@
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
- "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": {
"version": "3.1.1",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index a0149eadf..bd878ca8a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -70,12 +70,14 @@ class EmptyBox {
}
export abstract class FInfo {
description: string = '';
+ readOnly: boolean = false;
fieldType?: string;
values?: Field[];
// format?: string; // format to display values (e.g, decimal places, $, etc)
// parse?: ScriptField; // parse a value from a string
- constructor(d: string) {
+ constructor(d: string, readOnly?: boolean) {
this.description = d;
+ this.readOnly = readOnly ?? false;
}
}
class BoolInfo extends FInfo {
@@ -85,16 +87,16 @@ class BoolInfo extends FInfo {
class NumInfo extends FInfo {
fieldType? = 'number';
values?: number[] = [];
- constructor(d: string, values?: number[]) {
- super(d);
+ constructor(d: string, readOnly?: boolean, values?: number[]) {
+ super(d, readOnly);
this.values = values;
}
}
class StrInfo extends FInfo {
fieldType? = 'string';
values?: string[] = [];
- constructor(d: string, values?: string[]) {
- super(d);
+ constructor(d: string, readOnly?: boolean, values?: string[]) {
+ super(d, readOnly);
this.values = values;
}
}
@@ -102,21 +104,28 @@ class DocInfo extends FInfo {
fieldType? = 'Doc';
values?: Doc[] = [];
constructor(d: string, values?: Doc[]) {
- super(d);
+ super(d, true);
this.values = values;
}
}
class DimInfo extends FInfo {
fieldType? = 'DimUnit';
values? = [DimUnit.Pixel, DimUnit.Ratio];
+ readOnly = true;
}
class PEInfo extends FInfo {
fieldType? = 'pointerEvents';
values? = ['all', 'none'];
+ readOnly = true;
}
class DAInfo extends FInfo {
fieldType? = 'dropActionType';
values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
+ readOnly = true;
+}
+class DateInfo extends FInfo {
+ fieldType? = 'date';
+ values?: DateField[] = [];
}
type BOOLt = BoolInfo | boolean;
type NUMt = NumInfo | number;
@@ -125,15 +134,17 @@ type DOCt = DocInfo | Doc;
type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
type PEVt = PEInfo | 'none' | 'all';
type DROPt = DAInfo | dropActionType;
+type DATEt = DateInfo | number;
export class DocumentOptions {
x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
- z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', [1, 0]);
- system?: BOOLt = new BoolInfo('is this a system created/owned doc');
- type?: STRt = new StrInfo('type of document', Array.from(Object.keys(DocumentType)));
+ z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', false, [1, 0]);
+ system?: BOOLt = new BoolInfo('is this a system created/owned doc', true);
+ type?: STRt = new StrInfo('type of document', true, Array.from(Object.keys(DocumentType)));
title?: string;
+ creationDate?: DATEt = new DateInfo('date the document was created', true);
_dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
- allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?');
+ allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?', true);
childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
@@ -199,11 +210,11 @@ export class DocumentOptions {
_timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
_timecodeToHide?: number; // the time that a document should be hidden
_timelineLabel?: boolean; // whether the document exists on a timeline
- '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection');
- '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection');
- 'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view');
- 'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view');
- 'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)');
+ '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection', true);
+ '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection', true);
+ 'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view', true);
+ 'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view', true);
+ 'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)', true);
openFactoryLocation?: string; // an OpenWhere value to place the factory created document
openFactoryAsDelegate?: boolean; //
lat?: number;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 1ef2fb4ef..a9434fde3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -9,13 +9,18 @@
.schema-table {
background-color: $white;
cursor: grab;
- overflow: scroll;
+
+ .schema-table-content {
+ overflow: overlay;
+ scroll-behavior: smooth;
+ }
.schema-column-menu,
.schema-filter-menu {
background: $light-gray;
position: absolute;
min-width: 200px;
+ max-width: 400px;
display: flex;
flex-direction: column;
align-items: flex-start;
@@ -26,14 +31,20 @@
margin: 10px;
}
- .schema-key-search-result {
+ .schema-search-result {
cursor: pointer;
- padding: 2px 10px;
+ padding: 5px 10px;
width: 100%;
&:hover {
background-color: $medium-gray;
}
+
+ .schema-search-result-type,
+ .schema-search-result-desc {
+ color: $dark-gray;
+ font-size: $body-text;
+ }
}
.schema-key-search,
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 6d5a73e55..39e223c66 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -3,13 +3,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, observable, ObservableMap, untracked } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
-import { Doc, Field, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, StrListCast } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { listSpec } from '../../../../fields/Schema';
import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnDefault, returnEmptyDoclist, returnEmptyString, returnFalse, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../Utils';
-import { Docs, DocUtils } from '../../../documents/Documents';
+import { Docs, DocumentOptions, DocUtils, FInfo } from '../../../documents/Documents';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager } from '../../../util/DragManager';
import { SelectionManager } from '../../../util/SelectionManager';
@@ -24,13 +24,13 @@ import { CollectionSubView } from '../CollectionSubView';
import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
+import { Colors } from '../../global/globalEnums';
export enum ColumnType {
Number,
String,
Boolean,
- Doc,
- Image,
+ Date,
}
const defaultColumnKeys: string[] = ['title', 'type', 'author', 'creationDate', 'text'];
@@ -40,11 +40,13 @@ export class CollectionSchemaView extends CollectionSubView() {
private _closestDropIndex: number = 0;
private _previewRef: HTMLDivElement | null = null;
private _makeNewColumn: boolean = false;
+ private _documentOptions: DocumentOptions = new DocumentOptions();
public static _rowHeight: number = 40;
public static _minColWidth: number = 25;
public static _rowMenuWidth: number = 60;
public static _previewDividerWidth: number = 4;
+ public static _newNodeInputHeight: number = 30;
@computed get _selectedDocs() {
return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.context), this.props.Document));
@@ -53,16 +55,16 @@ export class CollectionSchemaView extends CollectionSubView() {
@observable _colEles: HTMLDivElement[] = [];
@observable _displayColumnWidths: number[] | undefined;
@observable _columnMenuIndex: number | undefined;
- @observable _menuOptions: string[] = [];
+ @observable _menuOptions: [string, { description: string; type: string; readOnly: boolean }][] = [];
@observable _newFieldWarning: string = '';
@observable _makeNewField: boolean = false;
@observable _newFieldDefault: any = 0;
@observable _newFieldType: ColumnType = ColumnType.Number;
@observable _menuValue: string = '';
@observable _filterColumnIndex: number | undefined;
- @observable _filterValue: string = '';
+ @observable _filterSearchValue: string = '';
- get documentKeys() {
+ get keyInfos() {
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.
@@ -74,7 +76,22 @@ export class CollectionSchemaView extends CollectionSubView() {
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));
+
+ let computedKeys: { [key: string]: { description: string; type: string; readOnly: boolean } } = {};
+ Object.keys(keys).forEach((key: string) => {
+ computedKeys[key] = { description: '', type: '', readOnly: false };
+ });
+
+ Object.entries(this._documentOptions).forEach((pair: [string, any]) => {
+ const info: FInfo = pair[1];
+ computedKeys[pair[0]] = { description: info.description, type: info.fieldType ?? '', readOnly: info.readOnly };
+ });
+
+ return computedKeys;
+ }
+
+ get documentKeys() {
+ return Object.keys(this.keyInfos);
}
@computed get previewWidth() {
@@ -177,7 +194,6 @@ export class CollectionSchemaView extends CollectionSubView() {
addRow = (doc: Doc | Doc[]) => {
const result: boolean = this.addDocument(doc);
- this.setSort(this.sortField, this.sortDesc);
return result;
};
@@ -270,18 +286,13 @@ export class CollectionSchemaView extends CollectionSubView() {
@undoBatch
@action
- swapColumns = (index1: number, index2: number) => {
- const tempKey = this.columnKeys[index1];
- const tempWidth = this.storedColumnWidths[index1];
-
- let currKeys = this.columnKeys;
- currKeys[index1] = currKeys[index2];
- currKeys[index2] = tempKey;
+ moveColumn = (fromIndex: number, toIndex: number) => {
+ let currKeys = this.columnKeys.slice();
+ currKeys.splice(toIndex, 0, currKeys.splice(fromIndex, 1)[0]);
this.layoutDoc.columnKeys = new List<string>(currKeys);
- let currWidths = this.storedColumnWidths;
- currWidths[index1] = currWidths[index2];
- currWidths[index2] = tempWidth;
+ let currWidths = this.storedColumnWidths.slice();
+ currWidths.splice(toIndex, 0, currWidths.splice(fromIndex, 1)[0]);
this.layoutDoc.columnWidths = new List<number>(currWidths);
};
@@ -294,10 +305,45 @@ export class CollectionSchemaView extends CollectionSubView() {
});
DragManager.StartColumnDrag(dragEles, dragData, e.x, e.y);
+ document.removeEventListener('pointermove', this.highlightDropColumn);
+ document.addEventListener('pointermove', this.highlightDropColumn);
+ let stopHighlight = (e: PointerEvent) => {
+ document.removeEventListener('pointermove', this.highlightDropColumn);
+ document.removeEventListener('pointerup', stopHighlight);
+ };
+ document.addEventListener('pointerup', stopHighlight);
+
return true;
};
@action
+ highlightDropColumn = (e: PointerEvent) => {
+ e.stopPropagation();
+ const mouseX = this.props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY)[0];
+ let index: number | undefined;
+ this.displayColumnWidths.reduce((total, curr, i) => {
+ if (total <= mouseX && total + curr >= mouseX) {
+ if (mouseX <= total + curr / 2) index = i;
+ else index = i + 1;
+ }
+ return total + curr;
+ }, CollectionSchemaView._rowMenuWidth);
+
+ this._colEles.forEach((colRef, i) => {
+ let leftStyle = '';
+ let rightStyle = '';
+ if (i + 1 === index) rightStyle = `solid 2px ${Colors.MEDIUM_BLUE}`;
+ if (i === index && i === 0) leftStyle = `solid 2px ${Colors.MEDIUM_BLUE}`;
+ colRef.style.borderLeft = leftStyle;
+ colRef.style.borderRight = rightStyle;
+ this.childDocs.forEach(doc => {
+ this._rowEles.get(doc).children[1].children[i].style.borderLeft = leftStyle;
+ this._rowEles.get(doc).children[1].children[i].style.borderRight = rightStyle;
+ });
+ });
+ };
+
+ @action
addRowRef = (doc: Doc, ref: HTMLDivElement) => this._rowEles.set(doc, ref);
@action
@@ -338,15 +384,25 @@ export class CollectionSchemaView extends CollectionSubView() {
if (de.complete.columnDragData) {
e.stopPropagation();
const mouseX = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y)[0];
- let i = de.complete.columnDragData.colIndex;
- this.displayColumnWidths.reduce((total, curr, index) => {
+ let index = de.complete.columnDragData.colIndex;
+ this.displayColumnWidths.reduce((total, curr, i) => {
if (total <= mouseX && total + curr >= mouseX) {
- i = index;
+ if (mouseX <= total + curr / 2) index = i;
+ else index = i + 1;
}
return total + curr;
}, CollectionSchemaView._rowMenuWidth);
- this.swapColumns(de.complete.columnDragData.colIndex, i);
- e.stopPropagation();
+ this.moveColumn(de.complete.columnDragData.colIndex, index);
+
+ this._colEles.forEach((colRef, i) => {
+ colRef.style.borderLeft = '';
+ colRef.style.borderRight = '';
+ this.childDocs.forEach(doc => {
+ this._rowEles.get(doc).children[1].children[i].style.borderLeft = '';
+ this._rowEles.get(doc).children[1].children[i].style.borderRight = '';
+ });
+ });
+
return true;
}
const draggedDocs = de.complete.docDragData?.draggedDocuments;
@@ -355,7 +411,6 @@ export class CollectionSchemaView extends CollectionSubView() {
const pushedAndDraggedDocs = [...pushedDocs, ...draggedDocs];
const removed = this.childDocs.slice().filter(doc => !pushedAndDraggedDocs.includes(doc));
this.dataDoc[this.fieldKey ?? 'data'] = new List<Doc>([...removed, ...draggedDocs, ...pushedDocs]);
- this.setSort(undefined);
SelectionManager.DeselectAll();
draggedDocs.forEach(doc => {
const draggedView = DocumentManager.Instance.getFirstDocumentView(doc);
@@ -371,7 +426,6 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
super.onExternalDrop(e, {}, undoBatch(action(docus => docus.map((doc: Doc) => this.addDocument(doc)))));
- this.setSort(undefined);
};
onDividerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, emptyFunction);
@@ -443,7 +497,8 @@ export class CollectionSchemaView extends CollectionSubView() {
onSearchKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'Enter':
- this._menuOptions.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuOptions[0]) : action(() => (this._makeNewField = true))();
+ const menuKeys = Object.keys(this._menuOptions);
+ menuKeys.length > 0 && this._menuValue.length > 0 ? this.setKey(menuKeys[0]) : action(() => (this._makeNewField = true))();
break;
case 'Escape':
this.closeColumnMenu();
@@ -472,7 +527,7 @@ export class CollectionSchemaView extends CollectionSubView() {
this._makeNewColumn = false;
this._columnMenuIndex = index;
this._menuValue = '';
- this._menuOptions = this.documentKeys;
+ this._menuOptions = Object.entries(this.keyInfos);
this._makeNewField = false;
this._newFieldWarning = '';
this._makeNewField = false;
@@ -485,24 +540,17 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
openFilterMenu = (index: number) => {
this._filterColumnIndex = index;
- this._filterValue = this.getFieldFilters(this.columnKeys[this._filterColumnIndex!]).map(filter => filter.split(':')[1])[0];
+ this._filterSearchValue = '';
};
@action
- closeFilterMenu = (setValue: boolean) => {
- if (setValue) {
- if (this._filterValue !== '') {
- Doc.setDocFilter(this.Document, this.columnKeys[this._filterColumnIndex!], this._filterValue, 'check', false, undefined, false);
- } else {
- this.removeFieldFilters(this.columnKeys[this._filterColumnIndex!]);
- }
- }
+ closeFilterMenu = () => {
this._filterColumnIndex = undefined;
};
openContextMenu = (x: number, y: number, index: number) => {
this.closeColumnMenu();
- this.closeFilterMenu(false);
+ this.closeFilterMenu();
ContextMenu.Instance.clearItems();
ContextMenu.Instance.addItem({
description: 'Change field',
@@ -525,7 +573,7 @@ export class CollectionSchemaView extends CollectionSubView() {
@action
updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => {
this._menuValue = e.target.value;
- this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
+ this._menuOptions = Object.entries(this.keyInfos).filter(value => value[0].toLowerCase().includes(this._menuValue.toLowerCase()));
};
getFieldFilters = (field: string) => StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field);
@@ -535,15 +583,16 @@ export class CollectionSchemaView extends CollectionSubView() {
};
onFilterKeyDown = (e: React.KeyboardEvent) => {
- //prettier-ignore
switch (e.key) {
- case 'Enter' : this.closeFilterMenu(true); break;
- case 'Escape': this.closeFilterMenu(false);break;
+ case 'Enter':
+ case 'Escape':
+ this.closeFilterMenu();
+ break;
}
};
@action
- updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterValue = e.target.value);
+ updateFilterSearch = (e: React.ChangeEvent<HTMLInputElement>) => (this._filterSearchValue = e.target.value);
@computed get newFieldMenu() {
return (
@@ -626,14 +675,23 @@ export class CollectionSchemaView extends CollectionSubView() {
{ passive: false }
)
}>
- {this._menuOptions.map(key => (
+ {this._menuOptions.map(([key, info]) => (
<div
- className="schema-key-search-result"
+ className="schema-search-result"
onPointerDown={e => {
e.stopPropagation();
this.setKey(key);
}}>
- {key}
+ <p>
+ <span className="schema-search-result-key">
+ {key}
+ {info.type ? ', ' : ''}
+ </span>
+ <span className="schema-search-result-type" style={{ color: info.readOnly ? 'red' : 'inherit' }}>
+ {info.type}
+ </span>
+ </p>
+ <p className="schema-search-result-desc">{info.description}</p>
</div>
))}
</div>
@@ -662,21 +720,16 @@ export class CollectionSchemaView extends CollectionSubView() {
@computed get renderFilterOptions() {
const keyOptions: string[] = [];
const columnKey = this.columnKeys[this._filterColumnIndex!];
- this.childDocs.forEach(doc => {
- const key = StrCast(doc[columnKey]);
- if (keyOptions.includes(key) === false && (key.includes(this._filterValue) || !this._filterValue) && key !== '') {
- keyOptions.push(key);
+ const allDocs = DocListCast(this.dataDoc[this.props.fieldKey]);
+ allDocs.forEach(doc => {
+ const value = StrCast(doc[columnKey]);
+ if (!keyOptions.includes(value) && value !== '' && (this._filterSearchValue === '' || value.includes(this._filterSearchValue))) {
+ keyOptions.push(value);
}
});
const filters = StrListCast(this.Document._docFilters);
- for (let i = 0; i < (filters?.length ?? 0) - 1; i++) {
- if (filters[i] === columnKey && keyOptions.includes(filters[i].split(':')[1]) === false) {
- keyOptions.push(filters[i + 1]);
- }
- }
-
- const options = keyOptions.map(key => {
+ return keyOptions.map(key => {
let bool = false;
if (filters !== undefined) {
const ind = filters.findIndex(filter => filter.split(':')[1] === key);
@@ -702,21 +755,19 @@ export class CollectionSchemaView extends CollectionSubView() {
</div>
);
});
-
- return options;
}
@computed get renderFilterMenu() {
const x = this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._filterColumnIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
return (
<div className="schema-filter-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
- <input className="schema-filter-input" type="text" value={this._filterValue} onKeyDown={this.onFilterKeyDown} onChange={this.updateFilterSearch} onPointerDown={e => e.stopPropagation()} />
+ <input className="schema-filter-input" type="text" value={this._filterSearchValue} onKeyDown={this.onFilterKeyDown} onChange={this.updateFilterSearch} onPointerDown={e => e.stopPropagation()} />
{this.renderFilterOptions}
<div
className="schema-column-menu-button"
onPointerDown={action(e => {
e.stopPropagation();
- this.closeFilterMenu(true);
+ this.closeFilterMenu();
})}>
done
</div>
@@ -729,7 +780,7 @@ export class CollectionSchemaView extends CollectionSubView() {
const desc = BoolCast(this.layoutDoc.sortDesc);
const docs = !field
? this.childDocs
- : this.childDocs.sort((docA, docB) => {
+ : [...this.childDocs].sort((docA, docB) => {
const aStr = Field.toString(docA[field] as Field);
const bStr = Field.toString(docB[field] as Field);
var out = 0;
@@ -780,8 +831,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{this._columnMenuIndex !== undefined && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
<CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} />
-
- <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} />
+ <EditableView GetValue={returnEmptyString} SetValue={this.addNewTextDoc} placeholder={"Type ':' for commands"} contents={'+ New Node'} menuCallback={this.menuCallback} height={CollectionSchemaView._newNodeInputHeight} />
</div>
{this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
{this.previewWidth > 0 && (
@@ -834,11 +884,11 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
childScreenToLocal = computedFn((index: number) => () => this.props.schema.props.ScreenToLocalTransform().translate(0, -CollectionSchemaView._rowHeight - index * this.rowHeightFunc()));
render() {
return (
- <div className="schema-table-content">
+ <div className="schema-table-content" style={{ height: `calc(100% - ${CollectionSchemaView._newNodeInputHeight + CollectionSchemaView._rowHeight}px)` }}>
{this.props.childDocs().docs.map((doc: Doc, index: number) => {
const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.schema.props.DataDoc;
return (
- <div className="schema-row-wrapper" style={{ maxHeight: CollectionSchemaView._rowHeight }}>
+ <div className="schema-row-wrapper" style={{ height: CollectionSchemaView._rowHeight }}>
<DocumentView
key={doc[Id]}
{...this.props.schema.props}
@@ -870,6 +920,7 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
hideLinkAnchors={true}
fitWidth={returnTrue}
scriptContext={this}
+ canEmbedOnDrag={true}
/>
</div>
);
diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
index d88d67c94..243fe0c61 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -1,10 +1,12 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed } from 'mobx';
+import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { emptyFunction, setupMoveUpEvents } from '../../../../Utils';
import { Colors } from '../../global/globalEnums';
import './CollectionSchemaView.scss';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { DragManager } from '../../../util/DragManager';
export interface SchemaColumnHeaderProps {
columnKeys: string[];
@@ -12,7 +14,7 @@ export interface SchemaColumnHeaderProps {
columnIndex: number;
sortField: string;
sortDesc: boolean;
- setSort: (field: string, desc: boolean) => void;
+ setSort: (field: string | undefined, desc?: boolean) => void;
removeColumn: (index: number) => void;
resizeColumn: (e: any, index: number) => void;
dragColumn: (e: any, index: number) => boolean;
@@ -22,6 +24,8 @@ export interface SchemaColumnHeaderProps {
@observer
export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> {
+ @observable _ref: HTMLDivElement | null = null;
+
@computed get fieldKey() {
return this.props.columnKeys[this.props.columnIndex];
}
@@ -30,8 +34,10 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
sortClicked = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
- if (this.props.sortField == this.fieldKey) {
- this.props.setSort(this.fieldKey, !this.props.sortDesc);
+ if (this.props.sortField == this.fieldKey && this.props.sortDesc) {
+ this.props.setSort(undefined);
+ } else if (this.props.sortField == this.fieldKey) {
+ this.props.setSort(this.fieldKey, true);
} else {
this.props.setSort(this.fieldKey, false);
}
@@ -44,7 +50,18 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps>
render() {
return (
- <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }} onPointerDown={this.onPointerDown} ref={col => col && this.props.setColRef(this.props.columnIndex, col)}>
+ <div
+ className="schema-column-header"
+ style={{
+ width: this.props.columnWidths[this.props.columnIndex],
+ }}
+ onPointerDown={this.onPointerDown}
+ ref={col => {
+ if (col) {
+ this._ref = col;
+ this.props.setColRef(this.props.columnIndex, col);
+ }
+ }}>
<div className="schema-column-resizer left" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex)}></div>
<div className="schema-column-title">{this.fieldKey}</div>
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 856537927..59b571b58 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -123,6 +123,7 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
<SchemaTableCell
key={key}
Document={this.rootDoc}
+ schemaView={this.schemaView}
fieldKey={key}
columnWidth={this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth}
isRowActive={this.props.isContentActive}
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 5f8ffe8b0..f9319050e 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -9,9 +9,11 @@ import { KeyValueBox } from '../../nodes/KeyValueBox';
import { DefaultStyleProvider } from '../../StyleProvider';
import { CollectionSchemaView } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
+import { computed } from 'mobx';
export interface SchemaTableCellProps {
Document: Doc;
+ schemaView: CollectionSchemaView | undefined;
fieldKey: string;
columnWidth: number;
isRowActive: () => boolean | undefined;
@@ -20,6 +22,10 @@ export interface SchemaTableCellProps {
@observer
export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
+ get readOnly() {
+ return this.props.schemaView?.keyInfos[this.props.fieldKey].readOnly;
+ }
+
render() {
const props: FieldViewProps = {
Document: this.props.Document,
@@ -48,7 +54,7 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> {
return (
<div className="schema-table-cell" style={{ width: this.props.columnWidth }}>
- <div className="schemacell-edit-wrapper" style={this.props.isRowActive() ? { cursor: 'text', pointerEvents: 'auto' } : { cursor: 'default', pointerEvents: 'none' }}>
+ <div className="schemacell-edit-wrapper" style={this.props.isRowActive() && !this.readOnly ? { cursor: 'text', pointerEvents: 'auto' } : { cursor: 'default', pointerEvents: 'none' }}>
<EditableView
contents={<FieldView {...props} />}
GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)}