@shopgate/engage
Version:
Shopgate's ENGAGE library.
194 lines (189 loc) • 6.13 kB
JavaScript
import _createClass from "@babel/runtime/helpers/createClass";
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ConnectedReactPortal } from '@shopgate/engage/components';
import { withForwardedRef } from "../../core";
import PickerModal from "./components/Modal";
import PickerButton from "./components/Button";
import PickerList from "./components/List";
/**
* Converts an item of any type (e.g. string or number)
* to an object representation containing value and label properties.
* @param {*} item - An item of any type.
* @returns {Object} An object representation of the item.
*/
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const normalizeItem = item => {
if (item !== null && typeof item !== 'undefined') {
const itemDefaults = {
value: item.value || item,
label: item.label || item.value || item,
disabled: item.disabled || false
};
if (typeof item === 'object') {
return {
...itemDefaults,
...item
};
}
return itemDefaults;
}
return null;
};
/**
* Finds an item index in a list of items by value.
* @param {Array} items - The list of items.
* @param {*} value - The value to look for.
* @returns {number} The found item index or undefined.
*/
const findItemIndexByValue = (items, value) => items.map(normalizeItem).findIndex(item => item.value === value);
/**
* The picker component (acts like a selectbox).
*/
let Picker = /*#__PURE__*/function (_Component) {
/**
* The constructor.
* @param {Object} props - The component props.
*/
function Picker(props) {
var _this;
_this = _Component.call(this, props) || this;
/**
* Triggers the onChange callback if the selected value has changed.
* @param {Object} nextValue - The value the user picked.
*/
_this.triggerChangeCallback = nextValue => {
const nextSelectedIndex = findItemIndexByValue(_this.props.items, nextValue);
if (nextSelectedIndex > -1 && _this.state.selectedIndex !== nextSelectedIndex) {
_this.props.onChange(nextValue);
}
_this.props.onSelect(nextValue);
};
/**
* Triggers the onChange callback if the selected value has changed.
* @param {Object} nextOpenState - The value the user picked.
*/
_this.triggerCloseCallback = nextOpenState => {
if (_this.state.isOpen && !nextOpenState) {
_this.props.onClose();
}
};
/**
* Gets called when a new item is selected
* @param {string} value - The selected value.
*/
_this.handleItemSelect = value => {
_this.triggerChangeCallback(value);
_this.setState({
selectedIndex: findItemIndexByValue(_this.props.items, value)
});
};
/**
* Sets or toggles the open state of the component.
* @param {boolean} [open] New open state.
*/
_this.toggleOpenState = open => {
_this.setState(({
isOpen
}) => {
const nextIsOpen = typeof open === 'boolean' ? open : !isOpen;
_this.triggerCloseCallback(nextIsOpen);
return {
isOpen: nextIsOpen
};
});
};
_this.state = {
selectedIndex: findItemIndexByValue(props.items, props.value),
isOpen: props.isOpen
};
return _this;
}
/**
* Updates the selected item when the value prop changes.
* @param {Object} nextProps - The next props.
*/
_inheritsLoose(Picker, _Component);
var _proto = Picker.prototype;
_proto.UNSAFE_componentWillReceiveProps = function UNSAFE_componentWillReceiveProps(nextProps) {
// Only update if a value is present and also changed.
if (!this.selectedItem || nextProps.value !== this.selectedItem.value) {
this.setState({
selectedIndex: findItemIndexByValue(nextProps.items, nextProps.value)
});
}
if (this.props.isOpen !== nextProps.isOpen && nextProps.isOpen !== this.state.isOpen) {
this.setState({
isOpen: nextProps.isOpen
});
}
}
/**
* Getter for the selected item.
*/;
/**
* Renders the component.
* @returns {JSX}
*/
_proto.render = function render() {
const hasSelection = !!this.selectedItem;
const buttonValue = hasSelection ? this.selectedItem.label : '';
const buttonLabel = hasSelection ? this.props.label : this.props.placeholder;
const buttonProps = {
...this.props.buttonProps,
label: buttonLabel,
value: buttonValue,
disabled: this.props.disabled,
openList: this.props.disabled ? null : () => {
this.toggleOpenState(true);
}
};
const modalProps = {
isOpen: this.state.isOpen,
duration: this.props.duration,
onClose: () => this.toggleOpenState(false)
};
const listProps = {
items: this.props.items.map(normalizeItem),
selectedIndex: this.state.selectedIndex,
onSelect: this.handleItemSelect
};
return /*#__PURE__*/_jsxs("div", {
role: "button",
className: `${this.props.className} engage__picker`,
ref: this.props.forwardedRef,
tabIndex: 0,
"aria-haspopup": true,
children: [/*#__PURE__*/React.createElement(this.props.buttonComponent, buttonProps), /*#__PURE__*/_jsx(ConnectedReactPortal, {
isOpened: true,
children: /*#__PURE__*/React.createElement(this.props.modalComponent, modalProps, /*#__PURE__*/React.createElement(this.props.listComponent, listProps))
})]
});
};
return _createClass(Picker, [{
key: "selectedItem",
get: function () {
return normalizeItem(this.props.items[this.state.selectedIndex]);
}
}]);
}(Component);
Picker.defaultProps = {
buttonComponent: PickerButton,
buttonProps: {},
duration: 300,
disabled: false,
forwardedRef: null,
label: '',
listComponent: PickerList,
modalComponent: PickerModal,
className: '',
isOpen: false,
items: [],
onChange: () => {},
onClose: () => {},
onSelect: () => {},
placeholder: 'Pick ...',
value: null
};
export default withForwardedRef(Picker);