UNPKG

@shopgate/engage

Version:
194 lines (189 loc) 6.13 kB
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);