import React, { useContext, useState, Component, Fragment } from 'react'; import styles from './styles.module.scss'; import classnames from 'classnames'; import { Icons, InputBox, AppBar, RadioButton, AppBarContext, } from '..'; import Checkbox from '../checkbox'; import colorVariables from '../../variables/colors.json'; import { spaceHierarchySearcher } from '@densityco/lib-space-helpers'; import { DisplaySpaceHierarchyNode } from '@densityco/lib-space-helpers/dist/types'; export enum SpacePickerSelectControlTypes { RADIOBUTTON = 'RADIOBUTTON', CHECKBOX = 'CHECKBOX', NONE = 'NONE', }; type SpacePickerProps = { value: Array | Array | DisplaySpaceHierarchyNode | string | null, onChange: (SpaceHierarchyDisplayItem) => any, formattedHierarchy: Array, showSearchBox?: boolean, searchBoxPlaceholder?: string, placeholder?: string, height?: React.ReactText, canSelectMultiple?: boolean, autoSelectChildren?: boolean, selectControl?: SpacePickerSelectControlTypes, isItemDisabled?: (SpaceHierarchyDisplayItem) => boolean, onCloseDropdown?: () => void, disabled?: boolean, }; function convertValueToSpaceIds( value: Array | Array | DisplaySpaceHierarchyNode | string | null, canSelectMultiple: boolean, ): Array { // Extract the id out of value if (canSelectMultiple) { if (!Array.isArray(value)) { throw new Error('Space Picker value prop is not an array, but `canSelectMultiple` is true! This is not a valid state.'); } if (value.length === 0) { return []; } else if (typeof value[0] === 'string') { // An array of ids: ['spc_xxx', 'spx_abc'] return value as Array; } else { // An array of formatted hierarchy items: // - [{space: {id: 'spc_xxx', ...}, ...}] return (value as Array).map(v => v.space.id); } } else { if (Array.isArray(value)) { throw new Error('Space Picker value prop is an array, but `canSelectMultiple` is false! This is not a valid state.'); } if (!value) { // No value is selected return []; } else if (typeof value === 'string') { // A single id: return [value]; } else { // An single formatted hierarchy item: // - {space: {id: 'spc_xxx', ...}, ...} return [value.space.id]; } } } export const SpacePickerContext = React.createContext<"CARD_PICKER" | null>(null); export const SpacePicker: React.FunctionComponent = ({ value, onChange, formattedHierarchy, canSelectMultiple=false, autoSelectChildren=false, isItemDisabled=(s) => false, height, showSearchBox=true, searchBoxPlaceholder=`ex: "New York"`, onCloseDropdown=() => {}, selectControl=undefined, disabled=false, }) => { const cardPickerContext = useContext(SpacePickerContext) === 'CARD_PICKER'; const [searchText, setSearchText] = useState(''); const selectedSpaceIds = convertValueToSpaceIds(value, canSelectMultiple); // The select control is the control to the left of each space that can be clicked to select it. const defaultSelectControl = canSelectMultiple ? SpacePickerSelectControlTypes.CHECKBOX : SpacePickerSelectControlTypes.RADIOBUTTON; selectControl = selectControl || defaultSelectControl; let filteredFormattedHierarchy = formattedHierarchy; if (searchText.length > 0) { filteredFormattedHierarchy = spaceHierarchySearcher(formattedHierarchy, searchText); } // This function normalizes the difference between when `canSelectMultiple` is set or unset. function callOnChange(item, isChecked) { if (canSelectMultiple) { if (isChecked) { onChange(formattedHierarchy.filter(h => { const isSpaceSelected = h.space.id === item.space.id; const isAutoSelectedChild = autoSelectChildren ? h.ancestry.map(a => a.id).includes(item.space.id) : false; const isAlreadySelected = selectedSpaceIds.includes(h.space.id); return isSpaceSelected || isAutoSelectedChild || isAlreadySelected; })); } else { onChange(formattedHierarchy.filter(h => { const isSpaceSelected = h.space.id === item.space.id; const isAutoSelectedChild = autoSelectChildren ? h.ancestry.map(a => a.id).includes(item.space.id) : false; const isAlreadySelected = selectedSpaceIds.includes(h.space.id); return !isSpaceSelected && !isAutoSelectedChild && isAlreadySelected; })); } } else { onChange(item); } } return ( {showSearchBox ?
} placeholder={searchBoxPlaceholder} width="100%" value={searchText} onChange={e => setSearchText(e.target.value)} disabled={disabled} />
: null}
{filteredFormattedHierarchy.map(item => { const spaceDisabled = disabled || !item.space.has_purview || isItemDisabled(item); const isChecked = Boolean(selectedSpaceIds.find(id => id === item.space.id)); return (
{ if (!spaceDisabled) { callOnChange(item, !isChecked); } // If only one item can be selected, close the dropdown if this control is in a dropdown. if (!canSelectMultiple) { onCloseDropdown(); } }} > {selectControl === SpacePickerSelectControlTypes.RADIOBUTTON ? ( {}} /> ) : null} {selectControl === SpacePickerSelectControlTypes.CHECKBOX ? ( {}} /> ) : null} {item.space.space_type === 'building' ? ( ) : null} {item.space.space_type === 'floor' ? ( ) : null} {item.space.space_type === 'space' && selectControl === SpacePickerSelectControlTypes.NONE ? ( ) : null} {item.space.name}
); })}
) } export default SpacePicker; type SpacePickerDropdownProps = SpacePickerProps & { width?: React.ReactText, dropdownWidth?: React.ReactText; }; export class SpacePickerDropdown extends Component { state = { opened: false, }; render() { let { value, placeholder, width, height, dropdownWidth, searchBoxPlaceholder, formattedHierarchy, canSelectMultiple, autoSelectChildren, isItemDisabled, onChange, } = this.props; isItemDisabled = typeof isItemDisabled === 'undefined' ? s => false : isItemDisabled; canSelectMultiple = typeof canSelectMultiple === 'undefined' ? false : canSelectMultiple; height = typeof height === 'undefined' ? 256 : height; const { opened } = this.state; // Calculate a list of space names for all spaces that this space picker has selected const selectedSpaceNames = convertValueToSpaceIds(value, canSelectMultiple) .map(id => formattedHierarchy.find(h => h.space.id === id)) .filter(hierarchyItem => typeof hierarchyItem !== 'undefined') .map((hierarchyItem: any) => hierarchyItem.space.name); return (
this.setState({opened: false})} />
{ e.preventDefault(); this.setState({opened: true}); }} onMouseDown={() => { this.setState({opened: !opened}); }} > {selectedSpaceNames.length > 0 ? ( {selectedSpaceNames.length > 1 ? `${selectedSpaceNames.length} spaces selected` : selectedSpaceNames[0]} ) : ( {placeholder || `No ${canSelectMultiple ? 'spaces' : 'space'} selected`} )}
this.setState({opened: false})} />
); } }