wix-style-react
Version:
wix-style-react
484 lines (426 loc) • 17.2 kB
JavaScript
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _class, _temp2;
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
import React from 'react';
import { bool, func, node, number, oneOf, string } from 'prop-types';
import WixComponent from '../BaseComponents/WixComponent';
import Loader from '../Loader/Loader';
import HeaderLayout from '../MessageBox/HeaderLayout';
import FooterLayout from '../MessageBox/FooterLayout';
import Selector from '../Selector/Selector';
import Search from '../Search/Search';
import InfiniteScroll from '../utils/InfiniteScroll';
import Text from '../Text';
import { dataHooks } from './ModalSelectorLayout.helpers';
import Checkbox from '../Checkbox';
import css from './ModalSelectorLayout.scss';
var DEFAULT_EMPTY = React.createElement(
'div',
{ className: css.defaultEmptyStateWrapper },
React.createElement(
Text,
null,
"You don't have any items"
)
);
/**
* Use this component when needed to select one / multiple items having complex descriptions.
* E.g.: choosing products to promote via ShoutOuts
*/
var ModalSelectorLayout = (_temp2 = _class = function (_WixComponent) {
_inherits(ModalSelectorLayout, _WixComponent);
function ModalSelectorLayout() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, ModalSelectorLayout);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ModalSelectorLayout.__proto__ || Object.getPrototypeOf(ModalSelectorLayout)).call.apply(_ref, [this].concat(args))), _this), _this.state = {
isLoaded: false,
isSearching: false,
items: [],
searchValue: '',
selectedItems: [],
shouldShowNoResultsFoundState: false,
isEmpty: false
}, _this._getEnabledItems = function (items) {
return items.filter(function (_ref2) {
var disabled = _ref2.disabled;
return !disabled;
});
}, _this._renderFooter = function () {
var selectedItems = _this.state.selectedItems;
var _this$props = _this.props,
onCancel = _this$props.onCancel,
_onOk = _this$props.onOk,
cancelButtonText = _this$props.cancelButtonText,
okButtonText = _this$props.okButtonText,
multiple = _this$props.multiple;
var enabledItems = _this._getEnabledItems(selectedItems);
return React.createElement(FooterLayout, {
onCancel: onCancel,
onOk: function onOk() {
return _onOk(multiple ? enabledItems : enabledItems[0]);
},
cancelText: cancelButtonText,
confirmText: okButtonText,
enableOk: !!selectedItems.length,
children: multiple && _this._renderFooterSelector()
});
}, _this._renderFooterSelector = function () {
var _this$props2 = _this.props,
selectAllText = _this$props2.selectAllText,
deselectAllText = _this$props2.deselectAllText;
var _this$state = _this.state,
selectedItems = _this$state.selectedItems,
items = _this$state.items;
var enabledItems = _this._getEnabledItems(items);
var selectedEnabled = selectedItems.filter(function (_ref3) {
var disabled = _ref3.disabled;
return !disabled;
});
var cases = {
select: {
text: selectAllText,
number: enabledItems.length,
onChange: function onChange() {
return _this.setState({ selectedItems: selectedItems.concat(enabledItems) });
},
indeterminate: false,
checked: false
},
deselect: {
text: deselectAllText,
number: selectedEnabled.length,
onChange: function onChange() {
return _this.setState({
selectedItems: selectedItems.filter(function (_ref4) {
var disabled = _ref4.disabled;
return disabled;
})
});
},
indeterminate: selectedEnabled.length < enabledItems.length,
checked: true
}
};
var _ref5 = selectedEnabled.length ? cases.deselect : cases.select,
text = _ref5.text,
num = _ref5.number,
onChange = _ref5.onChange,
checked = _ref5.checked,
indeterminate = _ref5.indeterminate;
return React.createElement(
Checkbox,
{
dataHook: 'footer-selector',
checked: checked,
onChange: onChange,
indeterminate: indeterminate
},
React.createElement(
Text,
{ weight: 'normal' },
' ' + text + ' (' + num + ')'
)
);
}, _temp), _possibleConstructorReturn(_this, _ret);
}
_createClass(ModalSelectorLayout, [{
key: 'render',
value: function render() {
var _this2 = this;
var _props = this.props,
title = _props.title,
subtitle = _props.subtitle,
onClose = _props.onClose,
searchPlaceholder = _props.searchPlaceholder,
emptyState = _props.emptyState,
noResultsFoundStateFactory = _props.noResultsFoundStateFactory,
withSearch = _props.withSearch,
height = _props.height;
var _state = this.state,
items = _state.items,
isLoaded = _state.isLoaded,
isEmpty = _state.isEmpty,
isSearching = _state.isSearching,
searchValue = _state.searchValue,
shouldShowNoResultsFoundState = _state.shouldShowNoResultsFoundState;
return React.createElement(
'div',
{ className: css.modalContent, style: { height: height } },
React.createElement(HeaderLayout, { title: title, onCancel: onClose }),
isLoaded && !isEmpty && React.createElement(
'div',
{ className: css.subheaderWrapper },
subtitle && React.createElement(
'div',
{ className: css.subtitleWrapper },
React.createElement(
Text,
{ dataHook: dataHooks.subtitle },
subtitle
)
),
withSearch && React.createElement(
'div',
{ className: css.searchWrapper },
React.createElement(Search, {
dataHook: dataHooks.search,
placeholder: searchPlaceholder,
value: searchValue,
onChange: function onChange(e) {
return _this2._onSearchChange(e);
}
})
)
),
React.createElement(
'div',
{ className: css.modalBody, 'data-hook': dataHooks.modalBody },
(items.length === 0 && !isLoaded || isSearching) && React.createElement(
'div',
{ className: css.mainLoaderWrapper },
React.createElement(Loader, { size: 'medium', dataHook: dataHooks.mainLoader })
),
isEmpty && React.createElement('div', {
'data-hook': dataHooks.emptyState,
className: css.emptyStateWrapper,
children: emptyState
}),
(!isLoaded || items.length > 0 || isSearching) && React.createElement(InfiniteScroll, {
key: searchValue,
loadMore: function loadMore() {
return _this2._loadMore();
},
hasMore: this._hasMore(),
useWindow: false,
children: this._renderItems(),
loader: items.length > 0 && React.createElement(
'div',
{ className: css.nextPageLoaderWrapper },
React.createElement(Loader, { size: 'small', dataHook: dataHooks.nextPageLoader })
)
}),
shouldShowNoResultsFoundState && React.createElement('div', {
'data-hook': dataHooks.noResultsFoundState,
className: css.noResultsFoundStateWrapper,
children: noResultsFoundStateFactory(searchValue)
})
),
this._renderFooter()
);
}
}, {
key: '_renderItems',
value: function _renderItems() {
var _this3 = this;
var _state2 = this.state,
items = _state2.items,
selectedItems = _state2.selectedItems;
var _props2 = this.props,
imageSize = _props2.imageSize,
imageShape = _props2.imageShape,
multiple = _props2.multiple;
var isSelected = function isSelected(item) {
return !!selectedItems.find(function (_ref6) {
var id = _ref6.id;
return item.id === id;
});
};
var _onToggle = function _onToggle(item) {
return _this3.setState({
selectedItems: multiple ? isSelected(item) ? selectedItems.filter(function (_ref7) {
var id = _ref7.id;
return item.id !== id;
}) : selectedItems.concat(item) : [item]
});
};
if (items.length > 0) {
return React.createElement(
'ul',
{ 'data-hook': dataHooks.list, className: css.list },
items.map(function (item) {
return React.createElement(Selector, {
id: item.id,
key: item.id,
dataHook: dataHooks.selector,
imageSize: imageSize,
imageShape: imageShape,
toggleType: multiple ? 'checkbox' : 'radio',
image: item.image,
title: item.title,
subtitle: item.subtitle,
extraNode: item.extraNode ? item.extraNode : React.createElement(
Text,
{ secondary: true },
item.extraText
),
isSelected: isSelected(item),
isDisabled: item.disabled,
onToggle: function onToggle() {
return !item.disabled && _onToggle(item);
}
});
})
);
}
}
}, {
key: '_onSearchChange',
value: function _onSearchChange(e) {
this.setState({
searchValue: e.target.value,
isSearching: true,
items: []
});
}
}, {
key: '_loadMore',
value: function _loadMore() {
var _this4 = this;
var _props3 = this.props,
dataSource = _props3.dataSource,
itemsPerPage = _props3.itemsPerPage;
var _state3 = this.state,
items = _state3.items,
searchValue = _state3.searchValue;
dataSource(searchValue, items.length, itemsPerPage).then(function (_ref8) {
var itemsFromNextPage = _ref8.items,
totalCount = _ref8.totalCount;
if (_this4.state.searchValue === searchValue) {
// react only to the resolve of the relevant search
var newItems = [].concat(_toConsumableArray(items), _toConsumableArray(itemsFromNextPage));
var selectedItems = _this4.state.selectedItems.concat(itemsFromNextPage.filter(function (_ref9) {
var selected = _ref9.selected;
return selected;
}));
var shouldShowNoResultsFoundState = newItems.length === 0 && searchValue;
var isEmpty = newItems.length === 0 && !searchValue;
_this4.setState({
items: newItems,
selectedItems: selectedItems,
isLoaded: true,
isEmpty: isEmpty,
isSearching: false,
totalCount: totalCount,
shouldShowNoResultsFoundState: shouldShowNoResultsFoundState
});
}
});
}
}, {
key: '_hasMore',
value: function _hasMore() {
var _state4 = this.state,
items = _state4.items,
isLoaded = _state4.isLoaded,
totalCount = _state4.totalCount,
isSearching = _state4.isSearching;
return items.length === 0 && !isLoaded || items.length < totalCount || isSearching;
}
}]);
return ModalSelectorLayout;
}(WixComponent), _class.displayName = 'ModalSelectorLayout', _class.propTypes = {
/** Title of the modal */
title: node,
/** Fixed text displayed above the list */
subtitle: node,
/** OK button callback, called with the currently selected item */
onOk: func,
/** X button callback */
onClose: func,
/** Cancel button callback */
onCancel: func,
/**
* paging function that should have a signature of
* ```typescript
* (searchQuery: string, offset: number, limit: number) =>
* Promise<{
* items: Array<{
* id: number | string,
* title: string,
* subtitle?: string,
* extraText?: string,
* extraNode?: string,
* disabled?: boolean // show item as disabled, dont count it in "select all", exclude from `onOk`
* selected?: boolean // force item as selected
* image?: node
* }>,
* totalCount: number
* }>
* ```
* `offset` - next requested item's index<br>
* `limit` - number of items requested<br>
* `totalCount` - total number of items that suffice the current search query
* */
dataSource: func.isRequired,
/** Cancel button's text */
cancelButtonText: string,
/** OK button's text */
okButtonText: string,
/** Image icon size */
imageSize: oneOf(['tiny', 'small', 'portrait', 'large', 'cinema']),
/**
* Image icon shape, `rectangular` or `circle`.<br>
* NOTE: `circle` is not compatible with `imageSize` of `portrait` or `cinema`
* */
imageShape: function imageShape(props, propName, componentName) {
if (['portrait', 'cinema'].includes(props.imageSize) && props[propName] === 'circle') {
return new Error(componentName + ': prop "imageSize" with value of "' + props.imageSize + '" is incompatible with prop imageShape with value of "circle" \u2014 use "rectangular" instead.');
}
},
/** Placeholder text of the search input */
searchPlaceholder: string,
/**
* Component/element that will be rendered when there is nothing to display,
* i.e. empty `{items:[], totalCount: 0}` was returned on the first call to `dataSource`
* */
emptyState: node.isRequired,
/**
* Function that will get the current `searchQuery` and should return the component/element
* that will be rendered when there are no items that suffice the entered search query
* */
noResultsFoundStateFactory: func,
/** Number of items loaded each time the user scrolls down */
itemsPerPage: number,
/** Whether to display the search input or not */
withSearch: bool,
height: string,
/** display checkbox and allow multi selection */
multiple: bool,
/** string to be displayed in footer when `multiple` prop is used and no items are selected */
selectAllText: string,
/** string to be displayed in footer when `multiple` prop is used and some or all items ar selected */
deselectAllText: string
}, _class.defaultProps = {
title: 'Choose Your Items',
okButtonText: 'Select',
cancelButtonText: 'Cancel',
searchPlaceholder: 'Search...',
imageSize: 'large',
imageShape: 'rectangular',
itemsPerPage: 50,
withSearch: true,
height: '100%',
emptyState: DEFAULT_EMPTY,
noResultsFoundStateFactory: function noResultsFoundStateFactory(searchValue) {
return React.createElement(
'div',
{ className: css.defaultNoResultsFoundStateWrapper },
React.createElement(
Text,
null,
'No items matched your search ',
'"' + searchValue + '"'
)
);
},
selectAllText: 'Select All',
deselectAllText: 'Deselect All'
}, _temp2);
export { ModalSelectorLayout as default };