@hanzogui/avatar 2.0.0-rc.41-hanzoai.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Avatar.tsx ADDED
@@ -0,0 +1,212 @@
1
+ // forked from radix https://github.com/radix-ui/primitives/blob/main/packages/react/avatar/src/Avatar.tsx
2
+
3
+ import type { GetProps, SizeTokens, HanzoguiElement } from '@hanzogui/core'
4
+ import { getTokens, getVariableValue, styled } from '@hanzogui/core'
5
+ import type { Scope } from '@hanzogui/create-context'
6
+ import { createContextScope } from '@hanzogui/create-context'
7
+ import { withStaticProperties } from '@hanzogui/helpers'
8
+ import type { ImageProps } from '@hanzogui/image'
9
+ import { Image } from '@hanzogui/image'
10
+ import { Square, getShapeSize } from '@hanzogui/shapes'
11
+ import { YStack } from '@hanzogui/stacks'
12
+ import * as React from 'react'
13
+
14
+ const AVATAR_NAME = 'Avatar'
15
+
16
+ type ScopedProps<P> = P & { __scopeAvatar?: Scope }
17
+ const [createAvatarContext, createAvatarScope] = createContextScope(AVATAR_NAME)
18
+
19
+ type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error'
20
+
21
+ type AvatarContextValue = {
22
+ size: SizeTokens
23
+ imageLoadingStatus: ImageLoadingStatus
24
+ onImageLoadingStatusChange(status: ImageLoadingStatus): void
25
+ }
26
+
27
+ const [AvatarProvider, useAvatarContext] =
28
+ createAvatarContext<AvatarContextValue>(AVATAR_NAME)
29
+
30
+ /* -------------------------------------------------------------------------------------------------
31
+ * AvatarImage
32
+ * -----------------------------------------------------------------------------------------------*/
33
+
34
+ const IMAGE_NAME = 'AvatarImage'
35
+
36
+ type AvatarImageProps = Partial<ImageProps> & {
37
+ onLoadingStatusChange?: (status: ImageLoadingStatus) => void
38
+ }
39
+
40
+ const AvatarImage = React.forwardRef<HanzoguiElement, AvatarImageProps>(
41
+ (props: ScopedProps<AvatarImageProps>, forwardedRef) => {
42
+ const {
43
+ __scopeAvatar,
44
+ src,
45
+ source,
46
+ onLoadingStatusChange = () => {},
47
+ ...imageProps
48
+ } = props
49
+ const context = useAvatarContext(IMAGE_NAME, __scopeAvatar)
50
+ const [status, setStatus] = React.useState<ImageLoadingStatus>('idle')
51
+ const shapeSize = getVariableValue(
52
+ getShapeSize(
53
+ context.size,
54
+ // @ts-expect-error
55
+ { tokens: getTokens() }
56
+ )?.width
57
+ ) as number
58
+
59
+ // Support both `src` (web) and `source` (RN) props
60
+ const resolvedSrc =
61
+ src ||
62
+ (source && typeof source === 'object' && 'uri' in source ? source.uri : source)
63
+
64
+ React.useEffect(() => {
65
+ // If src is falsy, immediately set error status so fallback renders
66
+ if (!resolvedSrc) {
67
+ setStatus('error')
68
+ } else {
69
+ setStatus('idle')
70
+ }
71
+ }, [resolvedSrc])
72
+
73
+ React.useEffect(() => {
74
+ onLoadingStatusChange(status)
75
+ context.onImageLoadingStatusChange(status)
76
+ }, [status])
77
+
78
+ // Don't render Image if src is falsy to avoid Android warning
79
+ if (!resolvedSrc) {
80
+ return null
81
+ }
82
+
83
+ return (
84
+ <YStack fullscreen zIndex={1}>
85
+ <Image
86
+ position="absolute"
87
+ top={0}
88
+ left={0}
89
+ right={0}
90
+ bottom={0}
91
+ objectFit="cover"
92
+ {...(typeof shapeSize === 'number' &&
93
+ !Number.isNaN(shapeSize) && {
94
+ width: shapeSize,
95
+ height: shapeSize,
96
+ })}
97
+ {...imageProps}
98
+ // @ts-ignore
99
+ ref={forwardedRef}
100
+ // @ts-ignore
101
+ src={resolvedSrc}
102
+ // onLoadStart={() => {
103
+ // // setStatus('loading')
104
+ // }}
105
+ onError={() => {
106
+ setStatus('error')
107
+ }}
108
+ onLoad={() => {
109
+ setStatus('loaded')
110
+ }}
111
+ />
112
+ </YStack>
113
+ )
114
+ }
115
+ )
116
+
117
+ AvatarImage.displayName = IMAGE_NAME
118
+
119
+ /* -------------------------------------------------------------------------------------------------
120
+ * AvatarFallback
121
+ * -----------------------------------------------------------------------------------------------*/
122
+
123
+ const FALLBACK_NAME = 'AvatarFallback'
124
+
125
+ export const AvatarFallbackFrame = styled(YStack, {
126
+ name: FALLBACK_NAME,
127
+ position: 'absolute',
128
+ fullscreen: true,
129
+ zIndex: 0,
130
+ })
131
+
132
+ type AvatarFallbackExtraProps = {
133
+ delayMs?: number
134
+ }
135
+ type AvatarFallbackProps = GetProps<typeof AvatarFallbackFrame> & AvatarFallbackExtraProps
136
+
137
+ const AvatarFallback = AvatarFallbackFrame.styleable<
138
+ ScopedProps<AvatarFallbackExtraProps>
139
+ >((props, forwardedRef) => {
140
+ const { __scopeAvatar, delayMs, ...fallbackProps } = props
141
+ const context = useAvatarContext(FALLBACK_NAME, __scopeAvatar)
142
+ const [canRender, setCanRender] = React.useState(delayMs === undefined)
143
+
144
+ React.useEffect(() => {
145
+ if (delayMs !== undefined) {
146
+ const timerId = setTimeout(() => setCanRender(true), delayMs)
147
+ return () => clearTimeout(timerId)
148
+ }
149
+ }, [delayMs])
150
+
151
+ return canRender && context.imageLoadingStatus !== 'loaded' ? (
152
+ <AvatarFallbackFrame {...fallbackProps} ref={forwardedRef} />
153
+ ) : null
154
+ })
155
+
156
+ AvatarFallback.displayName = FALLBACK_NAME
157
+
158
+ /* -------------------------------------------------------------------------------------------------
159
+ * Avatar
160
+ * -----------------------------------------------------------------------------------------------*/
161
+
162
+ export const AvatarFrame = styled(Square, {
163
+ name: AVATAR_NAME,
164
+ position: 'relative',
165
+ overflow: 'hidden',
166
+ })
167
+
168
+ type AvatarProps = GetProps<typeof AvatarFrame>
169
+
170
+ /**
171
+ * @summary A component that displays an image or a fallback icon.
172
+ * @see — Docs https://hanzogui.dev/ui/avatar
173
+ *
174
+ * @example
175
+ * ```tsx
176
+ * <Avatar circular size="$10">
177
+ * <Avatar.Image
178
+ * aria-label="Cam"
179
+ * src="https://images.unsplash.com/photo-1548142813-c348350df52b?&w=150&h=150&dpr=2&q=80"
180
+ * />
181
+ * <Avatar.Fallback backgroundColor="$blue10" />
182
+ * </Avatar>
183
+ * ```
184
+ */
185
+ const Avatar = withStaticProperties(
186
+ React.forwardRef<HanzoguiElement, AvatarProps>(
187
+ (props: ScopedProps<AvatarProps>, forwardedRef) => {
188
+ const { __scopeAvatar, size = '$true', ...avatarProps } = props
189
+ const [imageLoadingStatus, setImageLoadingStatus] =
190
+ React.useState<ImageLoadingStatus>('idle')
191
+ return (
192
+ <AvatarProvider
193
+ size={size}
194
+ scope={__scopeAvatar}
195
+ imageLoadingStatus={imageLoadingStatus}
196
+ onImageLoadingStatusChange={setImageLoadingStatus}
197
+ >
198
+ <AvatarFrame size={size} {...avatarProps} ref={forwardedRef} />
199
+ </AvatarProvider>
200
+ )
201
+ }
202
+ ),
203
+ {
204
+ Image: AvatarImage,
205
+ Fallback: AvatarFallback,
206
+ }
207
+ )
208
+
209
+ Avatar.displayName = AVATAR_NAME
210
+
211
+ export { createAvatarScope, Avatar, AvatarImage, AvatarFallback }
212
+ export type { AvatarProps, AvatarImageProps, AvatarFallbackProps }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './Avatar'
@@ -0,0 +1,105 @@
1
+ import type { GetProps, SizeTokens, HanzoguiElement } from '@hanzogui/core';
2
+ import type { Scope } from '@hanzogui/create-context';
3
+ import type { ImageProps } from '@hanzogui/image';
4
+ import * as React from 'react';
5
+ declare const createAvatarScope: import("@hanzogui/create-context").CreateScope;
6
+ type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error';
7
+ type AvatarImageProps = Partial<ImageProps> & {
8
+ onLoadingStatusChange?: (status: ImageLoadingStatus) => void;
9
+ };
10
+ declare const AvatarImage: React.ForwardRefExoticComponent<Partial<ImageProps> & {
11
+ onLoadingStatusChange?: (status: ImageLoadingStatus) => void;
12
+ } & React.RefAttributes<HanzoguiElement>>;
13
+ export declare const AvatarFallbackFrame: import("@hanzogui/core").HanzoguiComponent<import("@hanzogui/core").TamaDefer, HanzoguiElement, import("@hanzogui/core").RNHanzoguiViewNonStyleProps, import("@hanzogui/core").StackStyleBase, {
14
+ elevation?: number | SizeTokens | undefined;
15
+ fullscreen?: boolean | undefined;
16
+ }, import("@hanzogui/core").StaticConfigPublic>;
17
+ type AvatarFallbackExtraProps = {
18
+ delayMs?: number;
19
+ };
20
+ type AvatarFallbackProps = GetProps<typeof AvatarFallbackFrame> & AvatarFallbackExtraProps;
21
+ declare const AvatarFallback: import("@hanzogui/core").HanzoguiComponent<Omit<import("@hanzogui/core").GetFinalProps<import("@hanzogui/core").RNHanzoguiViewNonStyleProps, import("@hanzogui/core").StackStyleBase, {
22
+ elevation?: number | SizeTokens | undefined;
23
+ fullscreen?: boolean | undefined;
24
+ }>, "delayMs" | "__scopeAvatar"> & AvatarFallbackExtraProps & {
25
+ __scopeAvatar?: Scope;
26
+ }, HanzoguiElement, import("@hanzogui/core").RNHanzoguiViewNonStyleProps & AvatarFallbackExtraProps & {
27
+ __scopeAvatar?: Scope;
28
+ }, import("@hanzogui/core").StackStyleBase, {
29
+ elevation?: number | SizeTokens | undefined;
30
+ fullscreen?: boolean | undefined;
31
+ }, import("@hanzogui/core").StaticConfigPublic>;
32
+ export declare const AvatarFrame: import("@hanzogui/core").HanzoguiComponent<import("@hanzogui/core").TamaDefer, HanzoguiElement, import("@hanzogui/core").RNHanzoguiViewNonStyleProps, import("@hanzogui/core").StackStyleBase, {
33
+ elevation?: number | import("@hanzogui/web").SizeTokens | undefined;
34
+ size?: number | import("@hanzogui/web").SizeTokens | undefined;
35
+ transparent?: boolean | undefined;
36
+ fullscreen?: boolean | undefined;
37
+ circular?: boolean | undefined;
38
+ elevate?: boolean | undefined;
39
+ bordered?: boolean | undefined;
40
+ chromeless?: boolean | "all" | undefined;
41
+ }, import("@hanzogui/core").StaticConfigPublic & {
42
+ memo: true;
43
+ }>;
44
+ type AvatarProps = GetProps<typeof AvatarFrame>;
45
+ /**
46
+ * @summary A component that displays an image or a fallback icon.
47
+ * @see — Docs https://hanzogui.dev/ui/avatar
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * <Avatar circular size="$10">
52
+ * <Avatar.Image
53
+ * aria-label="Cam"
54
+ * src="https://images.unsplash.com/photo-1548142813-c348350df52b?&w=150&h=150&dpr=2&q=80"
55
+ * />
56
+ * <Avatar.Fallback backgroundColor="$blue10" />
57
+ * </Avatar>
58
+ * ```
59
+ */
60
+ declare const Avatar: React.ForwardRefExoticComponent<Omit<import("@hanzogui/core").RNHanzoguiViewNonStyleProps, "size" | "elevation" | keyof import("@hanzogui/core").StackStyleBase | "fullscreen" | "transparent" | "circular" | "elevate" | "bordered" | "chromeless"> & import("@hanzogui/core").WithThemeValues<import("@hanzogui/core").StackStyleBase> & {
61
+ elevation?: number | import("@hanzogui/web").SizeTokens | undefined;
62
+ size?: number | import("@hanzogui/web").SizeTokens | undefined;
63
+ transparent?: boolean | undefined;
64
+ fullscreen?: boolean | undefined;
65
+ circular?: boolean | undefined;
66
+ elevate?: boolean | undefined;
67
+ bordered?: boolean | undefined;
68
+ chromeless?: boolean | "all" | undefined;
69
+ } & import("@hanzogui/core").WithShorthands<import("@hanzogui/core").WithThemeValues<import("@hanzogui/core").StackStyleBase>> & import("@hanzogui/core").WithPseudoProps<import("@hanzogui/core").WithThemeValues<import("@hanzogui/core").StackStyleBase> & {
70
+ elevation?: number | import("@hanzogui/web").SizeTokens | undefined;
71
+ size?: number | import("@hanzogui/web").SizeTokens | undefined;
72
+ transparent?: boolean | undefined;
73
+ fullscreen?: boolean | undefined;
74
+ circular?: boolean | undefined;
75
+ elevate?: boolean | undefined;
76
+ bordered?: boolean | undefined;
77
+ chromeless?: boolean | "all" | undefined;
78
+ } & import("@hanzogui/core").WithShorthands<import("@hanzogui/core").WithThemeValues<import("@hanzogui/core").StackStyleBase>>> & import("@hanzogui/core").WithMediaProps<import("@hanzogui/core").WithThemeShorthandsAndPseudos<import("@hanzogui/core").StackStyleBase, {
79
+ elevation?: number | import("@hanzogui/web").SizeTokens | undefined;
80
+ size?: number | import("@hanzogui/web").SizeTokens | undefined;
81
+ transparent?: boolean | undefined;
82
+ fullscreen?: boolean | undefined;
83
+ circular?: boolean | undefined;
84
+ elevate?: boolean | undefined;
85
+ bordered?: boolean | undefined;
86
+ chromeless?: boolean | "all" | undefined;
87
+ }>> & React.RefAttributes<HanzoguiElement>> & {
88
+ Image: React.ForwardRefExoticComponent<Partial<ImageProps> & {
89
+ onLoadingStatusChange?: (status: ImageLoadingStatus) => void;
90
+ } & React.RefAttributes<HanzoguiElement>>;
91
+ Fallback: import("@hanzogui/core").HanzoguiComponent<Omit<import("@hanzogui/core").GetFinalProps<import("@hanzogui/core").RNHanzoguiViewNonStyleProps, import("@hanzogui/core").StackStyleBase, {
92
+ elevation?: number | SizeTokens | undefined;
93
+ fullscreen?: boolean | undefined;
94
+ }>, "delayMs" | "__scopeAvatar"> & AvatarFallbackExtraProps & {
95
+ __scopeAvatar?: Scope;
96
+ }, HanzoguiElement, import("@hanzogui/core").RNHanzoguiViewNonStyleProps & AvatarFallbackExtraProps & {
97
+ __scopeAvatar?: Scope;
98
+ }, import("@hanzogui/core").StackStyleBase, {
99
+ elevation?: number | SizeTokens | undefined;
100
+ fullscreen?: boolean | undefined;
101
+ }, import("@hanzogui/core").StaticConfigPublic>;
102
+ };
103
+ export { createAvatarScope, Avatar, AvatarImage, AvatarFallback };
104
+ export type { AvatarProps, AvatarImageProps, AvatarFallbackProps };
105
+ //# sourceMappingURL=Avatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../src/Avatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAE3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAIjD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,QAAA,MAA4B,iBAAiB,gDAAmC,CAAA;AAEhF,KAAK,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAiBjE,KAAK,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG;IAC5C,qBAAqB,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAA;CAC7D,CAAA;AAED,QAAA,MAAM,WAAW;4BAHS,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI;yCA8E7D,CAAA;AAUD,eAAO,MAAM,mBAAmB;;;+CAK9B,CAAA;AAEF,KAAK,wBAAwB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AACD,KAAK,mBAAmB,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,GAAG,wBAAwB,CAAA;AAE1F,QAAA,MAAM,cAAc;;;;oBAzHwB,KAAK;;oBAAL,KAAK;;;;+CA0I/C,CAAA;AAQF,eAAO,MAAM,WAAW;;;;;;;;;;;EAItB,CAAA;AAEF,KAAK,WAAW,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAA;AAE/C;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCApJc,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI;;;;;;wBArBlB,KAAK;;wBAAL,KAAK;;;;;CA+LhD,CAAA;AAID,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,CAAA;AACjE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './Avatar';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA"}