@shopify/polaris
Version:
Shopify’s product component library
471 lines (415 loc) • 17.7 kB
JavaScript
import React$1, { useState, useReducer, useRef, useCallback, useEffect, Children } from 'react';
import debounce$1 from 'lodash/debounce';
import { EventListener as EventListener$1 } from '../EventListener/EventListener.js';
import { useI18n } from '../../utilities/i18n/hooks.js';
import { classNames } from '../../utilities/css.js';
import { EnableSelectionMinor } from '@shopify/polaris-icons';
import { Spinner as Spinner$1 } from '../Spinner/Spinner.js';
import { isElementOfType } from '../../utilities/components.js';
import { Button as Button$1 } from '../Button/Button.js';
import { ResourceListContext } from '../../utilities/resource-list/context.js';
import { SELECT_ALL_ITEMS } from '../../utilities/resource-list/types.js';
import { CheckableButton as CheckableButton$1 } from '../CheckableButton/CheckableButton.js';
import { BulkActions as BulkActions$1 } from '../BulkActions/BulkActions.js';
import { EmptySearchResult as EmptySearchResult$1 } from '../EmptySearchResult/EmptySearchResult.js';
import { ResourceItem as ResourceItem$1 } from '../ResourceItem/ResourceItem.js';
import { Sticky as Sticky$1 } from '../Sticky/Sticky.js';
import { Select as Select$1 } from '../Select/Select.js';
import { useLazyRef as useLazyRef$1 } from '../../utilities/use-lazy-ref.js';
import styles from './ResourceList.scss.js';
var SMALL_SCREEN_WIDTH = 458;
var SMALL_SPINNER_HEIGHT = 28;
var LARGE_SPINNER_HEIGHT = 45;
function getAllItemsOnPage(items, idForItem) {
return items.map((item, index) => {
return idForItem(item, index);
});
}
var isSmallScreen = () => {
return typeof window === 'undefined' ? false : window.innerWidth < SMALL_SCREEN_WIDTH;
};
function defaultIdForItem(item, index) {
return Object.prototype.hasOwnProperty.call(item, 'id') ? item.id : index.toString();
}
var ResourceList = function ResourceList({
items,
filterControl,
emptyState,
emptySearchState,
resourceName: resourceNameProp,
promotedBulkActions,
bulkActions,
selectedItems = [],
isFiltered,
selectable,
hasMoreItems,
loading,
showHeader,
totalItemsCount,
sortValue,
sortOptions,
alternateTool,
onSortChange,
onSelectionChange,
renderItem,
idForItem = defaultIdForItem,
resolveItemId
}) {
var i18n = useI18n();
var [selectMode, setSelectMode] = useState(Boolean(selectedItems && selectedItems.length > 0));
var [loadingPosition, setLoadingPositionState] = useState(0);
var [lastSelected, setLastSelected] = useState();
var [smallScreen, setSmallScreen] = useState(isSmallScreen());
var forceUpdate = useReducer((x = 0) => x + 1, 0)[1];
var [checkableButtons, setCheckableButtons] = useState(new Map());
var defaultResourceName = useLazyRef$1(() => ({
singular: i18n.translate('Polaris.ResourceList.defaultItemSingular'),
plural: i18n.translate('Polaris.ResourceList.defaultItemPlural')
}));
var listRef = useRef(null);
var handleSelectMode = selectMode => {
setSelectMode(selectMode);
if (!selectMode && onSelectionChange) {
onSelectionChange([]);
}
};
var handleResize = debounce$1(() => {
var newSmallScreen = isSmallScreen();
if (selectedItems && selectedItems.length === 0 && selectMode && !newSmallScreen) {
handleSelectMode(false);
}
if (smallScreen !== newSmallScreen) {
setSmallScreen(newSmallScreen);
}
}, 50, {
leading: true,
trailing: true,
maxWait: 50
});
var isSelectable = Boolean(promotedBulkActions && promotedBulkActions.length > 0 || bulkActions && bulkActions.length > 0 || selectable);
var bulkSelectState = () => {
var selectState = 'indeterminate';
if (!selectedItems || Array.isArray(selectedItems) && selectedItems.length === 0) {
selectState = false;
} else if (selectedItems === SELECT_ALL_ITEMS || Array.isArray(selectedItems) && selectedItems.length === items.length) {
selectState = true;
}
return selectState;
};
var resourceName = resourceNameProp ? resourceNameProp : defaultResourceName.current;
var headerTitle = () => {
var itemsCount = items.length;
var resource = !loading && (!totalItemsCount && itemsCount === 1 || totalItemsCount === 1) ? resourceName.singular : resourceName.plural;
if (loading) {
return i18n.translate('Polaris.ResourceList.loading', {
resource
});
} else if (totalItemsCount) {
return i18n.translate('Polaris.ResourceList.showingTotalCount', {
itemsCount,
totalItemsCount,
resource
});
} else {
return i18n.translate('Polaris.ResourceList.showing', {
itemsCount,
resource
});
}
};
var bulkActionsLabel = () => {
var selectedItemsCount = selectedItems === SELECT_ALL_ITEMS ? "".concat(items.length, "+") : selectedItems.length;
return i18n.translate('Polaris.ResourceList.selected', {
selectedItemsCount
});
};
var bulkActionsAccessibilityLabel = () => {
var selectedItemsCount = selectedItems.length;
var totalItemsCount = items.length;
var allSelected = selectedItemsCount === totalItemsCount;
if (totalItemsCount === 1 && allSelected) {
return i18n.translate('Polaris.ResourceList.a11yCheckboxDeselectAllSingle', {
resourceNameSingular: resourceName.singular
});
} else if (totalItemsCount === 1) {
return i18n.translate('Polaris.ResourceList.a11yCheckboxSelectAllSingle', {
resourceNameSingular: resourceName.singular
});
} else if (allSelected) {
return i18n.translate('Polaris.ResourceList.a11yCheckboxDeselectAllMultiple', {
itemsLength: items.length,
resourceNamePlural: resourceName.plural
});
} else {
return i18n.translate('Polaris.ResourceList.a11yCheckboxSelectAllMultiple', {
itemsLength: items.length,
resourceNamePlural: resourceName.plural
});
}
};
var paginatedSelectAllText = () => {
if (!isSelectable || !hasMoreItems) {
return;
}
if (selectedItems === SELECT_ALL_ITEMS) {
return i18n.translate(isFiltered ? 'Polaris.ResourceList.allFilteredItemsSelected' : 'Polaris.ResourceList.allItemsSelected', {
itemsLength: items.length,
resourceNamePlural: resourceName.plural
});
}
};
var paginatedSelectAllAction = () => {
if (!isSelectable || !hasMoreItems) {
return;
}
var actionText = selectedItems === SELECT_ALL_ITEMS ? i18n.translate('Polaris.Common.undo') : i18n.translate(isFiltered ? 'Polaris.ResourceList.selectAllFilteredItems' : 'Polaris.ResourceList.selectAllItems', {
itemsLength: items.length,
resourceNamePlural: resourceName.plural
});
return {
content: actionText,
onAction: handleSelectAllItemsInStore
};
};
var emptySearchResultText = {
title: i18n.translate('Polaris.ResourceList.emptySearchResultTitle', {
resourceNamePlural: resourceName.plural
}),
description: i18n.translate('Polaris.ResourceList.emptySearchResultDescription')
};
var handleSelectAllItemsInStore = () => {
var newlySelectedItems = selectedItems === SELECT_ALL_ITEMS ? getAllItemsOnPage(items, idForItem) : SELECT_ALL_ITEMS;
if (onSelectionChange) {
onSelectionChange(newlySelectedItems);
}
};
var setLoadingPosition = useCallback(() => {
if (listRef.current != null) {
if (typeof window === 'undefined') {
return;
}
var overlay = listRef.current.getBoundingClientRect();
var viewportHeight = Math.max(document.documentElement ? document.documentElement.clientHeight : 0, window.innerHeight || 0);
var overflow = viewportHeight - overlay.height;
var spinnerHeight = items.length === 1 ? SMALL_SPINNER_HEIGHT : LARGE_SPINNER_HEIGHT;
var spinnerPosition = overflow > 0 ? (overlay.height - spinnerHeight) / 2 : (viewportHeight - overlay.top - spinnerHeight) / 2;
setLoadingPositionState(spinnerPosition);
}
}, [listRef, items.length]);
var itemsExist = items.length > 0;
useEffect(() => {
if (loading) {
setLoadingPosition();
}
}, [loading, setLoadingPosition]);
useEffect(() => {
if (selectedItems && selectedItems.length > 0 && !selectMode) {
setSelectMode(true);
}
if ((!selectedItems || selectedItems.length === 0) && !isSmallScreen()) {
setSelectMode(false);
}
}, [selectedItems, selectMode]);
useEffect(() => {
forceUpdate();
}, [forceUpdate, items]);
var renderItemWithId = (item, index) => {
var id = idForItem(item, index);
var itemContent = renderItem(item, id, index);
if (process.env.NODE_ENV === 'development' && !isElementOfType(itemContent, ResourceItem$1)) {
// eslint-disable-next-line no-console
console.warn('<ResourceList /> renderItem function should return a <ResourceItem />.');
}
return itemContent;
};
var handleMultiSelectionChange = (lastSelected, currentSelected, resolveItemId) => {
var min = Math.min(lastSelected, currentSelected);
var max = Math.max(lastSelected, currentSelected);
return items.slice(min, max + 1).map(resolveItemId);
};
var handleCheckableButtonRegistration = (key, button) => {
if (!checkableButtons.get(key)) {
setCheckableButtons(new Map(checkableButtons).set(key, button));
}
};
var handleSelectionChange = (selected, id, sortOrder, shiftKey) => {
if (selectedItems == null || onSelectionChange == null) {
return;
}
var newlySelectedItems = selectedItems === SELECT_ALL_ITEMS ? getAllItemsOnPage(items, idForItem) : [...selectedItems];
if (sortOrder !== undefined) {
setLastSelected(sortOrder);
}
var lastSelectedFromState = lastSelected;
var selectedIds = [id];
if (shiftKey && lastSelectedFromState != null && sortOrder !== undefined && resolveItemId) {
selectedIds = handleMultiSelectionChange(lastSelectedFromState, sortOrder, resolveItemId);
}
newlySelectedItems = [...new Set([...newlySelectedItems, ...selectedIds])];
if (!selected) {
for (var selectedId of selectedIds) {
newlySelectedItems.splice(newlySelectedItems.indexOf(selectedId), 1);
}
}
if (newlySelectedItems.length === 0 && !isSmallScreen()) {
handleSelectMode(false);
} else if (newlySelectedItems.length > 0) {
handleSelectMode(true);
}
if (onSelectionChange) {
onSelectionChange(newlySelectedItems);
}
};
var handleToggleAll = () => {
var newlySelectedItems;
if (Array.isArray(selectedItems) && selectedItems.length === items.length || selectedItems === SELECT_ALL_ITEMS) {
newlySelectedItems = [];
} else {
newlySelectedItems = items.map((item, index) => {
return idForItem(item, index);
});
}
if (newlySelectedItems.length === 0 && !isSmallScreen()) {
handleSelectMode(false);
} else if (newlySelectedItems.length > 0) {
handleSelectMode(true);
}
var checkbox;
if (isSmallScreen()) {
checkbox = checkableButtons.get('bulkSm');
} else if (newlySelectedItems.length === 0) {
checkbox = checkableButtons.get('plain');
} else {
checkbox = checkableButtons.get('bulkLg');
}
if (onSelectionChange) {
onSelectionChange(newlySelectedItems);
} // setTimeout ensures execution after the Transition on BulkActions
setTimeout(() => {
checkbox && checkbox.focus();
}, 0);
};
var bulkActionsMarkup = isSelectable ? /*#__PURE__*/React$1.createElement("div", {
className: styles.BulkActionsWrapper
}, /*#__PURE__*/React$1.createElement(BulkActions$1, {
label: bulkActionsLabel(),
accessibilityLabel: bulkActionsAccessibilityLabel(),
selected: bulkSelectState(),
onToggleAll: handleToggleAll,
selectMode: selectMode,
onSelectModeToggle: handleSelectMode,
promotedActions: promotedBulkActions,
paginatedSelectAllAction: paginatedSelectAllAction(),
paginatedSelectAllText: paginatedSelectAllText(),
actions: bulkActions,
disabled: loading,
smallScreen: smallScreen
})) : null;
var filterControlMarkup = filterControl ? /*#__PURE__*/React$1.createElement("div", {
className: styles.FiltersWrapper
}, filterControl) : null;
var sortingSelectMarkup = sortOptions && sortOptions.length > 0 && !alternateTool ? /*#__PURE__*/React$1.createElement("div", {
className: styles.SortWrapper
}, /*#__PURE__*/React$1.createElement(Select$1, {
label: i18n.translate('Polaris.ResourceList.sortingLabel'),
labelInline: !smallScreen,
labelHidden: smallScreen,
options: sortOptions,
onChange: onSortChange,
value: sortValue,
disabled: selectMode
})) : null;
var alternateToolMarkup = alternateTool && !sortingSelectMarkup ? /*#__PURE__*/React$1.createElement("div", {
className: styles.AlternateToolWrapper
}, alternateTool) : null;
var headerTitleMarkup = /*#__PURE__*/React$1.createElement("div", {
className: styles.HeaderTitleWrapper
}, headerTitle());
var selectButtonMarkup = isSelectable ? /*#__PURE__*/React$1.createElement("div", {
className: styles.SelectButtonWrapper
}, /*#__PURE__*/React$1.createElement(Button$1, {
disabled: selectMode,
icon: EnableSelectionMinor,
onClick: () => handleSelectMode(true)
}, i18n.translate('Polaris.ResourceList.selectButtonText'))) : null;
var checkableButtonMarkup = isSelectable ? /*#__PURE__*/React$1.createElement("div", {
className: styles.CheckableButtonWrapper
}, /*#__PURE__*/React$1.createElement(CheckableButton$1, {
accessibilityLabel: bulkActionsAccessibilityLabel(),
label: headerTitle(),
onToggleAll: handleToggleAll,
plain: true,
disabled: loading
})) : null;
var needsHeader = isSelectable || sortOptions && sortOptions.length > 0 || alternateTool;
var headerWrapperOverlay = loading ? /*#__PURE__*/React$1.createElement("div", {
className: styles['HeaderWrapper-overlay']
}) : null;
var showEmptyState = emptyState && !itemsExist && !loading;
var showEmptySearchState = !showEmptyState && filterControl && !itemsExist && !loading;
var _ref = /*#__PURE__*/React$1.createElement(EventListener$1, {
event: "resize",
handler: handleResize
});
var headerMarkup = !showEmptyState && showHeader !== false && !showEmptySearchState && (showHeader || needsHeader) && listRef.current && /*#__PURE__*/React$1.createElement("div", {
className: styles.HeaderOuterWrapper
}, /*#__PURE__*/React$1.createElement(Sticky$1, {
boundingElement: listRef.current
}, isSticky => {
var headerClassName = classNames(styles.HeaderWrapper, sortOptions && sortOptions.length > 0 && !alternateTool && styles['HeaderWrapper-hasSort'], alternateTool && styles['HeaderWrapper-hasAlternateTool'], isSelectable && styles['HeaderWrapper-hasSelect'], loading && styles['HeaderWrapper-disabled'], isSelectable && selectMode && styles['HeaderWrapper-inSelectMode'], isSticky && styles['HeaderWrapper-isSticky']);
return /*#__PURE__*/React$1.createElement("div", {
className: headerClassName
}, _ref, headerWrapperOverlay, /*#__PURE__*/React$1.createElement("div", {
className: styles.HeaderContentWrapper
}, headerTitleMarkup, checkableButtonMarkup, alternateToolMarkup, sortingSelectMarkup, selectButtonMarkup), bulkActionsMarkup);
}));
var emptySearchStateMarkup = showEmptySearchState ? emptySearchState || /*#__PURE__*/React$1.createElement("div", {
className: styles.EmptySearchResultWrapper
}, /*#__PURE__*/React$1.createElement(EmptySearchResult$1, Object.assign({}, emptySearchResultText, {
withIllustration: true
}))) : null;
var emptyStateMarkup = showEmptyState ? emptyState : null;
var defaultTopPadding = 8;
var topPadding = loadingPosition > 0 ? loadingPosition : defaultTopPadding;
var spinnerStyle = {
paddingTop: "".concat(topPadding, "px")
};
var spinnerSize = items.length < 2 ? 'small' : 'large';
var loadingOverlay = loading ? /*#__PURE__*/React$1.createElement(React$1.Fragment, null, /*#__PURE__*/React$1.createElement("div", {
className: styles.SpinnerContainer,
style: spinnerStyle
}, /*#__PURE__*/React$1.createElement(Spinner$1, {
size: spinnerSize,
accessibilityLabel: "Items are loading"
})), /*#__PURE__*/React$1.createElement("div", {
className: styles.LoadingOverlay
})) : null;
var className = classNames(styles.ItemWrapper, loading && styles['ItemWrapper-isLoading']);
var loadingWithoutItemsMarkup = loading && !itemsExist ? /*#__PURE__*/React$1.createElement("div", {
className: className,
tabIndex: -1
}, loadingOverlay) : null;
var resourceListClassName = classNames(styles.ResourceList, loading && styles.disabledPointerEvents, selectMode && styles.disableTextSelection);
var listMarkup = itemsExist ? /*#__PURE__*/React$1.createElement("ul", {
className: resourceListClassName,
ref: listRef,
"aria-live": "polite",
"aria-busy": loading
}, loadingOverlay, Children.toArray(items.map(renderItemWithId))) : null;
var context = {
selectable: isSelectable,
selectedItems,
selectMode,
resourceName,
loading,
onSelectionChange: handleSelectionChange,
registerCheckableButtons: handleCheckableButtonRegistration
};
return /*#__PURE__*/React$1.createElement(ResourceListContext.Provider, {
value: context
}, /*#__PURE__*/React$1.createElement("div", {
className: styles.ResourceListWrapper
}, filterControlMarkup, headerMarkup, listMarkup, emptySearchStateMarkup, emptyStateMarkup, loadingWithoutItemsMarkup));
};
ResourceList.Item = ResourceItem$1;
export { ResourceList };