@momentum-ui/react-collaboration
Version:
Cisco Momentum UI Framework for React Collaboration Applications
218 lines (191 loc) • 5.85 kB
JavaScript
/** @component select */
import filter from 'lodash/filter';
import find from 'lodash/find';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';
import uniqueId from 'lodash/uniqueId';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import { Button, Icon, List } from '@momentum-ui/react-collaboration';
import EventOverlay from '../EventOverlay';
import SelectContext from '../SelectContext';
/**
* @deprecated - Components in the legacy folder (/src/legacy) are deprecated. Please use a component from the components folder (/src/components) instead. Legacy components may not follow accessibility standards.
**/
class Select extends React.Component {
state = {
isOpen: false,
selected: [],
selectedIndex: [],
anchorNode: null,
anchorWidth: null,
id: this.props.id || uniqueId('md-select-'),
};
componentDidUpdate(prevProps, prevState) {
prevState.selected !== this.state.selected &&
this.props.onSelect &&
this.props.onSelect(this.state.selected);
}
hidePopover = () => {
this.setState({
isOpen: false,
});
};
handleSelect = (e, opts) => {
e.preventDefault();
const { selected, selectedIndex } = this.state;
const { isMulti } = this.props;
const { value, label, eventKey, keyboardKey } = opts;
const isActive = find(selected, { value, label });
!isMulti && this.setState({ isOpen: false });
if (isActive && !isMulti) return;
if (isActive && isMulti) {
return this.setState({
selected: filter(selected, (item) => !isEqual(item, { value, label })),
selectedIndex: selectedIndex.filter((i) => i !== keyboardKey),
});
} else if (!isActive && !isMulti) {
return this.setState({
selected: [{ value, label }],
selectedIndex: [eventKey],
});
} else {
return this.setState({
selected: [...selected, { value, label }],
selectedIndex: [...selectedIndex, keyboardKey],
});
}
};
choosePosition = () => {
const { isOpen, anchorNode } = this.state;
isOpen && this.setAnchorWidth(anchorNode);
};
handleToggle = () => {
this.setState(
{
isOpen: !this.state.isOpen,
anchorNode: ReactDOM.findDOMNode(this.clickTextField).parentNode,
},
() => this.choosePosition()
);
};
setAnchorWidth = (elementAnchor) => {
const anchor = elementAnchor && elementAnchor.getBoundingClientRect();
this.setState({ anchorWidth: anchor.width });
};
render() {
const {
buttonProps,
children,
className,
defaultValue,
isDynamic,
isMulti,
listProps,
overlayProps,
...props
} = this.props;
const { anchorNode, anchorWidth, id, isOpen, selected, selectedIndex } = this.state;
const otherProps = omit({ ...props }, ['id', 'onSelect']);
const currentValue = () => {
if (!isMulti && selected.length) return selected[0].label;
if (selected.length === 1) {
return `${selected.length} Item Selected`;
} else if (selected.length) {
return `${selected.length} Items Selected`;
}
};
const label = (
<div className="md-select__label" id={`${id}__label`}>
{currentValue() || defaultValue}
<Icon name={`arrow-down_16`} />
</div>
);
const text = (
<Button
active={isOpen}
ariaLabelledBy={`${id}__label`}
aria-haspopup="listbox"
id={id}
name={id}
onClick={this.handleToggle}
ref={(ref) => (this.clickTextField = ref)}
{...buttonProps}
>
{label}
</Button>
);
const dropdownElement = isOpen && (
<EventOverlay
allowClickAway
anchorNode={anchorNode}
close={this.hidePopover}
isDynamic={isDynamic}
isOpen={isOpen}
{...overlayProps}
>
<List
onSelect={this.handleSelect}
style={{ width: anchorWidth }}
ref={(ref) => (this.list = ref)}
role="listbox"
itemRole="option"
active={selectedIndex}
aria-labelledby={`${id}__label`}
aria-multiselectable={isMulti}
{...listProps}
>
{children}
</List>
</EventOverlay>
);
return (
<SelectContext.Provider value={isMulti}>
<div
className={'md-input-container md-select' + `${(className && ` ${className}`) || ''}`}
{...otherProps}
>
{text}
{dropdownElement}
</div>
</SelectContext.Provider>
);
}
}
Select.propTypes = {
/** @prop Sets the Button props | null */
buttonProps: PropTypes.shape({}),
/** @prop Children nodes to render inside Select component | null */
children: PropTypes.node,
/** @prop Optional CSS class name | '' */
className: PropTypes.string,
/** @prop Set the default selected option | '' */
defaultValue: PropTypes.string,
/** @prop Set ID for Select Component | null */
id: PropTypes.string,
/** @prop Sets the Select EventOverlay to be dynamic | true */
isDynamic: PropTypes.bool,
/** @prop Optional prop to know if multiple Select children can be active | false */
isMulti: PropTypes.bool,
/** @prop Sets the List props | null */
listProps: PropTypes.shape({}),
/** @prop Callback function invoked when user selects an item | null */
onSelect: PropTypes.func,
/** @prop Sets the EventOverlay props | null */
overlayProps: PropTypes.shape({}),
};
Select.defaultProps = {
buttonProps: null,
children: null,
className: '',
defaultValue: '',
id: null,
isDynamic: true,
isMulti: false,
listProps: null,
onSelect: null,
overlayProps: null,
};
Select.displayName = 'Select';
export default Select;