UNPKG

18 kBJavaScriptView Raw
1import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2import _extends from "@babel/runtime/helpers/esm/extends";
3const _excluded = ["avatar", "className", "clickable", "color", "component", "deleteIcon", "disabled", "icon", "label", "onClick", "onDelete", "onKeyDown", "onKeyUp", "size", "variant"];
4import * as React from 'react';
5import PropTypes from 'prop-types';
6import clsx from 'clsx';
7import { unstable_composeClasses as composeClasses } from '@mui/base';
8import { alpha } from '@mui/system';
9import CancelIcon from '../internal/svg-icons/Cancel';
10import useForkRef from '../utils/useForkRef';
11import unsupportedProp from '../utils/unsupportedProp';
12import capitalize from '../utils/capitalize';
13import ButtonBase from '../ButtonBase';
14import useThemeProps from '../styles/useThemeProps';
15import styled from '../styles/styled';
16import chipClasses, { getChipUtilityClass } from './chipClasses';
17import { jsx as _jsx } from "react/jsx-runtime";
18import { jsxs as _jsxs } from "react/jsx-runtime";
19
20const useUtilityClasses = ownerState => {
21 const {
22 classes,
23 disabled,
24 size,
25 color,
26 onDelete,
27 clickable,
28 variant
29 } = ownerState;
30 const slots = {
31 root: ['root', variant, disabled && 'disabled', `size${capitalize(size)}`, `color${capitalize(color)}`, clickable && 'clickable', clickable && `clickableColor${capitalize(color)}`, onDelete && 'deletable', onDelete && `deletableColor${capitalize(color)}`, `${variant}${capitalize(color)}`],
32 label: ['label', `label${capitalize(size)}`],
33 avatar: ['avatar', `avatar${capitalize(size)}`, `avatarColor${capitalize(color)}`],
34 icon: ['icon', `icon${capitalize(size)}`, `iconColor${capitalize(color)}`],
35 deleteIcon: ['deleteIcon', `deleteIcon${capitalize(size)}`, `deleteIconColor${capitalize(color)}`, `deleteIcon${capitalize(variant)}Color${capitalize(color)}`]
36 };
37 return composeClasses(slots, getChipUtilityClass, classes);
38};
39
40const ChipRoot = styled('div', {
41 name: 'MuiChip',
42 slot: 'Root',
43 overridesResolver: (props, styles) => {
44 const {
45 ownerState
46 } = props;
47 const {
48 color,
49 clickable,
50 onDelete,
51 size,
52 variant
53 } = ownerState;
54 return [{
55 [`& .${chipClasses.avatar}`]: styles.avatar
56 }, {
57 [`& .${chipClasses.avatar}`]: styles[`avatar${capitalize(size)}`]
58 }, {
59 [`& .${chipClasses.avatar}`]: styles[`avatarColor${capitalize(color)}`]
60 }, {
61 [`& .${chipClasses.icon}`]: styles.icon
62 }, {
63 [`& .${chipClasses.icon}`]: styles[`icon${capitalize(size)}`]
64 }, {
65 [`& .${chipClasses.icon}`]: styles[`iconColor${capitalize(color)}`]
66 }, {
67 [`& .${chipClasses.deleteIcon}`]: styles.deleteIcon
68 }, {
69 [`& .${chipClasses.deleteIcon}`]: styles[`deleteIcon${capitalize(size)}`]
70 }, {
71 [`& .${chipClasses.deleteIcon}`]: styles[`deleteIconColor${capitalize(color)}`]
72 }, {
73 [`& .${chipClasses.deleteIcon}`]: styles[`deleteIcon${capitalize(variant)}Color${capitalize(color)}`]
74 }, styles.root, styles[`size${capitalize(size)}`], styles[`color${capitalize(color)}`], clickable && styles.clickable, clickable && color !== 'default' && styles[`clickableColor${capitalize(color)})`], onDelete && styles.deletable, onDelete && color !== 'default' && styles[`deletableColor${capitalize(color)}`], styles[variant], styles[`${variant}${capitalize(color)}`]];
75 }
76})(({
77 theme,
78 ownerState
79}) => {
80 const deleteIconColor = alpha(theme.palette.text.primary, 0.26);
81 const textColor = theme.palette.mode === 'light' ? theme.palette.grey[700] : theme.palette.grey[300];
82 return _extends({
83 maxWidth: '100%',
84 fontFamily: theme.typography.fontFamily,
85 fontSize: theme.typography.pxToRem(13),
86 display: 'inline-flex',
87 alignItems: 'center',
88 justifyContent: 'center',
89 height: 32,
90 color: (theme.vars || theme).palette.text.primary,
91 backgroundColor: (theme.vars || theme).palette.action.selected,
92 borderRadius: 32 / 2,
93 whiteSpace: 'nowrap',
94 transition: theme.transitions.create(['background-color', 'box-shadow']),
95 // label will inherit this from root, then `clickable` class overrides this for both
96 cursor: 'default',
97 // We disable the focus ring for mouse, touch and keyboard users.
98 outline: 0,
99 textDecoration: 'none',
100 border: 0,
101 // Remove `button` border
102 padding: 0,
103 // Remove `button` padding
104 verticalAlign: 'middle',
105 boxSizing: 'border-box',
106 [`&.${chipClasses.disabled}`]: {
107 opacity: (theme.vars || theme).palette.action.disabledOpacity,
108 pointerEvents: 'none'
109 },
110 [`& .${chipClasses.avatar}`]: {
111 marginLeft: 5,
112 marginRight: -6,
113 width: 24,
114 height: 24,
115 color: theme.vars ? theme.vars.palette.Chip.defaultAvatarColor : textColor,
116 fontSize: theme.typography.pxToRem(12)
117 },
118 [`& .${chipClasses.avatarColorPrimary}`]: {
119 color: (theme.vars || theme).palette.primary.contrastText,
120 backgroundColor: (theme.vars || theme).palette.primary.dark
121 },
122 [`& .${chipClasses.avatarColorSecondary}`]: {
123 color: (theme.vars || theme).palette.secondary.contrastText,
124 backgroundColor: (theme.vars || theme).palette.secondary.dark
125 },
126 [`& .${chipClasses.avatarSmall}`]: {
127 marginLeft: 4,
128 marginRight: -4,
129 width: 18,
130 height: 18,
131 fontSize: theme.typography.pxToRem(10)
132 },
133 [`& .${chipClasses.icon}`]: _extends({
134 color: theme.vars ? theme.vars.palette.Chip.defaultIconColor : textColor,
135 marginLeft: 5,
136 marginRight: -6
137 }, ownerState.size === 'small' && {
138 fontSize: 18,
139 marginLeft: 4,
140 marginRight: -4
141 }, ownerState.color !== 'default' && {
142 color: 'inherit'
143 }),
144 [`& .${chipClasses.deleteIcon}`]: _extends({
145 WebkitTapHighlightColor: 'transparent',
146 color: theme.vars ? `rgba(${theme.vars.palette.text.primaryChannel} / 0.26)` : deleteIconColor,
147 fontSize: 22,
148 cursor: 'pointer',
149 margin: '0 5px 0 -6px',
150 '&:hover': {
151 color: theme.vars ? `rgba(${theme.vars.palette.text.primaryChannel} / 0.4)` : alpha(deleteIconColor, 0.4)
152 }
153 }, ownerState.size === 'small' && {
154 fontSize: 16,
155 marginRight: 4,
156 marginLeft: -4
157 }, ownerState.color !== 'default' && {
158 color: theme.vars ? `rgba(${theme.vars.palette[ownerState.color].contrastTextChannel} / 0.7)` : alpha(theme.palette[ownerState.color].contrastText, 0.7),
159 '&:hover, &:active': {
160 color: (theme.vars || theme).palette[ownerState.color].contrastText
161 }
162 })
163 }, ownerState.size === 'small' && {
164 height: 24
165 }, ownerState.color !== 'default' && {
166 backgroundColor: (theme.vars || theme).palette[ownerState.color].main,
167 color: (theme.vars || theme).palette[ownerState.color].contrastText
168 }, ownerState.onDelete && {
169 [`&.${chipClasses.focusVisible}`]: {
170 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.action.selectedChannel} / calc(${theme.vars.palette.action.selectedOpacity + theme.vars.palette.action.focusOpacity}))` : alpha(theme.palette.action.selected, theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity)
171 }
172 }, ownerState.onDelete && ownerState.color !== 'default' && {
173 [`&.${chipClasses.focusVisible}`]: {
174 backgroundColor: (theme.vars || theme).palette[ownerState.color].dark
175 }
176 });
177}, ({
178 theme,
179 ownerState
180}) => _extends({}, ownerState.clickable && {
181 userSelect: 'none',
182 WebkitTapHighlightColor: 'transparent',
183 cursor: 'pointer',
184 '&:hover': {
185 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.action.selectedChannel} / calc(${theme.vars.palette.action.selectedOpacity + theme.vars.palette.action.hoverOpacity}))` : alpha(theme.palette.action.selected, theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity)
186 },
187 [`&.${chipClasses.focusVisible}`]: {
188 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.action.selectedChannel} / calc(${theme.vars.palette.action.selectedOpacity + theme.vars.palette.action.focusOpacity}))` : alpha(theme.palette.action.selected, theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity)
189 },
190 '&:active': {
191 boxShadow: (theme.vars || theme).shadows[1]
192 }
193}, ownerState.clickable && ownerState.color !== 'default' && {
194 [`&:hover, &.${chipClasses.focusVisible}`]: {
195 backgroundColor: (theme.vars || theme).palette[ownerState.color].dark
196 }
197}), ({
198 theme,
199 ownerState
200}) => _extends({}, ownerState.variant === 'outlined' && {
201 backgroundColor: 'transparent',
202 border: theme.vars ? `1px solid ${theme.vars.palette.Chip.defaultBorder}` : `1px solid ${theme.palette.mode === 'light' ? theme.palette.grey[400] : theme.palette.grey[700]}`,
203 [`&.${chipClasses.clickable}:hover`]: {
204 backgroundColor: (theme.vars || theme).palette.action.hover
205 },
206 [`&.${chipClasses.focusVisible}`]: {
207 backgroundColor: (theme.vars || theme).palette.action.focus
208 },
209 [`& .${chipClasses.avatar}`]: {
210 marginLeft: 4
211 },
212 [`& .${chipClasses.avatarSmall}`]: {
213 marginLeft: 2
214 },
215 [`& .${chipClasses.icon}`]: {
216 marginLeft: 4
217 },
218 [`& .${chipClasses.iconSmall}`]: {
219 marginLeft: 2
220 },
221 [`& .${chipClasses.deleteIcon}`]: {
222 marginRight: 5
223 },
224 [`& .${chipClasses.deleteIconSmall}`]: {
225 marginRight: 3
226 }
227}, ownerState.variant === 'outlined' && ownerState.color !== 'default' && {
228 color: (theme.vars || theme).palette[ownerState.color].main,
229 border: `1px solid ${theme.vars ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.7)` : alpha(theme.palette[ownerState.color].main, 0.7)}`,
230 [`&.${chipClasses.clickable}:hover`]: {
231 backgroundColor: theme.vars ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / ${theme.vars.palette.action.hoverOpacity})` : alpha(theme.palette[ownerState.color].main, theme.palette.action.hoverOpacity)
232 },
233 [`&.${chipClasses.focusVisible}`]: {
234 backgroundColor: theme.vars ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / ${theme.vars.palette.action.focusOpacity})` : alpha(theme.palette[ownerState.color].main, theme.palette.action.focusOpacity)
235 },
236 [`& .${chipClasses.deleteIcon}`]: {
237 color: theme.vars ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / 0.7)` : alpha(theme.palette[ownerState.color].main, 0.7),
238 '&:hover, &:active': {
239 color: (theme.vars || theme).palette[ownerState.color].main
240 }
241 }
242}));
243const ChipLabel = styled('span', {
244 name: 'MuiChip',
245 slot: 'Label',
246 overridesResolver: (props, styles) => {
247 const {
248 ownerState
249 } = props;
250 const {
251 size
252 } = ownerState;
253 return [styles.label, styles[`label${capitalize(size)}`]];
254 }
255})(({
256 ownerState
257}) => _extends({
258 overflow: 'hidden',
259 textOverflow: 'ellipsis',
260 paddingLeft: 12,
261 paddingRight: 12,
262 whiteSpace: 'nowrap'
263}, ownerState.size === 'small' && {
264 paddingLeft: 8,
265 paddingRight: 8
266}));
267
268function isDeleteKeyboardEvent(keyboardEvent) {
269 return keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete';
270}
271/**
272 * Chips represent complex entities in small blocks, such as a contact.
273 */
274
275
276const Chip = /*#__PURE__*/React.forwardRef(function Chip(inProps, ref) {
277 const props = useThemeProps({
278 props: inProps,
279 name: 'MuiChip'
280 });
281
282 const {
283 avatar: avatarProp,
284 className,
285 clickable: clickableProp,
286 color = 'default',
287 component: ComponentProp,
288 deleteIcon: deleteIconProp,
289 disabled = false,
290 icon: iconProp,
291 label,
292 onClick,
293 onDelete,
294 onKeyDown,
295 onKeyUp,
296 size = 'medium',
297 variant = 'filled'
298 } = props,
299 other = _objectWithoutPropertiesLoose(props, _excluded);
300
301 const chipRef = React.useRef(null);
302 const handleRef = useForkRef(chipRef, ref);
303
304 const handleDeleteIconClick = event => {
305 // Stop the event from bubbling up to the `Chip`
306 event.stopPropagation();
307
308 if (onDelete) {
309 onDelete(event);
310 }
311 };
312
313 const handleKeyDown = event => {
314 // Ignore events from children of `Chip`.
315 if (event.currentTarget === event.target && isDeleteKeyboardEvent(event)) {
316 // Will be handled in keyUp, otherwise some browsers
317 // might init navigation
318 event.preventDefault();
319 }
320
321 if (onKeyDown) {
322 onKeyDown(event);
323 }
324 };
325
326 const handleKeyUp = event => {
327 // Ignore events from children of `Chip`.
328 if (event.currentTarget === event.target) {
329 if (onDelete && isDeleteKeyboardEvent(event)) {
330 onDelete(event);
331 } else if (event.key === 'Escape' && chipRef.current) {
332 chipRef.current.blur();
333 }
334 }
335
336 if (onKeyUp) {
337 onKeyUp(event);
338 }
339 };
340
341 const clickable = clickableProp !== false && onClick ? true : clickableProp;
342 const component = clickable || onDelete ? ButtonBase : ComponentProp || 'div';
343
344 const ownerState = _extends({}, props, {
345 component,
346 disabled,
347 size,
348 color,
349 onDelete: !!onDelete,
350 clickable,
351 variant
352 });
353
354 const classes = useUtilityClasses(ownerState);
355 const moreProps = component === ButtonBase ? _extends({
356 component: ComponentProp || 'div',
357 focusVisibleClassName: classes.focusVisible
358 }, onDelete && {
359 disableRipple: true
360 }) : {};
361 let deleteIcon = null;
362
363 if (onDelete) {
364 deleteIcon = deleteIconProp && /*#__PURE__*/React.isValidElement(deleteIconProp) ? /*#__PURE__*/React.cloneElement(deleteIconProp, {
365 className: clsx(deleteIconProp.props.className, classes.deleteIcon),
366 onClick: handleDeleteIconClick
367 }) : /*#__PURE__*/_jsx(CancelIcon, {
368 className: clsx(classes.deleteIcon),
369 onClick: handleDeleteIconClick
370 });
371 }
372
373 let avatar = null;
374
375 if (avatarProp && /*#__PURE__*/React.isValidElement(avatarProp)) {
376 avatar = /*#__PURE__*/React.cloneElement(avatarProp, {
377 className: clsx(classes.avatar, avatarProp.props.className)
378 });
379 }
380
381 let icon = null;
382
383 if (iconProp && /*#__PURE__*/React.isValidElement(iconProp)) {
384 icon = /*#__PURE__*/React.cloneElement(iconProp, {
385 className: clsx(classes.icon, iconProp.props.className)
386 });
387 }
388
389 if (process.env.NODE_ENV !== 'production') {
390 if (avatar && icon) {
391 console.error('MUI: The Chip component can not handle the avatar ' + 'and the icon prop at the same time. Pick one.');
392 }
393 }
394
395 return /*#__PURE__*/_jsxs(ChipRoot, _extends({
396 as: component,
397 className: clsx(classes.root, className),
398 disabled: clickable && disabled ? true : undefined,
399 onClick: onClick,
400 onKeyDown: handleKeyDown,
401 onKeyUp: handleKeyUp,
402 ref: handleRef,
403 ownerState: ownerState
404 }, moreProps, other, {
405 children: [avatar || icon, /*#__PURE__*/_jsx(ChipLabel, {
406 className: clsx(classes.label),
407 ownerState: ownerState,
408 children: label
409 }), deleteIcon]
410 }));
411});
412process.env.NODE_ENV !== "production" ? Chip.propTypes
413/* remove-proptypes */
414= {
415 // ----------------------------- Warning --------------------------------
416 // | These PropTypes are generated from the TypeScript type definitions |
417 // | To update them edit the d.ts file and run "yarn proptypes" |
418 // ----------------------------------------------------------------------
419
420 /**
421 * The Avatar element to display.
422 */
423 avatar: PropTypes.element,
424
425 /**
426 * This prop isn't supported.
427 * Use the `component` prop if you need to change the children structure.
428 */
429 children: unsupportedProp,
430
431 /**
432 * Override or extend the styles applied to the component.
433 */
434 classes: PropTypes.object,
435
436 /**
437 * @ignore
438 */
439 className: PropTypes.string,
440
441 /**
442 * If `true`, the chip will appear clickable, and will raise when pressed,
443 * even if the onClick prop is not defined.
444 * If `false`, the chip will not appear clickable, even if onClick prop is defined.
445 * This can be used, for example,
446 * along with the component prop to indicate an anchor Chip is clickable.
447 * Note: this controls the UI and does not affect the onClick event.
448 */
449 clickable: PropTypes.bool,
450
451 /**
452 * The color of the component.
453 * It supports both default and custom theme colors, which can be added as shown in the
454 * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors).
455 * @default 'default'
456 */
457 color: PropTypes
458 /* @typescript-to-proptypes-ignore */
459 .oneOfType([PropTypes.oneOf(['default', 'primary', 'secondary', 'error', 'info', 'success', 'warning']), PropTypes.string]),
460
461 /**
462 * The component used for the root node.
463 * Either a string to use a HTML element or a component.
464 */
465 component: PropTypes.elementType,
466
467 /**
468 * Override the default delete icon element. Shown only if `onDelete` is set.
469 */
470 deleteIcon: PropTypes.element,
471
472 /**
473 * If `true`, the component is disabled.
474 * @default false
475 */
476 disabled: PropTypes.bool,
477
478 /**
479 * Icon element.
480 */
481 icon: PropTypes.element,
482
483 /**
484 * The content of the component.
485 */
486 label: PropTypes.node,
487
488 /**
489 * @ignore
490 */
491 onClick: PropTypes.func,
492
493 /**
494 * Callback fired when the delete icon is clicked.
495 * If set, the delete icon will be shown.
496 */
497 onDelete: PropTypes.func,
498
499 /**
500 * @ignore
501 */
502 onKeyDown: PropTypes.func,
503
504 /**
505 * @ignore
506 */
507 onKeyUp: PropTypes.func,
508
509 /**
510 * The size of the component.
511 * @default 'medium'
512 */
513 size: PropTypes
514 /* @typescript-to-proptypes-ignore */
515 .oneOfType([PropTypes.oneOf(['medium', 'small']), PropTypes.string]),
516
517 /**
518 * The system prop that allows defining system overrides as well as additional CSS styles.
519 */
520 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
521
522 /**
523 * The variant to use.
524 * @default 'filled'
525 */
526 variant: PropTypes
527 /* @typescript-to-proptypes-ignore */
528 .oneOfType([PropTypes.oneOf(['filled', 'outlined']), PropTypes.string])
529} : void 0;
530export default Chip;
\No newline at end of file