aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormehekj <mehek.jethani@gmail.com>2023-03-14 19:31:52 -0400
committermehekj <mehek.jethani@gmail.com>2023-03-14 19:31:52 -0400
commitc68e751531f972fcf201089f26e69627cfe4fe3c (patch)
tree87173c8f7333a5f86d5f05abe6d80f387c0e8508
parent1d07b09c90861d002139a0232e5aa1c1eba5e114 (diff)
single filter through search added
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss35
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx323
-rw-r--r--src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx31
3 files changed, 258 insertions, 131 deletions
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<ViewAdjustment>(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<HTMLInputElement>) => {
+ this._filterValue = e.target.value;
+ };
+
+ @computed get newFieldMenu() {
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-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>
+ );
+ }
+
+ @computed get keysDropdown() {
+ return (
+ <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-column-menu-button"
- onPointerDown={action(e => {
+ className="schema-key-search-result"
+ onPointerDown={e => {
e.stopPropagation();
- this._makeNewField = true;
- })}>
- + new field
+ this.setKey(key);
+ }}>
+ {key}
</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>
+ </div>
+ );
+ }
+
+ @computed get renderColumnMenu() {
+ const x = this._columnMenuIndex! == -1 ? 0 : this.displayColumnWidths.reduce((total, curr, index) => total + (index < this._columnMenuIndex! ? curr : 0), CollectionSchemaView._rowMenuWidth);
+ return (
+ <div className="schema-column-menu" style={{ left: x, minWidth: CollectionSchemaView._minColWidth }}>
+ <input className="schema-key-search-input" type="text" value={this._menuValue} onKeyDown={this.onSearchKeyDown} onChange={this.updateKeySearch} onPointerDown={e => e.stopPropagation()} />
+ {this._makeNewField ? this.newFieldMenu : this.keysDropdown}
+ <div
+ className="schema-column-menu-button"
+ onPointerDown={action(e => {
+ e.stopPropagation();
+ this.closeColumnMenu();
+ })}>
+ cancel
+ </div>
+ </div>
+ );
+ }
+
+ @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()} />
+ <div
+ className="schema-column-menu-button"
+ onPointerDown={action(e => {
+ e.stopPropagation();
+ this.closeFilterMenu();
+ })}>
+ cancel
+ </div>
</div>
);
}
@@ -722,7 +824,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div
className="schema-header-button"
onPointerDown={e => {
- this.toggleColumnMenu(-1, true);
+ this.openColumnMenu(-1, true);
}}>
<FontAwesomeIcon icon="plus" />
</div>
@@ -739,12 +841,13 @@ export class CollectionSchemaView extends CollectionSubView() {
setSort={this.setSort}
removeColumn={this.removeColumn}
resizeColumn={this.startResize}
- toggleColumnMenu={this.toggleColumnMenu}
+ openContextMenu={this.openContextMenu}
/>
);
})}
</div>
{this._columnMenuIndex !== undefined && this.renderColumnMenu}
+ {this._filterColumnIndex !== undefined && this.renderFilterMenu}
<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 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<SchemaColumnHeaderProps>
<div className="schema-column-title">{this.fieldKey}</div>
<div className="schema-header-menu">
- <div
- className="schema-header-button"
- onPointerDown={e => {
- this.props.toggleColumnMenu(this.props.columnIndex, false);
- }}>
- <FontAwesomeIcon icon="pencil-alt" />
+ <div className="schema-header-button" onPointerDown={e => this.props.openContextMenu(e.clientX, e.clientY, this.props.columnIndex)}>
+ <FontAwesomeIcon icon="ellipsis-h" />
</div>
- <div
- className="schema-header-button"
- onPointerDown={e => {
- this.props.removeColumn(this.props.columnIndex);
- }}>
- <FontAwesomeIcon icon="trash" />
- </div>
- <div className="schema-header-button" onPointerDown={this.sortClicked} style={this.props.sortField == this.fieldKey ? { backgroundColor: Colors.MEDIUM_BLUE } : {}}>
+ <div className="schema-sort-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>