UNPKG

@mskcc/carbon-react

Version:

Carbon react components for the MSKCC DSM

383 lines (370 loc) 13.1 kB
/** * MSKCC 2021, 2024 */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var PropTypes = require('prop-types'); var React = require('react'); var cx = require('classnames'); var IconButton = require('../IconButton/IconButton.js'); var usePrefix = require('../../internal/usePrefix.js'); var MskIcon = require('../Icon/MskIcon.js'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes); var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); var _option; const translationIds = { 'carbon.pagination-nav.next': 'Next', 'carbon.pagination-nav.previous': 'Previous', 'carbon.pagination-nav.item': 'Page', 'carbon.pagination-nav.active': 'Active', 'carbon.pagination-nav.of': 'of' }; function translateWithId(messageId) { return translationIds[messageId]; } // https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state function usePrevious(value) { const ref = React.useRef(); React.useEffect(() => { ref.current = value; }); return ref.current; } function getCuts(page, totalItems, itemsThatFit) { let splitPoint = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; if (itemsThatFit >= totalItems) { return { front: 0, back: 0 }; } const split = splitPoint || Math.ceil(itemsThatFit / 2) - 1; let frontHidden = page + 1 - split; let backHidden = totalItems - page - (itemsThatFit - split) + 1; if (frontHidden <= 1) { backHidden -= frontHidden <= 0 ? Math.abs(frontHidden) + 1 : 0; frontHidden = 0; } if (backHidden <= 1) { frontHidden -= backHidden <= 0 ? Math.abs(backHidden) + 1 : 0; backHidden = 0; } return { front: frontHidden, back: backHidden }; } function DirectionButton(_ref) { let { direction, label, disabled, onClick } = _ref; const prefix = usePrefix.usePrefix(); return /*#__PURE__*/React__default["default"].createElement("li", { className: `${prefix}--pagination-nav__list-item` }, /*#__PURE__*/React__default["default"].createElement(IconButton.IconButton, { align: "bottom", disabled: disabled, kind: "ghost", label: label, onClick: onClick }, direction === 'forward' ? 'arrow_right' : 'arrow_left')); } function PaginationItem(_ref2) { let { page, isActive, onClick, translateWithId: t = translateWithId } = _ref2; const prefix = usePrefix.usePrefix(); const itemLabel = t('carbon.pagination-nav.item'); return /*#__PURE__*/React__default["default"].createElement("li", { className: `${prefix}--pagination-nav__list-item` }, /*#__PURE__*/React__default["default"].createElement("button", { type: "button", className: cx__default["default"](`${prefix}--pagination-nav__page`, { [`${prefix}--pagination-nav__page--active`]: isActive }), onClick: onClick, "data-page": page, "aria-current": isActive ? 'page' : null }, /*#__PURE__*/React__default["default"].createElement("span", { className: `${prefix}--pagination-nav__accessibility-label` }, isActive ? `${t('carbon.pagination-nav.active')}, ${itemLabel}` : itemLabel), page)); } function PaginationOverflow(_ref3) { let { fromIndex, count, onSelect, translateWithId: t = translateWithId } = _ref3; const prefix = usePrefix.usePrefix(); if (count > 1) { return /*#__PURE__*/React__default["default"].createElement("li", { className: `${prefix}--pagination-nav__list-item` }, /*#__PURE__*/React__default["default"].createElement("div", { className: `${prefix}--pagination-nav__select` }, /*#__PURE__*/React__default["default"].createElement("select", { className: `${prefix}--pagination-nav__page ${prefix}--pagination-nav__page--select`, "aria-label": `Select ${t('carbon.pagination-nav.item')} number`, onChange: e => { const index = Number(e.target.value); onSelect(index); } }, _option || (_option = /*#__PURE__*/React__default["default"].createElement("option", { value: "", hidden: true })), [...Array(count)].map((e, i) => /*#__PURE__*/React__default["default"].createElement("option", { value: (fromIndex + i).toString(), "data-page": fromIndex + i + 1, key: `overflow-${fromIndex + i}` }, fromIndex + i + 1))), /*#__PURE__*/React__default["default"].createElement("div", { className: `${prefix}--pagination-nav__select-icon-wrapper` }, /*#__PURE__*/React__default["default"].createElement(MskIcon.Icon, { size: "sm", icon: "more_horiz", className: `${prefix}--pagination-nav__select-icon` })))); } if (count === 1) { return /*#__PURE__*/React__default["default"].createElement(PaginationItem, { page: fromIndex + 1, translateWithId: t, onClick: () => { onSelect(fromIndex); } }); } return null; } const PaginationNav = /*#__PURE__*/React__default["default"].forwardRef(function PaginationNav(_ref4, ref) { let { className, onChange = () => {}, totalItems, itemsShown = 10, page = 0, loop = false, translateWithId: t = translateWithId, ...rest } = _ref4; const [currentPage, setCurrentPage] = React.useState(page); const [itemsThatFit, setItemsThatFit] = React.useState(itemsShown >= 4 ? itemsShown : 4); const [cuts, setCuts] = React.useState(getCuts(currentPage, totalItems, itemsThatFit)); const prevPage = usePrevious(currentPage); const prefix = usePrefix.usePrefix(); function jumpToItem(index) { if (index >= 0 && index < totalItems) { setCurrentPage(index); onChange(index); } } function jumpToNext() { const nextIndex = currentPage + 1; if (nextIndex >= totalItems) { if (loop) { jumpToItem(0); } } else { jumpToItem(nextIndex); } } function jumpToPrevious() { const previousIndex = currentPage - 1; if (previousIndex < 0) { if (loop) { jumpToItem(totalItems - 1); } } else { jumpToItem(previousIndex); } } function pageWouldBeHidden(page) { const startOffset = itemsThatFit <= 4 && page > 1 ? 0 : 1; const wouldBeHiddenInFront = page >= startOffset && page <= cuts.front; const wouldBeHiddenInBack = page >= totalItems - cuts.back - 1 && page <= totalItems - 2; return wouldBeHiddenInFront || wouldBeHiddenInBack; } // jump to new page if props.page is updated React.useEffect(() => { setCurrentPage(page); }, [page]); // re-calculate cuts if props.totalItems or props.itemsShown change React.useEffect(() => { setItemsThatFit(itemsShown >= 4 ? itemsShown : 4); setCuts(getCuts(currentPage, totalItems, itemsShown)); }, [totalItems, itemsShown]); // eslint-disable-line react-hooks/exhaustive-deps // update cuts if necessary whenever currentPage changes React.useEffect(() => { if (pageWouldBeHidden(currentPage)) { const delta = currentPage - prevPage || 0; if (delta > 0) { const splitPoint = itemsThatFit - 3; setCuts(getCuts(currentPage, totalItems, itemsThatFit, splitPoint)); } else { const splitPoint = itemsThatFit > 4 ? 2 : 1; setCuts(getCuts(currentPage, totalItems, itemsThatFit, splitPoint)); } } }, [currentPage]); // eslint-disable-line react-hooks/exhaustive-deps const classNames = cx__default["default"](`${prefix}--pagination-nav`, className); const backwardButtonDisabled = !loop && currentPage === 0; const forwardButtonDisabled = !loop && currentPage === totalItems - 1; const startOffset = itemsThatFit <= 4 && currentPage > 1 ? 0 : 1; return /*#__PURE__*/React__default["default"].createElement("nav", _rollupPluginBabelHelpers["extends"]({ className: classNames, ref: ref }, rest, { "aria-label": "pagination" }), /*#__PURE__*/React__default["default"].createElement("ul", { className: `${prefix}--pagination-nav__list` }, /*#__PURE__*/React__default["default"].createElement(DirectionButton, { direction: "backward", label: t('carbon.pagination-nav.previous'), disabled: backwardButtonDisabled, onClick: jumpToPrevious }), // render first item if at least 5 items can be displayed or // 4 items can be displayed and the current page is either 0 or 1 (itemsThatFit >= 5 || itemsThatFit <= 4 && currentPage <= 1) && /*#__PURE__*/React__default["default"].createElement(PaginationItem, { page: 1, translateWithId: t, isActive: currentPage === 0, onClick: () => { jumpToItem(0); } }), /*#__PURE__*/React__default["default"].createElement(PaginationOverflow, { fromIndex: startOffset, count: cuts.front, onSelect: jumpToItem }), // render items between overflows [...Array(totalItems)].map((e, i) => i).slice(startOffset + cuts.front, (1 + cuts.back) * -1).map(item => /*#__PURE__*/React__default["default"].createElement(PaginationItem, { key: `item-${item}`, page: item + 1, translateWithId: t, isActive: currentPage === item, onClick: () => { jumpToItem(item); } })), /*#__PURE__*/React__default["default"].createElement(PaginationOverflow, { fromIndex: totalItems - cuts.back - 1, count: cuts.back, onSelect: jumpToItem }), // render last item unless there is only one in total totalItems > 1 && /*#__PURE__*/React__default["default"].createElement(PaginationItem, { page: totalItems, translateWithId: t, isActive: currentPage === totalItems - 1, onClick: () => { jumpToItem(totalItems - 1); } }), /*#__PURE__*/React__default["default"].createElement(DirectionButton, { direction: "forward", label: t('carbon.pagination-nav.next'), disabled: forwardButtonDisabled, onClick: jumpToNext })), /*#__PURE__*/React__default["default"].createElement("div", { "aria-live": "polite", "aria-atomic": "true", className: `${prefix}--pagination-nav__accessibility-label` }, `${t('carbon.pagination-nav.item')} ${currentPage + 1} ${t('carbon.pagination-nav.of')} ${totalItems}`)); }); DirectionButton.propTypes = { /** * The direction this button represents ("forward" or "backward"). */ direction: PropTypes__default["default"].oneOf(['forward', 'backward']), /** * Whether or not the button should be disabled. */ disabled: PropTypes__default["default"].bool, /** * The label shown in the button's tooltip. */ label: PropTypes__default["default"].string, /** * The callback function called when the button is clicked. */ onClick: PropTypes__default["default"].func }; PaginationItem.propTypes = { /** * Whether or not this is the currently active page. */ isActive: PropTypes__default["default"].bool, /** * The callback function called when the item is clicked. */ onClick: PropTypes__default["default"].func, /** * The page number this item represents. */ page: PropTypes__default["default"].number, /** * Specify a custom translation function that takes in a message identifier * and returns the localized string for the message */ translateWithId: PropTypes__default["default"].func }; PaginationOverflow.propTypes = { /** * How many items to display in this overflow. */ count: PropTypes__default["default"].number, /** * From which index on this overflow should start displaying pages. */ fromIndex: PropTypes__default["default"].number, /** * The callback function called when the user selects a page from the overflow. */ onSelect: PropTypes__default["default"].func, /** * Specify a custom translation function that takes in a message identifier * and returns the localized string for the message */ translateWithId: PropTypes__default["default"].func }; PaginationNav.displayName = 'PaginationNav'; PaginationNav.propTypes = { /** * Additional CSS class names. */ className: PropTypes__default["default"].string, /** * The number of items to be shown. */ itemsShown: PropTypes__default["default"].number, /** * Whether user should be able to loop through the items when reaching first / last. */ loop: PropTypes__default["default"].bool, /** * The callback function called when the current page changes. */ onChange: PropTypes__default["default"].func, /** * The index of current page. */ page: PropTypes__default["default"].number, /** * The total number of items. */ totalItems: PropTypes__default["default"].number, /** * Specify a custom translation function that takes in a message identifier * and returns the localized string for the message */ translateWithId: PropTypes__default["default"].func }; var PaginationNav$1 = PaginationNav; exports["default"] = PaginationNav$1;