saagie-ui
Version:
Saagie UI from Saagie Design System
205 lines (183 loc) • 5.19 kB
JavaScript
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { Manager, Reference, Popper } from 'react-popper';
import { FormControlSelect } from '../../atoms/formControlSelect/FormControlSelect';
import { FormControlSelectThemeDefault } from '../../atoms/formControlSelect/themes/FormControlSelectThemeDefault';
import { Icon } from '../../atoms/icon/Icon';
import { keyCodes } from '../../../helpers';
const propTypes = {
children: PropTypes.node,
options: PropTypes.arrayOf(PropTypes.object),
onAdd: PropTypes.func,
onClose: PropTypes.func,
icon: PropTypes.string,
title: PropTypes.node,
placeholder: PropTypes.string,
isOpen: PropTypes.bool,
showInline: PropTypes.bool,
disableClickOutside: PropTypes.bool,
noAutoFocus: PropTypes.bool,
};
const defaultProps = {
children: null,
options: [],
onAdd: () => {},
onClose: () => {},
icon: 'plus',
title: 'Add',
placeholder: 'Search...',
isOpen: false,
showInline: false,
disableClickOutside: false,
noAutoFocus: false,
};
const theme = {
control: (styles, { isDisabled, isFocused }) => ({
...FormControlSelectThemeDefault.control(styles, { isDisabled, isFocused }),
margin: '0 0.8rem 0.8rem 0.8rem',
fontSize: '0.9rem',
}),
dropdownIndicator: () => ({
display: 'none',
}),
menu: (styles) => ({
...styles,
position: 'relative',
boxShadow: 'none',
border: 'none',
borderTop: '0.0625rem solid rgba(0, 0, 0, 0.05)',
borderRadius: 0,
marginBottom: 0,
}),
};
export class AddBox extends React.Component {
container = React.createRef();
componentWillUnmount() {
this.removeEvents();
}
addEvents = () => {
['click', 'touchstart', 'keyup'].forEach(
(event) => document.addEventListener(event, this.handleDocumentEvent, true)
);
}
removeEvents = () => {
['click', 'touchstart', 'keyup'].forEach(
(event) => document.removeEventListener(event, this.handleDocumentEvent, true)
);
}
handleDocumentEvent = (e) => {
const { onClose } = this.props;
if (e && (e.which === 3 || (e.type === 'keyup' && e.which !== keyCodes.esc))) {
return;
}
if (!this.container) {
return;
}
if (this.container.contains(e.target) && this.container !== e.target
&& (e.type !== 'keyup' || e.which === keyCodes.tab)
) {
return;
}
onClose();
}
handleAdd = (item) => {
const { onAdd } = this.props;
onAdd(item);
}
render() {
const {
children,
options,
icon,
title,
placeholder,
isOpen,
onClose,
showInline,
disableClickOutside,
noAutoFocus,
...otherProps
} = this.props;
this.removeEvents();
if (isOpen && !disableClickOutside) {
this.addEvents();
}
// autofocus in react-select and Popper doesn't work well together; it makes the first click on
// the close button ineffective
const autoFocus = showInline && !noAutoFocus;
const { handleAdd } = this;
// eslint-disable-next-line react/prop-types
const Menu = React.forwardRef(({ scheduleUpdate, ...attrs }, ref) => (
<div
ref={ref}
className="sui-m-add-box"
{...attrs}
>
<button
type="button"
className="sui-m-add-box__close"
onClick={() => onClose()}
>
<Icon name="close" size="sm" />
</button>
<div className="sui-m-add-box__title">
{!!icon && (
<Icon name={icon} size="sm" position="start" />
)}
{title}
</div>
<div className="sui-m-add-box__select">
<FormControlSelect
menuIsOpen
autoFocus={autoFocus}
placeholder={placeholder}
onChange={(item) => handleAdd(item)}
onInputChange={scheduleUpdate ? () => scheduleUpdate() : () => {}}
noOptionsMessage={({ inputValue }) => {
if (!inputValue) return 'No option';
return `No option "${inputValue}"`;
}}
theme={theme}
options={options}
isGroupOption
{...otherProps}
/>
</div>
</div>
));
if (showInline) {
return isOpen && (
<div ref={(node) => { this.container = node; }}>
{children}
<Menu />
</div>
);
}
return (
<Manager>
<Reference>{({ ref }) => <span style={{ display: 'inline-block' }} ref={ref}>{children}</span>}</Reference>
{isOpen && ReactDOM.createPortal(
<Popper
innerRef={(node) => { this.container = node; }}
placement="bottom-start"
>
{({
ref, style, placement, scheduleUpdate,
}) => (
<Menu
ref={ref}
style={style}
data-placement={placement}
scheduleUpdate={scheduleUpdate}
/>
)}
</Popper>,
document.body
)}
</Manager>
);
}
}
AddBox.propTypes = propTypes;
AddBox.defaultProps = defaultProps;