UNPKG

8.21 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import { isFragment } from 'react-is';
5import PropTypes from 'prop-types';
6import clsx from 'clsx';
7import integerPropType from '@mui/utils/integerPropType';
8import composeClasses from '@mui/utils/composeClasses';
9import useSlotProps from '@mui/utils/useSlotProps';
10import { styled } from "../zero-styled/index.js";
11import { useDefaultProps } from "../DefaultPropsProvider/index.js";
12import Typography from "../Typography/index.js";
13import BreadcrumbCollapsed from "./BreadcrumbCollapsed.js";
14import breadcrumbsClasses, { getBreadcrumbsUtilityClass } from "./breadcrumbsClasses.js";
15import { jsx as _jsx } from "react/jsx-runtime";
16const useUtilityClasses = ownerState => {
17 const {
18 classes
19 } = ownerState;
20 const slots = {
21 root: ['root'],
22 li: ['li'],
23 ol: ['ol'],
24 separator: ['separator']
25 };
26 return composeClasses(slots, getBreadcrumbsUtilityClass, classes);
27};
28const BreadcrumbsRoot = styled(Typography, {
29 name: 'MuiBreadcrumbs',
30 slot: 'Root',
31 overridesResolver: (props, styles) => {
32 return [{
33 [`& .${breadcrumbsClasses.li}`]: styles.li
34 }, styles.root];
35 }
36})({});
37const BreadcrumbsOl = styled('ol', {
38 name: 'MuiBreadcrumbs',
39 slot: 'Ol',
40 overridesResolver: (props, styles) => styles.ol
41})({
42 display: 'flex',
43 flexWrap: 'wrap',
44 alignItems: 'center',
45 padding: 0,
46 margin: 0,
47 listStyle: 'none'
48});
49const BreadcrumbsSeparator = styled('li', {
50 name: 'MuiBreadcrumbs',
51 slot: 'Separator',
52 overridesResolver: (props, styles) => styles.separator
53})({
54 display: 'flex',
55 userSelect: 'none',
56 marginLeft: 8,
57 marginRight: 8
58});
59function insertSeparators(items, className, separator, ownerState) {
60 return items.reduce((acc, current, index) => {
61 if (index < items.length - 1) {
62 acc = acc.concat(current, /*#__PURE__*/_jsx(BreadcrumbsSeparator, {
63 "aria-hidden": true,
64 className: className,
65 ownerState: ownerState,
66 children: separator
67 }, `separator-${index}`));
68 } else {
69 acc.push(current);
70 }
71 return acc;
72 }, []);
73}
74const Breadcrumbs = /*#__PURE__*/React.forwardRef(function Breadcrumbs(inProps, ref) {
75 const props = useDefaultProps({
76 props: inProps,
77 name: 'MuiBreadcrumbs'
78 });
79 const {
80 children,
81 className,
82 component = 'nav',
83 slots = {},
84 slotProps = {},
85 expandText = 'Show path',
86 itemsAfterCollapse = 1,
87 itemsBeforeCollapse = 1,
88 maxItems = 8,
89 separator = '/',
90 ...other
91 } = props;
92 const [expanded, setExpanded] = React.useState(false);
93 const ownerState = {
94 ...props,
95 component,
96 expanded,
97 expandText,
98 itemsAfterCollapse,
99 itemsBeforeCollapse,
100 maxItems,
101 separator
102 };
103 const classes = useUtilityClasses(ownerState);
104 const collapsedIconSlotProps = useSlotProps({
105 elementType: slots.CollapsedIcon,
106 externalSlotProps: slotProps.collapsedIcon,
107 ownerState
108 });
109 const listRef = React.useRef(null);
110 const renderItemsBeforeAndAfter = allItems => {
111 const handleClickExpand = () => {
112 setExpanded(true);
113
114 // The clicked element received the focus but gets removed from the DOM.
115 // Let's keep the focus in the component after expanding.
116 // Moving it to the <ol> or <nav> does not cause any announcement in NVDA.
117 // By moving it to some link/button at least we have some announcement.
118 const focusable = listRef.current.querySelector('a[href],button,[tabindex]');
119 if (focusable) {
120 focusable.focus();
121 }
122 };
123
124 // This defends against someone passing weird input, to ensure that if all
125 // items would be shown anyway, we just show all items without the EllipsisItem
126 if (itemsBeforeCollapse + itemsAfterCollapse >= allItems.length) {
127 if (process.env.NODE_ENV !== 'production') {
128 console.error(['MUI: You have provided an invalid combination of props to the Breadcrumbs.', `itemsAfterCollapse={${itemsAfterCollapse}} + itemsBeforeCollapse={${itemsBeforeCollapse}} >= maxItems={${maxItems}}`].join('\n'));
129 }
130 return allItems;
131 }
132 return [...allItems.slice(0, itemsBeforeCollapse), /*#__PURE__*/_jsx(BreadcrumbCollapsed, {
133 "aria-label": expandText,
134 slots: {
135 CollapsedIcon: slots.CollapsedIcon
136 },
137 slotProps: {
138 collapsedIcon: collapsedIconSlotProps
139 },
140 onClick: handleClickExpand
141 }, "ellipsis"), ...allItems.slice(allItems.length - itemsAfterCollapse, allItems.length)];
142 };
143 const allItems = React.Children.toArray(children).filter(child => {
144 if (process.env.NODE_ENV !== 'production') {
145 if (isFragment(child)) {
146 console.error(["MUI: The Breadcrumbs component doesn't accept a Fragment as a child.", 'Consider providing an array instead.'].join('\n'));
147 }
148 }
149 return /*#__PURE__*/React.isValidElement(child);
150 }).map((child, index) => /*#__PURE__*/_jsx("li", {
151 className: classes.li,
152 children: child
153 }, `child-${index}`));
154 return /*#__PURE__*/_jsx(BreadcrumbsRoot, {
155 ref: ref,
156 component: component,
157 color: "textSecondary",
158 className: clsx(classes.root, className),
159 ownerState: ownerState,
160 ...other,
161 children: /*#__PURE__*/_jsx(BreadcrumbsOl, {
162 className: classes.ol,
163 ref: listRef,
164 ownerState: ownerState,
165 children: insertSeparators(expanded || maxItems && allItems.length <= maxItems ? allItems : renderItemsBeforeAndAfter(allItems), classes.separator, separator, ownerState)
166 })
167 });
168});
169process.env.NODE_ENV !== "production" ? Breadcrumbs.propTypes /* remove-proptypes */ = {
170 // ┌────────────────────────────── Warning ──────────────────────────────┐
171 // │ These PropTypes are generated from the TypeScript type definitions. │
172 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
173 // └─────────────────────────────────────────────────────────────────────┘
174 /**
175 * The content of the component.
176 */
177 children: PropTypes.node,
178 /**
179 * Override or extend the styles applied to the component.
180 */
181 classes: PropTypes.object,
182 /**
183 * @ignore
184 */
185 className: PropTypes.string,
186 /**
187 * The component used for the root node.
188 * Either a string to use a HTML element or a component.
189 */
190 component: PropTypes.elementType,
191 /**
192 * Override the default label for the expand button.
193 *
194 * For localization purposes, you can use the provided [translations](https://mui.com/material-ui/guides/localization/).
195 * @default 'Show path'
196 */
197 expandText: PropTypes.string,
198 /**
199 * If max items is exceeded, the number of items to show after the ellipsis.
200 * @default 1
201 */
202 itemsAfterCollapse: integerPropType,
203 /**
204 * If max items is exceeded, the number of items to show before the ellipsis.
205 * @default 1
206 */
207 itemsBeforeCollapse: integerPropType,
208 /**
209 * Specifies the maximum number of breadcrumbs to display. When there are more
210 * than the maximum number, only the first `itemsBeforeCollapse` and last `itemsAfterCollapse`
211 * will be shown, with an ellipsis in between.
212 * @default 8
213 */
214 maxItems: integerPropType,
215 /**
216 * Custom separator node.
217 * @default '/'
218 */
219 separator: PropTypes.node,
220 /**
221 * The props used for each slot inside the Breadcumb.
222 * @default {}
223 */
224 slotProps: PropTypes.shape({
225 collapsedIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
226 }),
227 /**
228 * The components used for each slot inside the Breadcumb.
229 * Either a string to use a HTML element or a component.
230 * @default {}
231 */
232 slots: PropTypes.shape({
233 CollapsedIcon: PropTypes.elementType
234 }),
235 /**
236 * The system prop that allows defining system overrides as well as additional CSS styles.
237 */
238 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
239} : void 0;
240export default Breadcrumbs;
\No newline at end of file