import React = require("react"); import { action, observable } from "mobx"; import { observer } from "mobx-react"; import "./CollectionSchemaView.scss"; import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons'; import { library, IconProp } from "@fortawesome/fontawesome-svg-core"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Flyout, anchorPoints } from "../DocumentDecorations"; import { ColumnType } from "./CollectionSchemaView"; import { faFile } from "@fortawesome/free-regular-svg-icons"; import { SchemaHeaderField, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes); export interface HeaderProps { keyValue: SchemaHeaderField; possibleKeys: string[]; existingKeys: string[]; keyType: ColumnType; typeConst: boolean; onSelect: (oldKey: string, newKey: string, addnew: boolean) => void; setIsEditing: (isEditing: boolean) => void; deleteColumn: (column: string) => void; setColumnType: (column: SchemaHeaderField, type: ColumnType) => void; setColumnSort: (column: SchemaHeaderField, desc: boolean | undefined) => void; setColumnColor: (column: SchemaHeaderField, color: string) => void; } export class CollectionSchemaHeader extends React.Component { render() { const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" : this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify"; return (
{this.props.keyValue.heading}
} addNew={false} onSelect={this.props.onSelect} setIsEditing={this.props.setIsEditing} deleteColumn={this.props.deleteColumn} onlyShowOptions={false} setColumnType={this.props.setColumnType} setColumnSort={this.props.setColumnSort} setColumnColor={this.props.setColumnColor} /> ); } } export interface AddColumnHeaderProps { createColumn: () => void; } @observer export class CollectionSchemaAddColumnHeader extends React.Component { render() { return ( ); } } 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 { @observable private _isOpen: boolean = false; @observable private _node: HTMLDivElement | null = null; componentDidMount() { document.addEventListener("pointerdown", this.detectClick); } componentWillUnmount() { document.removeEventListener("pointerdown", this.detectClick); } detectClick = (e: PointerEvent): void => { if (this._node && this._node.contains(e.target as Node)) { } else { this._isOpen = false; this.props.setIsEditing(false); } } @action toggleIsOpen = (): void => { this._isOpen = !this._isOpen; this.props.setIsEditing(this._isOpen); } changeColumnType = (type: ColumnType): void => { this.props.setColumnType(this.props.columnField, type); } changeColumnSort = (desc: boolean | undefined): void => { this.props.setColumnSort(this.props.columnField, desc); } changeColumnColor = (color: string): void => { this.props.setColumnColor(this.props.columnField, color); } @action setNode = (node: HTMLDivElement): void => { if (node) { this._node = node; } } renderTypes = () => { if (this.props.typeConst) return <>; const type = this.props.columnField.type; return (
this.changeColumnType(ColumnType.Any)}> Any
this.changeColumnType(ColumnType.Number)}> Number
this.changeColumnType(ColumnType.String)}> Text
this.changeColumnType(ColumnType.Boolean)}> Checkbox
this.changeColumnType(ColumnType.Doc)}> Document
); } renderSorting = () => { const sort = this.props.columnField.desc; return (
this.changeColumnSort(true)}> Sort descending
this.changeColumnSort(false)}> Sort ascending
this.changeColumnSort(undefined)}> Clear sorting
); } 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 (
this.changeColumnColor(pink!)}>
this.changeColumnColor(purple!)}>
this.changeColumnColor(blue!)}>
this.changeColumnColor(yellow!)}>
this.changeColumnColor(red!)}>
this.changeColumnColor(gray)}>
); } renderContent = () => { return (
{this.props.onlyShowOptions ? <> : <> {this.renderTypes()} {this.renderSorting()} {this.renderColors()}
}
); } render() { return (
this.toggleIsOpen()}>{this.props.menuButtonContent}
); } } interface KeysDropdownProps { keyValue: string; possibleKeys: string[]; existingKeys: string[]; canAddNew: boolean; addNew: boolean; onSelect: (oldKey: string, newKey: string, addnew: boolean) => void; setIsEditing: (isEditing: boolean) => void; } @observer class KeysDropdown extends React.Component { @observable private _key: string = this.props.keyValue; @observable private _searchTerm: string = this.props.keyValue; @observable private _isOpen: boolean = false; @observable private _canClose: boolean = true; @observable private _inputRef: React.RefObject = 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); } @undoBatch onKeyDown = (e: React.KeyboardEvent): void => { if (e.key === "Enter") { const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { this.onSelect(this._searchTerm); } else { this.setSearchTerm(this._key); } } } onChange = (val: string): void => { this.setSearchTerm(val); } @action onFocus = (e: React.FocusEvent): void => { this._isOpen = true; this.props.setIsEditing(true); } @action onBlur = (e: React.FocusEvent): void => { if (this._canClose) { this._isOpen = false; this.props.setIsEditing(false); } } @action onPointerEnter = (e: React.PointerEvent): void => { this._canClose = false; } @action onPointerOut = (e: React.PointerEvent): void => { this._canClose = true; } renderOptions = (): JSX.Element[] | JSX.Element => { if (!this._isOpen) return <>; const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1); const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 || this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1; const options = keyOptions.map(key => { return
{ this.onSelect(key); this.setSearchTerm(""); }}>{key}
; }); // if search term does not already exist as a group type, give option to create new group type if (!exactFound && this._searchTerm !== "" && this.props.canAddNew) { options.push(
{ this.onSelect(this._searchTerm); this.setSearchTerm(""); }}> Create "{this._searchTerm}" key
); } return options; } render() { return (
this.onChange(e.target.value)} onFocus={this.onFocus} onBlur={this.onBlur}>
{this.renderOptions()}
); } }