wix-style-react
Version:
wix-style-react
537 lines (535 loc) • 18.3 kB
JavaScript
"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