rnnovaui 0.4.0 → 0.6.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnnovaui",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "react-native": "src/index.js",
@@ -0,0 +1,386 @@
1
+ import React, { memo, useMemo } from "react";
2
+
3
+ import { ActivityIndicator, Image, StyleSheet, Text, View } from "react-native";
4
+
5
+ import { useUITheme } from "../../theme";
6
+
7
+ const AVATAR_SIZES = {
8
+ xs: "xs",
9
+ sm: "sm",
10
+ md: "md",
11
+ lg: "lg",
12
+ xl: "xl",
13
+ xxl: "xxl",
14
+ };
15
+
16
+ const AVATAR_VARIANTS = {
17
+ circle: "circle",
18
+ rounded: "rounded",
19
+ square: "square",
20
+ };
21
+
22
+ const AVATAR_STATUS = {
23
+ online: "online",
24
+ offline: "offline",
25
+ busy: "busy",
26
+ away: "away",
27
+ };
28
+
29
+ const AVATAR_FALLBACK_COLORS = {
30
+ primary: "#FF5A1F",
31
+ secondary: "#7CFF32",
32
+ white: "#FFFFFF",
33
+ text: "#111111",
34
+ textSecondary: "#525252",
35
+ border: "#E5E5E5",
36
+ success: "#16A34A",
37
+ danger: "#DC2626",
38
+ warning: "#D97706",
39
+ muted: "#737373",
40
+ };
41
+
42
+ function UIAvatarComponent({
43
+ source,
44
+
45
+ uri,
46
+
47
+ name,
48
+
49
+ initials,
50
+
51
+ icon,
52
+
53
+ size = "md",
54
+
55
+ variant = "circle",
56
+
57
+ backgroundColor,
58
+
59
+ textColor,
60
+
61
+ borderColor,
62
+
63
+ borderWidth = 0,
64
+
65
+ status,
66
+
67
+ statusColor,
68
+
69
+ statusSize,
70
+
71
+ loading = false,
72
+
73
+ fallback = true,
74
+
75
+ style,
76
+
77
+ imageStyle,
78
+
79
+ textStyle,
80
+
81
+ iconContainerStyle,
82
+
83
+ statusStyle,
84
+
85
+ testID,
86
+
87
+ ...props
88
+ }) {
89
+ const { theme } = useUITheme();
90
+
91
+ const colors = theme?.colors || {};
92
+
93
+ const safeSize = AVATAR_SIZES[size] ? size : AVATAR_SIZES.md;
94
+
95
+ const safeVariant = AVATAR_VARIANTS[variant]
96
+ ? variant
97
+ : AVATAR_VARIANTS.circle;
98
+
99
+ const dimensions = useMemo(() => getDimensions(safeSize), [safeSize]);
100
+
101
+ const radius = useMemo(
102
+ () => getRadius(safeVariant, dimensions.size, theme),
103
+ [safeVariant, dimensions.size, theme],
104
+ );
105
+
106
+ const resolvedBackground =
107
+ backgroundColor || colors.primary || AVATAR_FALLBACK_COLORS.primary;
108
+
109
+ const resolvedTextColor =
110
+ textColor || colors.onPrimary || AVATAR_FALLBACK_COLORS.white;
111
+
112
+ const resolvedBorder =
113
+ borderColor || colors.border || AVATAR_FALLBACK_COLORS.border;
114
+
115
+ const imageSource = source || (uri ? { uri } : null);
116
+
117
+ const resolvedInitials = initials || getInitials(name);
118
+
119
+ const resolvedStatusColor = statusColor || getStatusColor(status, colors);
120
+
121
+ const resolvedStatusSize = statusSize || dimensions.statusSize;
122
+
123
+ const shouldShowFallback = fallback && !imageSource;
124
+
125
+ return (
126
+ <View
127
+ {...props}
128
+ testID={testID}
129
+ style={[
130
+ styles.wrapper,
131
+
132
+ {
133
+ width: dimensions.size,
134
+
135
+ height: dimensions.size,
136
+ },
137
+
138
+ style,
139
+ ]}
140
+ >
141
+ <View
142
+ style={[
143
+ styles.avatar,
144
+
145
+ {
146
+ width: dimensions.size,
147
+
148
+ height: dimensions.size,
149
+
150
+ borderRadius: radius,
151
+
152
+ backgroundColor: resolvedBackground,
153
+
154
+ borderColor: resolvedBorder,
155
+
156
+ borderWidth,
157
+ },
158
+ ]}
159
+ >
160
+ {loading ? (
161
+ <ActivityIndicator size="small" color={resolvedTextColor} />
162
+ ) : imageSource ? (
163
+ <Image
164
+ source={imageSource}
165
+ resizeMode="cover"
166
+ style={[
167
+ styles.image,
168
+
169
+ {
170
+ borderRadius: radius,
171
+ },
172
+
173
+ imageStyle,
174
+ ]}
175
+ />
176
+ ) : shouldShowFallback ? (
177
+ <Text
178
+ numberOfLines={1}
179
+ style={[
180
+ styles.initials,
181
+
182
+ {
183
+ color: resolvedTextColor,
184
+
185
+ fontSize: dimensions.fontSize,
186
+
187
+ lineHeight: dimensions.fontSize + 4,
188
+ },
189
+
190
+ textStyle,
191
+ ]}
192
+ >
193
+ {resolvedInitials}
194
+ </Text>
195
+ ) : icon ? (
196
+ <View style={[styles.iconContainer, iconContainerStyle]}>{icon}</View>
197
+ ) : null}
198
+ </View>
199
+
200
+ {status ? (
201
+ <View
202
+ style={[
203
+ styles.status,
204
+
205
+ {
206
+ width: resolvedStatusSize,
207
+
208
+ height: resolvedStatusSize,
209
+
210
+ borderRadius: resolvedStatusSize / 2,
211
+
212
+ backgroundColor: resolvedStatusColor,
213
+
214
+ borderColor:
215
+ colors.card ||
216
+ colors.background ||
217
+ AVATAR_FALLBACK_COLORS.white,
218
+
219
+ borderWidth: 2,
220
+ },
221
+
222
+ statusStyle,
223
+ ]}
224
+ />
225
+ ) : null}
226
+ </View>
227
+ );
228
+ }
229
+
230
+ function getInitials(name) {
231
+ if (!name) {
232
+ return "?";
233
+ }
234
+
235
+ const parts = String(name).trim().split(/\s+/).filter(Boolean);
236
+
237
+ if (parts.length === 1) {
238
+ return parts[0].slice(0, 2).toUpperCase();
239
+ }
240
+
241
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
242
+ }
243
+
244
+ function getDimensions(size) {
245
+ switch (size) {
246
+ case AVATAR_SIZES.xs:
247
+ return {
248
+ size: 28,
249
+ fontSize: 10,
250
+ statusSize: 8,
251
+ };
252
+
253
+ case AVATAR_SIZES.sm:
254
+ return {
255
+ size: 36,
256
+ fontSize: 12,
257
+ statusSize: 10,
258
+ };
259
+
260
+ case AVATAR_SIZES.lg:
261
+ return {
262
+ size: 64,
263
+ fontSize: 20,
264
+ statusSize: 16,
265
+ };
266
+
267
+ case AVATAR_SIZES.xl:
268
+ return {
269
+ size: 80,
270
+ fontSize: 24,
271
+ statusSize: 18,
272
+ };
273
+
274
+ case AVATAR_SIZES.xxl:
275
+ return {
276
+ size: 104,
277
+ fontSize: 30,
278
+ statusSize: 22,
279
+ };
280
+
281
+ case AVATAR_SIZES.md:
282
+ default:
283
+ return {
284
+ size: 48,
285
+ fontSize: 16,
286
+ statusSize: 13,
287
+ };
288
+ }
289
+ }
290
+
291
+ function getRadius(variant, size, theme) {
292
+ switch (variant) {
293
+ case AVATAR_VARIANTS.square:
294
+ return theme?.radius?.sm ?? 8;
295
+
296
+ case AVATAR_VARIANTS.rounded:
297
+ return theme?.radius?.md ?? 12;
298
+
299
+ case AVATAR_VARIANTS.circle:
300
+ default:
301
+ return size / 2;
302
+ }
303
+ }
304
+
305
+ function getStatusColor(status, colors) {
306
+ switch (status) {
307
+ case AVATAR_STATUS.online:
308
+ return colors.success || AVATAR_FALLBACK_COLORS.success;
309
+
310
+ case AVATAR_STATUS.busy:
311
+ return colors.danger || AVATAR_FALLBACK_COLORS.danger;
312
+
313
+ case AVATAR_STATUS.away:
314
+ return colors.warning || AVATAR_FALLBACK_COLORS.warning;
315
+
316
+ case AVATAR_STATUS.offline:
317
+ return colors.textMuted || AVATAR_FALLBACK_COLORS.muted;
318
+
319
+ default:
320
+ return colors.success || AVATAR_FALLBACK_COLORS.success;
321
+ }
322
+ }
323
+
324
+ const styles = StyleSheet.create({
325
+ wrapper: {
326
+ position: "relative",
327
+
328
+ flexShrink: 0,
329
+ },
330
+
331
+ avatar: {
332
+ alignItems: "center",
333
+
334
+ justifyContent: "center",
335
+
336
+ overflow: "hidden",
337
+ },
338
+
339
+ image: {
340
+ width: "100%",
341
+
342
+ height: "100%",
343
+ },
344
+
345
+ initials: {
346
+ fontWeight: "700",
347
+
348
+ includeFontPadding: false,
349
+
350
+ textAlign: "center",
351
+
352
+ textTransform: "uppercase",
353
+ },
354
+
355
+ iconContainer: {
356
+ alignItems: "center",
357
+
358
+ justifyContent: "center",
359
+
360
+ width: "100%",
361
+
362
+ height: "100%",
363
+ },
364
+
365
+ status: {
366
+ position: "absolute",
367
+
368
+ right: -1,
369
+
370
+ bottom: -1,
371
+
372
+ zIndex: 10,
373
+ },
374
+ });
375
+
376
+ export const UIAvatar = memo(UIAvatarComponent);
377
+
378
+ UIAvatar.displayName = "UIAvatar";
379
+
380
+ export {
381
+ AVATAR_SIZES as UIAvatarSizes,
382
+ AVATAR_VARIANTS as UIAvatarVariants,
383
+ AVATAR_STATUS as UIAvatarStatus,
384
+ };
385
+
386
+ export default UIAvatar;
@@ -0,0 +1,6 @@
1
+ export {
2
+ UIAvatar,
3
+ UIAvatarSizes,
4
+ UIAvatarVariants,
5
+ UIAvatarStatus,
6
+ } from "./UIAvatar";