diff options
author | mehekj <mehek.jethani@gmail.com> | 2023-03-06 17:53:36 -0500 |
---|---|---|
committer | mehekj <mehek.jethani@gmail.com> | 2023-03-06 17:53:36 -0500 |
commit | b55a757175051457c9260f80a1de937901f5cfff (patch) | |
tree | 2349956a95760e8e7e0eac8604781966127e1a33 | |
parent | 67a0081bb4fb713b730fa86a5b67d3c061426514 (diff) |
moved column menu
4 files changed, 254 insertions, 291 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 5c0b6d88b..931645857 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -9,6 +9,77 @@ .schema-table { background-color: $white; + .schema-column-menu { + background: $light-gray; + position: absolute; + min-width: 200px; + display: flex; + flex-direction: column; + align-items: flex-start; + z-index: 1; + + .schema-key-search-input { + width: calc(100% - 20px); + margin: 10px; + } + + .schema-key-search-result { + cursor: pointer; + padding: 2px 10px; + width: 100%; + + &:hover { + background-color: $medium-gray; + } + } + + .schema-key-search, + .schema-new-key-options { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + } + + .schema-new-key-options { + margin: 10px; + .schema-key-warning { + color: red; + font-weight: normal; + align-self: center; + } + } + + .schema-key-list { + width: 100%; + max-height: 300px; + overflow-y: auto; + } + + .schema-key-type-option { + margin: 2px 0px; + + input { + margin-right: 5px; + } + } + + .schema-key-default-val { + margin: 5px 0; + } + + .schema-column-menu-button { + cursor: pointer; + padding: 2px 5px; + background: $medium-blue; + border-radius: 9999px; + color: $white; + width: fit-content; + margin: 5px; + align-self: center; + } + } + .schema-header-row { justify-content: flex-end; @@ -19,6 +90,7 @@ justify-content: space-between; align-items: center; padding: 0; + z-index: 1; .schema-column-title { flex-grow: 2; @@ -46,78 +118,6 @@ .schema-column-resizer.left { align-self: flex-start; } - - .schema-column-menu { - background: $light-gray; - width: inherit; - position: absolute; - top: 35px; - min-width: 200px; - display: flex; - flex-direction: column; - align-items: flex-start; - - .schema-key-search-input { - width: calc(100% - 20px); - margin: 10px; - } - - .schema-key-search-result { - cursor: pointer; - padding: 2px 10px; - width: 100%; - - &:hover { - background-color: $medium-gray; - } - } - - .schema-key-search, - .schema-new-key-options { - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-start; - } - - .schema-new-key-options { - margin: 10px; - .schema-key-warning { - color: red; - font-weight: normal; - align-self: center; - } - } - - .schema-key-list { - width: 100%; - max-height: 300px; - overflow-y: auto; - } - - .schema-key-type-option { - margin: 2px 0px; - - input { - margin-right: 5px; - } - } - - .schema-key-default-val { - margin: 5px 0; - } - - .schema-column-menu-button { - cursor: pointer; - padding: 2px 5px; - background: $medium-blue; - border-radius: 9999px; - color: $white; - width: fit-content; - margin: 5px; - align-self: center; - } - } } } diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index c52a7f3e4..fd7790c4b 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -26,6 +26,7 @@ import { SchemaRowBox } from './SchemaRowBox'; import { DefaultStyleProvider } from '../../StyleProvider'; import { DocumentManager } from '../../../util/DocumentManager'; import { ScriptField } from '../../../../fields/ScriptField'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; export enum ColumnType { Number, @@ -43,16 +44,25 @@ export class CollectionSchemaView extends CollectionSubView() { private _selectedDocSortedArray: Doc[] = []; private _closestDropIndex: number = 0; private _previewRef: HTMLDivElement | null = null; + private _makeNewColumn: boolean = false; public static _rowHeight: number = 50; public static _minColWidth: number = 150; public static _rowMenuWidth: number = 100; public static _previewDividerWidth: number = 4; + @observable _lastSelectedRow: number | undefined; @observable _selectedDocs: ObservableSet = new ObservableSet<Doc>(); @observable _rowEles: ObservableMap = new ObservableMap<Doc, HTMLDivElement>(); @observable _isDragging: boolean = false; @observable _displayColumnWidths: number[] | undefined; + @observable _columnMenuIndex: number | undefined; + @observable _menuOptions: string[] = []; + @observable _newFieldWarning: string = ''; + @observable _makeNewField: boolean = false; + @observable _newFieldDefault: any = 0; + @observable _newFieldType: ColumnType = ColumnType.Number; + @observable _menuValue: string = ''; get documentKeys() { const docs = this.childDocs; @@ -154,8 +164,6 @@ export class CollectionSchemaView extends CollectionSubView() { this.layoutDoc.sortField = field; this.layoutDoc.sortDesc = desc; - console.log(field, desc); - if (field === undefined) return; this.childDocs.sort((docA, docB) => { @@ -189,7 +197,7 @@ export class CollectionSchemaView extends CollectionSubView() { @undoBatch @action - addColumn = (index: number, key: string, defaultVal?: any) => { + addColumn = (key: string, defaultVal?: any) => { if (!this.documentKeys.includes(key)) { this.addNewKey(key, defaultVal); } @@ -200,11 +208,11 @@ export class CollectionSchemaView extends CollectionSubView() { const proportion = w / (this.tableWidth - CollectionSchemaView._rowMenuWidth); return proportion * (this.tableWidth - CollectionSchemaView._rowMenuWidth - newColWidth); }); - currWidths.splice(index + 1, 0, newColWidth); + currWidths.splice(0, 0, newColWidth); this.layoutDoc.columnWidths = new List<number>(currWidths); let currKeys = [...this.columnKeys]; - currKeys.splice(index + 1, 0, key); + currKeys.splice(0, 0, key); this.layoutDoc.columnKeys = new List<string>(currKeys); }; @@ -386,6 +394,7 @@ export class CollectionSchemaView extends CollectionSubView() { this.addDocument(this._selectedDocSortedArray); this.addDocument(pushedDocs); this.setSort(undefined); + this.clearSelection(); return true; } return false; @@ -543,6 +552,157 @@ export class CollectionSchemaView extends CollectionSubView() { isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; + @computed get fieldDefaultInput() { + switch (this._newFieldType) { + case ColumnType.Number: + return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + case ColumnType.Boolean: + return ( + <> + <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} /> + {this._newFieldDefault ? 'true' : 'false'} + </> + ); + case ColumnType.String: + return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; + } + } + + 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))(); + break; + case 'Escape': + this.toggleColumnMenu(this._columnMenuIndex); + break; + } + }; + + @action + setKey = (key: string, defaultVal?: any) => { + if (this._makeNewColumn) { + this.addColumn(key, defaultVal); + } else { + this.changeColumnKey(this._columnMenuIndex!, key, defaultVal); + } + this.toggleColumnMenu(this._columnMenuIndex); + }; + + @action + toggleColumnMenu = (index: number | undefined, newCol?: boolean) => { + this._makeNewColumn = false; + if (this._columnMenuIndex !== undefined && index === this._columnMenuIndex) { + this._columnMenuIndex = undefined; + } else { + this._columnMenuIndex = index; + this._menuValue = ''; + this._menuOptions = this.documentKeys; + this._makeNewField = false; + this._newFieldWarning = ''; + this._makeNewField = false; + if (newCol) { + this._makeNewColumn = true; + } + } + }; + + @action + updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => { + this._menuValue = e.target.value; + this._menuOptions = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); + }; + + @computed get renderColumnMenu() { + const x = this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); + return ( + <div className="schema-column-menu" style={{ left: x }}> + <input className="schema-key-search-input" type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> + {this._makeNewField ? ( + <div className="schema-new-key-options"> + <div className="schema-key-type-option"> + <input + type="radio" + name="newFieldType" + id="" + checked={this._newFieldType == ColumnType.Number} + onChange={action(() => { + this._newFieldType = ColumnType.Number; + this._newFieldDefault = 0; + })} + /> + number + </div> + <div className="schema-key-type-option"> + <input + type="radio" + name="newFieldType" + id="" + checked={this._newFieldType == ColumnType.Boolean} + onChange={action(() => { + this._newFieldType = ColumnType.Boolean; + this._newFieldDefault = false; + })} + /> + boolean + </div> + <div className="schema-key-type-option"> + <input + type="radio" + name="newFieldType" + id="" + checked={this._newFieldType == ColumnType.String} + onChange={action(() => { + this._newFieldType = ColumnType.String; + this._newFieldDefault = ''; + })} + /> + string + </div> + <div className="schema-key-default-val">value: {this.fieldDefaultInput}</div> + <div className="schema-key-warning">{this._newFieldWarning}</div> + <div + className="schema-column-menu-button" + onPointerDown={action(e => { + if (this.documentKeys.includes(this._menuValue)) { + this._newFieldWarning = 'Field already exists'; + } else if (this._menuValue.length === 0) { + this._newFieldWarning = 'Field cannot be an empty string'; + } else { + this.setKey(this._menuValue, this._newFieldDefault); + } + })}> + done + </div> + </div> + ) : ( + <div className="schema-key-search"> + <div + className="schema-column-menu-button" + onPointerDown={action(e => { + e.stopPropagation(); + this._makeNewField = true; + })}> + + new field + </div> + <div className="schema-key-list"> + {this._menuOptions.map(key => ( + <div + className="schema-key-search-result" + onPointerDown={e => { + e.stopPropagation(); + this.setKey(key); + }}> + {key} + </div> + ))} + </div> + </div> + )} + </div> + ); + } + render() { return ( <div @@ -558,7 +718,15 @@ export class CollectionSchemaView extends CollectionSubView() { this.clearSelection(); })}> <div className="schema-header-row" style={{ height: CollectionSchemaView._rowHeight }}> - <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}></div> + <div className="row-menu" style={{ width: CollectionSchemaView._rowMenuWidth }}> + <div + className="schema-header-button" + onPointerDown={e => { + this.toggleColumnMenu(-1, true); + }}> + <FontAwesomeIcon icon="plus" /> + </div> + </div> {this.columnKeys.map((key, index) => { return ( <SchemaColumnHeader @@ -566,18 +734,17 @@ export class CollectionSchemaView extends CollectionSubView() { columnIndex={index} columnKeys={this.columnKeys} columnWidths={this.displayColumnWidths} - possibleKeys={this.documentKeys} sortField={this.sortField} sortDesc={this.sortDesc} setSort={this.setSort} - changeColumnKey={this.changeColumnKey} - addColumn={this.addColumn} removeColumn={this.removeColumn} resizeColumn={this.startResize} + toggleColumnMenu={this.toggleColumnMenu} /> ); })} </div> + {this._columnMenuIndex !== undefined && this.renderColumnMenu} <div className="schema-table-content" onPointerDown={e => e.stopPropagation()}> {this.childDocs.map((doc: Doc, index: number) => { const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc; diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 2e303f91c..ba63b352b 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -11,29 +11,17 @@ export interface SchemaColumnHeaderProps { columnKeys: string[]; columnWidths: number[]; columnIndex: number; - possibleKeys: string[]; sortField: string; sortDesc: boolean; setSort: (field: string, desc: boolean) => void; - changeColumnKey: (index: number, newKey: string, defaultVal?: any) => void; - addColumn: (index: number, key: string, defaultVal?: any) => void; removeColumn: (index: number) => void; resizeColumn: (e: any, index: number, left: boolean) => void; + toggleColumnMenu: (index: number | undefined, newCol?: boolean) => void; // dragColumn: (e: any, index: number) => boolean; } @observer export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> { - @observable _menuVisible: boolean = false; - @observable _makeNewField: boolean = false; - @observable _newFieldType: ColumnType = ColumnType.Number; - @observable _newFieldDefault: any = 0; - @observable _newFieldWarning: string = ''; - @observable _menuValue: string = ''; - @observable _menuOptions: string[] = []; - @observable _filterVisible: boolean = false; - private _makeNewColumn = false; - @computed get fieldKey() { return this.props.columnKeys[this.props.columnIndex]; } @@ -49,146 +37,6 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> } }; - @computed get fieldDefaultInput() { - switch (this._newFieldType) { - case ColumnType.Number: - return <input type="number" name="" id="" value={this._newFieldDefault ?? 0} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; - case ColumnType.Boolean: - return ( - <> - <input type="checkbox" name="" id="" value={this._newFieldDefault} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.checked))} /> - {this._newFieldDefault ? 'true' : 'false'} - </> - ); - case ColumnType.String: - return <input type="text" name="" id="" value={this._newFieldDefault ?? ''} onPointerDown={e => e.stopPropagation()} onChange={action(e => (this._newFieldDefault = e.target.value))} />; - } - } - - @computed get renderColumnMenu() { - return ( - <div className="schema-column-menu"> - <input className="schema-key-search-input" type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> - {this._makeNewField ? ( - <div className="schema-new-key-options"> - <div className="schema-key-type-option"> - <input - type="radio" - name="newFieldType" - id="" - checked={this._newFieldType == ColumnType.Number} - onChange={action(() => { - this._newFieldType = ColumnType.Number; - this._newFieldDefault = 0; - })} - /> - number - </div> - <div className="schema-key-type-option"> - <input - type="radio" - name="newFieldType" - id="" - checked={this._newFieldType == ColumnType.Boolean} - onChange={action(() => { - this._newFieldType = ColumnType.Boolean; - this._newFieldDefault = false; - })} - /> - boolean - </div> - <div className="schema-key-type-option"> - <input - type="radio" - name="newFieldType" - id="" - checked={this._newFieldType == ColumnType.String} - onChange={action(() => { - this._newFieldType = ColumnType.String; - this._newFieldDefault = ''; - })} - /> - string - </div> - <div className="schema-key-default-val">value: {this.fieldDefaultInput}</div> - <div className="schema-key-warning">{this._newFieldWarning}</div> - <div - className="schema-column-menu-button" - onPointerDown={action(e => { - if (this.props.possibleKeys.includes(this._menuValue)) { - this._newFieldWarning = 'Field already exists'; - } else if (this._menuValue.length === 0) { - this._newFieldWarning = 'Field cannot be an empty string'; - } else { - this.setKey(this._menuValue, this._newFieldDefault); - } - })}> - done - </div> - </div> - ) : ( - <div className="schema-key-search"> - <div - className="schema-column-menu-button" - onPointerDown={action(e => { - e.stopPropagation(); - this._makeNewField = true; - })}> - + new field - </div> - <div className="schema-key-list"> - {this._menuOptions.map(key => ( - <div - className="schema-key-search-result" - onPointerDown={e => { - e.stopPropagation(); - this.setKey(key); - }}> - {key} - </div> - ))} - </div> - </div> - )} - </div> - ); - } - - @computed get columnFilterMenu() { - return ( - <div className="schema-column-menu"> - <input className="schema-key-search-input" type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} /> - </div> - ); - } - - onSearchKeyDown = (e: React.KeyboardEvent) => { - switch (e.key) { - case 'Enter': - this.setKey(this._menuOptions.length > 0 && this._menuValue.length > 0 ? this._menuOptions[0] : this._menuValue); - break; - case 'Escape': - this.toggleColumnMenu(); - break; - } - }; - - @action - setKey = (key: string, defaultVal?: any) => { - if (this._makeNewColumn) { - this.props.addColumn(this.props.columnIndex, key, defaultVal); - } else { - this.props.changeColumnKey(this.props.columnIndex, key, defaultVal); - } - this.toggleColumnMenu(); - }; - - @action - updateKeySearch = (e: React.ChangeEvent<HTMLInputElement>) => { - this._menuValue = e.target.value; - this._menuOptions = this.props.possibleKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase())); - }; - // @action // onPointerDown = (e: React.PointerEvent) => { // e.stopPropagation(); @@ -196,36 +44,6 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> // setupMoveUpEvents(this, e, e => this.props.dragColumn(e, this.props.columnIndex), emptyFunction, emptyFunction); // }; - @action - toggleColumnMenu = (newCol?: boolean) => { - this._makeNewColumn = false; - if (this._menuVisible) { - this._menuVisible = false; - } else { - this._filterVisible = false; - this._menuVisible = true; - this._menuValue = ''; - this._menuOptions = this.props.possibleKeys; - this._makeNewField = false; - this._newFieldWarning = ''; - this._makeNewField = false; - if (newCol) { - this._makeNewColumn = true; - } - } - }; - - @action - toggleFilterMenu = () => { - console.log(this._filterVisible); - if (this._filterVisible) { - this._filterVisible = false; - } else { - this._filterVisible = true; - this._menuVisible = false; - } - }; - render() { return ( <div className="schema-column-header" style={{ width: this.props.columnWidths[this.props.columnIndex] }}> @@ -236,20 +54,13 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> <div className="schema-header-button" onPointerDown={e => { - this.toggleColumnMenu(); + this.props.toggleColumnMenu(this.props.columnIndex, false); }}> <FontAwesomeIcon icon="pencil-alt" /> </div> <div className="schema-header-button" onPointerDown={e => { - this.toggleColumnMenu(true); - }}> - <FontAwesomeIcon icon="plus" /> - </div> - <div - className="schema-header-button" - onPointerDown={e => { this.props.removeColumn(this.props.columnIndex); }}> <FontAwesomeIcon icon="trash" /> @@ -257,15 +68,9 @@ export class SchemaColumnHeader extends React.Component<SchemaColumnHeaderProps> <div className="schema-header-button" onPointerDown={this.sortClicked} style={this.props.sortField == this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}> <FontAwesomeIcon icon="caret-right" style={this.props.sortField == this.fieldKey ? { transform: `rotate(${this.props.sortDesc ? '270deg' : '90deg'})` } : {}} /> </div> - <div className="schema-header-button" onPointerDown={e => this.toggleFilterMenu()}> - <FontAwesomeIcon icon="filter" /> - </div> </div> <div className="schema-column-resizer right" onPointerDown={e => this.props.resizeColumn(e, this.props.columnIndex, false)}></div> - - {this._menuVisible && this.renderColumnMenu} - {this._filterVisible && this.columnFilterMenu} </div> ); } diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 0f832e7c4..43b7da544 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,20 +1,14 @@ import React = require('react'); import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field } from '../../../../fields/Doc'; -import './CollectionSchemaView.scss'; -import { type } from 'jquery'; -import { action } from 'mobx'; -import { ComputedField } from '../../../../fields/ScriptField'; -import { FieldValue } from '../../../../fields/Types'; -import { CompileScript } from '../../../util/Scripting'; +import { Doc, Field } from '../../../../fields/Doc'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../Utils'; +import { Transform } from '../../../util/Transform'; import { EditableView } from '../../EditableView'; -import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { KeyValueBox } from '../../nodes/KeyValueBox'; -import { returnEmptyFilter, returnEmptyDoclist, returnFalse, emptyFunction, returnZero } from '../../../../Utils'; import { DefaultStyleProvider } from '../../StyleProvider'; -import { Transform } from '../../../util/Transform'; import { CollectionSchemaView } from './CollectionSchemaView'; +import './CollectionSchemaView.scss'; export interface SchemaTableCellProps { Document: Doc; @@ -28,7 +22,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { render() { const props: FieldViewProps = { Document: this.props.Document, - // DataDoc: this.props.doc, docFilters: returnEmptyFilter, docRangeFilters: returnEmptyFilter, searchFilterDocs: returnEmptyDoclist, @@ -56,8 +49,6 @@ export class SchemaTableCell extends React.Component<SchemaTableCellProps> { return ( <div className="schema-table-cell" style={{ width: this.props.columnWidth, pointerEvents: this.props.isRowActive() ? 'all' : 'none' }}> - {/* {Field.toString(this.props.Document[this.props.fieldKey] as Field)} */} - {/* <EditableView contents={Field.toString(this.props.Document[this.props.fieldKey] as Field)} GetValue={() => Field.toKeyValueString(this.props.Document, this.props.fieldKey)} SetValue={(value: string) => true} /> */} <EditableView contents={<FieldView {...props} />} height={'auto'} |