UNPKG

wix-style-react

Version:
537 lines (535 loc) 18.3 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _Content = _interopRequireDefault(require("./Content")); var _Box = _interopRequireDefault(require("../Box")); var _Search = _interopRequireDefault(require("../Search")); var _Text = _interopRequireDefault(require("../Text")); var _ToggleAllCheckbox = _interopRequireDefault(require("./ToggleAllCheckbox")); var _SelectorList2 = require("./SelectorList.helpers"); var _constants = require("./constants"); var _SelectorListSt = require("./SelectorList.st.css"); var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/SelectorList/SelectorList.js"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /** * Use this component when needed to select one / multiple items having complex descriptions. * E.g.: choosing products to promote via ShoutOuts */ class SelectorList extends _react.default.PureComponent { constructor() { var _this; super(...arguments); _this = this; this.state = { isLoaded: false, isSearching: false, items: [], searchValue: '', selectedItems: [], indeterminateItems: [], noResultsFound: false, isEmpty: false }; this._renderList = () => { var { dataHook, emptyState, renderNoResults, height, maxHeight, size, imageSize, imageShape, multiple, showDivider } = this.props; var { items, isLoaded, isEmpty, isSearching, searchValue, noResultsFound, selectedItems } = this.state; var hasMore = this._hasMore(); var contentProps = { items, selectedItems, onToggle: this._onToggle, emptyState, renderNoResults, isEmpty, isLoading: !isLoaded || isSearching, noResultsFound, size, imageSize: imageSize || _constants.DEFAULT_IMAGE_SIZE_BY_SIZE[size], imageShape, multiple, showDivider, loadMore: this._loadMore, hasMore, checkIsSelected: this._checkIsSelected, checkIndeterminate: this._checkIndeterminate, searchValue }; var shouldRenderSubheader = isLoaded && !isEmpty; return /*#__PURE__*/_react.default.createElement(_Box.default, { direction: "vertical", overflow: "hidden", dataHook, height, maxHeight, __self: this, __source: { fileName: _jsxFileName, lineNumber: 218, columnNumber: 7 } }, shouldRenderSubheader && this._renderSubheader(), /*#__PURE__*/_react.default.createElement(_Content.default, (0, _extends2.default)({}, contentProps, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 228, columnNumber: 9 } }))); }; this._renderSubheader = () => { var { subtitle, withSearch, size, searchDebounceMs, searchPlaceholder } = this.props; var { searchValue } = this.state; return /*#__PURE__*/_react.default.createElement("div", { className: (0, _SelectorListSt.st)(_SelectorListSt.classes.subheaderWrapper, { withSearch, size }), __self: this, __source: { fileName: _jsxFileName, lineNumber: 239, columnNumber: 7 } }, subtitle && /*#__PURE__*/_react.default.createElement("div", { className: _SelectorListSt.classes.subtitleWrapper, __self: this, __source: { fileName: _jsxFileName, lineNumber: 246, columnNumber: 11 } }, typeof subtitle === 'string' ? /*#__PURE__*/_react.default.createElement(_Text.default, { dataHook: _SelectorList2.dataHooks.subtitle, __self: this, __source: { fileName: _jsxFileName, lineNumber: 248, columnNumber: 15 } }, subtitle) : subtitle), withSearch && /*#__PURE__*/_react.default.createElement(_Search.default, { dataHook: _SelectorList2.dataHooks.search, placeholder: searchPlaceholder, onChange: this._onSearchChange, onClear: this._onClear, debounceMs: searchDebounceMs, value: searchValue, size: _constants.SEARCH_SIZE_BY_SIZE[size], __self: this, __source: { fileName: _jsxFileName, lineNumber: 255, columnNumber: 11 } })); }; this._renderToggleAllCheckbox = () => { var { selectAllText, deselectAllText, size } = this.props; var { items, selectedItems } = this.state; var enabledItemsAmount = this._getEnabledItems(items).length; var selectedEnabledItemsAmount = this._getEnabledItems(selectedItems).length; var checkboxProps = { selectAllText, deselectAllText, size, enabledItemsAmount, selectedEnabledItemsAmount, selectAll: this._selectAll, deselectAll: this._deselectAll }; return /*#__PURE__*/_react.default.createElement(_ToggleAllCheckbox.default, (0, _extends2.default)({}, checkboxProps, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 287, columnNumber: 12 } })); }; this._updateSearchValue = searchValue => this.setState({ searchValue, isSearching: true, items: [] }, () => this._loadInitialItems(searchValue)); this._onSearchChange = event => this._updateSearchValue(event.target.value); this._onClear = () => { var searchValue = ''; this.setState({ searchValue, isSearching: true, items: [] }, () => { this._getInitialData(searchValue).then(dataSourceProps => { var { items, selectedItems, indeterminateItems } = this.state; var newItems = [...items, ...dataSourceProps.items]; var newSelectedItems = selectedItems; var newIndeterminateItems = indeterminateItems; var noResultsFound = newItems.length === 0 && Boolean(searchValue); var isEmpty = newItems.length === 0 && !searchValue; this.setState({ items: newItems, selectedItems: newSelectedItems, indeterminateItems: newIndeterminateItems, isLoaded: true, isEmpty, isSearching: false, noResultsFound, totalCount: dataSourceProps.totalCount }); }); }); }; this._checkIsSelected = item => { var { selectedItems } = this.state; return !!selectedItems.find(_ref => { var { id } = _ref; return item.id === id; }); }; this._checkIndeterminate = item => { var { indeterminateItems } = this.state; return !!indeterminateItems.find(_ref2 => { var { id } = _ref2; return item.id === id; }); }; this._toggleItem = item => { var { multiple } = this.props; this.setState(_ref3 => { var { selectedItems, indeterminateItems } = _ref3; return { selectedItems: multiple ? this._checkIsSelected(item) ? selectedItems.filter(_ref4 => { var { id } = _ref4; return item.id !== id; }) : selectedItems.concat(item) : [item], indeterminateItems: indeterminateItems.filter(_ref5 => { var { id } = _ref5; return item.id !== id; }) }; }); }; this._onToggle = item => { var { onSelect } = this.props; this._toggleItem(item); if (onSelect) { onSelect(item); } }; this._selectAll = () => { var { selectedItems, items } = this.state; var enabledItems = this._getEnabledItems(items); this.setState({ selectedItems: selectedItems.concat(enabledItems), indeterminateItems: [] }); }; this._deselectAll = () => this.setState(_ref6 => { var { selectedItems } = _ref6; return { selectedItems: selectedItems.filter(_ref7 => { var { disabled } = _ref7; return disabled; }), indeterminateItems: [] }; }); this._updateItems = _ref8 => { var { resetItems, items: nextPageItems, totalCount, searchValue } = _ref8; var { items, selectedItems, indeterminateItems } = this.state; // react only to the resolve of the relevant search if (searchValue !== this.state.searchValue) { return; } var newItems = [...(resetItems ? [] : items), ...nextPageItems]; var newSelectedItems = selectedItems.concat(nextPageItems.filter(_ref9 => { var { selected } = _ref9; return selected; })).filter((value, index, self) => self.findIndex(_ref0 => { var { id } = _ref0; return id === value.id; }) === index); var newIndeterminateItems = indeterminateItems.concat(nextPageItems.filter(_ref1 => { var { indeterminate } = _ref1; return indeterminate; })).filter((value, index, self) => self.findIndex(_ref10 => { var { id } = _ref10; return id === value.id; }) === index); var noResultsFound = newItems.length === 0 && Boolean(searchValue); var isEmpty = newItems.length === 0 && !searchValue; this.setState({ items: newItems, selectedItems: newSelectedItems, indeterminateItems: newIndeterminateItems, isLoaded: true, isEmpty, isSearching: false, totalCount, noResultsFound }); }; this._loadInitialItems = function () { var searchValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var { resetItems } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _this._getInitialData(searchValue).then(dataSourceProps => { return _this._updateItems(_objectSpread(_objectSpread({ resetItems }, dataSourceProps), {}, { searchValue })); }); }; this._getInitialData = function () { var searchValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var { dataSource, itemsPerPage } = _this.props; var initialAmountToLoad = _this.props.initialAmountToLoad || itemsPerPage; return dataSource(searchValue, 0, initialAmountToLoad); }; this._loadMore = () => { var { dataSource, itemsPerPage } = this.props; var { items, searchValue } = this.state; dataSource(searchValue, items.length, itemsPerPage).then(dataSourceProps => this._updateItems(_objectSpread(_objectSpread({}, dataSourceProps), {}, { searchValue }))); }; this._getEnabledItems = items => items.filter(_ref11 => { var { disabled } = _ref11; return !disabled; }); } componentDidMount() { this._loadInitialItems(); } /** Resets list items and loads first page from dataSource while persisting searchValue */ reloadInitialItems() { var searchValue = this.state.searchValue; this.setState({ searchValue, isSearching: Boolean(searchValue), isLoaded: false }); this._loadInitialItems(searchValue, { resetItems: true }); } render() { var { children } = this.props; var { selectedItems } = this.state; if (typeof children === 'function') { return children({ renderList: this._renderList, renderToggleAllCheckbox: this._renderToggleAllCheckbox, selectedItems }); } return this._renderList(); } _hasMore() { var { items, isLoaded, totalCount, isSearching } = this.state; return items.length === 0 && !isLoaded || items.length < totalCount || isSearching; } } exports.default = SelectorList; SelectorList.displayName = 'SelectorList'; SelectorList.propTypes = { /** Applies a data-hook HTML attribute to be used in the tests */ dataHook: _propTypes.default.string, /** * Returns data source for the list described in a following structure: * * ```typescript * (searchQuery: string, offset: number, limit: number) => * Promise<{ * items: Array<{ * id: number | string, // sets the unique item ID (required) * title: node, defines // an item’s title (required) * subtitle?: string, // defines an item’s subtitle * extraText?: string, // contains any text at the end of an item * extraNode?: node, // contains any component at the end of an item * disabled?: boolean, // controls if an item is disabled for selection or not * selected?: boolean, // sets an item as selected * indeterminate?: boolean, // sets an item as indeterminate * image?: node, // contains <Image/> or other component to illustrate an item * subtitleNode?: node, // contains any component below an item’s subtitle * belowNode?: node, // contains any component below the item, to be shown after an item is selected * showBelowNodeOnSelect?: boolean, // allows to show belowNode content when an item is selected * }>, * offset: number, // specifies the item index in the data source to start fetching from<br> * limit: number, // sets a max amount of items to load from the data source<br> * totalCount: number, // sets a max amount of items to load from the data source on user’s search query * }> * ``` * */ dataSource: _propTypes.default.func.isRequired, /** Controls the size of component paddings and list items */ size: _propTypes.default.oneOf(['small', 'medium']), /** Controls the size of item images. Note: `portrait` and `cinema` sizes are only compatible with `rectangular` image shape. */ imageSize: _propTypes.default.oneOf(['tiny', 'small', 'portrait', 'large', 'cinema']), /** * Controls the shape of item images * */ imageShape: _propTypes.default.oneOf(['rectangular', 'circle']), /** * Use to display a divider between items */ showDivider: _propTypes.default.bool, /** Defines placeholder value shown in the search input */ searchPlaceholder: _propTypes.default.string, /** * Contains a component which is shown when there are no items to display in the selector list. * * i.e. empty `{items:[], totalCount: 0}` was returned on the first call to `dataSource`. Render `<EmptyState/>` component in `section` theme for this purpose. * */ emptyState: _propTypes.default.node, /** * Defines a function that gets the current `searchQuery` and returns the component that is shown when no items are found. Render `<EmptyState />` component in `section` theme for this purpose. * */ renderNoResults: _propTypes.default.func, /** Sets the number of items to be loaded each time users scroll down to the end of the list */ itemsPerPage: _propTypes.default.number, /** Controls whether to display the search input */ withSearch: _propTypes.default.bool, /** Sets search debounce in milliseconds */ searchDebounceMs: _propTypes.default.number, /** Sets the height of the component in % or px */ height: _propTypes.default.string, /** Sets the maximum height of the component in % or px */ maxHeight: _propTypes.default.string, /** Renders checkboxes instead of radio buttons and allows users to select multiple items */ multiple: _propTypes.default.bool, /** Defines callback that triggers on select and return selected item object */ onSelect: _propTypes.default.func, /** Sets the label for the checkbox which allows to select all items */ selectAllText: _propTypes.default.string, /** Sets the label for the checkbox which allows to deselect all selected items */ deselectAllText: _propTypes.default.string, /** Sets the number of items to load on initial render or after search. If not defined, it will be equal to `itemsPerPage` value. */ initialAmountToLoad: _propTypes.default.number, /** Contains text or other component in a fixed position at the top of the list */ subtitle: _propTypes.default.node, /** Displays a checkbox which allows to select and deselect all items at once */ renderToggleAllCheckbox: _propTypes.default.func }; SelectorList.defaultProps = { searchPlaceholder: 'Search...', size: 'medium', imageShape: 'rectangular', showDivider: false, itemsPerPage: 50, withSearch: true, height: '100%', maxHeight: '100%', deselectAllText: 'Deselect all', multiple: false, selectAllText: 'Select all' }; //# sourceMappingURL=SelectorList.js.map