UNPKG

8.12 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import composeClasses from '@mui/utils/composeClasses';
7import { styled } from "../zero-styled/index.js";
8import memoTheme from "../utils/memoTheme.js";
9import { useDefaultProps } from "../DefaultPropsProvider/index.js";
10import Person from "../internal/svg-icons/Person.js";
11import { getAvatarUtilityClass } from "./avatarClasses.js";
12import useSlot from "../utils/useSlot.js";
13import { jsx as _jsx } from "react/jsx-runtime";
14const useUtilityClasses = ownerState => {
15 const {
16 classes,
17 variant,
18 colorDefault
19 } = ownerState;
20 const slots = {
21 root: ['root', variant, colorDefault && 'colorDefault'],
22 img: ['img'],
23 fallback: ['fallback']
24 };
25 return composeClasses(slots, getAvatarUtilityClass, classes);
26};
27const AvatarRoot = styled('div', {
28 name: 'MuiAvatar',
29 slot: 'Root',
30 overridesResolver: (props, styles) => {
31 const {
32 ownerState
33 } = props;
34 return [styles.root, styles[ownerState.variant], ownerState.colorDefault && styles.colorDefault];
35 }
36})(memoTheme(({
37 theme
38}) => ({
39 position: 'relative',
40 display: 'flex',
41 alignItems: 'center',
42 justifyContent: 'center',
43 flexShrink: 0,
44 width: 40,
45 height: 40,
46 fontFamily: theme.typography.fontFamily,
47 fontSize: theme.typography.pxToRem(20),
48 lineHeight: 1,
49 borderRadius: '50%',
50 overflow: 'hidden',
51 userSelect: 'none',
52 variants: [{
53 props: {
54 variant: 'rounded'
55 },
56 style: {
57 borderRadius: (theme.vars || theme).shape.borderRadius
58 }
59 }, {
60 props: {
61 variant: 'square'
62 },
63 style: {
64 borderRadius: 0
65 }
66 }, {
67 props: {
68 colorDefault: true
69 },
70 style: {
71 color: (theme.vars || theme).palette.background.default,
72 ...(theme.vars ? {
73 backgroundColor: theme.vars.palette.Avatar.defaultBg
74 } : {
75 backgroundColor: theme.palette.grey[400],
76 ...theme.applyStyles('dark', {
77 backgroundColor: theme.palette.grey[600]
78 })
79 })
80 }
81 }]
82})));
83const AvatarImg = styled('img', {
84 name: 'MuiAvatar',
85 slot: 'Img',
86 overridesResolver: (props, styles) => styles.img
87})({
88 width: '100%',
89 height: '100%',
90 textAlign: 'center',
91 // Handle non-square image.
92 objectFit: 'cover',
93 // Hide alt text.
94 color: 'transparent',
95 // Hide the image broken icon, only works on Chrome.
96 textIndent: 10000
97});
98const AvatarFallback = styled(Person, {
99 name: 'MuiAvatar',
100 slot: 'Fallback',
101 overridesResolver: (props, styles) => styles.fallback
102})({
103 width: '75%',
104 height: '75%'
105});
106function useLoaded({
107 crossOrigin,
108 referrerPolicy,
109 src,
110 srcSet
111}) {
112 const [loaded, setLoaded] = React.useState(false);
113 React.useEffect(() => {
114 if (!src && !srcSet) {
115 return undefined;
116 }
117 setLoaded(false);
118 let active = true;
119 const image = new Image();
120 image.onload = () => {
121 if (!active) {
122 return;
123 }
124 setLoaded('loaded');
125 };
126 image.onerror = () => {
127 if (!active) {
128 return;
129 }
130 setLoaded('error');
131 };
132 image.crossOrigin = crossOrigin;
133 image.referrerPolicy = referrerPolicy;
134 image.src = src;
135 if (srcSet) {
136 image.srcset = srcSet;
137 }
138 return () => {
139 active = false;
140 };
141 }, [crossOrigin, referrerPolicy, src, srcSet]);
142 return loaded;
143}
144const Avatar = /*#__PURE__*/React.forwardRef(function Avatar(inProps, ref) {
145 const props = useDefaultProps({
146 props: inProps,
147 name: 'MuiAvatar'
148 });
149 const {
150 alt,
151 children: childrenProp,
152 className,
153 component = 'div',
154 slots = {},
155 slotProps = {},
156 imgProps,
157 sizes,
158 src,
159 srcSet,
160 variant = 'circular',
161 ...other
162 } = props;
163 let children = null;
164
165 // Use a hook instead of onError on the img element to support server-side rendering.
166 const loaded = useLoaded({
167 ...imgProps,
168 src,
169 srcSet
170 });
171 const hasImg = src || srcSet;
172 const hasImgNotFailing = hasImg && loaded !== 'error';
173 const ownerState = {
174 ...props,
175 colorDefault: !hasImgNotFailing,
176 component,
177 variant
178 };
179 // This issue explains why this is required: https://github.com/mui/material-ui/issues/42184
180 delete ownerState.ownerState;
181 const classes = useUtilityClasses(ownerState);
182 const [ImgSlot, imgSlotProps] = useSlot('img', {
183 className: classes.img,
184 elementType: AvatarImg,
185 externalForwardedProps: {
186 slots,
187 slotProps: {
188 img: {
189 ...imgProps,
190 ...slotProps.img
191 }
192 }
193 },
194 additionalProps: {
195 alt,
196 src,
197 srcSet,
198 sizes
199 },
200 ownerState
201 });
202 if (hasImgNotFailing) {
203 children = /*#__PURE__*/_jsx(ImgSlot, {
204 ...imgSlotProps
205 });
206 // We only render valid children, non valid children are rendered with a fallback
207 // We consider that invalid children are all falsy values, except 0, which is valid.
208 } else if (!!childrenProp || childrenProp === 0) {
209 children = childrenProp;
210 } else if (hasImg && alt) {
211 children = alt[0];
212 } else {
213 children = /*#__PURE__*/_jsx(AvatarFallback, {
214 ownerState: ownerState,
215 className: classes.fallback
216 });
217 }
218 return /*#__PURE__*/_jsx(AvatarRoot, {
219 as: component,
220 className: clsx(classes.root, className),
221 ref: ref,
222 ...other,
223 ownerState: ownerState,
224 children: children
225 });
226});
227process.env.NODE_ENV !== "production" ? Avatar.propTypes /* remove-proptypes */ = {
228 // ┌────────────────────────────── Warning ──────────────────────────────┐
229 // │ These PropTypes are generated from the TypeScript type definitions. │
230 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
231 // └─────────────────────────────────────────────────────────────────────┘
232 /**
233 * Used in combination with `src` or `srcSet` to
234 * provide an alt attribute for the rendered `img` element.
235 */
236 alt: PropTypes.string,
237 /**
238 * Used to render icon or text elements inside the Avatar if `src` is not set.
239 * This can be an element, or just a string.
240 */
241 children: PropTypes.node,
242 /**
243 * Override or extend the styles applied to the component.
244 */
245 classes: PropTypes.object,
246 /**
247 * @ignore
248 */
249 className: PropTypes.string,
250 /**
251 * The component used for the root node.
252 * Either a string to use a HTML element or a component.
253 */
254 component: PropTypes.elementType,
255 /**
256 * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) applied to the `img` element if the component is used to display an image.
257 * It can be used to listen for the loading error event.
258 */
259 imgProps: PropTypes.object,
260 /**
261 * The `sizes` attribute for the `img` element.
262 */
263 sizes: PropTypes.string,
264 /**
265 * The props used for each slot inside.
266 * @default {}
267 */
268 slotProps: PropTypes.shape({
269 img: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
270 }),
271 /**
272 * The components used for each slot inside.
273 * @default {}
274 */
275 slots: PropTypes.shape({
276 img: PropTypes.elementType
277 }),
278 /**
279 * The `src` attribute for the `img` element.
280 */
281 src: PropTypes.string,
282 /**
283 * The `srcSet` attribute for the `img` element.
284 * Use this attribute for responsive image display.
285 */
286 srcSet: PropTypes.string,
287 /**
288 * The system prop that allows defining system overrides as well as additional CSS styles.
289 */
290 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
291 /**
292 * The shape of the avatar.
293 * @default 'circular'
294 */
295 variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['circular', 'rounded', 'square']), PropTypes.string])
296} : void 0;
297export default Avatar;
\No newline at end of file