UNPKG

@mui/material

Version:

React components that implement Google's Material Design.

239 lines (237 loc) 8.23 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; const _excluded = ["children", "className", "component", "slots", "slotProps", "expandText", "itemsAfterCollapse", "itemsBeforeCollapse", "maxItems", "separator"]; import * as React from 'react'; import { isFragment } from 'react-is'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import { integerPropType } from '@mui/utils'; import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; import Typography from '../Typography'; import BreadcrumbCollapsed from './BreadcrumbCollapsed'; import breadcrumbsClasses, { getBreadcrumbsUtilityClass } from './breadcrumbsClasses'; import { jsx as _jsx } from "react/jsx-runtime"; const useUtilityClasses = ownerState => { const { classes } = ownerState; const slots = { root: ['root'], li: ['li'], ol: ['ol'], separator: ['separator'] }; return composeClasses(slots, getBreadcrumbsUtilityClass, classes); }; const BreadcrumbsRoot = styled(Typography, { name: 'MuiBreadcrumbs', slot: 'Root', overridesResolver: (props, styles) => { return [{ [`& .${breadcrumbsClasses.li}`]: styles.li }, styles.root]; } })({}); const BreadcrumbsOl = styled('ol', { name: 'MuiBreadcrumbs', slot: 'Ol', overridesResolver: (props, styles) => styles.ol })({ display: 'flex', flexWrap: 'wrap', alignItems: 'center', padding: 0, margin: 0, listStyle: 'none' }); const BreadcrumbsSeparator = styled('li', { name: 'MuiBreadcrumbs', slot: 'Separator', overridesResolver: (props, styles) => styles.separator })({ display: 'flex', userSelect: 'none', marginLeft: 8, marginRight: 8 }); function insertSeparators(items, className, separator, ownerState) { return items.reduce((acc, current, index) => { if (index < items.length - 1) { acc = acc.concat(current, /*#__PURE__*/_jsx(BreadcrumbsSeparator, { "aria-hidden": true, className: className, ownerState: ownerState, children: separator }, `separator-${index}`)); } else { acc.push(current); } return acc; }, []); } const Breadcrumbs = /*#__PURE__*/React.forwardRef(function Breadcrumbs(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiBreadcrumbs' }); const { children, className, component = 'nav', slots = {}, slotProps = {}, expandText = 'Show path', itemsAfterCollapse = 1, itemsBeforeCollapse = 1, maxItems = 8, separator = '/' } = props, other = _objectWithoutPropertiesLoose(props, _excluded); const [expanded, setExpanded] = React.useState(false); const ownerState = _extends({}, props, { component, expanded, expandText, itemsAfterCollapse, itemsBeforeCollapse, maxItems, separator }); const classes = useUtilityClasses(ownerState); const collapsedIconSlotProps = useSlotProps({ elementType: slots.CollapsedIcon, externalSlotProps: slotProps.collapsedIcon, ownerState }); const listRef = React.useRef(null); const renderItemsBeforeAndAfter = allItems => { const handleClickExpand = () => { setExpanded(true); // The clicked element received the focus but gets removed from the DOM. // Let's keep the focus in the component after expanding. // Moving it to the <ol> or <nav> does not cause any announcement in NVDA. // By moving it to some link/button at least we have some announcement. const focusable = listRef.current.querySelector('a[href],button,[tabindex]'); if (focusable) { focusable.focus(); } }; // This defends against someone passing weird input, to ensure that if all // items would be shown anyway, we just show all items without the EllipsisItem if (itemsBeforeCollapse + itemsAfterCollapse >= allItems.length) { if (process.env.NODE_ENV !== 'production') { console.error(['MUI: You have provided an invalid combination of props to the Breadcrumbs.', `itemsAfterCollapse={${itemsAfterCollapse}} + itemsBeforeCollapse={${itemsBeforeCollapse}} >= maxItems={${maxItems}}`].join('\n')); } return allItems; } return [...allItems.slice(0, itemsBeforeCollapse), /*#__PURE__*/_jsx(BreadcrumbCollapsed, { "aria-label": expandText, slots: { CollapsedIcon: slots.CollapsedIcon }, slotProps: { collapsedIcon: collapsedIconSlotProps }, onClick: handleClickExpand }, "ellipsis"), ...allItems.slice(allItems.length - itemsAfterCollapse, allItems.length)]; }; const allItems = React.Children.toArray(children).filter(child => { if (process.env.NODE_ENV !== 'production') { if (isFragment(child)) { console.error(["MUI: The Breadcrumbs component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n')); } } return /*#__PURE__*/React.isValidElement(child); }).map((child, index) => /*#__PURE__*/_jsx("li", { className: classes.li, children: child }, `child-${index}`)); return /*#__PURE__*/_jsx(BreadcrumbsRoot, _extends({ ref: ref, component: component, color: "text.secondary", className: clsx(classes.root, className), ownerState: ownerState }, other, { children: /*#__PURE__*/_jsx(BreadcrumbsOl, { className: classes.ol, ref: listRef, ownerState: ownerState, children: insertSeparators(expanded || maxItems && allItems.length <= maxItems ? allItems : renderItemsBeforeAndAfter(allItems), classes.separator, separator, ownerState) }) })); }); process.env.NODE_ENV !== "production" ? Breadcrumbs.propTypes /* remove-proptypes */ = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the d.ts file and run "yarn proptypes" | // ---------------------------------------------------------------------- /** * The content of the component. */ children: PropTypes.node, /** * Override or extend the styles applied to the component. */ classes: PropTypes.object, /** * @ignore */ className: PropTypes.string, /** * The component used for the root node. * Either a string to use a HTML element or a component. */ component: PropTypes.elementType, /** * Override the default label for the expand button. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'Show path' */ expandText: PropTypes.string, /** * If max items is exceeded, the number of items to show after the ellipsis. * @default 1 */ itemsAfterCollapse: integerPropType, /** * If max items is exceeded, the number of items to show before the ellipsis. * @default 1 */ itemsBeforeCollapse: integerPropType, /** * Specifies the maximum number of breadcrumbs to display. When there are more * than the maximum number, only the first `itemsBeforeCollapse` and last `itemsAfterCollapse` * will be shown, with an ellipsis in between. * @default 8 */ maxItems: integerPropType, /** * Custom separator node. * @default '/' */ separator: PropTypes.node, /** * The props used for each slot inside the Breadcumb. * @default {} */ slotProps: PropTypes.shape({ collapsedIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]) }), /** * The components used for each slot inside the Breadcumb. * Either a string to use a HTML element or a component. * @default {} */ slots: PropTypes.shape({ CollapsedIcon: PropTypes.elementType }), /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]) } : void 0; export default Breadcrumbs;