diff options
author | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-07-19 13:11:10 -0400 |
---|---|---|
committer | Sam Wilkins <abdullah_ahmed@brown.edu> | 2019-07-19 13:11:10 -0400 |
commit | ae51e87874a714fdb46d4093fee513787b413ed8 (patch) | |
tree | f61d4747a56a8a89ac38ed2179cfc7be0271a52a | |
parent | 8a1be635352177ba05845851289d1a67b4060708 (diff) |
can sort columns by asc, desc
-rw-r--r-- | src/client/views/EditableView.tsx | 12 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaCells.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaHeaders.tsx | 90 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.scss | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaView.tsx | 204 |
5 files changed, 159 insertions, 150 deletions
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 989fb1be9..42faedf9d 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -31,6 +31,7 @@ export interface EditableProps { oneLine?: boolean; editing?: boolean; onClick?: (e: React.MouseEvent) => boolean; + isEditingCallback?: (isEditing: boolean) => void; } /** @@ -48,6 +49,11 @@ export class EditableView extends React.Component<EditableProps> { } @action + componentWillReceiveProps(nextProps: EditableProps) { + this._editing = nextProps.editing ? true : false; + } + + @action onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Tab") { this.props.OnTab && this.props.OnTab(); @@ -55,13 +61,16 @@ export class EditableView extends React.Component<EditableProps> { if (!e.ctrlKey) { if (this.props.SetValue(e.currentTarget.value, e.shiftKey)) { this._editing = false; + this.props.isEditingCallback && this.props.isEditingCallback(false); } } else if (this.props.OnFillDown) { this.props.OnFillDown(e.currentTarget.value); this._editing = false; + this.props.isEditingCallback && this.props.isEditingCallback(false); } } else if (e.key === "Escape") { this._editing = false; + this.props.isEditingCallback && this.props.isEditingCallback(false); } } @@ -69,6 +78,7 @@ export class EditableView extends React.Component<EditableProps> { onClick = (e: React.MouseEvent) => { if (!this.props.onClick || !this.props.onClick(e)) { this._editing = true; + this.props.isEditingCallback && this.props.isEditingCallback(true); } e.stopPropagation(); } @@ -85,7 +95,7 @@ export class EditableView extends React.Component<EditableProps> { render() { if (this._editing) { return <input className="editableView-input" defaultValue={this.props.GetValue()} onKeyDown={this.onKeyDown} autoFocus - onBlur={action(() => this._editing = false)} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} + onBlur={action(() => {this._editing = false; this.props.isEditingCallback && this.props.isEditingCallback(false);})} onPointerDown={this.stopPropagation} onClick={this.stopPropagation} onPointerUp={this.stopPropagation} style={{ display: this.props.display, fontSize: this.props.fontSize }} />; } else { return ( diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 51e9016b7..5b40032eb 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -125,7 +125,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { <div className="collectionSchemaView-cellContents" onPointerDown={onItemDown} key={props.Document[Id]} ref={reference}> <EditableView editing={this._isEditing} - // isEditingCallback={this.isEditingCallback} + isEditingCallback={this.isEditingCallback} display={"inline"} contents={contents} height={Number(MAX_ROW_HEIGHT)} diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index c81cf1aef..6002d64ce 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -21,6 +21,8 @@ export interface HeaderProps { setIsEditing: (isEditing: boolean) => void; deleteColumn: (column: string) => void; setColumnType: (key: string, type: ColumnType) => void; + setColumnSort: (key: string, desc: boolean) => void; + removeColumnSort: (key: string) => void; } export class CollectionSchemaHeader extends React.Component<HeaderProps> { @@ -43,6 +45,8 @@ export class CollectionSchemaHeader extends React.Component<HeaderProps> { deleteColumn={this.props.deleteColumn} onlyShowOptions={false} setColumnType={this.props.setColumnType} + setColumnSort={this.props.setColumnSort} + removeColumnSort={this.props.removeColumnSort} /> </div> ); @@ -59,18 +63,10 @@ export interface AddColumnHeaderProps { @observer export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> { - // @observable private _creatingColumn: boolean = false; - - // @action - // onClick = (e: React.MouseEvent): void => { - // this._creatingColumn = true; - // } - render() { let addButton = <button><FontAwesomeIcon icon="plus" size="sm" /></button>; return ( <div className="collectionSchemaView-header-addColumn" > - {/* {this._creatingColumn ? <></> : */} <CollectionSchemaColumnMenu keyValue="" possibleKeys={this.props.possibleKeys} @@ -84,6 +80,8 @@ export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHe deleteColumn={action(emptyFunction)} onlyShowOptions={true} setColumnType={action(emptyFunction)} + setColumnSort={action(emptyFunction)} + removeColumnSort={action(emptyFunction)} /> </div> ); @@ -105,12 +103,38 @@ export interface ColumnMenuProps { deleteColumn: (column: string) => void; onlyShowOptions: boolean; setColumnType: (key: string, type: ColumnType) => void; + setColumnSort: (key: string, desc: boolean) => void; + removeColumnSort: (key: string) => void; } @observer export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> { @observable private _isOpen: boolean = false; + // @observable private _node : HTMLDivElement | null = null; + @observable private _node = React.createRef<HTMLDivElement>(); - @action toggleIsOpen = (): void => { + componentDidMount() { + document.addEventListener("pointerdown", this.onPointerDown); + console.log("did mount", this._node); + } + + componentWillUnmount() { + document.removeEventListener("pointerdown", this.onPointerDown); + } + + onPointerDown (e: PointerEvent) { + console.log("pointer down", this._node); + if (this._node ) { + // && this._node.contains(e.target as Node) + console.log("CLICKED INSNIDE"); + } else { + console.log("CLICKED OUTSIDE"); + // console.log(this._node); + // console.log(e.target as Node); + } + } + + @action + toggleIsOpen = (): void => { this._isOpen = !this._isOpen; this.props.setIsEditing(this._isOpen); } @@ -121,6 +145,14 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> this.props.setColumnType(this.props.keyValue, type); } + // @action + // setNode = (node: HTMLDivElement): void => { + // if (node) { + // this._node = node; + // console.log("set node to ", this._node); + // } + // } + renderTypes = () => { if (this.props.typeConst) return <></>; return ( @@ -147,6 +179,19 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> ); } + renderSorting = () => { + return ( + <div className="collectionSchema-headerMenu-group"> + <label>Sort by:</label> + <div className="columnMenu-sort"> + <div className="columnMenu-option" onClick={() => this.props.setColumnSort(this.props.keyValue, false)}>Sort ascending</div> + <div className="columnMenu-option" onClick={() => this.props.setColumnSort(this.props.keyValue, true)}>Sort descending</div> + <div className="columnMenu-option" onClick={() => this.props.removeColumnSort(this.props.keyValue)}>Clear sorting</div> + </div> + </div> + ); + } + renderContent = () => { return ( <div className="collectionSchema-header-menuOptions"> @@ -159,11 +204,13 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> canAddNew={true} addNew={this.props.addNew} onSelect={this.props.onSelect} + setIsEditing={this.props.setIsEditing} /> </div> {this.props.onlyShowOptions ? <></> : <> {this.renderTypes()} + {this.renderSorting()} <div className="collectionSchema-headerMenu-group"> <button onClick={() => this.props.deleteColumn(this.props.keyValue)}>Delete Column</button> </div> @@ -174,24 +221,17 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> } render() { + console.log("render", this._node); return ( - <div className="collectionSchema-header-menu"> + <div className="collectionSchema-header-menu" ref={this._node}> <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderContent()}> - {/* <div onClick={this.toggleIsOpen}> */} - <div className="collectionSchema-header-toggler" onClick={() => { this.props.setIsEditing(true); }}>{this.props.menuButtonContent}</div> - {/* {this._isOpen ? this.renderContent() : <></>} */} - {/* </div> */} + <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div> </ Flyout > </div> ); } } -{/* // <div className="collectionSchema-header-menu"> - // <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div> - // {this.renderContent()} - // </div> */} - interface KeysDropdownProps { keyValue: string; @@ -200,6 +240,7 @@ interface KeysDropdownProps { canAddNew: boolean; addNew: boolean; onSelect: (oldKey: string, newKey: string, addnew: boolean) => void; + setIsEditing: (isEditing: boolean) => void; } @observer class KeysDropdown extends React.Component<KeysDropdownProps> { @@ -217,6 +258,7 @@ class KeysDropdown extends React.Component<KeysDropdownProps> { this.props.onSelect(this._key, key, this.props.addNew); this.setKey(key); this._isOpen = false; + this.props.setIsEditing(false); } onChange = (val: string): void => { @@ -226,15 +268,15 @@ class KeysDropdown extends React.Component<KeysDropdownProps> { @action onFocus = (e: React.FocusEvent): void => { this._isOpen = true; + this.props.setIsEditing(true); } @action onBlur = (e: React.FocusEvent): void => { - // const that = this; - if (this._canClose) this._isOpen = false; - // setTimeout(function() { // TODO: this might be too hacky lol - // that.setIsOpen(false); - // }, 100); + if (this._canClose) { + this._isOpen = false; + this.props.setIsEditing(false); + } } @action diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 15dec4e44..4ae9628a9 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -163,7 +163,6 @@ .collectionSchema-col{ height: 100%; - background-color: green; .collectionSchema-col-wrapper { &.col-before { diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b9b299e13..10b1e895d 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -39,8 +39,6 @@ library.add(faCog); library.add(faPlus); // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 -// const MovableTable = CollectionSchemaMovableSchemaHOC(ReactTable); - export enum ColumnType { Any, Number, @@ -62,7 +60,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { private _startPreviewWidth = 0; private DIVIDER_WIDTH = 4; - // @observable _columns: Array<string> = ["title", "data", "author"]; @observable _selectedIndex = 0; @observable _columnsPercentage = 0; @observable _keys: string[] = []; @@ -71,6 +68,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @observable _headerIsEditing: boolean = false; @observable _cellIsEditing: boolean = false; @observable _focusedCell: {row: number, col: number} = {row: 0, col: 0}; + @observable _sortedColumns: Map<string, {id: string, desc: boolean}> = new Map(); @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } @@ -80,7 +78,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } @computed get tableColumns(): Column<Doc>[] { let possibleKeys = this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.toUpperCase() === key.toUpperCase()) === -1); - let cols = this.columns.map(col => { let focusedRow = this._focusedCell.row; let focusedCol = this._focusedCell.col; @@ -95,6 +92,8 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { setIsEditing={this.setHeaderIsEditing} deleteColumn={this.deleteColumn} setColumnType={this.setColumnType} + setColumnSort={this.setColumnSort} + removeColumnSort={this.removeColumnSort} />; return { @@ -148,37 +147,19 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return cols; } - @action - setColumns = (columns: string[]) => { - this.columns = columns; - } - - reorderColumns = (toMove: string, relativeTo: string, before: boolean, columnsValues: string[]) => { - let columns = [...columnsValues]; - let oldIndex = columns.indexOf(toMove); - let relIndex = columns.indexOf(relativeTo); - let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex; - - if (oldIndex === newIndex) return; - - columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); - this.setColumns(columns); - } - - - onHeaderDrag = (columnName: string) => { - let schemaDoc = Cast(this.props.Document.schemaDoc, Doc); - if (schemaDoc instanceof Doc) { - let columnDocs = DocListCast(schemaDoc.data); - if (columnDocs) { - let ddoc = columnDocs.find(doc => doc.title === columnName); - if (ddoc) { - return ddoc; - } - } - } - return this.props.Document; - } + // onHeaderDrag = (columnName: string) => { + // let schemaDoc = Cast(this.props.Document.schemaDoc, Doc); + // if (schemaDoc instanceof Doc) { + // let columnDocs = DocListCast(schemaDoc.data); + // if (columnDocs) { + // let ddoc = columnDocs.find(doc => doc.title === columnName); + // if (ddoc) { + // return ddoc; + // } + // } + // } + // return this.props.Document; + // } componentDidMount() { document.addEventListener("keydown", this.onKeyDown); @@ -213,37 +194,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this._mainCont = ele; super.CreateDropTarget(ele); } - @action - changeFocusedCellByDirection = (direction: string): void => { - switch (direction) { - case "tab": - if (this._focusedCell.col + 1 === this.columns.length && this._focusedCell.row + 1 === this.childDocs.length) { - this._focusedCell = { row: 0, col: 0 }; - } else if (this._focusedCell.col + 1 === this.columns.length) { - this._focusedCell = { row: this._focusedCell.row + 1, col: 0 }; - } else { - this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 }; - } - break; - case "right": - this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 === this.columns.length ? this._focusedCell.col : this._focusedCell.col + 1 }; - break; - case "left": - this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col === 0 ? this._focusedCell.col : this._focusedCell.col - 1 }; - break; - case "up": - this._focusedCell = { row: this._focusedCell.row === 0 ? this._focusedCell.row : this._focusedCell.row - 1, col: this._focusedCell.col }; - break; - case "down": - this._focusedCell = { row: this._focusedCell.row + 1 === this.childDocs.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col }; - break; - } - } - - @action - changeFocusedCellByIndex = (row: number, col: number): void => { - this._focusedCell = { row: row, col: col }; - } @action setCellIsEditing = (isEditing: boolean): void => { @@ -255,21 +205,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this._headerIsEditing = isEditing; } - // @action - // toggleKey = (key: string) => { - // let list = Cast(this.props.Document.schemaColumns, listSpec("string")); - // if (list === undefined) { - // this.props.Document.schemaColumns = list = new List<string>([key]); - // } else { - // const index = list.indexOf(key); - // if (index === -1) { - // list.push(key); - // } else { - // list.splice(index, 1); - // } - // } - // } - //toggles preview side-panel of schema @action toggleExpander = () => { @@ -324,6 +259,38 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } @action + changeFocusedCellByDirection = (direction: string): void => { + switch (direction) { + case "tab": + if (this._focusedCell.col + 1 === this.columns.length && this._focusedCell.row + 1 === this.childDocs.length) { + this._focusedCell = { row: 0, col: 0 }; + } else if (this._focusedCell.col + 1 === this.columns.length) { + this._focusedCell = { row: this._focusedCell.row + 1, col: 0 }; + } else { + this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 }; + } + break; + case "right": + this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col + 1 === this.columns.length ? this._focusedCell.col : this._focusedCell.col + 1 }; + break; + case "left": + this._focusedCell = { row: this._focusedCell.row, col: this._focusedCell.col === 0 ? this._focusedCell.col : this._focusedCell.col - 1 }; + break; + case "up": + this._focusedCell = { row: this._focusedCell.row === 0 ? this._focusedCell.row : this._focusedCell.row - 1, col: this._focusedCell.col }; + break; + case "down": + this._focusedCell = { row: this._focusedCell.row + 1 === this.childDocs.length ? this._focusedCell.row : this._focusedCell.row + 1, col: this._focusedCell.col }; + break; + } + } + + @action + changeFocusedCellByIndex = (row: number, col: number): void => { + this._focusedCell = { row: row, col: col }; + } + + @action makeDB = async () => { let csv: string = this.columns.reduce((val, col) => val + col + ",", ""); csv = csv.substr(0, csv.length - 1) + "\n"; @@ -400,10 +367,32 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { } } - // @action - // newKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => { - // this._newKeyName = e.currentTarget.value; - // } + @action + setColumns = (columns: string[]) => { + this.columns = columns; + } + + reorderColumns = (toMove: string, relativeTo: string, before: boolean, columnsValues: string[]) => { + let columns = [...columnsValues]; + let oldIndex = columns.indexOf(toMove); + let relIndex = columns.indexOf(relativeTo); + let newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex; + + if (oldIndex === newIndex) return; + + columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); + this.setColumns(columns); + } + + @action + setColumnSort = (column: string, descending: boolean) => { + this._sortedColumns.set(column, {id: column, desc: descending}); + } + + @action + removeColumnSort = (column: string) => { + this._sortedColumns.delete(column); + } @computed get previewDocument(): Doc | undefined { @@ -430,44 +419,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return Array.from(Object.keys(keys)); } - // get documentKeysCheckList() { - // const docs = DocListCast(this.props.Document[this.props.fieldKey]); - // let keys: { [key: string]: boolean } = {}; - // // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // // is displayed (unlikely) it won't show up until something else changes. - // //TODO Types - // untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); - - // this.columns.forEach(key => keys[key] = true); - // return Array.from(Object.keys(keys)).map(item => - // (<KeyToggle checked={keys[item]} key={item} keyName={item} toggle={this.toggleKey} />)); - // } - - // get tableOptionsPanel() { - // return !this.props.active() ? (null) : - // (<Flyout - // anchorPoint={anchorPoints.RIGHT_TOP} - // content={<div> - // <div id="schema-options-header"><h5><b>Options</b></h5></div> - // <div id="options-flyout-div"> - // <h6 className="schema-options-subHeader">Preview Window</h6> - // <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> Show Preview </div> - // <h6 className="schema-options-subHeader" >Displayed Columns</h6> - // <ul id="schema-col-checklist" > - // {this.documentKeysCheckList} - // </ul> - // <input value={this._newKeyName} onChange={this.newKeyChange} /> - // <button onClick={this.addColumn}><FontAwesomeIcon style={{ color: "white" }} icon="plus" size="lg" /></button> - // </div> - // </div> - // }> - // <button id="schemaOptionsMenuBtn" ><FontAwesomeIcon style={{ color: "white" }} icon="cog" size="sm" /></button> - // </Flyout>); - // } - @computed get reactTable() { let previewWidth = this.previewWidth() + 2 * this.borderWidth + this.DIVIDER_WIDTH + 1; @@ -481,6 +432,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { getTrProps={this.getTrProps} sortable={false} TrComponent={MovableRow} + sorted={Array.from(this._sortedColumns.values())} />; } @@ -614,7 +566,13 @@ export class CollectionSchemaPreview extends React.Component<CollectionSchemaPre } return undefined; } + + render() { + + // <div id="preview-schema-checkbox-div"><input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> Show Preview </div> + + let input = this.props.previewScript === undefined ? (null) : <div ref={this.createTarget}><input className="collectionSchemaView-input" value={this.props.previewScript} onChange={this.onPreviewScriptChange} style={{ left: `calc(50% - ${Math.min(75, (this.props.Document ? this.PanelWidth() / 2 : 75))}px)` }} /></div>; |