From c68e751531f972fcf201089f26e69627cfe4fe3c Mon Sep 17 00:00:00 2001 From: mehekj Date: Tue, 14 Mar 2023 19:31:52 -0400 Subject: single filter through search added --- .../collectionSchema/CollectionSchemaView.scss | 35 ++- .../collectionSchema/CollectionSchemaView.tsx | 323 ++++++++++++++------- .../collectionSchema/SchemaColumnHeader.tsx | 31 +- 3 files changed, 258 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index 931645857..5eb5cc86d 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -9,7 +9,8 @@ .schema-table { background-color: $white; - .schema-column-menu { + .schema-column-menu, + .schema-filter-menu { background: $light-gray; position: absolute; min-width: 200px; @@ -83,6 +84,11 @@ .schema-header-row { justify-content: flex-end; + .row-menu { + display: flex; + justify-content: flex-end; + } + .schema-column-header { font-weight: bold; display: flex; @@ -112,10 +118,12 @@ } .schema-column-resizer.right { + margin-left: 5px; align-self: flex-end; } .schema-column-resizer.left { + margin-right: 5px; align-self: flex-start; } } @@ -139,6 +147,10 @@ overflow: hidden; } +.schema-header-row { + background-color: $light-gray; +} + .schema-header-row, .schema-row { display: flex; @@ -163,8 +175,8 @@ .row-menu { display: flex; flex-direction: row; - justify-content: center; min-width: 50px; + justify-content: flex-end; } .row-cells { @@ -176,9 +188,22 @@ .schema-row-button, .schema-header-button { - width: 20px; - height: 20px; - border-radius: 100%; + color: $dark-gray; + margin: 3px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + + svg { + width: 15px; + } +} + +.schema-sort-button { + width: 17px; + height: 17px; + border-radius: 30%; background-color: $dark-gray; color: white; margin: 3px; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index fd7790c4b..e1c2d989f 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,7 +1,7 @@ import React = require('react'); import { action, computed, observable, ObservableMap, ObservableSet, untracked } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, DocListCast, Field, Opt } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { RichTextField } from '../../../../fields/RichTextField'; @@ -17,7 +17,7 @@ import { undoBatch } from '../../../util/UndoManager'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; import { EditableView } from '../../EditableView'; -import { DocComponentView, DocFocusOptions, DocumentView, ViewAdjustment } from '../../nodes/DocumentView'; +import { DocComponentView, DocFocusOptions, DocumentView } from '../../nodes/DocumentView'; import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; import { CollectionSubView } from '../CollectionSubView'; import './CollectionSchemaView.scss'; @@ -46,7 +46,7 @@ export class CollectionSchemaView extends CollectionSubView() { private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; - public static _rowHeight: number = 50; + public static _rowHeight: number = 40; public static _minColWidth: number = 150; public static _rowMenuWidth: number = 100; public static _previewDividerWidth: number = 4; @@ -63,6 +63,8 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _newFieldDefault: any = 0; @observable _newFieldType: ColumnType = ColumnType.Number; @observable _menuValue: string = ''; + @observable _filterColumnIndex: number | undefined; + @observable _filterValue: string = ''; get documentKeys() { const docs = this.childDocs; @@ -533,20 +535,18 @@ export class CollectionSchemaView extends CollectionSubView() { focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); - let focusSpeed = 0; + const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); if (found) { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = options.zoomTime ?? 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + let focusSpeed = options.zoomTime ?? 500; + smoothScroll(focusSpeed, this._mainCont!, localTop[1] + this._mainCont!.scrollTop, options.easeFunc); + return focusSpeed; } } - const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing; - this.props.focus(this.rootDoc, { - ...options, - afterFocus: (didFocus: boolean) => new Promise(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)), - }); + return undefined; }; isChildContentActive = () => @@ -574,7 +574,7 @@ export class CollectionSchemaView extends CollectionSubView() { this._menuOptions.length > 0 && this._menuValue.length > 0 ? this.setKey(this._menuOptions[0]) : action(() => (this._makeNewField = true))(); break; case 'Escape': - this.toggleColumnMenu(this._columnMenuIndex); + this.closeColumnMenu(); break; } }; @@ -586,25 +586,63 @@ export class CollectionSchemaView extends CollectionSubView() { } else { this.changeColumnKey(this._columnMenuIndex!, key, defaultVal); } - this.toggleColumnMenu(this._columnMenuIndex); + this.closeColumnMenu(); }; @action - toggleColumnMenu = (index: number | undefined, newCol?: boolean) => { + openColumnMenu = (index: number, 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; - } - } + this._columnMenuIndex = index; + this._menuValue = ''; + this._menuOptions = this.documentKeys; + this._makeNewField = false; + this._newFieldWarning = ''; + this._makeNewField = false; + this._makeNewColumn = newCol; + }; + + @action + closeColumnMenu = () => { + this._columnMenuIndex = undefined; + }; + + @action + openFilterMenu = (index: number) => { + this._filterColumnIndex = index; + this._filterValue = this.getFieldFilters(this.columnKeys[this._filterColumnIndex!]).map(filter => filter.split(':')[1])[0]; + }; + + @action + closeFilterMenu = () => { + this._filterColumnIndex = undefined; + }; + + openContextMenu = (x: number, y: number, index: number) => { + this.closeColumnMenu(); + this.closeFilterMenu(); + ContextMenu.Instance.clearItems(); + ContextMenu.Instance.addItem({ + description: 'Change field', + event: () => { + this.openColumnMenu(index, false); + }, + icon: 'pencil-alt', + }); + ContextMenu.Instance.addItem({ + description: 'Filter field', + event: () => { + this.openFilterMenu(index); + }, + icon: 'filter', + }); + ContextMenu.Instance.addItem({ + description: 'Delete column', + event: () => { + this.removeColumn(index); + }, + icon: 'trash', + }); + ContextMenu.Instance.displayMenu(x, y, undefined, false); }; @action @@ -613,92 +651,156 @@ export class CollectionSchemaView extends CollectionSubView() { 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); + getFieldFilters = (field: string) => { + return StrListCast(this.Document._docFilters).filter(filter => filter.split(':')[0] == field); + }; + + removeFieldFilters = (field: string) => { + this.getFieldFilters(field).forEach(filter => { + Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'); + }); + }; + + onFilterKeyDown = (e: React.KeyboardEvent) => { + switch (e.key) { + case 'Enter': + if (this._filterValue !== '') { + Doc.setDocFilter(this.Document, this.columnKeys[this._filterColumnIndex!], this._filterValue, 'check', false, undefined, false); + } else { + this.removeFieldFilters(this.columnKeys[this._filterColumnIndex!]); + } + this.closeFilterMenu(); + break; + case 'Escape': + this.closeFilterMenu(); + break; + } + }; + + @action + updateFilterSearch = (e: React.ChangeEvent) => { + this._filterValue = e.target.value; + }; + + @computed get newFieldMenu() { return ( -
- e.stopPropagation()} /> - {this._makeNewField ? ( -
-
- { - this._newFieldType = ColumnType.Number; - this._newFieldDefault = 0; - })} - /> - number -
-
- { - this._newFieldType = ColumnType.Boolean; - this._newFieldDefault = false; - })} - /> - boolean -
-
- { - this._newFieldType = ColumnType.String; - this._newFieldDefault = ''; - })} - /> - string -
-
value: {this.fieldDefaultInput}
-
{this._newFieldWarning}
-
{ - 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 -
-
- ) : ( -
+
+
+ { + this._newFieldType = ColumnType.Number; + this._newFieldDefault = 0; + })} + /> + number +
+
+ { + this._newFieldType = ColumnType.Boolean; + this._newFieldDefault = false; + })} + /> + boolean +
+
+ { + this._newFieldType = ColumnType.String; + this._newFieldDefault = ''; + })} + /> + string +
+
value: {this.fieldDefaultInput}
+
{this._newFieldWarning}
+
{ + 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 +
+
+ ); + } + + @computed get keysDropdown() { + return ( +
+
{ + e.stopPropagation(); + this._makeNewField = true; + })}> + + new field +
+
+ {this._menuOptions.map(key => (
{ + className="schema-key-search-result" + onPointerDown={e => { e.stopPropagation(); - this._makeNewField = true; - })}> - + new field + this.setKey(key); + }}> + {key}
-
- {this._menuOptions.map(key => ( -
{ - e.stopPropagation(); - this.setKey(key); - }}> - {key} -
- ))} -
-
- )} + ))} +
+
+ ); + } + + @computed get renderColumnMenu() { + const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); + return ( +
+ e.stopPropagation()} /> + {this._makeNewField ? this.newFieldMenu : this.keysDropdown} +
{ + e.stopPropagation(); + this.closeColumnMenu(); + })}> + cancel +
+
+ ); + } + + @computed get renderFilterMenu() { + const x = this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._filterColumnIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth); + return ( +
+ e.stopPropagation()} /> +
{ + e.stopPropagation(); + this.closeFilterMenu(); + })}> + cancel +
); } @@ -722,7 +824,7 @@ export class CollectionSchemaView extends CollectionSubView() {
{ - this.toggleColumnMenu(-1, true); + this.openColumnMenu(-1, true); }}>
@@ -739,12 +841,13 @@ export class CollectionSchemaView extends CollectionSubView() { setSort={this.setSort} removeColumn={this.removeColumn} resizeColumn={this.startResize} - toggleColumnMenu={this.toggleColumnMenu} + openContextMenu={this.openContextMenu} /> ); })}
{this._columnMenuIndex !== undefined && this.renderColumnMenu} + {this._filterColumnIndex !== undefined && this.renderFilterMenu}
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 ba63b352b..42626697a 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -5,7 +5,17 @@ import { observer } from 'mobx-react'; import { emptyFunction, setupMoveUpEvents } from '../../../../Utils'; import './CollectionSchemaView.scss'; import { ColumnType } from './CollectionSchemaView'; -import { Colors } from 'browndash-components'; +import { IconButton } from 'browndash-components'; +import { Colors } from '../../global/globalEnums'; +import { ContextMenu } from '../../ContextMenu'; +import { Doc, DocListCast } from '../../../../fields/Doc'; +import { Id } from '../../../../fields/FieldSymbols'; +import { RichTextField } from '../../../../fields/RichTextField'; +import { StrCast } from '../../../../fields/Types'; +import { ImageField } from '../../../../fields/URLField'; +import { DocUtils, Docs } from '../../../documents/Documents'; +import { ContextMenuProps } from '../../ContextMenuItem'; +import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox'; export interface SchemaColumnHeaderProps { columnKeys: string[]; @@ -16,8 +26,8 @@ export interface SchemaColumnHeaderProps { setSort: (field: string, desc: boolean) => 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; + openContextMenu: (x: number, y: number, index: number) => void; } @observer @@ -51,21 +61,10 @@ export class SchemaColumnHeader extends React.Component
{this.fieldKey}
-
{ - this.props.toggleColumnMenu(this.props.columnIndex, false); - }}> - +
this.props.openContextMenu(e.clientX, e.clientY, this.props.columnIndex)}> +
-
{ - this.props.removeColumn(this.props.columnIndex); - }}> - -
-
+
-- cgit v1.2.3-70-g09d2