diff options
Diffstat (limited to 'src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx')
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx | 513 |
1 files changed, 0 insertions, 513 deletions
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx deleted file mode 100644 index 9653f2808..000000000 --- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx +++ /dev/null @@ -1,513 +0,0 @@ -import React = require("react"); -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, runInAction, trace } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, DocListCast, Opt, StrListCast } from "../../../../fields/Doc"; -import { listSpec } from "../../../../fields/Schema"; -import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; -import { ScriptField } from "../../../../fields/ScriptField"; -import { Cast, StrCast } from "../../../../fields/Types"; -import { undoBatch } from "../../../util/UndoManager"; -import { CollectionView } from "../CollectionView"; -import { ColumnType } from "./CollectionSchemaView"; -import "./CollectionSchemaView.scss"; - -const higflyout = require("@hig/flyout"); -export const { anchorPoints } = higflyout; -export const Flyout = higflyout.default; - - -export interface AddColumnHeaderProps { - createColumn: () => void; -} - -@observer -export class CollectionSchemaAddColumnHeader extends React.Component<AddColumnHeaderProps> { - // the button that allows the user to add a column - render() { - return <button className="add-column" onClick={() => this.props.createColumn()}> - <FontAwesomeIcon icon="plus" size="sm" /> - </button>; - } -} - -export interface ColumnMenuProps { - columnField: SchemaHeaderField; - // keyValue: string; - possibleKeys: string[]; - existingKeys: string[]; - // keyType: ColumnType; - typeConst: boolean; - menuButtonContent: JSX.Element; - addNew: boolean; - onSelect: (oldKey: string, newKey: string, addnew: boolean) => void; - setIsEditing: (isEditing: boolean) => void; - deleteColumn: (column: string) => void; - onlyShowOptions: boolean; - setColumnType: (column: SchemaHeaderField, type: ColumnType) => void; - setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void; - anchorPoint?: any; - setColumnColor: (column: SchemaHeaderField, color: string) => void; -} -@observer -export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps> { - @observable private _isOpen: boolean = false; - @observable private _node: HTMLDivElement | null = null; - - componentDidMount() { document.addEventListener("pointerdown", this.detectClick); } - - componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); } - - @action - detectClick = (e: PointerEvent) => { - !this._node?.contains(e.target as Node) && this.props.setIsEditing(this._isOpen = false); - } - - @action - toggleIsOpen = (): void => { - this.props.setIsEditing(this._isOpen = !this._isOpen); - } - - changeColumnType = (type: ColumnType) => { - this.props.setColumnType(this.props.columnField, type); - } - - changeColumnSort = (desc: boolean | undefined) => { - this.props.setColumnSort(this.props.columnField, desc); - } - - changeColumnColor = (color: string) => { - this.props.setColumnColor(this.props.columnField, color); - } - - @action - setNode = (node: HTMLDivElement): void => { - if (node) { - this._node = node; - } - } - - renderTypes = () => { - if (this.props.typeConst) return (null); - - const type = this.props.columnField.type; - return ( - <div className="collectionSchema-headerMenu-group"> - <label>Column type:</label> - <div className="columnMenu-types"> - <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Any)}> - <FontAwesomeIcon icon={"align-justify"} size="sm" /> - Any - </div> - <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Number)}> - <FontAwesomeIcon icon={"hashtag"} size="sm" /> - Number - </div> - <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.String)}> - <FontAwesomeIcon icon={"font"} size="sm" /> - Text - </div> - <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Boolean)}> - <FontAwesomeIcon icon={"check-square"} size="sm" /> - Checkbox - </div> - <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}> - <FontAwesomeIcon icon={"list-ul"} size="sm" /> - List - </div> - <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}> - <FontAwesomeIcon icon={"file"} size="sm" /> - Document - </div> - <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}> - <FontAwesomeIcon icon={"image"} size="sm" /> - Image - </div> - <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Date)}> - <FontAwesomeIcon icon={"calendar"} size="sm" /> - Date - </div> - </div> - </div > - ); - } - - renderSorting = () => { - const sort = this.props.columnField.desc; - return ( - <div className="collectionSchema-headerMenu-group"> - <label>Sort by:</label> - <div className="columnMenu-sort"> - <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.changeColumnSort(true)}> - <FontAwesomeIcon icon="sort-amount-down" size="sm" /> - Sort descending - </div> - <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.changeColumnSort(false)}> - <FontAwesomeIcon icon="sort-amount-up" size="sm" /> - Sort ascending - </div> - <div className="columnMenu-option" onClick={() => this.changeColumnSort(undefined)}> - <FontAwesomeIcon icon="times" size="sm" /> - Clear sorting - </div> - </div> - </div> - ); - } - - renderColors = () => { - const selected = this.props.columnField.color; - - const pink = PastelSchemaPalette.get("pink2"); - const purple = PastelSchemaPalette.get("purple2"); - const blue = PastelSchemaPalette.get("bluegreen1"); - const yellow = PastelSchemaPalette.get("yellow4"); - const red = PastelSchemaPalette.get("red2"); - const gray = "#f1efeb"; - - return ( - <div className="collectionSchema-headerMenu-group"> - <label>Color:</label> - <div className="columnMenu-colors"> - <div className={"columnMenu-colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.changeColumnColor(pink!)}></div> - <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.changeColumnColor(purple!)}></div> - <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.changeColumnColor(blue!)}></div> - <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.changeColumnColor(yellow!)}></div> - <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.changeColumnColor(red!)}></div> - <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.changeColumnColor(gray)}></div> - </div> - </div> - ); - } - - renderContent = () => { - return ( - <div className="collectionSchema-header-menuOptions"> - {this.props.onlyShowOptions ? <></> : - <> - {this.renderTypes()} - {this.renderSorting()} - {this.renderColors()} - <div className="collectionSchema-headerMenu-group"> - <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Hide Column</button> - </div> - </> - } - </div> - ); - } - - render() { - return ( - <div className="collectionSchema-header-menu" ref={this.setNode}> - <Flyout anchorPoint={this.props.anchorPoint ? this.props.anchorPoint : anchorPoints.TOP_CENTER} content={this.renderContent()}> - <div className="collectionSchema-header-toggler" onClick={() => this.toggleIsOpen()}>{this.props.menuButtonContent}</div> - </ Flyout > - </div> - ); - } -} - - -export interface KeysDropdownProps { - keyValue: string; - possibleKeys: string[]; - existingKeys: string[]; - canAddNew: boolean; - addNew: boolean; - onSelect: (oldKey: string, newKey: string, addnew: boolean, filter?: string) => void; - setIsEditing: (isEditing: boolean) => void; - width?: string; - docs?: Doc[]; - Document: Doc; - dataDoc: Doc | undefined; - fieldKey: string; - ContainingCollectionDoc: Doc | undefined; - ContainingCollectionView: Opt<CollectionView>; - active?: (outsideReaction?: boolean) => boolean | undefined; - openHeader: (column: any, screenx: number, screeny: number) => void; - col: SchemaHeaderField; - icon: IconProp; -} -@observer -export class KeysDropdown extends React.Component<KeysDropdownProps> { - @observable private _key: string = this.props.keyValue; - @observable private _searchTerm: string = this.props.keyValue + ":"; - @observable private _isOpen: boolean = false; - @observable private _node: HTMLDivElement | null = null; - @observable private _inputRef: React.RefObject<HTMLInputElement> = React.createRef(); - - @action setSearchTerm = (value: string): void => { this._searchTerm = value; }; - @action setKey = (key: string): void => { this._key = key; }; - @action setIsOpen = (isOpen: boolean): void => { this._isOpen = isOpen; }; - - @action - onSelect = (key: string): void => { - this.props.onSelect(this._key, key, this.props.addNew); - this.setKey(key); - this._isOpen = false; - this.props.setIsEditing(false); - } - - @action - setNode = (node: HTMLDivElement): void => { - if (node) { - this._node = node; - } - } - - componentDidMount() { - document.addEventListener("pointerdown", this.detectClick); - const filters = Cast(this.props.Document._docFilters, listSpec("string")); - if (filters?.some(filter => filter.split(":")[0] === this._key)) { - runInAction(() => this.closeResultsVisibility = "contents"); - } - } - - @action - detectClick = (e: PointerEvent): void => { - if (this._node && this._node.contains(e.target as Node)) { - } else { - this._isOpen = false; - this.props.setIsEditing(false); - } - } - - private tempfilter: string = ""; - @undoBatch - onKeyDown = (e: React.KeyboardEvent): void => { - if (e.key === "Enter") { - e.stopPropagation(); - if (this._searchTerm.includes(":")) { - const colpos = this._searchTerm.indexOf(":"); - const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); - if (temp === "") { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); - this.updateFilter(); - } - else { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); - this.tempfilter = temp; - Doc.setDocFilter(this.props.Document, this._key, temp, "check"); - this.props.col.setColor("green"); - this.closeResultsVisibility = "contents"; - } - } - else { - Doc.setDocFilter(this.props.Document, this._key, this.tempfilter, "remove"); - this.updateFilter(); - if (this.showKeys.length) { - this.onSelect(this.showKeys[0]); - } else if (this._searchTerm !== "" && this.props.canAddNew) { - this.setSearchTerm(this._searchTerm || this._key); - this.onSelect(this._searchTerm); - } - } - } - } - - onChange = (val: string): void => { - this.setSearchTerm(val); - } - - @action - onFocus = (e: React.FocusEvent): void => { - this._isOpen = true; - this.props.setIsEditing(true); - } - - @computed get showKeys() { - const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"]; - const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); - const showKeys = new Set<string>(); - [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode || - whitelistKeys.includes(key) - || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null); - return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm)); - } - - @computed get renderOptions() { - if (!this._isOpen) { - this.defaultMenuHeight = 0; - return (null); - } - const options = this.showKeys.map(key => { - return <div key={key} className="key-option" style={{ - border: "1px solid lightgray", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", - }} - onPointerDown={e => { - e.stopPropagation(); - }} - onClick={() => { - this.onSelect(key); - this.setSearchTerm(""); - }}>{key}</div>; - }); - - // if search term does not already exist as a group type, give option to create new group type - - if (this._key !== this._searchTerm.slice(0, this._key.length)) { - if (this._searchTerm !== "" && this.props.canAddNew) { - options.push(<div key={""} className="key-option" style={{ - border: "1px solid lightgray", width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", - }} - onClick={() => { this.onSelect(this._searchTerm); this.setSearchTerm(""); }}> - Create "{this._searchTerm}" key</div>); - } - } - - if (options.length === 0) { - this.defaultMenuHeight = 0; - } - else { - if (this.props.docs) { - const panesize = this.props.docs.length * 30; - options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; - } - else { - options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; - } - } - return options; - } - - @computed get docSafe() { return DocListCast(this.props.dataDoc?.[this.props.fieldKey]); } - - @computed get renderFilterOptions() { - if (!this._isOpen || !this.props.dataDoc) { - this.defaultMenuHeight = 0; - return (null); - } - const keyOptions: string[] = []; - const colpos = this._searchTerm.indexOf(":"); - const temp = this._searchTerm.slice(colpos + 1, this._searchTerm.length); - this.docSafe.forEach(doc => { - const key = StrCast(doc[this._key]); - if (keyOptions.includes(key) === false && key.includes(temp) && key !== "") { - keyOptions.push(key); - } - }); - - const filters = StrListCast(this.props.Document._docFilters); - if (filters.some(filter => filter.split(":")[0] === this._key) === false) { - this.props.col.setColor("rgb(241, 239, 235)"); - this.closeResultsVisibility = "none"; - } - for (let i = 0; i < (filters?.length ?? 0) - 1; i++) { - if (filters[i] === this.props.col.heading && keyOptions.includes(filters[i].split(":")[1]) === false) { - keyOptions.push(filters[i + 1]); - } - } - const options = keyOptions.map(key => { - let bool = false; - if (filters !== undefined) { - const ind = filters.findIndex(filter => filter.split(":")[1] === key); - const fields = ind === -1 ? undefined : filters[ind].split(":"); - bool = fields ? fields[2] === "check" : false; - } - return <div key={key} className="key-option" style={{ - paddingLeft: 5, textAlign: "left", - width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white", - }} - > - <input type="checkbox" - onPointerDown={e => e.stopPropagation()} - onClick={e => e.stopPropagation()} - onChange={action(e => { - if (e.target.checked) { - Doc.setDocFilter(this.props.Document, this._key, key, "check"); - this.closeResultsVisibility = "contents"; - this.props.col.setColor("green"); - } else { - Doc.setDocFilter(this.props.Document, this._key, key, "remove"); - this.updateFilter(); - } - })} - checked={bool} - /> - <span style={{ paddingLeft: 4 }}> - {key} - </span> - - </div>; - }); - if (options.length === 0) { - this.defaultMenuHeight = 0; - } - else { - if (this.props.docs) { - const panesize = this.props.docs.length * 30; - options.length * 20 + 8 - 10 > panesize ? this.defaultMenuHeight = panesize : this.defaultMenuHeight = options.length * 20 + 8; - } - else { - options.length > 5 ? this.defaultMenuHeight = 108 : this.defaultMenuHeight = options.length * 20 + 8; - } - - } - return options; - } - - @observable defaultMenuHeight = 0; - - - updateFilter() { - const filters = Cast(this.props.Document._docFilters, listSpec("string")); - if (filters === undefined || filters.length === 0 || filters.some(filter => filter.split(":")[0] === this._key) === false) { - this.props.col.setColor("rgb(241, 239, 235)"); - this.closeResultsVisibility = "none"; - } - } - - @computed get scriptField() { - const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)"; - const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); - return script ? () => script : undefined; - } - filterBackground = () => "rgba(105, 105, 105, 0.432)"; - @observable filterOpen: boolean | undefined = undefined; - closeResultsVisibility: string = "none"; - - removeFilters = (e: React.PointerEvent): void => { - const keyOptions: string[] = []; - this.docSafe.forEach(doc => { - const key = StrCast(doc[this._key]); - if (keyOptions.includes(key) === false) { - keyOptions.push(key); - } - }); - - Doc.setDocFilter(this.props.Document, this._key, "", "remove"); - this.props.col.setColor("rgb(241, 239, 235)"); - this.closeResultsVisibility = "none"; - } - render() { - return ( - <div style={{ display: "flex", width: '100%', alignContent: 'center', alignItems: 'center' }} ref={this.setNode}> - <div className="schema-icon" onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }}> - <FontAwesomeIcon icon={this.props.icon} size="lg" style={{ display: "inline" }} /> - </div> - - <div className="keys-dropdown" style={{ zIndex: 1, width: this.props.width, maxWidth: this.props.width }}> - <input className="keys-search" style={{ width: "100%" }} - ref={this._inputRef} type="text" - value={this._searchTerm} placeholder="Column key" - onKeyDown={this.onKeyDown} - onChange={e => this.onChange(e.target.value)} - onClick={(e) => { e.stopPropagation(); this._inputRef.current?.focus(); }} - onFocus={this.onFocus} ></input> - <div style={{ display: this.closeResultsVisibility }}> - <FontAwesomeIcon onPointerDown={this.removeFilters} icon={"times-circle"} size="lg" - style={{ cursor: "hand", color: "grey", padding: 2, left: -20, top: -1, height: 15, position: "relative" }} /> - </div> - {!this._isOpen ? (null) : <div className="keys-options-wrapper" style={{ - width: this.props.width, maxWidth: this.props.width, height: "auto", - }}> - {this._searchTerm.includes(":") ? this.renderFilterOptions : this.renderOptions} - </div>} - </div > - </div> - ); - } -} |