@shopgate/pwa-common
Version:
Common library for the Shopgate Connect PWA.
180 lines (174 loc) • 5.55 kB
JavaScript
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styles from "./style";
import SelectItem from "./components/Item";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const DEFAULT_PLACEHOLDER_TEXT = 'Select ...';
/**
* Finds an item in a list of items by value.
* @param {Array} items - The list of items.
* @param {*} value - The value to look for.
* @returns {*} The found item or undefined.
*/
const findItemByValue = (items, value) => items.filter(item => item.value === value).shift();
/**
* 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.
*/
const normalizeItem = item => ({
value: item.value || item,
label: item.label || item.value || item
});
/**
* The select component.
* @param {Object} props - The component props.
* @param {React.Children} props.children - Some content to display inside.
*/
let Select = /*#__PURE__*/function (_Component) {
/**
* The constructor.
* @param {Object} props - The component props.
*/
function Select(props) {
var _this;
_this = _Component.call(this, props) || this;
/**
* Triggers the onChange callback if the selected value has changed.
* @param {Object} nextState - The next state.
*/
_this.triggerChangeCallback = nextState => {
if (_this.state.selected && _this.state.selected.value === nextState.selected.value) {
return;
}
if (_this.props.onChange instanceof Function) {
_this.props.onChange(nextState.selected.value);
}
};
/**
* Handles any interaction the user does outside of the component.
* In this case the select gets closed.
* @param {Event} event - The event of the user interaction (e.g. TouchEvent).
*/
_this.handleInteractionOutside = event => {
if (!_this.domElement.contains(event.target)) {
_this.setState({
isOpen: false
});
}
};
/**
* Gets called when a new item is selected
* @param {*} value - The selected value.
* @param {string} label - The selected label.
*/
_this.handleItemSelect = (value, label) => {
const stateUpdate = {
selected: {
label,
value
},
isOpen: false
};
_this.triggerChangeCallback(stateUpdate);
_this.setState(stateUpdate);
};
/**
* Toggles the open state of the component.
*/
_this.toggleOpenState = () => {
_this.setState(({
isOpen
}) => ({
isOpen: !isOpen
}));
};
_this.state = {
selected: null,
isOpen: false
};
_this.domElement = null;
if (props.value) {
_this.state.selected = normalizeItem(findItemByValue(props.items, props.value));
}
return _this;
}
/**
* Adds event listener when the component is mounted.
*/
_inheritsLoose(Select, _Component);
var _proto = Select.prototype;
_proto.componentDidMount = function componentDidMount() {
document.addEventListener('touchstart', this.handleInteractionOutside);
}
/**
* Updates the selected item when the value prop changes.
* @param {Object} nextProps - The next props.
*/;
_proto.UNSAFE_componentWillReceiveProps = function UNSAFE_componentWillReceiveProps(nextProps) {
if (!this.state.selected || nextProps.value !== this.state.selected.value) {
this.state.selected = normalizeItem(findItemByValue(nextProps.items, nextProps.value));
}
}
/**
* Removes event listener when the component will unmount.
*/;
_proto.componentWillUnmount = function componentWillUnmount() {
document.removeEventListener('touchstart', this.handleInteractionOutside);
};
/**
* Renders the component.
* @returns {JSX}
*/
_proto.render = function render() {
const hasSelection = this.state.selected && this.state.selected.value !== undefined;
const selectedLabel = hasSelection ? this.state.selected.label : this.props.placeholder;
const items = this.state.isOpen ? /*#__PURE__*/_jsx("div", {
className: styles.items,
children: this.props.items.map(item => {
const normalizedItem = normalizeItem(item);
const selected = hasSelection && this.state.selected.value === normalizedItem.value;
return /*#__PURE__*/_jsx(SelectItem, {
value: normalizedItem.value,
label: normalizedItem.label,
selected: selected,
onSelect: this.handleItemSelect
}, normalizedItem.value);
})
}) : null;
return /*#__PURE__*/_jsxs("div", {
className: `${styles.container} ${this.props.className} common_select`,
ref: ref => {
this.domElement = ref;
},
children: [/*#__PURE__*/_jsxs("div", {
onTouchStart: this.toggleOpenState,
children: [/*#__PURE__*/_jsx("span", {
children: selectedLabel
}), /*#__PURE__*/_jsx("span", {
className: styles.selectHandle,
children: "\u25BE"
})]
}), items]
});
};
return Select;
}(Component);
/**
* The component prop types.
* @type {Object}
*/
/**
* The component default props.
* @type {Object}
*/
Select.defaultProps = {
className: '',
items: [],
onChange: () => {},
placeholder: DEFAULT_PLACEHOLDER_TEXT,
value: null
};
export default Select;