@expokit/cli 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +32 -2
- package/bin/expo-kit.js +1204 -0
- package/bin/lib.mjs +343 -0
- package/bin/mcp.mjs +338 -0
- package/package.json +44 -4
- package/registry/_docs/accordion.json +99 -0
- package/registry/_docs/action-sheet.json +110 -0
- package/registry/_docs/alert.json +127 -0
- package/registry/_docs/avatar.json +203 -0
- package/registry/_docs/badge.json +117 -0
- package/registry/_docs/bottom-nav.json +119 -0
- package/registry/_docs/bottom-sheet.json +158 -0
- package/registry/_docs/bullet.json +24 -0
- package/registry/_docs/button-3d.json +122 -0
- package/registry/_docs/button.json +115 -0
- package/registry/_docs/calendar.json +182 -0
- package/registry/_docs/card-stack.json +108 -0
- package/registry/_docs/card.json +111 -0
- package/registry/_docs/carousel.json +102 -0
- package/registry/_docs/checkbox.json +93 -0
- package/registry/_docs/checkout.json +102 -0
- package/registry/_docs/chip-group.json +116 -0
- package/registry/_docs/color-swatch.json +66 -0
- package/registry/_docs/confetti.json +82 -0
- package/registry/_docs/coupon.json +176 -0
- package/registry/_docs/cube-pager.json +84 -0
- package/registry/_docs/dashboard.json +120 -0
- package/registry/_docs/date-picker.json +249 -0
- package/registry/_docs/dialog.json +162 -0
- package/registry/_docs/drawer.json +171 -0
- package/registry/_docs/fab.json +120 -0
- package/registry/_docs/flip-card.json +111 -0
- package/registry/_docs/floating-input.json +191 -0
- package/registry/_docs/gift-card.json +108 -0
- package/registry/_docs/glass-button.json +118 -0
- package/registry/_docs/glass-nav-bar.json +78 -0
- package/registry/_docs/glass-search-bar.json +90 -0
- package/registry/_docs/glass-segmented.json +66 -0
- package/registry/_docs/glass-sheet.json +108 -0
- package/registry/_docs/glass-tab-bar.json +76 -0
- package/registry/_docs/glass.json +78 -0
- package/registry/_docs/inbox.json +72 -0
- package/registry/_docs/isometric-bar.json +72 -0
- package/registry/_docs/kit.json +11 -0
- package/registry/_docs/kpi-card.json +11 -0
- package/registry/_docs/like-button.json +72 -0
- package/registry/_docs/liquidglass-tab.json +27 -0
- package/registry/_docs/list-item.json +200 -0
- package/registry/_docs/loyalty-card.json +108 -0
- package/registry/_docs/morph-button.json +108 -0
- package/registry/_docs/number-ticker.json +96 -0
- package/registry/_docs/offers.json +78 -0
- package/registry/_docs/onboarding.json +66 -0
- package/registry/_docs/otp-input.json +134 -0
- package/registry/_docs/pagination-dots.json +88 -0
- package/registry/_docs/parallax-layers.json +84 -0
- package/registry/_docs/parallax-scroll.json +186 -0
- package/registry/_docs/password-input.json +92 -0
- package/registry/_docs/payment-card.json +153 -0
- package/registry/_docs/pixel-bar.json +66 -0
- package/registry/_docs/polar-area.json +78 -0
- package/registry/_docs/profile.json +84 -0
- package/registry/_docs/progress-steps.json +92 -0
- package/registry/_docs/progress.json +132 -0
- package/registry/_docs/radio-group.json +92 -0
- package/registry/_docs/rating.json +90 -0
- package/registry/_docs/ring-carousel.json +102 -0
- package/registry/_docs/scratch-card.json +90 -0
- package/registry/_docs/search-bar.json +140 -0
- package/registry/_docs/search-results.json +120 -0
- package/registry/_docs/segmented-control.json +103 -0
- package/registry/_docs/select.json +252 -0
- package/registry/_docs/settings.json +72 -0
- package/registry/_docs/shimmer-button.json +78 -0
- package/registry/_docs/sign-in.json +198 -0
- package/registry/_docs/skeleton.json +142 -0
- package/registry/_docs/slide-to-confirm.json +84 -0
- package/registry/_docs/slider.json +157 -0
- package/registry/_docs/spin-wheel.json +114 -0
- package/registry/_docs/spinner.json +91 -0
- package/registry/_docs/stepper.json +168 -0
- package/registry/_docs/swipe-deck.json +102 -0
- package/registry/_docs/swipeable-row.json +72 -0
- package/registry/_docs/switch.json +104 -0
- package/registry/_docs/tabs.json +105 -0
- package/registry/_docs/tag-input.json +128 -0
- package/registry/_docs/textarea.json +175 -0
- package/registry/_docs/ticket-card.json +174 -0
- package/registry/_docs/ticket-shape.json +11 -0
- package/registry/_docs/tilt-card.json +141 -0
- package/registry/_docs/time-picker.json +253 -0
- package/registry/_docs/timeline-chart.json +30 -0
- package/registry/_docs/timeline.json +156 -0
- package/registry/_docs/toast.json +161 -0
- package/registry/_docs/treemap.json +30 -0
- package/registry/_docs/waterfall.json +36 -0
- package/registry/_docs/wheel-list.json +102 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
- package/registry/accordion/files/src/lib/kit.tsx +219 -0
- package/registry/accordion.json +17 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
- package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/action-sheet.json +18 -0
- package/registry/alert/files/src/components/ui/alert.tsx +654 -0
- package/registry/alert/files/src/components/ui/button.tsx +274 -0
- package/registry/alert/files/src/lib/kit.tsx +219 -0
- package/registry/alert.json +20 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
- package/registry/avatar/files/src/lib/kit.tsx +219 -0
- package/registry/avatar.json +18 -0
- package/registry/badge/files/src/components/ui/badge.tsx +316 -0
- package/registry/badge/files/src/lib/kit.tsx +219 -0
- package/registry/badge.json +17 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
- package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-nav.json +18 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
- package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-sheet.json +17 -0
- package/registry/bullet.json +14 -0
- package/registry/button/files/src/components/ui/button.tsx +274 -0
- package/registry/button/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
- package/registry/button-3d/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d.json +18 -0
- package/registry/button.json +18 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
- package/registry/calendar/files/src/lib/kit.tsx +219 -0
- package/registry/calendar.json +17 -0
- package/registry/card/files/src/components/ui/card.tsx +300 -0
- package/registry/card/files/src/lib/kit.tsx +219 -0
- package/registry/card-stack.json +15 -0
- package/registry/card.json +17 -0
- package/registry/carousel.json +15 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
- package/registry/checkbox/files/src/lib/kit.tsx +219 -0
- package/registry/checkbox.json +17 -0
- package/registry/checkout.json +26 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
- package/registry/chip-group/files/src/lib/kit.tsx +219 -0
- package/registry/chip-group.json +17 -0
- package/registry/color-swatch.json +17 -0
- package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
- package/registry/confetti/files/src/lib/kit.tsx +219 -0
- package/registry/confetti.json +15 -0
- package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
- package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/coupon/files/src/lib/kit.tsx +219 -0
- package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/coupon.json +23 -0
- package/registry/cube-pager.json +15 -0
- package/registry/dashboard.json +32 -0
- package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
- package/registry/date-picker/files/src/lib/kit.tsx +219 -0
- package/registry/date-picker.json +19 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
- package/registry/dialog/files/src/lib/kit.tsx +219 -0
- package/registry/dialog.json +18 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
- package/registry/drawer/files/src/lib/kit.tsx +219 -0
- package/registry/drawer.json +18 -0
- package/registry/fab.json +18 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/flip-card/files/src/lib/kit.tsx +219 -0
- package/registry/flip-card.json +15 -0
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/floating-input/files/src/lib/kit.tsx +219 -0
- package/registry/floating-input.json +17 -0
- package/registry/gift-card.json +21 -0
- package/registry/glass-button.json +22 -0
- package/registry/glass-nav-bar.json +21 -0
- package/registry/glass-search-bar.json +21 -0
- package/registry/glass-segmented.json +22 -0
- package/registry/glass-sheet.json +22 -0
- package/registry/glass-tab-bar.json +22 -0
- package/registry/glass.json +19 -0
- package/registry/inbox.json +30 -0
- package/registry/isometric-bar.json +14 -0
- package/registry/kit/files/src/lib/kit.tsx +219 -0
- package/registry/kit.json +11 -0
- package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
- package/registry/kpi-card.json +14 -0
- package/registry/like-button.json +18 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
- package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
- package/registry/liquidglass-tab.json +23 -0
- package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
- package/registry/list-item/files/src/lib/kit.tsx +219 -0
- package/registry/list-item.json +17 -0
- package/registry/loyalty-card.json +23 -0
- package/registry/morph-button.json +18 -0
- package/registry/number-ticker.json +15 -0
- package/registry/offers.json +24 -0
- package/registry/onboarding.json +17 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
- package/registry/otp-input/files/src/lib/kit.tsx +219 -0
- package/registry/otp-input.json +17 -0
- package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
- package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
- package/registry/pagination-dots.json +15 -0
- package/registry/parallax-layers.json +15 -0
- package/registry/parallax-scroll.json +17 -0
- package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
- package/registry/password-input/files/src/lib/kit.tsx +219 -0
- package/registry/password-input.json +19 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/payment-card/files/src/lib/kit.tsx +219 -0
- package/registry/payment-card.json +21 -0
- package/registry/pixel-bar.json +14 -0
- package/registry/polar-area.json +14 -0
- package/registry/profile.json +17 -0
- package/registry/progress/files/src/components/ui/progress.tsx +883 -0
- package/registry/progress/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
- package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps.json +18 -0
- package/registry/progress.json +17 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
- package/registry/radio-group/files/src/lib/kit.tsx +219 -0
- package/registry/radio-group.json +18 -0
- package/registry/rating.json +18 -0
- package/registry/ring-carousel.json +15 -0
- package/registry/scratch-card.json +18 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
- package/registry/search-bar/files/src/lib/kit.tsx +219 -0
- package/registry/search-bar.json +17 -0
- package/registry/search-results.json +30 -0
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
- package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
- package/registry/segmented-control.json +18 -0
- package/registry/select/files/src/components/ui/select.tsx +1398 -0
- package/registry/select/files/src/lib/kit.tsx +219 -0
- package/registry/select.json +17 -0
- package/registry/settings.json +26 -0
- package/registry/shimmer-button.json +17 -0
- package/registry/sign-in.json +26 -0
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
- package/registry/skeleton/files/src/lib/kit.tsx +219 -0
- package/registry/skeleton.json +17 -0
- package/registry/slide-to-confirm.json +18 -0
- package/registry/slider/files/src/components/ui/slider.tsx +716 -0
- package/registry/slider/files/src/lib/kit.tsx +219 -0
- package/registry/slider.json +17 -0
- package/registry/spin-wheel.json +18 -0
- package/registry/spinner/files/src/components/ui/spinner.tsx +335 -0
- package/registry/spinner/files/src/lib/kit.tsx +219 -0
- package/registry/spinner.json +17 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
- package/registry/stepper/files/src/lib/kit.tsx +219 -0
- package/registry/stepper.json +17 -0
- package/registry/swipe-deck.json +15 -0
- package/registry/swipeable-row.json +15 -0
- package/registry/switch/files/src/components/ui/switch.tsx +344 -0
- package/registry/switch/files/src/lib/kit.tsx +219 -0
- package/registry/switch.json +18 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
- package/registry/tabs/files/src/lib/kit.tsx +219 -0
- package/registry/tabs.json +17 -0
- package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
- package/registry/tag-input/files/src/lib/kit.tsx +219 -0
- package/registry/tag-input.json +17 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
- package/registry/textarea/files/src/lib/kit.tsx +219 -0
- package/registry/textarea.json +17 -0
- package/registry/ticket-card.json +24 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
- package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/ticket-shape.json +17 -0
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
- package/registry/tilt-card.json +17 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
- package/registry/time-picker/files/src/lib/kit.tsx +219 -0
- package/registry/time-picker.json +17 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
- package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
- package/registry/timeline/files/src/lib/kit.tsx +219 -0
- package/registry/timeline-chart.json +14 -0
- package/registry/timeline.json +20 -0
- package/registry/toast/files/src/components/ui/button.tsx +274 -0
- package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
- package/registry/toast/files/src/lib/kit.tsx +219 -0
- package/registry/toast.json +20 -0
- package/registry/treemap.json +14 -0
- package/registry/waterfall.json +14 -0
- package/registry/wheel-list.json +17 -0
- package/schema/components.schema.json +13 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React, { createContext, useContext, useMemo } from 'react';
|
|
2
|
+
import { Platform, useColorScheme, type TextStyle } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ExpoKit design tokens. Every component reads them through useKit(), so one
|
|
6
|
+
* <KitProvider> (optional) re-skins the whole kit: light / dark, accent colour, radius.
|
|
7
|
+
* Without a provider, components follow the system colour scheme.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Bumped whenever components start using a new kit API (2: radius.scale / radius.capsule).
|
|
12
|
+
* The CLI won't keep an edited kit.tsx older than the component being added needs.
|
|
13
|
+
*/
|
|
14
|
+
export const KIT_VERSION = 2;
|
|
15
|
+
|
|
16
|
+
export type ColorScheme = 'light' | 'dark';
|
|
17
|
+
|
|
18
|
+
export interface Colors {
|
|
19
|
+
background: string;
|
|
20
|
+
foreground: string;
|
|
21
|
+
/** Cards, sheets, inputs. */
|
|
22
|
+
card: string;
|
|
23
|
+
cardForeground: string;
|
|
24
|
+
/** Raised surfaces: popovers, toasts. */
|
|
25
|
+
popover: string;
|
|
26
|
+
muted: string;
|
|
27
|
+
mutedForeground: string;
|
|
28
|
+
border: string;
|
|
29
|
+
input: string;
|
|
30
|
+
primary: string;
|
|
31
|
+
primaryForeground: string;
|
|
32
|
+
secondary: string;
|
|
33
|
+
secondaryForeground: string;
|
|
34
|
+
accent: string;
|
|
35
|
+
accentForeground: string;
|
|
36
|
+
success: string;
|
|
37
|
+
warning: string;
|
|
38
|
+
danger: string;
|
|
39
|
+
dangerForeground: string;
|
|
40
|
+
ring: string;
|
|
41
|
+
overlay: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface Theme {
|
|
45
|
+
dark: boolean;
|
|
46
|
+
colors: Colors;
|
|
47
|
+
/** Corner radius scale. `radius.full` makes pills. */
|
|
48
|
+
radius: {
|
|
49
|
+
sm: number;
|
|
50
|
+
md: number;
|
|
51
|
+
lg: number;
|
|
52
|
+
xl: number;
|
|
53
|
+
full: number;
|
|
54
|
+
/** A corner designed at the default radius (12), scaled to the current one: `radius.scale(16)` is 16 at 12, 0 at 0. */
|
|
55
|
+
scale: (n: number) => number;
|
|
56
|
+
/** Corner for a bar or chip of height `h`: a pill at radius ≥ 12, softer below, square at 0. */
|
|
57
|
+
capsule: (h: number) => number;
|
|
58
|
+
};
|
|
59
|
+
/** 4pt spacing scale: space(4) === 16. */
|
|
60
|
+
space: (n: number) => number;
|
|
61
|
+
font: { family?: string; mono: string };
|
|
62
|
+
text: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl', TextStyle>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const light: Colors = {
|
|
66
|
+
background: '#ffffff',
|
|
67
|
+
foreground: '#09090b',
|
|
68
|
+
card: '#ffffff',
|
|
69
|
+
cardForeground: '#09090b',
|
|
70
|
+
popover: '#ffffff',
|
|
71
|
+
muted: '#f4f4f5',
|
|
72
|
+
mutedForeground: '#71717a',
|
|
73
|
+
border: '#e4e4e7',
|
|
74
|
+
input: '#d4d4d8',
|
|
75
|
+
primary: '#18181b',
|
|
76
|
+
primaryForeground: '#fafafa',
|
|
77
|
+
secondary: '#f4f4f5',
|
|
78
|
+
secondaryForeground: '#18181b',
|
|
79
|
+
accent: '#18181b',
|
|
80
|
+
accentForeground: '#ffffff',
|
|
81
|
+
success: '#16a34a',
|
|
82
|
+
warning: '#f59e0b',
|
|
83
|
+
danger: '#e5484d',
|
|
84
|
+
dangerForeground: '#ffffff',
|
|
85
|
+
ring: '#18181b',
|
|
86
|
+
overlay: 'rgba(0,0,0,0.4)',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const dark: Colors = {
|
|
90
|
+
background: '#09090b',
|
|
91
|
+
foreground: '#fafafa',
|
|
92
|
+
card: '#111113',
|
|
93
|
+
cardForeground: '#fafafa',
|
|
94
|
+
popover: '#18181b',
|
|
95
|
+
muted: '#1c1c20',
|
|
96
|
+
mutedForeground: '#a1a1aa',
|
|
97
|
+
border: '#27272a',
|
|
98
|
+
input: '#34343a',
|
|
99
|
+
primary: '#fafafa',
|
|
100
|
+
primaryForeground: '#18181b',
|
|
101
|
+
secondary: '#1f1f23',
|
|
102
|
+
secondaryForeground: '#fafafa',
|
|
103
|
+
accent: '#fafafa',
|
|
104
|
+
accentForeground: '#09090b',
|
|
105
|
+
success: '#3dd68c',
|
|
106
|
+
warning: '#ffb224',
|
|
107
|
+
danger: '#ff6369',
|
|
108
|
+
dangerForeground: '#ffffff',
|
|
109
|
+
ring: '#d4d4d8',
|
|
110
|
+
overlay: 'rgba(0,0,0,0.6)',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const palettes = { light, dark };
|
|
114
|
+
|
|
115
|
+
function build(scheme: ColorScheme, overrides?: ThemeOverrides): Theme {
|
|
116
|
+
const base = scheme === 'dark' ? dark : light;
|
|
117
|
+
const accent = overrides?.accent;
|
|
118
|
+
const colors: Colors = {
|
|
119
|
+
...base,
|
|
120
|
+
...(accent ? { accent, ring: accent, accentForeground: readableOn(accent) } : null),
|
|
121
|
+
...overrides?.colors?.[scheme],
|
|
122
|
+
};
|
|
123
|
+
const r = overrides?.radius ?? 12;
|
|
124
|
+
return {
|
|
125
|
+
dark: scheme === 'dark',
|
|
126
|
+
colors,
|
|
127
|
+
radius: {
|
|
128
|
+
sm: Math.round(r * 0.5),
|
|
129
|
+
md: r,
|
|
130
|
+
lg: Math.round(r * 1.35),
|
|
131
|
+
xl: Math.round(r * 1.8),
|
|
132
|
+
full: 999,
|
|
133
|
+
scale: (n) => Math.round((n * r) / 12),
|
|
134
|
+
capsule: (h) => Math.round((h / 2) * Math.min(1, r / 12)),
|
|
135
|
+
},
|
|
136
|
+
space: (n) => n * 4,
|
|
137
|
+
font: {
|
|
138
|
+
family: overrides?.fontFamily,
|
|
139
|
+
mono: Platform.select({ ios: 'Menlo', android: 'monospace', default: 'ui-monospace, SFMono-Regular, Menlo, monospace' })!,
|
|
140
|
+
},
|
|
141
|
+
text: {
|
|
142
|
+
xs: { fontSize: 12, lineHeight: 16 },
|
|
143
|
+
sm: { fontSize: 14, lineHeight: 20 },
|
|
144
|
+
md: { fontSize: 16, lineHeight: 22 },
|
|
145
|
+
lg: { fontSize: 18, lineHeight: 24, fontWeight: '400' },
|
|
146
|
+
xl: { fontSize: 22, lineHeight: 28, fontWeight: '400', letterSpacing: -0.3 },
|
|
147
|
+
xxl: { fontSize: 30, lineHeight: 36, fontWeight: '400', letterSpacing: -0.8 },
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface ThemeOverrides {
|
|
153
|
+
/** Brand colour used for focus rings, selected states and accent buttons. */
|
|
154
|
+
accent?: string;
|
|
155
|
+
/** Base corner radius in points (default 12). */
|
|
156
|
+
radius?: number;
|
|
157
|
+
fontFamily?: string;
|
|
158
|
+
/** Override any token per scheme. */
|
|
159
|
+
colors?: Partial<Record<ColorScheme, Partial<Colors>>>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const KitContext = createContext<{ scheme?: ColorScheme; overrides?: ThemeOverrides } | null>(null);
|
|
163
|
+
|
|
164
|
+
export function KitProvider({
|
|
165
|
+
scheme,
|
|
166
|
+
children,
|
|
167
|
+
...overrides
|
|
168
|
+
}: ThemeOverrides & { scheme?: ColorScheme | 'system'; children: React.ReactNode }) {
|
|
169
|
+
const value = useMemo(
|
|
170
|
+
() => ({ scheme: scheme === 'system' ? undefined : scheme, overrides }),
|
|
171
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
172
|
+
[scheme, overrides.accent, overrides.radius, overrides.fontFamily, overrides.colors]
|
|
173
|
+
);
|
|
174
|
+
return <KitContext.Provider value={value}>{children}</KitContext.Provider>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function useKit(): Theme {
|
|
178
|
+
const ctx = useContext(KitContext);
|
|
179
|
+
const system = useColorScheme();
|
|
180
|
+
const scheme: ColorScheme = ctx?.scheme ?? (system === 'dark' ? 'dark' : 'light');
|
|
181
|
+
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Shared motion presets so the whole kit moves the same way. */
|
|
185
|
+
export const motion = {
|
|
186
|
+
/** Toggles, selection indicators. */
|
|
187
|
+
snappy: { damping: 18, stiffness: 260, mass: 0.8 },
|
|
188
|
+
/** Press feedback, pops. */
|
|
189
|
+
bouncy: { damping: 11, stiffness: 220, mass: 0.7 },
|
|
190
|
+
/** Sheets, layout changes. */
|
|
191
|
+
gentle: { damping: 22, stiffness: 160, mass: 1 },
|
|
192
|
+
fast: 160,
|
|
193
|
+
normal: 240,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** Adds alpha to a #rrggbb colour: alpha('#6366f1', 0.2). */
|
|
197
|
+
export function alpha(hex: string, a: number) {
|
|
198
|
+
if (!hex.startsWith('#') || (hex.length !== 7 && hex.length !== 4)) return hex;
|
|
199
|
+
const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
|
|
200
|
+
return full + Math.round(Math.max(0, Math.min(1, a)) * 255).toString(16).padStart(2, '0');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Black or white, whichever reads better on `hex`. */
|
|
204
|
+
export function readableOn(hex: string) {
|
|
205
|
+
const full = hex.length === 4 ? '#' + [...hex.slice(1)].map((c) => c + c).join('') : hex;
|
|
206
|
+
const n = parseInt(full.slice(1, 7), 16);
|
|
207
|
+
if (Number.isNaN(n)) return '#ffffff';
|
|
208
|
+
const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
209
|
+
return (r * 299 + g * 587 + b * 114) / 1000 > 150 ? '#09090b' : '#ffffff';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* True for children that must be wrapped in <Text>: strings, numbers and arrays of them —
|
|
214
|
+
* `{count} new` is the array [3, ' new'], and raw text outside <Text> crashes on native.
|
|
215
|
+
*/
|
|
216
|
+
export function isTextNode(node: React.ReactNode): node is string | number | (string | number)[] {
|
|
217
|
+
if (typeof node === 'string' || typeof node === 'number') return true;
|
|
218
|
+
return Array.isArray(node) && node.length > 0 && node.every((n) => typeof n === 'string' || typeof n === 'number');
|
|
219
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slider",
|
|
3
|
+
"type": "component",
|
|
4
|
+
"title": "Slider",
|
|
5
|
+
"description": "A slider that feels native on iPhone and Android: thumb and fill ride the native driver, the thumb swells under your finger, tap the track to seek, and values spring-snap to the step. Thin iOS track, a Control-Center style bar with an icon that inverts as the fill passes, a stepped track with ticks, and two-thumb range mode with an optional histogram.",
|
|
6
|
+
"tier": "free",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react-native-svg"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [
|
|
11
|
+
"kit"
|
|
12
|
+
],
|
|
13
|
+
"sharedFiles": [
|
|
14
|
+
"lib/kit.tsx"
|
|
15
|
+
],
|
|
16
|
+
"filesDir": "slider/files"
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spin-wheel",
|
|
3
|
+
"type": "component",
|
|
4
|
+
"title": "Spin Wheel",
|
|
5
|
+
"description": "A casino-grade \"spin & win\" wheel for coupons and rewards: glossy wedges in a gold rim of chasing lights, a 3D SPIN hub, and a flapper that ticks over every divider with haptics. Tap, flick or spin it from code — it keeps turning while your server picks the prize, then eases onto it and lights up the win.",
|
|
6
|
+
"tier": "pro",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react-native-svg",
|
|
9
|
+
"expo-haptics"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [
|
|
12
|
+
"kit"
|
|
13
|
+
],
|
|
14
|
+
"sharedFiles": [
|
|
15
|
+
"lib/kit.tsx"
|
|
16
|
+
],
|
|
17
|
+
"filesDir": null
|
|
18
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AccessibilityInfo,
|
|
4
|
+
Animated,
|
|
5
|
+
Easing,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
View,
|
|
9
|
+
type StyleProp,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
} from 'react-native';
|
|
12
|
+
import Svg, { Circle } from 'react-native-svg';
|
|
13
|
+
|
|
14
|
+
import { alpha, useKit } from '@/lib/kit';
|
|
15
|
+
|
|
16
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
17
|
+
|
|
18
|
+
export type SpinnerVariant = 'ring' | 'dots';
|
|
19
|
+
export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
20
|
+
export type SpinnerLabelPosition = 'none' | 'right' | 'bottom';
|
|
21
|
+
|
|
22
|
+
export interface SpinnerProps {
|
|
23
|
+
variant?: SpinnerVariant;
|
|
24
|
+
/** 'sm' 16 · 'md' 24 · 'lg' 36 · 'xl' 52 — or any number of points. */
|
|
25
|
+
size?: SpinnerSize | number;
|
|
26
|
+
/** Indicator colour (default: theme accent). */
|
|
27
|
+
color?: string;
|
|
28
|
+
/** Speed multiplier: 2 is twice as fast. */
|
|
29
|
+
speed?: number;
|
|
30
|
+
/** Read by screen readers, and shown when `labelPosition` is set. */
|
|
31
|
+
label?: string;
|
|
32
|
+
/** Show `label` beside or under the spinner with an animated ellipsis (default 'none': screen readers only). */
|
|
33
|
+
labelPosition?: SpinnerLabelPosition;
|
|
34
|
+
/** Faint full circle behind the ring. */
|
|
35
|
+
track?: boolean;
|
|
36
|
+
style?: StyleProp<ViewStyle>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const SIZES: Record<SpinnerSize, number> = { sm: 16, md: 24, lg: 36, xl: 52 };
|
|
40
|
+
/** Arcs stacked from the head backwards: the shortest is solid, the rest faint, so the tail fades out. */
|
|
41
|
+
const TAIL = 7;
|
|
42
|
+
|
|
43
|
+
function useReduceMotion() {
|
|
44
|
+
const [reduce, setReduce] = useState(false);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
let alive = true;
|
|
47
|
+
AccessibilityInfo.isReduceMotionEnabled()
|
|
48
|
+
.then((r) => alive && setReduce(r))
|
|
49
|
+
.catch(() => {});
|
|
50
|
+
const sub = AccessibilityInfo.addEventListener('reduceMotionChanged', setReduce);
|
|
51
|
+
return () => {
|
|
52
|
+
alive = false;
|
|
53
|
+
sub.remove();
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
56
|
+
return reduce;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Looping value 0 → 1. Each spinner part interpolates it. */
|
|
60
|
+
function useLoop(duration: number, easing: (t: number) => number, native: boolean, running = true) {
|
|
61
|
+
const v = useRef(new Animated.Value(0)).current;
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
v.setValue(0);
|
|
64
|
+
if (!running) return;
|
|
65
|
+
const loop = Animated.loop(
|
|
66
|
+
Animated.timing(v, {
|
|
67
|
+
toValue: 1,
|
|
68
|
+
duration,
|
|
69
|
+
easing,
|
|
70
|
+
useNativeDriver: native,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
loop.start();
|
|
74
|
+
return () => loop.stop();
|
|
75
|
+
}, [v, duration, easing, native, running]);
|
|
76
|
+
return v;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const linear = Easing.linear;
|
|
80
|
+
const sine = Easing.inOut(Easing.sin);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Pre-samples an eased piecewise curve (each segment eased like `interpolate({ easing })`) so it can
|
|
84
|
+
* run on the native driver, which doesn't support `easing` on interpolations.
|
|
85
|
+
*/
|
|
86
|
+
function eased(points: number[], values: number[], ease: (t: number) => number, steps = 12) {
|
|
87
|
+
const inputRange: number[] = [];
|
|
88
|
+
const outputRange: number[] = [];
|
|
89
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
90
|
+
for (let k = 0; k < steps; k++) {
|
|
91
|
+
const t = k / steps;
|
|
92
|
+
inputRange.push(points[i] + (points[i + 1] - points[i]) * t);
|
|
93
|
+
outputRange.push(values[i] + (values[i + 1] - values[i]) * ease(t));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
inputRange.push(points[points.length - 1]);
|
|
97
|
+
outputRange.push(values[values.length - 1]);
|
|
98
|
+
return { inputRange, outputRange };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Loading indicator — a comet arc whose tail fades out and breathes as it spins, or a soft wave of
|
|
103
|
+
* dots. Optionally shows a label with an animated ellipsis. Rotation runs on the native driver.
|
|
104
|
+
*/
|
|
105
|
+
export function Spinner({
|
|
106
|
+
variant = 'ring',
|
|
107
|
+
size = 'md',
|
|
108
|
+
color,
|
|
109
|
+
speed = 1,
|
|
110
|
+
label = 'Loading',
|
|
111
|
+
labelPosition = 'none',
|
|
112
|
+
track = false,
|
|
113
|
+
style,
|
|
114
|
+
}: SpinnerProps) {
|
|
115
|
+
const { colors } = useKit();
|
|
116
|
+
const reduce = useReduceMotion();
|
|
117
|
+
const px = typeof size === 'number' ? size : SIZES[size];
|
|
118
|
+
const tint = color ?? colors.accent;
|
|
119
|
+
const Body = BODIES[variant] ?? Ring;
|
|
120
|
+
const s = Math.max(0.1, speed);
|
|
121
|
+
const body = <Body size={px} color={tint} speed={s} track={track} reduce={reduce} />;
|
|
122
|
+
|
|
123
|
+
if (labelPosition === 'none' || !label) {
|
|
124
|
+
return (
|
|
125
|
+
<View
|
|
126
|
+
accessible
|
|
127
|
+
accessibilityRole="progressbar"
|
|
128
|
+
accessibilityLabel={label}
|
|
129
|
+
accessibilityState={{ busy: true }}
|
|
130
|
+
style={[styles.root, { width: px, height: px }, style]}
|
|
131
|
+
>
|
|
132
|
+
{body}
|
|
133
|
+
</View>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const bottom = labelPosition === 'bottom';
|
|
138
|
+
return (
|
|
139
|
+
<View
|
|
140
|
+
accessible
|
|
141
|
+
accessibilityRole="progressbar"
|
|
142
|
+
accessibilityLabel={label}
|
|
143
|
+
accessibilityState={{ busy: true }}
|
|
144
|
+
style={[bottom ? styles.column : styles.inline, { gap: bottom ? 10 : 8 }, style]}
|
|
145
|
+
>
|
|
146
|
+
<View style={[styles.root, { width: px, height: px }]}>{body}</View>
|
|
147
|
+
<Ellipsis
|
|
148
|
+
text={label}
|
|
149
|
+
speed={s}
|
|
150
|
+
reduce={reduce}
|
|
151
|
+
color={colors.mutedForeground}
|
|
152
|
+
fontSize={bottom ? 14 : Math.max(13, Math.min(15, px * 0.62))}
|
|
153
|
+
/>
|
|
154
|
+
</View>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
interface BodyProps {
|
|
159
|
+
size: number;
|
|
160
|
+
color: string;
|
|
161
|
+
speed: number;
|
|
162
|
+
track: boolean;
|
|
163
|
+
reduce: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function Ring({ size, color, speed, track, reduce }: BodyProps) {
|
|
167
|
+
const stroke = Math.max(2, size / 9);
|
|
168
|
+
const r = (size - stroke) / 2;
|
|
169
|
+
const c = 2 * Math.PI * r;
|
|
170
|
+
// Native: a steady spin plus an eased surge on a nested view. JS: the arc length breathing on the SVG.
|
|
171
|
+
const spin = useLoop(1100 / speed, linear, true);
|
|
172
|
+
const lunge = useLoop(1500 / speed, linear, true, !reduce);
|
|
173
|
+
const breath = useLoop(1500 / speed, linear, false, !reduce);
|
|
174
|
+
|
|
175
|
+
const rotate = spin.interpolate({
|
|
176
|
+
inputRange: [0, 1],
|
|
177
|
+
outputRange: ['0deg', '360deg'],
|
|
178
|
+
});
|
|
179
|
+
// The head races ahead while the arc grows, then eases while the tail catches up.
|
|
180
|
+
const surge = eased([0, 0.5, 1], [0, 290, 360], sine);
|
|
181
|
+
const rotateSurge = lunge.interpolate({ inputRange: surge.inputRange, outputRange: surge.outputRange.map((d) => `${d}deg`) });
|
|
182
|
+
// Arc length breathes 14% → 62% → 14% of the circle.
|
|
183
|
+
const min = c * 0.14;
|
|
184
|
+
const max = c * 0.62;
|
|
185
|
+
const steady = c * 0.36;
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<Animated.View style={{ width: size, height: size, transform: [{ rotate }] }}>
|
|
189
|
+
<Animated.View style={[StyleSheet.absoluteFill, { transform: [{ rotate: rotateSurge }] }]}>
|
|
190
|
+
{/* Mirrored so the dash grows backwards from a fixed head at the path start. */}
|
|
191
|
+
<View style={styles.mirror}>
|
|
192
|
+
<Svg width={size} height={size}>
|
|
193
|
+
{track ? (
|
|
194
|
+
<Circle cx={size / 2} cy={size / 2} r={r} stroke={alpha(color, 0.14)} strokeWidth={stroke} fill="none" />
|
|
195
|
+
) : null}
|
|
196
|
+
{Array.from({ length: TAIL }, (_, k) => {
|
|
197
|
+
// k = 0 is the full tail (faintest layer), k = TAIL - 1 the solid head.
|
|
198
|
+
const part = (TAIL - k) / TAIL;
|
|
199
|
+
const headLayer = k === TAIL - 1;
|
|
200
|
+
const longest = max * part;
|
|
201
|
+
const offset = reduce
|
|
202
|
+
? longest - steady * part
|
|
203
|
+
: breath.interpolate({
|
|
204
|
+
inputRange: [0, 0.5, 1],
|
|
205
|
+
outputRange: [longest - min * part, 0, longest - min * part],
|
|
206
|
+
easing: sine,
|
|
207
|
+
});
|
|
208
|
+
return (
|
|
209
|
+
<AnimatedCircle
|
|
210
|
+
key={k}
|
|
211
|
+
cx={size / 2}
|
|
212
|
+
cy={size / 2}
|
|
213
|
+
r={r}
|
|
214
|
+
stroke={color}
|
|
215
|
+
strokeOpacity={headLayer ? 1 : 0.2}
|
|
216
|
+
strokeWidth={stroke}
|
|
217
|
+
strokeLinecap="round"
|
|
218
|
+
strokeDasharray={`${longest} ${c * 2}`}
|
|
219
|
+
strokeDashoffset={offset}
|
|
220
|
+
fill="none"
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
})}
|
|
224
|
+
</Svg>
|
|
225
|
+
</View>
|
|
226
|
+
</Animated.View>
|
|
227
|
+
</Animated.View>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function Dots({ size, color, speed, reduce }: BodyProps) {
|
|
232
|
+
const t = useLoop(1200 / speed, linear, true, !reduce);
|
|
233
|
+
const dot = size / 4.2;
|
|
234
|
+
return (
|
|
235
|
+
<View style={[styles.row, { gap: dot / 2.2 }]}>
|
|
236
|
+
{[0, 1, 2].map((i) => {
|
|
237
|
+
// A smooth wave: each dot swells, brightens and lifts slightly during its slice of the cycle.
|
|
238
|
+
const s = i * 0.16;
|
|
239
|
+
const range = {
|
|
240
|
+
inputRange: [0, s, s + 0.22, s + 0.44, 1],
|
|
241
|
+
easing: sine,
|
|
242
|
+
};
|
|
243
|
+
return (
|
|
244
|
+
<Animated.View
|
|
245
|
+
key={i}
|
|
246
|
+
style={{
|
|
247
|
+
width: dot,
|
|
248
|
+
height: dot,
|
|
249
|
+
borderRadius: dot / 2,
|
|
250
|
+
backgroundColor: color,
|
|
251
|
+
opacity: reduce
|
|
252
|
+
? 0.4 + i * 0.3
|
|
253
|
+
: t.interpolate({
|
|
254
|
+
...range,
|
|
255
|
+
outputRange: [0.35, 0.35, 1, 0.35, 0.35],
|
|
256
|
+
}),
|
|
257
|
+
transform: reduce
|
|
258
|
+
? []
|
|
259
|
+
: [
|
|
260
|
+
{
|
|
261
|
+
translateY: t.interpolate({
|
|
262
|
+
...range,
|
|
263
|
+
outputRange: [0, 0, -size / 9, 0, 0],
|
|
264
|
+
}),
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
scale: t.interpolate({
|
|
268
|
+
...range,
|
|
269
|
+
outputRange: [0.65, 0.65, 1.1, 0.65, 0.65],
|
|
270
|
+
}),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
}}
|
|
274
|
+
/>
|
|
275
|
+
);
|
|
276
|
+
})}
|
|
277
|
+
</View>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Label whose three trailing dots fade in one after another (native opacity). */
|
|
282
|
+
function Ellipsis({
|
|
283
|
+
text,
|
|
284
|
+
speed,
|
|
285
|
+
reduce,
|
|
286
|
+
color,
|
|
287
|
+
fontSize,
|
|
288
|
+
}: {
|
|
289
|
+
text: string;
|
|
290
|
+
speed: number;
|
|
291
|
+
reduce: boolean;
|
|
292
|
+
color: string;
|
|
293
|
+
fontSize: number;
|
|
294
|
+
}) {
|
|
295
|
+
const t = useLoop(1400 / speed, linear, true, !reduce);
|
|
296
|
+
const base = text.replace(/(…|\.{1,3})\s*$/, '');
|
|
297
|
+
return (
|
|
298
|
+
<View style={styles.ellipsis}>
|
|
299
|
+
<Text style={[styles.label, { color, fontSize }]}>{base}</Text>
|
|
300
|
+
{[0, 1, 2].map((i) => (
|
|
301
|
+
<Animated.Text
|
|
302
|
+
key={i}
|
|
303
|
+
style={{
|
|
304
|
+
color,
|
|
305
|
+
fontSize,
|
|
306
|
+
fontWeight: '400',
|
|
307
|
+
opacity: reduce
|
|
308
|
+
? 1
|
|
309
|
+
: t.interpolate({
|
|
310
|
+
inputRange: [0, 0.15 + i * 0.18, 0.3 + i * 0.18, 0.85, 1],
|
|
311
|
+
outputRange: [0, 0, 1, 1, 0],
|
|
312
|
+
}),
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
.
|
|
316
|
+
</Animated.Text>
|
|
317
|
+
))}
|
|
318
|
+
</View>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const BODIES: Record<SpinnerVariant, (p: BodyProps) => React.JSX.Element> = {
|
|
323
|
+
ring: Ring,
|
|
324
|
+
dots: Dots,
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const styles = StyleSheet.create({
|
|
328
|
+
root: { alignItems: 'center', justifyContent: 'center' },
|
|
329
|
+
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center' },
|
|
330
|
+
column: { flexDirection: 'column', alignItems: 'center' },
|
|
331
|
+
inline: { flexDirection: 'row', alignItems: 'center' },
|
|
332
|
+
mirror: { transform: [{ scaleX: -1 }] },
|
|
333
|
+
ellipsis: { flexDirection: 'row', alignItems: 'baseline', flexShrink: 1 },
|
|
334
|
+
label: { fontWeight: '400', flexShrink: 1 },
|
|
335
|
+
});
|