@expokit/cli 0.4.3 → 0.5.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/README.md +30 -3
- package/bin/expo-kit.js +749 -288
- package/bin/lib.mjs +313 -4
- package/package.json +1 -1
- package/registry/_docs/action-sheet.json +1 -1
- package/registry/_docs/alert.json +6 -0
- package/registry/_docs/bottom-sheet.json +6 -0
- package/registry/_docs/bullet.json +6 -0
- package/registry/_docs/dialog.json +1 -1
- package/registry/_docs/drawer.json +3 -3
- package/registry/_docs/flip-calendar.json +106 -0
- package/registry/_docs/glass-search-bar.json +1 -1
- package/registry/_docs/grain.json +6 -1
- package/registry/_docs/isometric-bar.json +6 -0
- package/registry/_docs/liquidglass-tab.json +1 -1
- package/registry/_docs/password-input.json +1 -1
- package/registry/_docs/paywall.json +144 -0
- package/registry/_docs/pixel-bar.json +6 -0
- package/registry/_docs/polar-area.json +6 -0
- package/registry/_docs/qr-code-button.json +9 -3
- package/registry/_docs/ruler-time-picker.json +147 -0
- package/registry/_docs/scratch-card.json +7 -1
- package/registry/_docs/search-bar.json +1 -1
- package/registry/_docs/select.json +2 -2
- package/registry/_docs/slide-to-confirm.json +2 -2
- package/registry/_docs/spinner.json +1 -1
- package/registry/_docs/stateful-button.json +2 -2
- package/registry/_docs/swipe-deck.json +1 -1
- package/registry/_docs/time-picker.json +2 -2
- package/registry/_docs/timeline-chart.json +6 -0
- package/registry/_docs/toast.json +15 -3
- package/registry/_docs/treemap.json +6 -0
- package/registry/_docs/waterfall.json +6 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +6 -4
- package/registry/accordion/files/src/lib/kit.tsx +462 -12
- package/registry/accordion.json +1 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +50 -58
- package/registry/action-sheet/files/src/lib/kit.tsx +462 -12
- package/registry/action-sheet.json +1 -0
- package/registry/alert/files/src/components/ui/alert.tsx +20 -13
- package/registry/alert/files/src/lib/kit.tsx +462 -12
- package/registry/alert.json +3 -4
- package/registry/assistant-app.json +1 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +89 -74
- package/registry/avatar/files/src/lib/kit.tsx +462 -12
- package/registry/avatar.json +1 -0
- package/registry/badge/files/src/components/ui/badge.tsx +36 -36
- package/registry/badge/files/src/lib/kit.tsx +462 -12
- package/registry/badge.json +1 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +39 -44
- package/registry/bottom-nav/files/src/lib/kit.tsx +462 -12
- package/registry/bottom-nav.json +5 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +357 -300
- package/registry/bottom-sheet/files/src/lib/kit.tsx +462 -12
- package/registry/bottom-sheet.json +5 -1
- package/registry/bullet.json +1 -0
- package/registry/button/files/src/components/ui/button.tsx +23 -34
- package/registry/button/files/src/lib/kit.tsx +462 -12
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +6 -9
- package/registry/button-3d/files/src/lib/kit.tsx +462 -12
- package/registry/button-3d.json +1 -0
- package/registry/button.json +1 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +77 -79
- package/registry/calendar/files/src/lib/kit.tsx +462 -12
- package/registry/calendar.json +1 -0
- package/registry/card/files/src/components/ui/card.tsx +7 -6
- package/registry/card/files/src/lib/kit.tsx +462 -12
- package/registry/card-stack.json +1 -0
- package/registry/card.json +1 -0
- package/registry/carousel.json +1 -0
- package/registry/chat.json +7 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +4 -4
- package/registry/checkbox/files/src/lib/kit.tsx +462 -12
- package/registry/checkbox.json +1 -0
- package/registry/checkout.json +1 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +24 -24
- package/registry/chip-group/files/src/lib/kit.tsx +462 -12
- package/registry/chip-group.json +1 -0
- package/registry/confetti/files/src/components/ui/confetti.tsx +15 -14
- package/registry/confetti.json +3 -6
- package/registry/coupon/files/src/components/ui/coupon.tsx +47 -52
- package/registry/coupon/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/coupon/files/src/lib/kit.tsx +462 -12
- package/registry/coupon/files/src/lib/ticket-shape.tsx +54 -44
- package/registry/coupon.json +1 -0
- package/registry/cube-pager.json +1 -0
- package/registry/dashboard.json +1 -0
- package/registry/date-picker/files/src/components/ui/calendar.tsx +77 -79
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +64 -71
- package/registry/date-picker/files/src/lib/kit.tsx +462 -12
- package/registry/date-picker.json +1 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +66 -68
- package/registry/dialog/files/src/lib/kit.tsx +462 -12
- package/registry/dialog.json +1 -0
- package/registry/donut-shop.json +1 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +76 -88
- package/registry/drawer/files/src/lib/kit.tsx +462 -12
- package/registry/drawer.json +1 -0
- package/registry/fab.json +1 -0
- package/registry/fintech-app.json +1 -0
- package/registry/flip-calendar/files/src/components/ui/flip-calendar.tsx +632 -0
- package/registry/flip-calendar/files/src/lib/kit.tsx +672 -0
- package/registry/flip-calendar.json +24 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/flip-card/files/src/lib/kit.tsx +462 -12
- package/registry/flip-card.json +1 -0
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +33 -33
- package/registry/floating-input/files/src/lib/kit.tsx +462 -12
- package/registry/floating-input.json +1 -0
- package/registry/food-app.json +1 -0
- package/registry/gift-card.json +1 -0
- package/registry/glass-button.json +5 -0
- package/registry/glass-nav-bar.json +1 -0
- package/registry/glass-search-bar.json +1 -0
- package/registry/glass-segmented.json +1 -0
- package/registry/glass-sheet.json +1 -0
- package/registry/glass-tab-bar.json +5 -0
- package/registry/glass.json +1 -0
- package/registry/grain/files/src/components/ui/grain.tsx +16 -14
- package/registry/grain.json +8 -6
- package/registry/inbox.json +1 -0
- package/registry/isometric-bar.json +1 -0
- package/registry/kit/files/src/lib/kit.tsx +462 -12
- package/registry/kit.json +1 -0
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +0 -1
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +29 -16
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +33 -19
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +1 -1
- package/registry/liquidglass-tab.json +1 -0
- package/registry/list-item/files/src/components/ui/list-item.tsx +43 -43
- package/registry/list-item/files/src/lib/kit.tsx +462 -12
- package/registry/list-item.json +1 -0
- package/registry/loyalty-card.json +1 -0
- package/registry/morphing-button-group.json +1 -0
- package/registry/number-ticker.json +1 -0
- package/registry/offers.json +1 -0
- package/registry/onboarding.json +1 -0
- package/registry/orb-reveal.json +1 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +49 -30
- package/registry/otp-input/files/src/lib/kit.tsx +462 -12
- package/registry/otp-input.json +1 -0
- package/registry/parallax-layers.json +1 -0
- package/registry/parallax-scroll.json +1 -0
- package/registry/password-input/files/src/components/ui/floating-input.tsx +33 -33
- package/registry/password-input/files/src/components/ui/password-input.tsx +29 -25
- package/registry/password-input/files/src/lib/kit.tsx +462 -12
- package/registry/password-input.json +1 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +9 -14
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +26 -20
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/payment-card/files/src/lib/kit.tsx +462 -12
- package/registry/payment-card.json +1 -0
- package/registry/paywall.json +23 -0
- package/registry/pixel-bar.json +1 -0
- package/registry/polar-area.json +1 -0
- package/registry/profile.json +1 -0
- package/registry/progress/files/src/components/ui/progress.tsx +31 -29
- package/registry/progress/files/src/lib/kit.tsx +462 -12
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +21 -21
- package/registry/progress-steps/files/src/lib/kit.tsx +462 -12
- package/registry/progress-steps.json +1 -0
- package/registry/progress.json +1 -0
- package/registry/qr-code-button.json +1 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +19 -19
- package/registry/radio-group/files/src/lib/kit.tsx +462 -12
- package/registry/radio-group.json +1 -0
- package/registry/rating.json +1 -0
- package/registry/ring-carousel.json +1 -0
- package/registry/ruler-time-picker/files/src/components/ui/ruler-time-picker.tsx +832 -0
- package/registry/ruler-time-picker/files/src/lib/kit.tsx +672 -0
- package/registry/ruler-time-picker.json +26 -0
- package/registry/scratch-card.json +1 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +28 -28
- package/registry/search-bar/files/src/lib/kit.tsx +462 -12
- package/registry/search-bar.json +1 -0
- package/registry/search-results.json +4 -0
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +11 -17
- package/registry/segmented-control/files/src/lib/kit.tsx +462 -12
- package/registry/segmented-control.json +1 -0
- package/registry/select/files/src/components/ui/select.tsx +107 -104
- package/registry/select/files/src/lib/kit.tsx +462 -12
- package/registry/select.json +6 -0
- package/registry/settings.json +1 -0
- package/registry/sign-in.json +1 -0
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +149 -80
- package/registry/skeleton/files/src/lib/kit.tsx +462 -12
- package/registry/skeleton.json +1 -0
- package/registry/slide-to-confirm.json +1 -0
- package/registry/slider/files/src/components/ui/slider.tsx +106 -108
- package/registry/slider/files/src/lib/kit.tsx +462 -12
- package/registry/slider.json +5 -0
- package/registry/spin-wheel.json +1 -0
- package/registry/spinner/files/src/components/ui/spinner.tsx +39 -29
- package/registry/spinner/files/src/lib/kit.tsx +462 -12
- package/registry/spinner.json +6 -0
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +39 -32
- package/registry/stateful-button/files/src/lib/kit.tsx +462 -12
- package/registry/stateful-button.json +5 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +40 -38
- package/registry/stepper/files/src/lib/kit.tsx +462 -12
- package/registry/stepper.json +1 -0
- package/registry/swipe-deck.json +1 -0
- package/registry/swipeable-row.json +4 -0
- package/registry/switch/files/src/components/ui/switch.tsx +41 -47
- package/registry/switch/files/src/lib/kit.tsx +462 -12
- package/registry/switch.json +1 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +61 -64
- package/registry/tabs/files/src/lib/kit.tsx +462 -12
- package/registry/tabs.json +1 -0
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +55 -42
- package/registry/text-animation/files/src/lib/kit.tsx +462 -12
- package/registry/text-animation.json +7 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +10 -10
- package/registry/textarea/files/src/lib/kit.tsx +462 -12
- package/registry/textarea.json +1 -0
- package/registry/ticket-card.json +1 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +462 -12
- package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +54 -44
- package/registry/ticket-shape.json +1 -0
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +80 -101
- package/registry/tilt-card/files/src/lib/kit.tsx +462 -12
- package/registry/tilt-card.json +1 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +136 -131
- package/registry/time-picker/files/src/lib/kit.tsx +462 -12
- package/registry/time-picker.json +1 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +89 -74
- package/registry/timeline/files/src/components/ui/timeline.tsx +49 -57
- package/registry/timeline/files/src/lib/kit.tsx +462 -12
- package/registry/timeline-chart.json +1 -0
- package/registry/timeline.json +1 -0
- package/registry/toast/files/src/components/ui/toast.tsx +543 -465
- package/registry/toast/files/src/lib/kit.tsx +462 -12
- package/registry/toast.json +7 -5
- package/registry/treemap.json +1 -0
- package/registry/waterfall.json +1 -0
- package/registry/wheel-list.json +1 -0
- package/schema/components.schema.json +1 -0
- package/registry/alert/files/src/components/ui/button.tsx +0 -303
- package/registry/confetti/files/src/lib/kit.tsx +0 -222
- package/registry/grain/files/src/lib/kit.tsx +0 -222
- package/registry/toast/files/src/components/ui/button.tsx +0 -303
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Haptics from 'expo-haptics';
|
|
2
|
-
import React, { useEffect, useId,
|
|
2
|
+
import React, { useEffect, useId, useState } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
AccessibilityInfo,
|
|
5
5
|
Animated,
|
|
@@ -25,41 +25,41 @@ export type AvatarFallback = 'soft' | 'solid' | 'gradient';
|
|
|
25
25
|
|
|
26
26
|
export interface AvatarProps {
|
|
27
27
|
/** Image URL. Initials show while it loads, and stay if it fails. */
|
|
28
|
-
src?: string;
|
|
28
|
+
src?: string | undefined;
|
|
29
29
|
/** Used for the initials, the fallback colour (same name → same colour) and the accessibility label. */
|
|
30
|
-
name?: string;
|
|
30
|
+
name?: string | undefined;
|
|
31
31
|
/** 'xs' 24 · 'sm' 32 · 'md' 40 · 'lg' 56 · 'xl' 72, or any number of points. */
|
|
32
|
-
size?: AvatarSize | number;
|
|
33
|
-
shape?: AvatarShape;
|
|
32
|
+
size?: AvatarSize | number | undefined;
|
|
33
|
+
shape?: AvatarShape | undefined;
|
|
34
34
|
/** Presence dot cut into the bottom-right edge. */
|
|
35
|
-
status?: AvatarStatus;
|
|
35
|
+
status?: AvatarStatus | undefined;
|
|
36
36
|
/** Soft pulse behind the status dot (default: on for 'online'). */
|
|
37
|
-
pulse?: boolean;
|
|
37
|
+
pulse?: boolean | undefined;
|
|
38
38
|
/** Ring around the face, separated by a clear gap. */
|
|
39
|
-
ring?: AvatarRing;
|
|
39
|
+
ring?: AvatarRing | undefined;
|
|
40
40
|
/** The story has been watched: the ring cross-fades to a quiet hairline. */
|
|
41
|
-
seen?: boolean;
|
|
41
|
+
seen?: boolean | undefined;
|
|
42
42
|
/** Ring colour override — the first entry is used as a solid colour (default: `accent`). */
|
|
43
|
-
ringColors?: string[];
|
|
43
|
+
ringColors?: string[] | undefined;
|
|
44
44
|
/** Spin a broken story ring — a "story is loading" state. */
|
|
45
|
-
ringSpin?: boolean;
|
|
45
|
+
ringSpin?: boolean | undefined;
|
|
46
46
|
/** Initials look when there is no photo. Default 'soft'. */
|
|
47
|
-
fallback?: AvatarFallback;
|
|
47
|
+
fallback?: AvatarFallback | undefined;
|
|
48
48
|
/** Soft layered shadow under the face (default true for 72pt and up). */
|
|
49
|
-
elevated?: boolean;
|
|
49
|
+
elevated?: boolean | undefined;
|
|
50
50
|
/** Notification count (`true` = dot), top-right. */
|
|
51
|
-
badge?: number | boolean;
|
|
51
|
+
badge?: number | boolean | undefined;
|
|
52
52
|
/** Verified seal with a check, bottom-right (bottom-left when a status dot is shown). */
|
|
53
|
-
verified?: boolean;
|
|
53
|
+
verified?: boolean | undefined;
|
|
54
54
|
/** Colour behind the avatar — the dot, badge and seal cut-outs are punched in it. Default: theme background. */
|
|
55
|
-
surface?: string;
|
|
55
|
+
surface?: string | undefined;
|
|
56
56
|
/** Ring and verified seal colour (default: theme accent). */
|
|
57
|
-
accent?: string;
|
|
57
|
+
accent?: string | undefined;
|
|
58
58
|
/** Makes it a button with a spring press and a light haptic tick. */
|
|
59
|
-
onPress?: () => void;
|
|
59
|
+
onPress?: (() => void) | undefined;
|
|
60
60
|
/** Haptic tick on press (default true). */
|
|
61
|
-
haptics?: boolean;
|
|
62
|
-
style?: StyleProp<ViewStyle
|
|
61
|
+
haptics?: boolean | undefined;
|
|
62
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const SIZES: Record<AvatarSize, number> = { xs: 24, sm: 32, md: 40, lg: 56, xl: 72 };
|
|
@@ -99,7 +99,9 @@ export function colorFromName(name = '') {
|
|
|
99
99
|
export function initialsFrom(name = '', max = 2) {
|
|
100
100
|
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
101
101
|
if (!parts.length) return '';
|
|
102
|
-
const
|
|
102
|
+
const first = parts[0] ?? '';
|
|
103
|
+
const last = parts[parts.length - 1] ?? '';
|
|
104
|
+
const letters = parts.length === 1 ? [first[0]] : [first[0], last[0]];
|
|
103
105
|
return letters.slice(0, max).join('').toUpperCase();
|
|
104
106
|
}
|
|
105
107
|
|
|
@@ -144,13 +146,13 @@ export function Avatar({
|
|
|
144
146
|
// Remember which URL failed so a new `src` gets a fresh try without resetting state in an effect.
|
|
145
147
|
const [failedSrc, setFailedSrc] = useState<string>();
|
|
146
148
|
const failed = !!src && failedSrc === src;
|
|
147
|
-
const loaded =
|
|
148
|
-
const press =
|
|
149
|
-
const spin =
|
|
150
|
-
const pulseV =
|
|
149
|
+
const [loaded] = useState(() => new Animated.Value(0));
|
|
150
|
+
const [press] = useState(() => new Animated.Value(1));
|
|
151
|
+
const [spin] = useState(() => new Animated.Value(0));
|
|
152
|
+
const [pulseV] = useState(() => new Animated.Value(0));
|
|
151
153
|
|
|
152
154
|
const isSeen = seen ?? ring === 'seen';
|
|
153
|
-
const seenV =
|
|
155
|
+
const [seenV] = useState(() => new Animated.Value(isSeen ? 1 : 0));
|
|
154
156
|
|
|
155
157
|
useEffect(() => {
|
|
156
158
|
loaded.setValue(0);
|
|
@@ -248,11 +250,8 @@ export function Avatar({
|
|
|
248
250
|
{ width: px, height: px, borderRadius: faceRadius, backgroundColor: faceBg },
|
|
249
251
|
showElevated
|
|
250
252
|
? {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
shadowRadius: px * 0.16,
|
|
254
|
-
shadowOffset: { width: 0, height: Math.max(2, px * 0.07) },
|
|
255
|
-
elevation: Math.round(px * 0.1),
|
|
253
|
+
// boxShadow scales with the avatar and looks the same on iOS, Android and web.
|
|
254
|
+
boxShadow: `0px ${Math.round(Math.max(2, px * 0.07))}px ${Math.round(px * 0.32)}px rgba(0,0,0,${dark ? 0.5 : 0.14})`,
|
|
256
255
|
}
|
|
257
256
|
: null,
|
|
258
257
|
]}
|
|
@@ -338,7 +337,7 @@ export function Avatar({
|
|
|
338
337
|
strokeWidth={width}
|
|
339
338
|
fill="none"
|
|
340
339
|
strokeLinecap="round"
|
|
341
|
-
|
|
340
|
+
{...(dashed ? { strokeDasharray: [circ * 0.7, circ * 0.3] } : null)}
|
|
342
341
|
/>
|
|
343
342
|
) : (
|
|
344
343
|
<Rect
|
|
@@ -571,12 +570,15 @@ const SEAL = (() => {
|
|
|
571
570
|
const rr = k % 2 === 0 ? 12 : 10.1;
|
|
572
571
|
return [12 + rr * Math.cos(a), 12 + rr * Math.sin(a)] as const;
|
|
573
572
|
});
|
|
574
|
-
|
|
573
|
+
type Pt = readonly [number, number];
|
|
574
|
+
const mid = (a: Pt, b: Pt): Pt => [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
|
575
575
|
const f = (v: number) => v.toFixed(2);
|
|
576
|
-
|
|
576
|
+
// `pts` has 2n points and every index below is taken modulo its length.
|
|
577
|
+
const at = (k: number) => pts[k % pts.length]!;
|
|
578
|
+
const start = mid(at(pts.length - 1), at(0));
|
|
577
579
|
let d = `M${f(start[0])} ${f(start[1])}`;
|
|
578
580
|
pts.forEach((pt, i) => {
|
|
579
|
-
const m = mid(pt,
|
|
581
|
+
const m = mid(pt, at(i + 1));
|
|
580
582
|
d += `Q${f(pt[0])} ${f(pt[1])} ${f(m[0])} ${f(m[1])}`;
|
|
581
583
|
});
|
|
582
584
|
return d + 'Z';
|
|
@@ -596,20 +598,20 @@ export interface AvatarGroupProps {
|
|
|
596
598
|
/** `<Avatar>` elements. The group sets their size, shape and surface. */
|
|
597
599
|
children: React.ReactNode;
|
|
598
600
|
/** Show at most this many, then a "+N" chip. */
|
|
599
|
-
max?: number;
|
|
600
|
-
size?: AvatarSize | number;
|
|
601
|
-
shape?: AvatarShape;
|
|
601
|
+
max?: number | undefined;
|
|
602
|
+
size?: AvatarSize | number | undefined;
|
|
603
|
+
shape?: AvatarShape | undefined;
|
|
602
604
|
/** How far each avatar tucks under the previous one, as a fraction of its size (default 0.28). */
|
|
603
|
-
overlap?: number;
|
|
605
|
+
overlap?: number | undefined;
|
|
604
606
|
/** Real total when it is larger than the children you pass (e.g. 1,284 members). */
|
|
605
|
-
total?: number;
|
|
607
|
+
total?: number | undefined;
|
|
606
608
|
/** Staggered spring-in on mount (skipped with reduce motion). */
|
|
607
|
-
animated?: boolean;
|
|
609
|
+
animated?: boolean | undefined;
|
|
608
610
|
/** Colour of the separating borders — match what the group sits on (default: theme background). */
|
|
609
|
-
surface?: string;
|
|
611
|
+
surface?: string | undefined;
|
|
610
612
|
/** Makes the "+N" chip a button. */
|
|
611
|
-
onPressMore?: () => void;
|
|
612
|
-
style?: StyleProp<ViewStyle
|
|
613
|
+
onPressMore?: (() => void) | undefined;
|
|
614
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
613
615
|
}
|
|
614
616
|
|
|
615
617
|
/** Overlapping avatars separated by cut-out borders, with a "+N" chip and a staggered spring-in. */
|
|
@@ -625,7 +627,7 @@ export function AvatarGroup({
|
|
|
625
627
|
onPressMore,
|
|
626
628
|
style,
|
|
627
629
|
}: AvatarGroupProps) {
|
|
628
|
-
const { colors, radius, dark, font } = useKit();
|
|
630
|
+
const { colors, radius, dark, font, labels } = useKit();
|
|
629
631
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
630
632
|
const reduceMotion = useReduceMotion();
|
|
631
633
|
const px = resolveSize(size);
|
|
@@ -638,8 +640,13 @@ export function AvatarGroup({
|
|
|
638
640
|
const inner = shape === 'circle' ? radius.full : Math.min(px / 2, radius.scale(Math.round(px * 0.3)));
|
|
639
641
|
const wrapCorner = shape === 'circle' ? radius.full : inner + border;
|
|
640
642
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
+
// One value per avatar, grown (never shrunk) as children are added; this render already uses the grown list.
|
|
644
|
+
const [pool, setPool] = useState<Animated.Value[]>([]);
|
|
645
|
+
let anims = pool;
|
|
646
|
+
if (pool.length < count) {
|
|
647
|
+
anims = [...pool, ...Array.from({ length: count - pool.length }, () => new Animated.Value(animated ? 0 : 1))];
|
|
648
|
+
setPool(anims);
|
|
649
|
+
}
|
|
643
650
|
|
|
644
651
|
useEffect(() => {
|
|
645
652
|
if (!animated) return;
|
|
@@ -657,35 +664,41 @@ export function AvatarGroup({
|
|
|
657
664
|
const names = shown.map((c) => c.props.name).filter(Boolean) as string[];
|
|
658
665
|
const label =
|
|
659
666
|
names.length === 0
|
|
660
|
-
?
|
|
661
|
-
:
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
667
|
+
? labels.peopleCount(total ?? items.length)
|
|
668
|
+
: (total ?? items.length) - Math.min(3, names.length) > 0
|
|
669
|
+
? labels.namesAndMore(names.slice(0, 3).join(', '), (total ?? items.length) - Math.min(3, names.length))
|
|
670
|
+
: names.slice(0, 3).join(', ');
|
|
671
|
+
|
|
672
|
+
// `anims` was grown to `count` above, and i < count.
|
|
673
|
+
const wrap = (child: React.ReactNode, i: number, key: React.Key) => {
|
|
674
|
+
const v = anims[i]!;
|
|
675
|
+
return (
|
|
676
|
+
<Animated.View
|
|
677
|
+
key={key}
|
|
678
|
+
style={{
|
|
679
|
+
marginLeft: i === 0 ? 0 : -px * overlap,
|
|
680
|
+
borderRadius: wrapCorner,
|
|
681
|
+
padding: border,
|
|
682
|
+
backgroundColor: bg,
|
|
683
|
+
zIndex: count - i,
|
|
684
|
+
opacity: v,
|
|
685
|
+
transform: [
|
|
686
|
+
{ translateX: v.interpolate({ inputRange: [0, 1], outputRange: [-8, 0] }) },
|
|
687
|
+
{ scale: v.interpolate({ inputRange: [0, 1], outputRange: [0.6, 1] }) },
|
|
688
|
+
],
|
|
689
|
+
}}
|
|
690
|
+
>
|
|
691
|
+
{child}
|
|
692
|
+
</Animated.View>
|
|
693
|
+
);
|
|
694
|
+
};
|
|
682
695
|
|
|
683
696
|
const extraText = extra > 999 ? `${(extra / 1000).toFixed(extra >= 10000 ? 0 : 1).replace(/\.0$/, '')}k` : String(extra);
|
|
684
697
|
const more =
|
|
685
698
|
extra > 0 ? (
|
|
686
699
|
<Pressable
|
|
687
700
|
accessibilityRole={onPressMore ? 'button' : 'text'}
|
|
688
|
-
accessibilityLabel={
|
|
701
|
+
accessibilityLabel={labels.moreCount(extra)}
|
|
689
702
|
disabled={!onPressMore}
|
|
690
703
|
onPress={onPressMore}
|
|
691
704
|
hitSlop={px < 44 ? Math.ceil((44 - px) / 2) : undefined}
|
|
@@ -774,20 +787,22 @@ function luminance(hex: string) {
|
|
|
774
787
|
}
|
|
775
788
|
|
|
776
789
|
function contrast(a: string, b: string) {
|
|
777
|
-
const
|
|
790
|
+
const la = luminance(a);
|
|
791
|
+
const lb = luminance(b);
|
|
792
|
+
const [x, y] = la >= lb ? [la, lb] : [lb, la];
|
|
778
793
|
return (x + 0.05) / (y + 0.05);
|
|
779
794
|
}
|
|
780
795
|
|
|
781
796
|
const styles = StyleSheet.create({
|
|
782
797
|
face: { overflow: 'visible' },
|
|
783
798
|
clip: { flex: 1, overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },
|
|
784
|
-
initials: { fontWeight: '400', includeFontPadding: false, textAlign: 'center' },
|
|
799
|
+
initials: { fontWeight: '400', includeFontPadding: false, textAlign: 'center', textAlignVertical: 'center' },
|
|
785
800
|
inkShadow: { textShadowColor: 'rgba(0,0,0,0.18)', textShadowRadius: 2, textShadowOffset: { width: 0, height: 0.5 } },
|
|
786
801
|
pulse: { position: 'absolute' },
|
|
787
802
|
dot: { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
|
|
788
803
|
badge: { position: 'absolute', alignItems: 'center', justifyContent: 'center' },
|
|
789
|
-
badgeText: { fontWeight: '400', letterSpacing: -0.2, includeFontPadding: false },
|
|
804
|
+
badgeText: { fontWeight: '400', letterSpacing: -0.2, includeFontPadding: false, textAlignVertical: 'center' },
|
|
790
805
|
group: { flexDirection: 'row', alignItems: 'center' },
|
|
791
806
|
more: { alignItems: 'center', justifyContent: 'center' },
|
|
792
|
-
moreText: { fontWeight: '400', letterSpacing: -0.2 },
|
|
807
|
+
moreText: { fontWeight: '400', letterSpacing: -0.2, includeFontPadding: false, textAlignVertical: 'center' },
|
|
793
808
|
});
|