UNPKG

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