UNPKG

14 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import usePreviousProps from '@mui/utils/usePreviousProps';
7import composeClasses from '@mui/utils/composeClasses';
8import useSlotProps from '@mui/utils/useSlotProps';
9import useBadge from "./useBadge.js";
10import { styled } from "../zero-styled/index.js";
11import memoTheme from "../utils/memoTheme.js";
12import createSimplePaletteValueFilter from "../utils/createSimplePaletteValueFilter.js";
13import { useDefaultProps } from "../DefaultPropsProvider/index.js";
14import capitalize from "../utils/capitalize.js";
15import badgeClasses, { getBadgeUtilityClass } from "./badgeClasses.js";
16import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17const RADIUS_STANDARD = 10;
18const RADIUS_DOT = 4;
19const useUtilityClasses = ownerState => {
20 const {
21 color,
22 anchorOrigin,
23 invisible,
24 overlap,
25 variant,
26 classes = {}
27 } = ownerState;
28 const slots = {
29 root: ['root'],
30 badge: ['badge', variant, invisible && 'invisible', `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}`, `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}${capitalize(overlap)}`, `overlap${capitalize(overlap)}`, color !== 'default' && `color${capitalize(color)}`]
31 };
32 return composeClasses(slots, getBadgeUtilityClass, classes);
33};
34const BadgeRoot = styled('span', {
35 name: 'MuiBadge',
36 slot: 'Root',
37 overridesResolver: (props, styles) => styles.root
38})({
39 position: 'relative',
40 display: 'inline-flex',
41 // For correct alignment with the text.
42 verticalAlign: 'middle',
43 flexShrink: 0
44});
45const BadgeBadge = styled('span', {
46 name: 'MuiBadge',
47 slot: 'Badge',
48 overridesResolver: (props, styles) => {
49 const {
50 ownerState
51 } = props;
52 return [styles.badge, styles[ownerState.variant], styles[`anchorOrigin${capitalize(ownerState.anchorOrigin.vertical)}${capitalize(ownerState.anchorOrigin.horizontal)}${capitalize(ownerState.overlap)}`], ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], ownerState.invisible && styles.invisible];
53 }
54})(memoTheme(({
55 theme
56}) => ({
57 display: 'flex',
58 flexDirection: 'row',
59 flexWrap: 'wrap',
60 justifyContent: 'center',
61 alignContent: 'center',
62 alignItems: 'center',
63 position: 'absolute',
64 boxSizing: 'border-box',
65 fontFamily: theme.typography.fontFamily,
66 fontWeight: theme.typography.fontWeightMedium,
67 fontSize: theme.typography.pxToRem(12),
68 minWidth: RADIUS_STANDARD * 2,
69 lineHeight: 1,
70 padding: '0 6px',
71 height: RADIUS_STANDARD * 2,
72 borderRadius: RADIUS_STANDARD,
73 zIndex: 1,
74 // Render the badge on top of potential ripples.
75 transition: theme.transitions.create('transform', {
76 easing: theme.transitions.easing.easeInOut,
77 duration: theme.transitions.duration.enteringScreen
78 }),
79 variants: [...Object.entries(theme.palette).filter(createSimplePaletteValueFilter(['contrastText'])).map(([color]) => ({
80 props: {
81 color
82 },
83 style: {
84 backgroundColor: (theme.vars || theme).palette[color].main,
85 color: (theme.vars || theme).palette[color].contrastText
86 }
87 })), {
88 props: {
89 variant: 'dot'
90 },
91 style: {
92 borderRadius: RADIUS_DOT,
93 height: RADIUS_DOT * 2,
94 minWidth: RADIUS_DOT * 2,
95 padding: 0
96 }
97 }, {
98 props: ({
99 ownerState
100 }) => ownerState.anchorOrigin.vertical === 'top' && ownerState.anchorOrigin.horizontal === 'right' && ownerState.overlap === 'rectangular',
101 style: {
102 top: 0,
103 right: 0,
104 transform: 'scale(1) translate(50%, -50%)',
105 transformOrigin: '100% 0%',
106 [`&.${badgeClasses.invisible}`]: {
107 transform: 'scale(0) translate(50%, -50%)'
108 }
109 }
110 }, {
111 props: ({
112 ownerState
113 }) => ownerState.anchorOrigin.vertical === 'bottom' && ownerState.anchorOrigin.horizontal === 'right' && ownerState.overlap === 'rectangular',
114 style: {
115 bottom: 0,
116 right: 0,
117 transform: 'scale(1) translate(50%, 50%)',
118 transformOrigin: '100% 100%',
119 [`&.${badgeClasses.invisible}`]: {
120 transform: 'scale(0) translate(50%, 50%)'
121 }
122 }
123 }, {
124 props: ({
125 ownerState
126 }) => ownerState.anchorOrigin.vertical === 'top' && ownerState.anchorOrigin.horizontal === 'left' && ownerState.overlap === 'rectangular',
127 style: {
128 top: 0,
129 left: 0,
130 transform: 'scale(1) translate(-50%, -50%)',
131 transformOrigin: '0% 0%',
132 [`&.${badgeClasses.invisible}`]: {
133 transform: 'scale(0) translate(-50%, -50%)'
134 }
135 }
136 }, {
137 props: ({
138 ownerState
139 }) => ownerState.anchorOrigin.vertical === 'bottom' && ownerState.anchorOrigin.horizontal === 'left' && ownerState.overlap === 'rectangular',
140 style: {
141 bottom: 0,
142 left: 0,
143 transform: 'scale(1) translate(-50%, 50%)',
144 transformOrigin: '0% 100%',
145 [`&.${badgeClasses.invisible}`]: {
146 transform: 'scale(0) translate(-50%, 50%)'
147 }
148 }
149 }, {
150 props: ({
151 ownerState
152 }) => ownerState.anchorOrigin.vertical === 'top' && ownerState.anchorOrigin.horizontal === 'right' && ownerState.overlap === 'circular',
153 style: {
154 top: '14%',
155 right: '14%',
156 transform: 'scale(1) translate(50%, -50%)',
157 transformOrigin: '100% 0%',
158 [`&.${badgeClasses.invisible}`]: {
159 transform: 'scale(0) translate(50%, -50%)'
160 }
161 }
162 }, {
163 props: ({
164 ownerState
165 }) => ownerState.anchorOrigin.vertical === 'bottom' && ownerState.anchorOrigin.horizontal === 'right' && ownerState.overlap === 'circular',
166 style: {
167 bottom: '14%',
168 right: '14%',
169 transform: 'scale(1) translate(50%, 50%)',
170 transformOrigin: '100% 100%',
171 [`&.${badgeClasses.invisible}`]: {
172 transform: 'scale(0) translate(50%, 50%)'
173 }
174 }
175 }, {
176 props: ({
177 ownerState
178 }) => ownerState.anchorOrigin.vertical === 'top' && ownerState.anchorOrigin.horizontal === 'left' && ownerState.overlap === 'circular',
179 style: {
180 top: '14%',
181 left: '14%',
182 transform: 'scale(1) translate(-50%, -50%)',
183 transformOrigin: '0% 0%',
184 [`&.${badgeClasses.invisible}`]: {
185 transform: 'scale(0) translate(-50%, -50%)'
186 }
187 }
188 }, {
189 props: ({
190 ownerState
191 }) => ownerState.anchorOrigin.vertical === 'bottom' && ownerState.anchorOrigin.horizontal === 'left' && ownerState.overlap === 'circular',
192 style: {
193 bottom: '14%',
194 left: '14%',
195 transform: 'scale(1) translate(-50%, 50%)',
196 transformOrigin: '0% 100%',
197 [`&.${badgeClasses.invisible}`]: {
198 transform: 'scale(0) translate(-50%, 50%)'
199 }
200 }
201 }, {
202 props: {
203 invisible: true
204 },
205 style: {
206 transition: theme.transitions.create('transform', {
207 easing: theme.transitions.easing.easeInOut,
208 duration: theme.transitions.duration.leavingScreen
209 })
210 }
211 }]
212})));
213function getAnchorOrigin(anchorOrigin) {
214 return {
215 vertical: anchorOrigin?.vertical ?? 'top',
216 horizontal: anchorOrigin?.horizontal ?? 'right'
217 };
218}
219const Badge = /*#__PURE__*/React.forwardRef(function Badge(inProps, ref) {
220 const props = useDefaultProps({
221 props: inProps,
222 name: 'MuiBadge'
223 });
224 const {
225 anchorOrigin: anchorOriginProp,
226 className,
227 classes: classesProp,
228 component,
229 components = {},
230 componentsProps = {},
231 children,
232 overlap: overlapProp = 'rectangular',
233 color: colorProp = 'default',
234 invisible: invisibleProp = false,
235 max: maxProp = 99,
236 badgeContent: badgeContentProp,
237 slots,
238 slotProps,
239 showZero = false,
240 variant: variantProp = 'standard',
241 ...other
242 } = props;
243 const {
244 badgeContent,
245 invisible: invisibleFromHook,
246 max,
247 displayValue: displayValueFromHook
248 } = useBadge({
249 max: maxProp,
250 invisible: invisibleProp,
251 badgeContent: badgeContentProp,
252 showZero
253 });
254 const prevProps = usePreviousProps({
255 anchorOrigin: getAnchorOrigin(anchorOriginProp),
256 color: colorProp,
257 overlap: overlapProp,
258 variant: variantProp,
259 badgeContent: badgeContentProp
260 });
261 const invisible = invisibleFromHook || badgeContent == null && variantProp !== 'dot';
262 const {
263 color = colorProp,
264 overlap = overlapProp,
265 anchorOrigin: anchorOriginPropProp,
266 variant = variantProp
267 } = invisible ? prevProps : props;
268 const anchorOrigin = getAnchorOrigin(anchorOriginPropProp);
269 const displayValue = variant !== 'dot' ? displayValueFromHook : undefined;
270 const ownerState = {
271 ...props,
272 badgeContent,
273 invisible,
274 max,
275 displayValue,
276 showZero,
277 anchorOrigin,
278 color,
279 overlap,
280 variant
281 };
282 const classes = useUtilityClasses(ownerState);
283
284 // support both `slots` and `components` for backward compatibility
285 const RootSlot = slots?.root ?? components.Root ?? BadgeRoot;
286 const BadgeSlot = slots?.badge ?? components.Badge ?? BadgeBadge;
287 const rootSlotProps = slotProps?.root ?? componentsProps.root;
288 const badgeSlotProps = slotProps?.badge ?? componentsProps.badge;
289 const rootProps = useSlotProps({
290 elementType: RootSlot,
291 externalSlotProps: rootSlotProps,
292 externalForwardedProps: other,
293 additionalProps: {
294 ref,
295 as: component
296 },
297 ownerState,
298 className: clsx(rootSlotProps?.className, classes.root, className)
299 });
300 const badgeProps = useSlotProps({
301 elementType: BadgeSlot,
302 externalSlotProps: badgeSlotProps,
303 ownerState,
304 className: clsx(classes.badge, badgeSlotProps?.className)
305 });
306 return /*#__PURE__*/_jsxs(RootSlot, {
307 ...rootProps,
308 children: [children, /*#__PURE__*/_jsx(BadgeSlot, {
309 ...badgeProps,
310 children: displayValue
311 })]
312 });
313});
314process.env.NODE_ENV !== "production" ? Badge.propTypes /* remove-proptypes */ = {
315 // ┌────────────────────────────── Warning ──────────────────────────────┐
316 // │ These PropTypes are generated from the TypeScript type definitions. │
317 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
318 // └─────────────────────────────────────────────────────────────────────┘
319 /**
320 * The anchor of the badge.
321 * @default {
322 * vertical: 'top',
323 * horizontal: 'right',
324 * }
325 */
326 anchorOrigin: PropTypes.shape({
327 horizontal: PropTypes.oneOf(['left', 'right']),
328 vertical: PropTypes.oneOf(['bottom', 'top'])
329 }),
330 /**
331 * The content rendered within the badge.
332 */
333 badgeContent: PropTypes.node,
334 /**
335 * The badge will be added relative to this node.
336 */
337 children: PropTypes.node,
338 /**
339 * Override or extend the styles applied to the component.
340 */
341 classes: PropTypes.object,
342 /**
343 * @ignore
344 */
345 className: PropTypes.string,
346 /**
347 * The color of the component.
348 * It supports both default and custom theme colors, which can be added as shown in the
349 * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors).
350 * @default 'default'
351 */
352 color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['default', 'primary', 'secondary', 'error', 'info', 'success', 'warning']), PropTypes.string]),
353 /**
354 * The component used for the root node.
355 * Either a string to use a HTML element or a component.
356 */
357 component: PropTypes.elementType,
358 /**
359 * The components used for each slot inside.
360 *
361 * @deprecated use the `slots` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details.
362 *
363 * @default {}
364 */
365 components: PropTypes.shape({
366 Badge: PropTypes.elementType,
367 Root: PropTypes.elementType
368 }),
369 /**
370 * The extra props for the slot components.
371 * You can override the existing props or add new ones.
372 *
373 * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details.
374 *
375 * @default {}
376 */
377 componentsProps: PropTypes.shape({
378 badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
379 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
380 }),
381 /**
382 * If `true`, the badge is invisible.
383 * @default false
384 */
385 invisible: PropTypes.bool,
386 /**
387 * Max count to show.
388 * @default 99
389 */
390 max: PropTypes.number,
391 /**
392 * Wrapped shape the badge should overlap.
393 * @default 'rectangular'
394 */
395 overlap: PropTypes.oneOf(['circular', 'rectangular']),
396 /**
397 * Controls whether the badge is hidden when `badgeContent` is zero.
398 * @default false
399 */
400 showZero: PropTypes.bool,
401 /**
402 * The props used for each slot inside the Badge.
403 * @default {}
404 */
405 slotProps: PropTypes.shape({
406 badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
407 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
408 }),
409 /**
410 * The components used for each slot inside the Badge.
411 * Either a string to use a HTML element or a component.
412 * @default {}
413 */
414 slots: PropTypes.shape({
415 badge: PropTypes.elementType,
416 root: PropTypes.elementType
417 }),
418 /**
419 * The system prop that allows defining system overrides as well as additional CSS styles.
420 */
421 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
422 /**
423 * The variant to use.
424 * @default 'standard'
425 */
426 variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['dot', 'standard']), PropTypes.string])
427} : void 0;
428export default Badge;
\No newline at end of file