@expokit/cli 0.3.0 → 0.4.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 +8 -4
- package/bin/expo-kit.js +137 -24
- package/bin/lib.mjs +110 -1
- package/bin/mcp.mjs +40 -18
- package/package.json +2 -2
- package/registry/_docs/assistant-app.json +47 -0
- package/registry/_docs/avatar.json +1 -1
- package/registry/_docs/badge.json +1 -1
- package/registry/_docs/bottom-nav.json +6 -6
- package/registry/_docs/button-3d.json +2 -2
- package/registry/_docs/calendar.json +5 -5
- package/registry/_docs/checkout.json +7 -1
- package/registry/_docs/confetti.json +3 -3
- package/registry/_docs/date-picker.json +4 -4
- package/registry/_docs/donut-shop.json +88 -0
- package/registry/_docs/drawer.json +4 -4
- package/registry/_docs/fintech-app.json +49 -0
- package/registry/_docs/floating-input.json +5 -5
- package/registry/_docs/food-app.json +47 -0
- package/registry/_docs/glass.json +1 -1
- package/registry/_docs/grain.json +80 -0
- package/registry/_docs/morphing-button-group.json +96 -0
- package/registry/_docs/orb-reveal.json +132 -0
- package/registry/_docs/otp-input.json +4 -4
- package/registry/_docs/password-input.json +3 -3
- package/registry/_docs/qr-code-button.json +122 -0
- package/registry/_docs/search-bar.json +3 -3
- package/registry/_docs/select.json +7 -7
- package/registry/_docs/settings.json +6 -0
- package/registry/_docs/skeleton.json +2 -2
- package/registry/_docs/slider.json +6 -6
- package/registry/_docs/spinner.json +1 -1
- package/registry/_docs/stepper.json +7 -7
- package/registry/_docs/textarea.json +2 -2
- package/registry/_docs/ticket-shape.json +1 -1
- package/registry/_docs/tilt-card.json +4 -4
- package/registry/_docs/time-picker.json +6 -6
- package/registry/_docs/timeline.json +2 -2
- package/registry/_docs/toast.json +8 -8
- package/registry/accordion/files/src/components/ui/accordion.tsx +5 -4
- package/registry/accordion/files/src/lib/kit.tsx +8 -5
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +9 -8
- package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
- package/registry/alert/files/src/components/ui/alert.tsx +7 -4
- package/registry/alert/files/src/components/ui/button.tsx +4 -3
- package/registry/alert/files/src/lib/kit.tsx +8 -5
- package/registry/assistant-app.json +49 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
- package/registry/avatar/files/src/lib/kit.tsx +8 -5
- package/registry/badge/files/src/components/ui/badge.tsx +21 -7
- package/registry/badge/files/src/lib/kit.tsx +8 -5
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +14 -7
- package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
- package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
- package/registry/button/files/src/components/ui/button.tsx +4 -3
- package/registry/button/files/src/lib/kit.tsx +8 -5
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +3 -2
- package/registry/button-3d/files/src/lib/kit.tsx +8 -5
- package/registry/calendar/files/src/components/ui/calendar.tsx +9 -5
- package/registry/calendar/files/src/lib/kit.tsx +8 -5
- package/registry/card/files/src/components/ui/card.tsx +8 -6
- package/registry/card/files/src/lib/kit.tsx +8 -5
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +6 -5
- package/registry/checkbox/files/src/lib/kit.tsx +8 -5
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +4 -2
- package/registry/chip-group/files/src/lib/kit.tsx +8 -5
- package/registry/confetti/files/src/lib/kit.tsx +8 -5
- package/registry/coupon/files/src/components/ui/coupon.tsx +23 -20
- package/registry/coupon/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/coupon/files/src/lib/kit.tsx +8 -5
- package/registry/date-picker/files/src/components/ui/calendar.tsx +9 -5
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +2 -2
- package/registry/date-picker/files/src/lib/kit.tsx +8 -5
- package/registry/dialog/files/src/components/ui/dialog.tsx +12 -6
- package/registry/dialog/files/src/lib/kit.tsx +8 -5
- package/registry/donut-shop.json +18 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +25 -19
- package/registry/drawer/files/src/lib/kit.tsx +8 -5
- package/registry/fintech-app.json +57 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/flip-card/files/src/lib/kit.tsx +8 -5
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +16 -6
- package/registry/floating-input/files/src/lib/kit.tsx +8 -5
- package/registry/food-app.json +55 -0
- package/registry/grain/files/src/components/ui/grain.tsx +113 -0
- package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
- package/registry/grain.json +17 -0
- package/registry/kit/files/src/lib/kit.tsx +8 -5
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
- package/registry/list-item/files/src/components/ui/list-item.tsx +20 -16
- package/registry/list-item/files/src/lib/kit.tsx +8 -5
- package/registry/morphing-button-group.json +18 -0
- package/registry/orb-reveal.json +20 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +8 -6
- package/registry/otp-input/files/src/lib/kit.tsx +8 -5
- package/registry/password-input/files/src/components/ui/floating-input.tsx +16 -6
- package/registry/password-input/files/src/components/ui/password-input.tsx +2 -2
- package/registry/password-input/files/src/lib/kit.tsx +8 -5
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +27 -14
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/payment-card/files/src/lib/kit.tsx +8 -5
- package/registry/progress/files/src/components/ui/progress.tsx +8 -5
- package/registry/progress/files/src/lib/kit.tsx +8 -5
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +15 -11
- package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
- package/registry/qr-code-button.json +18 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +10 -9
- package/registry/radio-group/files/src/lib/kit.tsx +8 -5
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +13 -11
- package/registry/search-bar/files/src/lib/kit.tsx +8 -5
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +4 -2
- package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
- package/registry/select/files/src/components/ui/select.tsx +3 -3
- package/registry/select/files/src/lib/kit.tsx +8 -5
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
- package/registry/skeleton/files/src/lib/kit.tsx +8 -5
- package/registry/slider/files/src/components/ui/slider.tsx +5 -4
- package/registry/slider/files/src/lib/kit.tsx +8 -5
- package/registry/spinner/files/src/components/ui/spinner.tsx +17 -12
- package/registry/spinner/files/src/lib/kit.tsx +8 -5
- package/registry/stateful-button/files/src/lib/kit.tsx +8 -5
- package/registry/stepper/files/src/components/ui/stepper.tsx +8 -4
- package/registry/stepper/files/src/lib/kit.tsx +8 -5
- package/registry/switch/files/src/components/ui/switch.tsx +4 -3
- package/registry/switch/files/src/lib/kit.tsx +8 -5
- package/registry/tabs/files/src/components/ui/tabs.tsx +10 -8
- package/registry/tabs/files/src/lib/kit.tsx +8 -5
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +4 -4
- package/registry/text-animation/files/src/lib/kit.tsx +8 -5
- package/registry/textarea/files/src/components/ui/textarea.tsx +11 -9
- package/registry/textarea/files/src/lib/kit.tsx +8 -5
- package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +2 -2
- package/registry/time-picker/files/src/lib/kit.tsx +8 -5
- package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
- package/registry/timeline/files/src/components/ui/timeline.tsx +12 -10
- package/registry/timeline/files/src/lib/kit.tsx +8 -5
- package/registry/toast/files/src/components/ui/button.tsx +4 -3
- package/registry/toast/files/src/components/ui/toast.tsx +13 -11
- package/registry/toast/files/src/lib/kit.tsx +8 -5
- package/schema/components.schema.json +1 -1
- package/registry/_docs/color-swatch.json +0 -82
- package/registry/_docs/kpi-card.json +0 -14
- package/registry/_docs/like-button.json +0 -92
- package/registry/_docs/morph-button.json +0 -128
- package/registry/_docs/pagination-dots.json +0 -108
- package/registry/_docs/shimmer-button.json +0 -98
- package/registry/_docs/tag-input.json +0 -148
- package/registry/color-swatch.json +0 -17
- package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
- package/registry/kpi-card.json +0 -14
- package/registry/like-button.json +0 -18
- package/registry/morph-button.json +0 -18
- package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
- package/registry/pagination-dots/files/src/lib/kit.tsx +0 -219
- package/registry/pagination-dots.json +0 -15
- package/registry/shimmer-button.json +0 -17
- package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
- package/registry/tag-input.json +0 -17
|
@@ -13,6 +13,7 @@ import Svg, { Circle, ClipPath, Defs, G, Line, LinearGradient, Path, Rect, Stop
|
|
|
13
13
|
|
|
14
14
|
import { FlipCard, useFlipProgress } from '@/components/ui/flip-card';
|
|
15
15
|
import { TiltCard } from '@/components/ui/tilt-card';
|
|
16
|
+
import { useKit } from '@/lib/kit';
|
|
16
17
|
|
|
17
18
|
export type PaymentCardBrand = 'visa' | 'mastercard' | 'amex' | 'rupay' | 'none';
|
|
18
19
|
|
|
@@ -30,8 +31,8 @@ export interface PaymentCardProps {
|
|
|
30
31
|
bank?: string;
|
|
31
32
|
/** Small label top-right, e.g. "Debit", "World Elite". */
|
|
32
33
|
label?: string;
|
|
33
|
-
/** Card art — a photo or texture (`require('./card.jpg')` or `{ uri }`). Default: brushed graphite. */
|
|
34
|
-
image?: ImageSourcePropType;
|
|
34
|
+
/** Card art — a photo or texture (`require('./card.jpg')` or `{ uri }`). Or any element drawn as the art, e.g. `<GrainGradient palette=\"ocean\" style={{ flex: 1 }} />`. Default: brushed graphite. */
|
|
35
|
+
image?: ImageSourcePropType | React.ReactElement;
|
|
35
36
|
/** `light` text for dark art (default), `dark` text for pale art. */
|
|
36
37
|
tone?: 'light' | 'dark';
|
|
37
38
|
/** EMV chip finish. */
|
|
@@ -222,6 +223,8 @@ export function PaymentCard({
|
|
|
222
223
|
labels,
|
|
223
224
|
style,
|
|
224
225
|
}: PaymentCardProps) {
|
|
226
|
+
const { font } = useKit();
|
|
227
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
225
228
|
const controlled = flippedProp !== undefined;
|
|
226
229
|
const [own, setOwn] = useState(defaultFlipped);
|
|
227
230
|
// A frozen card always shows its front (where the frost and the label are).
|
|
@@ -253,7 +256,17 @@ export function PaymentCard({
|
|
|
253
256
|
|
|
254
257
|
const art = (darken: number) => (
|
|
255
258
|
<>
|
|
256
|
-
{image ?
|
|
259
|
+
{image ? (
|
|
260
|
+
React.isValidElement(image) ? (
|
|
261
|
+
<View style={StyleSheet.absoluteFill} pointerEvents="none">
|
|
262
|
+
{image}
|
|
263
|
+
</View>
|
|
264
|
+
) : (
|
|
265
|
+
<Image alt="" source={image} style={StyleSheet.absoluteFill} resizeMode="cover" accessibilityIgnoresInvertColors />
|
|
266
|
+
)
|
|
267
|
+
) : (
|
|
268
|
+
<Graphite />
|
|
269
|
+
)}
|
|
257
270
|
{darken > 0 ? <View style={[StyleSheet.absoluteFill, { backgroundColor: `rgba(0,0,0,${darken})` }]} /> : null}
|
|
258
271
|
</>
|
|
259
272
|
);
|
|
@@ -265,28 +278,28 @@ export function PaymentCard({
|
|
|
265
278
|
{tone === 'light' ? <Shade strength={image ? 0.38 : 0.2} /> : null}
|
|
266
279
|
<View style={styles.inner}>
|
|
267
280
|
<View style={styles.row}>
|
|
268
|
-
<Text style={[{ color: ink, fontSize: 16, fontWeight: '700', letterSpacing: -0.2 }, emboss]}>{bank ?? ''}</Text>
|
|
269
|
-
{label ? <Text style={[{ color: soft, fontSize: 11, fontWeight: '600', letterSpacing: 1 }, emboss]}>{label.toUpperCase()}</Text> : null}
|
|
281
|
+
<Text style={[{ color: ink, fontSize: 16, fontWeight: '700', letterSpacing: -0.2 }, ff, emboss]}>{bank ?? ''}</Text>
|
|
282
|
+
{label ? <Text style={[{ color: soft, fontSize: 11, fontWeight: '600', letterSpacing: 1 }, ff, emboss]}>{label.toUpperCase()}</Text> : null}
|
|
270
283
|
</View>
|
|
271
284
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
|
|
272
285
|
<Chip finish={chip} />
|
|
273
286
|
<Contactless ink={ink} />
|
|
274
287
|
</View>
|
|
275
288
|
<View style={{ gap: 10 }}>
|
|
276
|
-
<Text style={[{ color: ink, fontSize: 19, fontWeight: '500', letterSpacing: 2, fontVariant: ['tabular-nums'] }, emboss]}>
|
|
289
|
+
<Text style={[{ color: ink, fontSize: 19, fontWeight: '500', letterSpacing: 2, fontVariant: ['tabular-nums'] }, ff, emboss]}>
|
|
277
290
|
{formatNumber(number, brand, masked)}
|
|
278
291
|
</Text>
|
|
279
292
|
<View style={[styles.row, { alignItems: 'flex-end' }]}>
|
|
280
293
|
<View style={{ flexDirection: 'row', gap: 20, flexShrink: 1 }}>
|
|
281
294
|
<View style={{ flexShrink: 1 }}>
|
|
282
|
-
<Text style={[styles.tiny, { color: soft }]}>{text.cardholder}</Text>
|
|
283
|
-
<Text style={[{ color: ink, fontSize: 13, fontWeight: '600', letterSpacing: 0.8 }, emboss]} numberOfLines={1}>
|
|
295
|
+
<Text style={[styles.tiny, ff, { color: soft }]}>{text.cardholder}</Text>
|
|
296
|
+
<Text style={[{ color: ink, fontSize: 13, fontWeight: '600', letterSpacing: 0.8 }, ff, emboss]} numberOfLines={1}>
|
|
284
297
|
{name.toUpperCase()}
|
|
285
298
|
</Text>
|
|
286
299
|
</View>
|
|
287
300
|
<View>
|
|
288
|
-
<Text style={[styles.tiny, { color: soft }]}>{text.validThru}</Text>
|
|
289
|
-
<Text style={[{ color: ink, fontSize: 13, fontWeight: '600', fontVariant: ['tabular-nums'] }, emboss]}>{expiry}</Text>
|
|
301
|
+
<Text style={[styles.tiny, ff, { color: soft }]}>{text.validThru}</Text>
|
|
302
|
+
<Text style={[{ color: ink, fontSize: 13, fontWeight: '600', fontVariant: ['tabular-nums'] }, ff, emboss]}>{expiry}</Text>
|
|
290
303
|
</View>
|
|
291
304
|
</View>
|
|
292
305
|
<BrandMark brand={brand} ink={ink} />
|
|
@@ -296,7 +309,7 @@ export function PaymentCard({
|
|
|
296
309
|
{frozen ? (
|
|
297
310
|
<View style={[StyleSheet.absoluteFill, styles.center, { backgroundColor: 'rgba(214,232,255,0.28)' }]}>
|
|
298
311
|
<View style={styles.frozenPill}>
|
|
299
|
-
<Text style={{ color: '#ffffff', fontSize: 13, fontWeight: '700', letterSpacing: 0.4 }}>❄︎ {text.frozen}</Text>
|
|
312
|
+
<Text style={[{ color: '#ffffff', fontSize: 13, fontWeight: '700', letterSpacing: 0.4 }, ff]}>❄︎ {text.frozen}</Text>
|
|
300
313
|
</View>
|
|
301
314
|
</View>
|
|
302
315
|
) : null}
|
|
@@ -317,15 +330,15 @@ export function PaymentCard({
|
|
|
317
330
|
</Svg>
|
|
318
331
|
</View>
|
|
319
332
|
<View style={styles.cvv}>
|
|
320
|
-
<Text style={{ color: '#111', fontSize: 15, fontWeight: '700', fontStyle: 'italic', letterSpacing: 1.5, fontVariant: ['tabular-nums'] }}>{cvv ?? '•••'}</Text>
|
|
333
|
+
<Text style={[{ color: '#111', fontSize: 15, fontWeight: '700', fontStyle: 'italic', letterSpacing: 1.5, fontVariant: ['tabular-nums'] }, ff]}>{cvv ?? '•••'}</Text>
|
|
321
334
|
</View>
|
|
322
335
|
</View>
|
|
323
336
|
<View style={styles.backFoot}>
|
|
324
337
|
<View style={{ flex: 1, gap: 3 }}>
|
|
325
|
-
<Text style={{ color: 'rgba(255,255,255,0.78)', fontSize: 9, lineHeight: 12 }}>
|
|
338
|
+
<Text style={[{ color: 'rgba(255,255,255,0.78)', fontSize: 9, lineHeight: 12 }, ff]}>
|
|
326
339
|
{fill(text.legal)}
|
|
327
340
|
</Text>
|
|
328
|
-
<Text style={{ color: 'rgba(255,255,255,0.9)', fontSize: 10, fontWeight: '600' }}>{fill(text.back)}</Text>
|
|
341
|
+
<Text style={[{ color: 'rgba(255,255,255,0.9)', fontSize: 10, fontWeight: '600' }, ff]}>{fill(text.back)}</Text>
|
|
329
342
|
</View>
|
|
330
343
|
{/* Security hologram. */}
|
|
331
344
|
<Svg width={40} height={30} viewBox="0 0 40 30">
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { createContext, useContext, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Easing,
|
|
@@ -267,7 +267,7 @@ export function TiltCard({
|
|
|
267
267
|
const touching = useRef(false);
|
|
268
268
|
const origin = useRef({ x: 0, y: 0 });
|
|
269
269
|
const moved = useRef(false);
|
|
270
|
-
const viewRef = useRef<View
|
|
270
|
+
const viewRef = useRef<ComponentRef<typeof View>>(null);
|
|
271
271
|
const disabledRef = useRef(disabled);
|
|
272
272
|
disabledRef.current = disabled;
|
|
273
273
|
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -104,7 +104,8 @@ export function Progress({
|
|
|
104
104
|
striped = false,
|
|
105
105
|
style,
|
|
106
106
|
}: ProgressProps) {
|
|
107
|
-
const { colors, dark } = useKit();
|
|
107
|
+
const { colors, dark, font } = useKit();
|
|
108
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
108
109
|
const reduce = useReduceMotion();
|
|
109
110
|
const top = max ?? (variant === 'steps' ? steps : 100);
|
|
110
111
|
const unknown = indeterminate || value === undefined;
|
|
@@ -137,7 +138,7 @@ export function Progress({
|
|
|
137
138
|
done={done}
|
|
138
139
|
reduce={reduce}
|
|
139
140
|
/>
|
|
140
|
-
{label ? <Text style={[styles.ringLabel, { color: colors.mutedForeground }]}>{label}</Text> : null}
|
|
141
|
+
{label ? <Text style={[styles.ringLabel, ff, { color: colors.mutedForeground }]}>{label}</Text> : null}
|
|
141
142
|
</View>
|
|
142
143
|
);
|
|
143
144
|
}
|
|
@@ -147,13 +148,13 @@ export function Progress({
|
|
|
147
148
|
const header =
|
|
148
149
|
label || valueShown ? (
|
|
149
150
|
<View style={styles.header}>
|
|
150
|
-
<Text numberOfLines={1} style={[styles.label, { color: colors.foreground }]}>
|
|
151
|
+
<Text numberOfLines={1} style={[styles.label, ff, { color: colors.foreground }]}>
|
|
151
152
|
{label}
|
|
152
153
|
</Text>
|
|
153
154
|
{checkShown ? (
|
|
154
155
|
<DrawnCheck color={tint} size={18} />
|
|
155
156
|
) : valueShown ? (
|
|
156
|
-
<Text style={[styles.value, { color: colors.mutedForeground }]}>{text}</Text>
|
|
157
|
+
<Text style={[styles.value, ff, { color: colors.mutedForeground }]}>{text}</Text>
|
|
157
158
|
) : null}
|
|
158
159
|
</View>
|
|
159
160
|
) : null;
|
|
@@ -685,6 +686,8 @@ function Ring({
|
|
|
685
686
|
reduce: boolean;
|
|
686
687
|
}) {
|
|
687
688
|
const { d, w, font } = RING[size];
|
|
689
|
+
const kit = useKit();
|
|
690
|
+
const ff = kit.font.family ? { fontFamily: kit.font.family } : null;
|
|
688
691
|
const id = useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
689
692
|
const r = (d - w) / 2;
|
|
690
693
|
const c = 2 * Math.PI * r;
|
|
@@ -835,7 +838,7 @@ function Ring({
|
|
|
835
838
|
],
|
|
836
839
|
}}
|
|
837
840
|
>
|
|
838
|
-
<Text style={[styles.ringValue, { color: fg, fontSize: font }]}>
|
|
841
|
+
<Text style={[styles.ringValue, ff, { color: fg, fontSize: font }]}>
|
|
839
842
|
{format ? format() : <RingPercent fill={fill} initial={ratio} />}
|
|
840
843
|
{format ? null : <Text style={{ fontSize: font * 0.6, color: alpha(fg, 0.55) }}>%</Text>}
|
|
841
844
|
</Text>
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -70,7 +70,8 @@ export function ProgressSteps({
|
|
|
70
70
|
accent,
|
|
71
71
|
style,
|
|
72
72
|
}: ProgressStepsProps) {
|
|
73
|
-
const { colors, dark } = useKit();
|
|
73
|
+
const { colors, dark, font } = useKit();
|
|
74
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
74
75
|
const reduce = useReduceMotion();
|
|
75
76
|
const tint = accent ?? colors.accent;
|
|
76
77
|
const items = steps.map((s) => (typeof s === 'string' ? { label: s } : s));
|
|
@@ -118,7 +119,7 @@ export function ProgressSteps({
|
|
|
118
119
|
Animated.timing(pulse, { toValue: 1, duration: 1500, easing: Easing.out(Easing.quad), useNativeDriver: true }),
|
|
119
120
|
Animated.delay(250),
|
|
120
121
|
]),
|
|
121
|
-
{ resetBeforeIteration: true }
|
|
122
|
+
{ iterations: -1, resetBeforeIteration: true }
|
|
122
123
|
);
|
|
123
124
|
loop.start();
|
|
124
125
|
return () => loop.stop();
|
|
@@ -146,6 +147,7 @@ export function ProgressSteps({
|
|
|
146
147
|
numberOfLines={1}
|
|
147
148
|
style={[
|
|
148
149
|
styles.barLabel,
|
|
150
|
+
ff,
|
|
149
151
|
{ color: failed ? colors.danger : colors.foreground },
|
|
150
152
|
{
|
|
151
153
|
opacity: labelIn,
|
|
@@ -155,7 +157,7 @@ export function ProgressSteps({
|
|
|
155
157
|
>
|
|
156
158
|
{done ? 'All done' : items[shown]!.label}
|
|
157
159
|
</Animated.Text>
|
|
158
|
-
<Text style={[styles.barStep, { color: colors.mutedForeground }]}>
|
|
160
|
+
<Text style={[styles.barStep, ff, { color: colors.mutedForeground }]}>
|
|
159
161
|
{done ? `${n} of ${n}` : `Step ${shown + 1} of ${n}`}
|
|
160
162
|
</Text>
|
|
161
163
|
</View>
|
|
@@ -255,7 +257,8 @@ function Step({
|
|
|
255
257
|
reduce: boolean;
|
|
256
258
|
onPress?: () => void;
|
|
257
259
|
}) {
|
|
258
|
-
const { colors } = useKit();
|
|
260
|
+
const { colors, font } = useKit();
|
|
261
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
259
262
|
const press = useRef(new Animated.Value(0)).current;
|
|
260
263
|
const pressTo = (v: number) =>
|
|
261
264
|
Animated.spring(press, { toValue: v, useNativeDriver: true, ...(v ? motion.snappy : motion.bouncy) }).start();
|
|
@@ -301,9 +304,9 @@ function Step({
|
|
|
301
304
|
{!last ? line(styles.vLine) : null}
|
|
302
305
|
</View>
|
|
303
306
|
<View style={[styles.vBody, !last && styles.vBodyGap]}>
|
|
304
|
-
<Text style={[styles.vLabel, { color: labelColor }]}>{step.label}</Text>
|
|
307
|
+
<Text style={[styles.vLabel, ff, { color: labelColor }]}>{step.label}</Text>
|
|
305
308
|
{step.description ? (
|
|
306
|
-
<Text style={[styles.vDesc, { color: state === 'error' ? colors.danger : colors.mutedForeground }]}>
|
|
309
|
+
<Text style={[styles.vDesc, ff, { color: state === 'error' ? colors.danger : colors.mutedForeground }]}>
|
|
307
310
|
{step.description}
|
|
308
311
|
</Text>
|
|
309
312
|
) : null}
|
|
@@ -315,11 +318,11 @@ function Step({
|
|
|
315
318
|
return (
|
|
316
319
|
<Wrapper {...a11y} {...pressProps} style={styles.hCol}>
|
|
317
320
|
<View style={styles.hTop}>{node}</View>
|
|
318
|
-
<Text numberOfLines={2} style={[styles.hLabel, { color: labelColor }]}>
|
|
321
|
+
<Text numberOfLines={2} style={[styles.hLabel, ff, { color: labelColor }]}>
|
|
319
322
|
{step.label}
|
|
320
323
|
</Text>
|
|
321
324
|
{step.description ? (
|
|
322
|
-
<Text numberOfLines={2} style={[styles.hDesc, { color: colors.mutedForeground }]}>
|
|
325
|
+
<Text numberOfLines={2} style={[styles.hDesc, ff, { color: colors.mutedForeground }]}>
|
|
323
326
|
{step.description}
|
|
324
327
|
</Text>
|
|
325
328
|
) : null}
|
|
@@ -344,7 +347,8 @@ function Node({
|
|
|
344
347
|
color: string;
|
|
345
348
|
reduce: boolean;
|
|
346
349
|
}) {
|
|
347
|
-
const { colors, radius, dark } = useKit();
|
|
350
|
+
const { colors, radius, dark, font } = useKit();
|
|
351
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
348
352
|
const clamp = 'clamp' as const;
|
|
349
353
|
const round = { borderRadius: radius.capsule(NODE) };
|
|
350
354
|
const activeIn = pNative.interpolate({ inputRange: [i - 0.3, i], outputRange: [0, 1], extrapolate: clamp });
|
|
@@ -370,7 +374,7 @@ function Node({
|
|
|
370
374
|
<View
|
|
371
375
|
style={[styles.node, round, { borderWidth: 1.5, borderColor: alpha(colors.foreground, dark ? 0.22 : 0.16) }]}
|
|
372
376
|
>
|
|
373
|
-
<Text style={[styles.num, { color: colors.mutedForeground }]}>{i + 1}</Text>
|
|
377
|
+
<Text style={[styles.num, ff, { color: colors.mutedForeground }]}>{i + 1}</Text>
|
|
374
378
|
</View>
|
|
375
379
|
|
|
376
380
|
{/* Current: soft halo, a ring that breathes outwards, accent ring with a tinted core. */}
|
|
@@ -411,7 +415,7 @@ function Node({
|
|
|
411
415
|
]}
|
|
412
416
|
>
|
|
413
417
|
<View style={[StyleSheet.absoluteFill, round, { backgroundColor: alpha(color, 0.12) }]} />
|
|
414
|
-
<Text style={[styles.num, { color }]}>{i + 1}</Text>
|
|
418
|
+
<Text style={[styles.num, ff, { color }]}>{i + 1}</Text>
|
|
415
419
|
</Animated.View>
|
|
416
420
|
|
|
417
421
|
{/* Done: solid disc that pops in, then the check draws itself. */}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qr-code-button",
|
|
3
|
+
"type": "component",
|
|
4
|
+
"title": "QR Code Button",
|
|
5
|
+
"description": "A \"Show QR Code\" pill that swells under your finger and morphs, on a smooth ease, into a white card holding a real, scannable QR code — Copy and close below it. The code is encoded inside the component (byte mode, error correction M, every version up to 40) and drawn as one crisp SVG path, so there's no QR library to install.",
|
|
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
|
+
}
|
|
@@ -113,7 +113,8 @@ interface RadioItemProps {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
function RadioItem({ option, active, onPress, variant, horizontal, first, tint, disabled }: RadioItemProps) {
|
|
116
|
-
const { colors, radius } = useKit();
|
|
116
|
+
const { colors, radius, font } = useKit();
|
|
117
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
117
118
|
// p: colours + svg (JS driver). dot: scale/opacity pops (native). press: press feedback (native).
|
|
118
119
|
// ripple: a ring that expands and fades when the item gets selected, 0 → 1 (native, rests at 1).
|
|
119
120
|
const p = useRef(new Animated.Value(active ? 1 : 0)).current;
|
|
@@ -154,17 +155,17 @@ function RadioItem({ option, active, onPress, variant, horizontal, first, tint,
|
|
|
154
155
|
|
|
155
156
|
const text = (
|
|
156
157
|
<View style={styles.text}>
|
|
157
|
-
<Text numberOfLines={2} style={[styles.label, { color: fg }]}>
|
|
158
|
+
<Text numberOfLines={2} style={[styles.label, ff, { color: fg }]}>
|
|
158
159
|
{option.label}
|
|
159
160
|
</Text>
|
|
160
|
-
{option.description ? <Text style={[styles.description, { color: muted }]}>{option.description}</Text> : null}
|
|
161
|
+
{option.description ? <Text style={[styles.description, ff, { color: muted }]}>{option.description}</Text> : null}
|
|
161
162
|
</View>
|
|
162
163
|
);
|
|
163
164
|
|
|
164
165
|
if (variant === 'card') {
|
|
165
166
|
const lit = (from: string, to: string) => p.interpolate({ inputRange: [0, 1], outputRange: [from, to], extrapolate: 'clamp' });
|
|
166
167
|
const hint = option.hint ? (
|
|
167
|
-
<Animated.Text style={[styles.hint, horizontal && styles.hintColumn, { color: lit(fg, tint) }]}>{option.hint}</Animated.Text>
|
|
168
|
+
<Animated.Text style={[styles.hint, ff, horizontal && styles.hintColumn, { color: lit(fg, tint) }]}>{option.hint}</Animated.Text>
|
|
168
169
|
) : null;
|
|
169
170
|
return (
|
|
170
171
|
<Animated.View style={[horizontal && styles.flex, { opacity: disabled ? 0.5 : 1, transform: [{ scale: press }] }]}>
|
|
@@ -191,10 +192,10 @@ function RadioItem({ option, active, onPress, variant, horizontal, first, tint,
|
|
|
191
192
|
</Animated.View>
|
|
192
193
|
) : null}
|
|
193
194
|
<View style={[styles.text, horizontal && styles.cardTextColumn]}>
|
|
194
|
-
<Text numberOfLines={2} style={[styles.label, { color: fg }]}>
|
|
195
|
+
<Text numberOfLines={2} style={[styles.label, ff, { color: fg }]}>
|
|
195
196
|
{option.label}
|
|
196
197
|
</Text>
|
|
197
|
-
{option.description ? <Text style={[styles.description, { color: muted }]}>{option.description}</Text> : null}
|
|
198
|
+
{option.description ? <Text style={[styles.description, ff, { color: muted }]}>{option.description}</Text> : null}
|
|
198
199
|
{horizontal ? hint : null}
|
|
199
200
|
</View>
|
|
200
201
|
{horizontal ? null : hint}
|
|
@@ -241,11 +242,11 @@ function RadioItem({ option, active, onPress, variant, horizontal, first, tint,
|
|
|
241
242
|
<View style={styles.text}>
|
|
242
243
|
<Animated.Text
|
|
243
244
|
numberOfLines={1}
|
|
244
|
-
style={[styles.label, { color: p.interpolate({ inputRange: [0, 1], outputRange: [fg, tint], extrapolate: 'clamp' }) }]}
|
|
245
|
+
style={[styles.label, ff, { color: p.interpolate({ inputRange: [0, 1], outputRange: [fg, tint], extrapolate: 'clamp' }) }]}
|
|
245
246
|
>
|
|
246
247
|
{option.label}
|
|
247
248
|
</Animated.Text>
|
|
248
|
-
{option.description ? <Text style={[styles.description, { color: muted }]}>{option.description}</Text> : null}
|
|
249
|
+
{option.description ? <Text style={[styles.description, ff, { color: muted }]}>{option.description}</Text> : null}
|
|
249
250
|
</View>
|
|
250
251
|
<Svg width={20} height={20} viewBox="0 0 24 24">
|
|
251
252
|
<AnimatedPath
|
|
@@ -286,7 +287,7 @@ function RadioItem({ option, active, onPress, variant, horizontal, first, tint,
|
|
|
286
287
|
</Animated.View>
|
|
287
288
|
</Animated.View>
|
|
288
289
|
{horizontal && !option.description ? (
|
|
289
|
-
<Text style={[styles.label, { color: fg }]}>{option.label}</Text>
|
|
290
|
+
<Text style={[styles.label, ff, { color: fg }]}>{option.label}</Text>
|
|
290
291
|
) : (
|
|
291
292
|
text
|
|
292
293
|
)}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
1
|
+
import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Easing,
|
|
@@ -75,7 +75,7 @@ const webNoOutline = Platform.OS === 'web' ? ({ outlineStyle: 'none', outlineWid
|
|
|
75
75
|
* panel drops down while focused. The 'minimal' variant rests as a round icon and expands
|
|
76
76
|
* into a full field when tapped.
|
|
77
77
|
*/
|
|
78
|
-
export const SearchBar = forwardRef<TextInput
|
|
78
|
+
export const SearchBar = forwardRef<ComponentRef<typeof TextInput>, SearchBarProps>(function SearchBar(
|
|
79
79
|
{
|
|
80
80
|
variant = 'default',
|
|
81
81
|
size = 'md',
|
|
@@ -104,14 +104,15 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBa
|
|
|
104
104
|
},
|
|
105
105
|
ref
|
|
106
106
|
) {
|
|
107
|
-
const { colors, radius } = useKit();
|
|
107
|
+
const { colors, radius, font } = useKit();
|
|
108
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
108
109
|
const s = SIZES[size];
|
|
109
110
|
const tint = accent ?? colors.accent;
|
|
110
111
|
const minimal = variant === 'minimal';
|
|
111
112
|
const cancelEnabled = !minimal && (showCancel ?? true);
|
|
112
113
|
|
|
113
|
-
const inputRef = useRef<TextInput
|
|
114
|
-
useImperativeHandle(ref, () => inputRef.current as TextInput);
|
|
114
|
+
const inputRef = useRef<ComponentRef<typeof TextInput>>(null);
|
|
115
|
+
useImperativeHandle(ref, () => inputRef.current as ComponentRef<typeof TextInput>);
|
|
115
116
|
|
|
116
117
|
const [focused, setFocused] = useState(false);
|
|
117
118
|
const [inner, setInner] = useState(defaultValue ?? '');
|
|
@@ -293,7 +294,7 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBa
|
|
|
293
294
|
if (minimal && !text) setExpanded(false);
|
|
294
295
|
onBlur?.(e);
|
|
295
296
|
}}
|
|
296
|
-
style={[styles.input, { color: colors.foreground, fontSize: s.font, height: s.h - 3 }, webNoOutline]}
|
|
297
|
+
style={[styles.input, ff, { color: colors.foreground, fontSize: s.font, height: s.h - 3 }, webNoOutline]}
|
|
297
298
|
/>
|
|
298
299
|
</Animated.View>
|
|
299
300
|
<Animated.View
|
|
@@ -343,7 +344,7 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBa
|
|
|
343
344
|
<Text
|
|
344
345
|
pointerEvents="none"
|
|
345
346
|
onLayout={(e) => setCancelW(Math.ceil(e.nativeEvent.layout.width) + 14)}
|
|
346
|
-
style={[styles.cancelText, styles.measure, { fontSize: s.font }]}
|
|
347
|
+
style={[styles.cancelText, styles.measure, ff, { fontSize: s.font }]}
|
|
347
348
|
>
|
|
348
349
|
{cancelLabel}
|
|
349
350
|
</Text>
|
|
@@ -366,7 +367,7 @@ export const SearchBar = forwardRef<TextInput, SearchBarProps>(function SearchBa
|
|
|
366
367
|
accessibilityLabel={cancelLabel}
|
|
367
368
|
onPress={cancel}
|
|
368
369
|
>
|
|
369
|
-
<Text numberOfLines={1} style={[styles.cancelText, { color: tint, fontSize: s.font }]}>
|
|
370
|
+
<Text numberOfLines={1} style={[styles.cancelText, ff, { color: tint, fontSize: s.font }]}>
|
|
370
371
|
{cancelLabel}
|
|
371
372
|
</Text>
|
|
372
373
|
</Pressable>
|
|
@@ -397,7 +398,8 @@ function SuggestionPanel({
|
|
|
397
398
|
tint: string;
|
|
398
399
|
onSelect: (item: SearchSuggestion) => void;
|
|
399
400
|
}) {
|
|
400
|
-
const { colors, radius } = useKit();
|
|
401
|
+
const { colors, radius, font } = useKit();
|
|
402
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
401
403
|
const [mounted, setMounted] = useState(open);
|
|
402
404
|
const enter = useRef(new Animated.Value(0)).current;
|
|
403
405
|
const last = useRef(items);
|
|
@@ -443,11 +445,11 @@ function SuggestionPanel({
|
|
|
443
445
|
<View style={[styles.optionIcon, { backgroundColor: colors.muted }]}>
|
|
444
446
|
<Icon name={item.icon ?? 'search'} size={15} color={item.icon === 'trending' ? tint : colors.mutedForeground} />
|
|
445
447
|
</View>
|
|
446
|
-
<Text numberOfLines={1} style={[styles.optionText, { color: colors.mutedForeground }]}>
|
|
448
|
+
<Text numberOfLines={1} style={[styles.optionText, ff, { color: colors.mutedForeground }]}>
|
|
447
449
|
<Highlight label={item.label} query={query} color={colors.foreground} />
|
|
448
450
|
</Text>
|
|
449
451
|
{item.hint ? (
|
|
450
|
-
<Text numberOfLines={1} style={[styles.hint, { color: colors.mutedForeground }]}>
|
|
452
|
+
<Text numberOfLines={1} style={[styles.hint, ff, { color: colors.mutedForeground }]}>
|
|
451
453
|
{item.hint}
|
|
452
454
|
</Text>
|
|
453
455
|
) : null}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -57,7 +57,7 @@ const SIZES: Record<SegmentedControlSize, { h: number; font: number; px: number;
|
|
|
57
57
|
const INSET = 3;
|
|
58
58
|
// Liquid move: the edge in the direction of travel leads, the other one trails behind.
|
|
59
59
|
const LEAD = motion.snappy;
|
|
60
|
-
const TRAIL = { damping:
|
|
60
|
+
const TRAIL = { damping: 24, stiffness: 150, mass: 0.9 };
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Segmented control with a liquid indicator: its leading edge springs ahead and the trailing
|
|
@@ -274,6 +274,8 @@ interface SegmentProps {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
function Segment({ option, active, onPress, onPressIn, onLayout, fullWidth, size, height, activeFg, idleFg, disabled }: SegmentProps) {
|
|
277
|
+
const { font } = useKit();
|
|
278
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
277
279
|
// a: text colour (JS). press: content squish, pop: little bounce on selection (native, same view).
|
|
278
280
|
const a = useRef(new Animated.Value(active ? 1 : 0)).current;
|
|
279
281
|
const press = useRef(new Animated.Value(1)).current;
|
|
@@ -315,7 +317,7 @@ function Segment({ option, active, onPress, onPressIn, onLayout, fullWidth, size
|
|
|
315
317
|
<Animated.View style={[styles.content, { transform: [{ scale: Animated.multiply(press, pop) }] }]}>
|
|
316
318
|
{icon ? <View style={{ minWidth: size.icon, alignItems: 'center' }}>{icon}</View> : null}
|
|
317
319
|
{option.label ? (
|
|
318
|
-
<Animated.Text numberOfLines={1} style={[styles.label, { fontSize: size.font, color }]}>
|
|
320
|
+
<Animated.Text numberOfLines={1} style={[styles.label, ff, { fontSize: size.font, color }]}>
|
|
319
321
|
{option.label}
|
|
320
322
|
</Animated.Text>
|
|
321
323
|
) : null}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Dimensions,
|
|
@@ -219,8 +219,8 @@ export function Select<T extends string | number = string>({
|
|
|
219
219
|
const [anchor, setAnchor] = useState({ x: 0, y: 0, w: 0, h: 0 });
|
|
220
220
|
const [hi, setHi] = useState(-1); // highlighted row (keyboard / hover, web)
|
|
221
221
|
const [kb, setKb] = useState(0); // on-screen keyboard height while the dropdown is open
|
|
222
|
-
const fieldRef = useRef<View
|
|
223
|
-
const scrollRef = useRef<ScrollView
|
|
222
|
+
const fieldRef = useRef<ComponentRef<typeof View>>(null);
|
|
223
|
+
const scrollRef = useRef<ComponentRef<typeof ScrollView>>(null);
|
|
224
224
|
const rowBox = useRef<Record<string, { y: number; h: number }>>({});
|
|
225
225
|
const scrollY = useRef(0);
|
|
226
226
|
const viewH = useRef(0);
|