aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss17
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx108
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx8
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx1
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx8
5 files changed, 87 insertions, 55 deletions
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 fd9bcf681..d3992d12c 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';
@@ -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;
};
@@ -355,7 +371,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 +386,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 +457,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 +487,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 +500,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 +533,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 +543,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 +635,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 +680,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 +715,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 +740,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;
@@ -781,7 +792,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{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 && (
@@ -836,11 +847,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}
@@ -874,6 +885,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..b133347cf 100644
--- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx
@@ -12,7 +12,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;
@@ -30,8 +30,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);
}
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index 2defaae00..aa8a61205 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 13e45963e..42c9f9d48 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,
@@ -50,7 +56,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)}