@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,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useId, useMemo,
|
|
1
|
+
import React, { useEffect, useId, useMemo, useState } from 'react';
|
|
2
2
|
import { Animated, Image, PixelRatio, StyleSheet, Text, View, type ImageSourcePropType, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
import Svg, { Circle, ClipPath, Defs, G, Line, LinearGradient, Path, Pattern, Rect, Stop } from 'react-native-svg';
|
|
4
4
|
|
|
@@ -28,13 +28,13 @@ export interface TicketRect {
|
|
|
28
28
|
export interface TicketGeometry {
|
|
29
29
|
width: number;
|
|
30
30
|
height: number;
|
|
31
|
-
corners?: TicketCorners;
|
|
31
|
+
corners?: TicketCorners | undefined;
|
|
32
32
|
/** Round bites cut into a side — e.g. both ends of a tear line. */
|
|
33
|
-
notches?: readonly TicketNotch[];
|
|
33
|
+
notches?: readonly TicketNotch[] | undefined;
|
|
34
34
|
/** Pattern per side (default straight). */
|
|
35
|
-
edges?: Partial<Record<TicketSide, TicketEdge
|
|
35
|
+
edges?: Partial<Record<TicketSide, TicketEdge | undefined>> | undefined;
|
|
36
36
|
/** Bite radius, scallop depth or tooth height of the edge patterns (default 5). */
|
|
37
|
-
edgeSize?: number;
|
|
37
|
+
edgeSize?: number | undefined;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const SIDES: readonly TicketSide[] = ['top', 'right', 'bottom', 'left'];
|
|
@@ -140,20 +140,25 @@ export function ticketSafeRects(g: TicketGeometry, offset: { x: number; y: numbe
|
|
|
140
140
|
// Each round hole is covered by a few stepped rects that hug its curve, so almost nothing
|
|
141
141
|
// around it is left without light.
|
|
142
142
|
const holes: [number, number, number, number][] = [];
|
|
143
|
-
|
|
143
|
+
// Consecutive pairs of [0, 0.45, 0.75, 1].
|
|
144
|
+
const STEPS: readonly (readonly [number, number])[] = [
|
|
145
|
+
[0, 0.45],
|
|
146
|
+
[0.45, 0.75],
|
|
147
|
+
[0.75, 1],
|
|
148
|
+
];
|
|
144
149
|
/** A quarter disc of radius r at corner (cx, cy), opening toward (dx, dy) = ±1. */
|
|
145
150
|
const quarter = (cx: number, cy: number, r: number, dx: number, dy: number) => {
|
|
146
|
-
for (
|
|
147
|
-
const reach = r * Math.sqrt(1 -
|
|
148
|
-
const [a, b] = [cy + dy * r *
|
|
151
|
+
for (const [s0, s1] of STEPS) {
|
|
152
|
+
const reach = r * Math.sqrt(1 - s0 * s0);
|
|
153
|
+
const [a, b] = [cy + dy * r * s0, cy + dy * r * s1];
|
|
149
154
|
holes.push([Math.min(cx, cx + dx * reach), Math.min(a, b), Math.max(cx, cx + dx * reach), Math.max(a, b)]);
|
|
150
155
|
}
|
|
151
156
|
};
|
|
152
157
|
/** The same with x and y swapped (steps run along x). */
|
|
153
158
|
const quarterX = (cx: number, cy: number, r: number, dx: number, dy: number) => {
|
|
154
|
-
for (
|
|
155
|
-
const reach = r * Math.sqrt(1 -
|
|
156
|
-
const [a, b] = [cx + dx * r *
|
|
159
|
+
for (const [s0, s1] of STEPS) {
|
|
160
|
+
const reach = r * Math.sqrt(1 - s0 * s0);
|
|
161
|
+
const [a, b] = [cx + dx * r * s0, cx + dx * r * s1];
|
|
157
162
|
holes.push([Math.min(a, b), Math.min(cy, cy + dy * reach), Math.max(a, b), Math.max(cy, cy + dy * reach)]);
|
|
158
163
|
}
|
|
159
164
|
};
|
|
@@ -191,25 +196,25 @@ export function ticketSafeRects(g: TicketGeometry, offset: { x: number; y: numbe
|
|
|
191
196
|
for (const hole of holes) [hole[0], hole[1], hole[2], hole[3]] = [down(hole[0]), down(hole[1]), up(hole[2]), up(hole[3])];
|
|
192
197
|
|
|
193
198
|
const cuts = (lo: number, hi: number, more: number[]) => [...new Set([lo, hi, ...more])].filter((v) => v >= lo && v <= hi).sort((a, b) => a - b);
|
|
199
|
+
/** Each neighbouring pair of sorted cuts: [v0, v1], [v1, v2], … */
|
|
200
|
+
const bands = (v: number[]) => v.slice(1).map((b, i): [number, number] => [v[i] ?? b, b]);
|
|
194
201
|
const xs = cuts(near(x0), near(x1), holes.flatMap(([a, , c]) => [a, c]));
|
|
195
202
|
const ys = cuts(near(y0), near(y1), holes.flatMap(([, b, , d]) => [b, d]));
|
|
196
203
|
const rows: { y: number; h: number; spans: [number, number][] }[] = [];
|
|
197
204
|
// Every cut is on the pixel grid, so anything thinner than half a pixel is a rounding twin; a
|
|
198
205
|
// one-pixel row (where two stepped holes overlap) must stay, or the reflection shows a gap there.
|
|
199
206
|
const thin = 0.5 / px;
|
|
200
|
-
for (
|
|
201
|
-
const [ya, yb] = [ys[j], ys[j + 1]];
|
|
207
|
+
for (const [ya, yb] of bands(ys)) {
|
|
202
208
|
if (yb - ya < thin) continue;
|
|
203
209
|
const spans: [number, number][] = [];
|
|
204
|
-
for (
|
|
205
|
-
const [xa, xb] = [xs[i], xs[i + 1]];
|
|
210
|
+
for (const [xa, xb] of bands(xs)) {
|
|
206
211
|
if (xb - xa < thin || holes.some(([hx0, hy0, hx1, hy1]) => xa < hx1 && xb > hx0 && ya < hy1 && yb > hy0)) continue;
|
|
207
212
|
const last = spans[spans.length - 1];
|
|
208
213
|
if (last && Math.abs(last[1] - xa) < 0.01) last[1] = xb;
|
|
209
214
|
else spans.push([xa, xb]);
|
|
210
215
|
}
|
|
211
216
|
const prev = rows[rows.length - 1];
|
|
212
|
-
const same = prev && prev.spans.length === spans.length && prev.spans.every((s, i) => s[0] === spans[i][0] && s[1] === spans[i][1]);
|
|
217
|
+
const same = prev && prev.spans.length === spans.length && prev.spans.every((s, i) => s[0] === spans[i]?.[0] && s[1] === spans[i]?.[1]);
|
|
213
218
|
if (prev && same && Math.abs(prev.y + prev.h - ya) < 0.01) prev.h += yb - ya;
|
|
214
219
|
else rows.push({ y: ya, h: yb - ya, spans });
|
|
215
220
|
}
|
|
@@ -231,8 +236,8 @@ function random(seed: string) {
|
|
|
231
236
|
const CONFETTI = ['#ffd23f', '#ff5fa2', '#3ddcff', '#7dff7a', '#ffffff'];
|
|
232
237
|
|
|
233
238
|
/** A rubber stamp ("USED", "CHECKED IN") that thumps down whenever its text changes. */
|
|
234
|
-
export function TicketStamp({ text, color, size = 17, style }: { text: string; color: string; size?: number; style?: StyleProp<ViewStyle> }) {
|
|
235
|
-
const thump =
|
|
239
|
+
export function TicketStamp({ text, color, size = 17, style }: { text: string; color: string; size?: number | undefined; style?: StyleProp<ViewStyle> | undefined }) {
|
|
240
|
+
const [thump] = useState(() => new Animated.Value(0));
|
|
236
241
|
useEffect(() => {
|
|
237
242
|
thump.setValue(0);
|
|
238
243
|
Animated.spring(thump, { toValue: 1, ...motion.bouncy, useNativeDriver: true }).start();
|
|
@@ -258,12 +263,17 @@ export function TicketStamp({ text, color, size = 17, style }: { text: string; c
|
|
|
258
263
|
}
|
|
259
264
|
|
|
260
265
|
/** A QR-style matrix (finder squares, timing lines, data) drawn as one path — a look-alike, not a real code. */
|
|
261
|
-
export function QrLike({ code, size, color = '#111111' }: { code: string; size: number; color?: string }) {
|
|
266
|
+
export function QrLike({ code, size, color = '#111111' }: { code: string; size: number; color?: string | undefined }) {
|
|
262
267
|
const d = useMemo(() => {
|
|
263
268
|
const N = 25;
|
|
264
269
|
const next = random(code + '|qr');
|
|
265
270
|
const finder = (x: number, y: number) => {
|
|
266
|
-
|
|
271
|
+
const corners: [number, number][] = [
|
|
272
|
+
[0, 0],
|
|
273
|
+
[N - 7, 0],
|
|
274
|
+
[0, N - 7],
|
|
275
|
+
];
|
|
276
|
+
for (const [fx, fy] of corners) {
|
|
267
277
|
const [u, v] = [x - fx, y - fy];
|
|
268
278
|
if (u >= -1 && u <= 7 && v >= -1 && v <= 7) {
|
|
269
279
|
if (u < 0 || v < 0 || u > 6 || v > 6) return 0; // the white ring around a finder
|
|
@@ -289,7 +299,7 @@ export function QrLike({ code, size, color = '#111111' }: { code: string; size:
|
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
/** A Code 128-style barcode drawn as one path — a look-alike. `vertical` turns it on its side. */
|
|
292
|
-
export function BarcodeLike({ code, width, height, color = '#111111', vertical = false }: { code: string; width: number; height: number; color?: string; vertical?: boolean }) {
|
|
302
|
+
export function BarcodeLike({ code, width, height, color = '#111111', vertical = false }: { code: string; width: number; height: number; color?: string | undefined; vertical?: boolean | undefined }) {
|
|
293
303
|
const { d, units } = useMemo(() => {
|
|
294
304
|
const next = random(code + '|bars');
|
|
295
305
|
const bars: [number, number][] = [];
|
|
@@ -314,39 +324,39 @@ export function BarcodeLike({ code, width, height, color = '#111111', vertical =
|
|
|
314
324
|
}
|
|
315
325
|
|
|
316
326
|
export interface TicketShapeProps extends Omit<TicketGeometry, 'width' | 'height'> {
|
|
317
|
-
children?: React.ReactNode;
|
|
327
|
+
children?: React.ReactNode | undefined;
|
|
318
328
|
/** A colour, or 2+ colours for a gradient (default white). */
|
|
319
|
-
fill?: string | readonly string[];
|
|
329
|
+
fill?: string | readonly string[] | undefined;
|
|
320
330
|
/** Gradient direction in degrees: 0 runs left → right, 90 top → bottom (default 125). */
|
|
321
|
-
angle?: number;
|
|
331
|
+
angle?: number | undefined;
|
|
322
332
|
/** Photo painted inside the outline (cover), over the fill (`require()` or `{ uri }`). */
|
|
323
|
-
image?: ImageSourcePropType;
|
|
324
|
-
imageOpacity?: number;
|
|
333
|
+
image?: ImageSourcePropType | undefined;
|
|
334
|
+
imageOpacity?: number | undefined;
|
|
325
335
|
/** Darkens the paint toward the bottom (0 – 1) so white text reads on a photo — inside the outline only. */
|
|
326
|
-
shade?: number;
|
|
336
|
+
shade?: number | undefined;
|
|
327
337
|
/** Texture over the fill. */
|
|
328
|
-
pattern?: TicketPattern;
|
|
338
|
+
pattern?: TicketPattern | undefined;
|
|
329
339
|
/** Pattern ink (default: white or black at low opacity, whichever suits the fill; confetti is multicolour). */
|
|
330
|
-
patternColor?: string;
|
|
340
|
+
patternColor?: string | undefined;
|
|
331
341
|
/** Keeps the confetti scatter the same on every render (e.g. the coupon code). */
|
|
332
|
-
seed?: string;
|
|
342
|
+
seed?: string | undefined;
|
|
333
343
|
/** Outline colour. */
|
|
334
|
-
stroke?: string;
|
|
335
|
-
strokeWidth?: number;
|
|
344
|
+
stroke?: string | undefined;
|
|
345
|
+
strokeWidth?: number | undefined;
|
|
336
346
|
/** Dash the outline — a "cut here" coupon. */
|
|
337
|
-
dashed?: boolean;
|
|
347
|
+
dashed?: boolean | undefined;
|
|
338
348
|
/** Dashed tear line along this side. */
|
|
339
|
-
tear?: TicketSide;
|
|
340
|
-
tearColor?: string;
|
|
349
|
+
tear?: TicketSide | undefined;
|
|
350
|
+
tearColor?: string | undefined;
|
|
341
351
|
/**
|
|
342
352
|
* Paint as one piece of a bigger ticket: that ticket's rect in this shape's points. Gradients,
|
|
343
353
|
* patterns and photos then line up across the pieces.
|
|
344
354
|
*/
|
|
345
|
-
frame?: TicketRect;
|
|
355
|
+
frame?: TicketRect | undefined;
|
|
346
356
|
/** Reports the measured size after layout. */
|
|
347
|
-
onSize?: (size: { width: number; height: number }) => void;
|
|
357
|
+
onSize?: ((size: { width: number; height: number }) => void) | undefined;
|
|
348
358
|
/** Padding, width, margins… — the shape takes its size from this box. */
|
|
349
|
-
style?: StyleProp<ViewStyle
|
|
359
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
350
360
|
}
|
|
351
361
|
|
|
352
362
|
/**
|
|
@@ -381,7 +391,7 @@ export function TicketShape({
|
|
|
381
391
|
const id = 'ts' + useId().replace(/[^A-Za-z0-9_-]/g, '');
|
|
382
392
|
const { w, h } = size;
|
|
383
393
|
const colors = typeof fill === 'string' ? [fill] : fill.length ? fill : ['#ffffff'];
|
|
384
|
-
const ink = readableOn(colors[Math.floor((colors.length - 1) / 2)]);
|
|
394
|
+
const ink = readableOn(colors[Math.floor((colors.length - 1) / 2)] ?? '#ffffff');
|
|
385
395
|
const d = ticketPath({ width: w, height: h, corners, notches, edges, edgeSize });
|
|
386
396
|
const f = frameProp ?? { x: 0, y: 0, width: w, height: h };
|
|
387
397
|
|
|
@@ -395,7 +405,7 @@ export function TicketShape({
|
|
|
395
405
|
const clip = `url(#${id}-clip)`;
|
|
396
406
|
const corner = cornerList(corners);
|
|
397
407
|
const [s0, s1] = tear === 'top' || tear === 'bottom' ? (tear === 'top' ? [corner[0], corner[1]] : [corner[3], corner[2]]) : tear === 'left' ? [corner[0], corner[3]] : [corner[1], corner[2]];
|
|
398
|
-
const inset = [Math.abs(s0) + 7, Math.abs(s1) + 7];
|
|
408
|
+
const inset: [number, number] = [Math.abs(s0) + 7, Math.abs(s1) + 7];
|
|
399
409
|
const tearLine =
|
|
400
410
|
tear === 'top' || tear === 'bottom'
|
|
401
411
|
? { x1: inset[0], x2: w - inset[1], y1: tear === 'top' ? 0.75 : h - 0.75, y2: tear === 'top' ? 0.75 : h - 0.75 }
|
|
@@ -419,7 +429,7 @@ export function TicketShape({
|
|
|
419
429
|
const x = f.x + rnd() * f.width;
|
|
420
430
|
const y = f.y + rnd() * f.height;
|
|
421
431
|
const turn = Math.round(rnd() * 180);
|
|
422
|
-
const color = patternColor ?? CONFETTI[Math.floor(rnd() * CONFETTI.length)];
|
|
432
|
+
const color = patternColor ?? CONFETTI[Math.floor(rnd() * CONFETTI.length)] ?? '#ffffff';
|
|
423
433
|
const o = 0.35 + rnd() * 0.4;
|
|
424
434
|
return rnd() < 0.55 ? (
|
|
425
435
|
<Rect key={i} x={x - 1.6} y={y - 4.5} width={3.2} height={9} rx={1} fill={color} opacity={o} transform={`rotate(${turn} ${r2(x)} ${r2(y)})`} />
|
|
@@ -446,7 +456,7 @@ export function TicketShape({
|
|
|
446
456
|
))}
|
|
447
457
|
</LinearGradient>
|
|
448
458
|
) : null;
|
|
449
|
-
const fillPath = <Path d={d} fill={colors.length > 1 ? `url(#${id}-fill)` : colors[0]} />;
|
|
459
|
+
const fillPath = <Path d={d} fill={colors.length > 1 ? `url(#${id}-fill)` : (colors[0] ?? '#ffffff')} />;
|
|
450
460
|
const overlayDefs = (
|
|
451
461
|
<>
|
|
452
462
|
<ClipPath id={`${id}-clip`}>
|
|
@@ -482,7 +492,7 @@ export function TicketShape({
|
|
|
482
492
|
<>
|
|
483
493
|
{shade > 0 ? <Rect width={w} height={h} fill={`url(#${id}-shade)`} clipPath={clip} /> : null}
|
|
484
494
|
{texture ? <G clipPath={clip}>{texture}</G> : null}
|
|
485
|
-
{stroke ? <Path d={d} fill="none" stroke={stroke} strokeWidth={strokeWidth}
|
|
495
|
+
{stroke ? <Path d={d} fill="none" stroke={stroke} strokeWidth={strokeWidth} {...(dashed ? { strokeDasharray: [7, 5] } : null)} strokeLinecap="round" /> : null}
|
|
486
496
|
{tear ? <Line {...tearLine} stroke={tearColor ?? alpha(ink, 0.28)} strokeWidth={1.6} strokeDasharray={[6, 5]} strokeLinecap="round" /> : null}
|
|
487
497
|
</>
|
|
488
498
|
);
|
package/registry/coupon.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"title": "Coupon",
|
|
5
5
|
"description": "Coupons people actually want to tap — a vivid value panel (sunburst, confetti, waves…) joined to a details panel by a perforated tear line with real cut-outs. Ten themes, five outlines (ticket, stamp, receipt, scallop, cut-out), copyable code, Apply button, live countdown, applied / used / expired / locked states, a light sweep, and an optional 3D tilt with a holo sheen that flips over to the T&C.",
|
|
6
6
|
"tier": "free",
|
|
7
|
+
"aliases": [],
|
|
7
8
|
"dependencies": [
|
|
8
9
|
"react-native-svg"
|
|
9
10
|
],
|
package/registry/cube-pager.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"title": "Cube Pager",
|
|
5
5
|
"description": "Swipeable pages that turn like the faces of a cube — from the outside, the inside, or spinning in place. Follows your finger, carries flick momentum, rubber-bands at the ends and shades faces as they turn away. Great for stories and onboarding.",
|
|
6
6
|
"tier": "pro",
|
|
7
|
+
"aliases": [],
|
|
7
8
|
"dependencies": [],
|
|
8
9
|
"registryDependencies": [
|
|
9
10
|
"kit"
|
package/registry/dashboard.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"title": "Dashboard",
|
|
5
5
|
"description": "A finance / analytics home screen: a collapsing “Overview” large title with bell and avatar, time-of-day greeting, Day / Week / Month switcher, a revenue area chart that rises in on every period change and scrubs under your finger while the headline number rolls, KPI tiles with sparklines, springy quick actions and recent transactions — plus skeleton loading and pull-to-refresh.",
|
|
6
6
|
"tier": "pro",
|
|
7
|
+
"aliases": [],
|
|
7
8
|
"dependencies": [
|
|
8
9
|
"react-native-svg",
|
|
9
10
|
"expo-haptics"
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
AccessibilityInfo,
|
|
4
4
|
Animated,
|
|
5
5
|
Easing,
|
|
6
|
-
PanResponder,
|
|
7
6
|
Pressable,
|
|
8
7
|
StyleSheet,
|
|
9
8
|
Text,
|
|
@@ -14,7 +13,7 @@ import {
|
|
|
14
13
|
} from 'react-native';
|
|
15
14
|
import Svg, { Path } from 'react-native-svg';
|
|
16
15
|
|
|
17
|
-
import { alpha, motion, readableOn, useKit } from '@/lib/kit';
|
|
16
|
+
import { alpha, motion, readableOn, useKit, usePanResponder } from '@/lib/kit';
|
|
18
17
|
|
|
19
18
|
export type CalendarMode = 'single' | 'range' | 'multiple';
|
|
20
19
|
|
|
@@ -30,65 +29,65 @@ export type CalendarMarks = Record<string, string | string[]>;
|
|
|
30
29
|
|
|
31
30
|
export interface CalendarBaseProps {
|
|
32
31
|
/** Controlled visible month (any date inside it). */
|
|
33
|
-
month?: Date;
|
|
32
|
+
month?: Date | undefined;
|
|
34
33
|
/** Month shown first (default: the selected date, else today). */
|
|
35
|
-
defaultMonth?: Date;
|
|
36
|
-
onMonthChange?: (month: Date) => void;
|
|
34
|
+
defaultMonth?: Date | undefined;
|
|
35
|
+
onMonthChange?: ((month: Date) => void) | undefined;
|
|
37
36
|
/** 0 = Sunday, 1 = Monday. */
|
|
38
|
-
weekStartsOn?: 0 | 1;
|
|
37
|
+
weekStartsOn?: 0 | 1 | undefined;
|
|
39
38
|
/** BCP 47 locale for month and weekday names (default: device locale). */
|
|
40
|
-
locale?: string;
|
|
39
|
+
locale?: string | undefined;
|
|
41
40
|
/** Earliest selectable day. */
|
|
42
|
-
minDate?: Date;
|
|
41
|
+
minDate?: Date | undefined;
|
|
43
42
|
/** Latest selectable day. */
|
|
44
|
-
maxDate?: Date;
|
|
43
|
+
maxDate?: Date | undefined;
|
|
45
44
|
/** Return true to disable a day (booked, holidays…). */
|
|
46
|
-
isDateDisabled?: (date: Date) => boolean;
|
|
45
|
+
isDateDisabled?: ((date: Date) => boolean) | undefined;
|
|
47
46
|
/** Coloured dots under dates: `{ [toDateKey(date)]: '#22c55e' | ['#22c55e', '#f59e0b'] }`. */
|
|
48
|
-
markedDates?: CalendarMarks;
|
|
47
|
+
markedDates?: CalendarMarks | undefined;
|
|
49
48
|
/** Fill whole days with a colour (birthdays, shifts, people…): `{ [toDateKey(date)]: '#f472b6' }`. */
|
|
50
|
-
highlightedDates?: Record<string, string
|
|
49
|
+
highlightedDates?: Record<string, string> | undefined;
|
|
51
50
|
/** `tiles` puts every day on a soft tile; `minimal` shows bare numbers. */
|
|
52
|
-
variant?: 'tiles' | 'minimal';
|
|
51
|
+
variant?: 'tiles' | 'minimal' | undefined;
|
|
53
52
|
/** Weekday header: `narrow` (M T W) or `short` (Mon Tue Wed). */
|
|
54
|
-
weekdayFormat?: 'narrow' | 'short';
|
|
53
|
+
weekdayFormat?: 'narrow' | 'short' | undefined;
|
|
55
54
|
/** Show a "Today" pill in the header while another month is on screen. */
|
|
56
|
-
showTodayButton?: boolean;
|
|
55
|
+
showTodayButton?: boolean | undefined;
|
|
57
56
|
/** Show the faded trailing / leading days of the neighbouring months. */
|
|
58
|
-
showOutsideDays?: boolean;
|
|
57
|
+
showOutsideDays?: boolean | undefined;
|
|
59
58
|
/** Ring and tint today's date. */
|
|
60
|
-
showToday?: boolean;
|
|
59
|
+
showToday?: boolean | undefined;
|
|
61
60
|
/** Always render six week rows so the height never changes between months. */
|
|
62
|
-
fixedWeeks?: boolean;
|
|
61
|
+
fixedWeeks?: boolean | undefined;
|
|
63
62
|
/** Tap the month title to jump through a month / year grid. */
|
|
64
|
-
monthPicker?: boolean;
|
|
63
|
+
monthPicker?: boolean | undefined;
|
|
65
64
|
/** Most dates selectable in `multiple` mode. */
|
|
66
|
-
max?: number;
|
|
65
|
+
max?: number | undefined;
|
|
67
66
|
/** Selection and today colour (default: theme accent). */
|
|
68
|
-
accent?: string;
|
|
69
|
-
style?: StyleProp<ViewStyle
|
|
67
|
+
accent?: string | undefined;
|
|
68
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
export interface CalendarSingleProps {
|
|
73
|
-
mode?: 'single';
|
|
74
|
-
value?: Date | null;
|
|
75
|
-
defaultValue?: Date | null;
|
|
76
|
-
onChange?: (date: Date) => void;
|
|
72
|
+
mode?: 'single' | undefined;
|
|
73
|
+
value?: Date | null | undefined;
|
|
74
|
+
defaultValue?: Date | null | undefined;
|
|
75
|
+
onChange?: ((date: Date) => void) | undefined;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
78
|
export interface CalendarRangeProps {
|
|
80
79
|
mode: 'range';
|
|
81
|
-
value?: DateRange | null;
|
|
82
|
-
defaultValue?: DateRange | null;
|
|
80
|
+
value?: DateRange | null | undefined;
|
|
81
|
+
defaultValue?: DateRange | null | undefined;
|
|
83
82
|
/** Fires on every tap: first with `{ start, end: null }`, then with both ends. */
|
|
84
|
-
onChange?: (range: DateRange) => void;
|
|
83
|
+
onChange?: ((range: DateRange) => void) | undefined;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
export interface CalendarMultipleProps {
|
|
88
87
|
mode: 'multiple';
|
|
89
|
-
value?: Date[];
|
|
90
|
-
defaultValue?: Date[];
|
|
91
|
-
onChange?: (dates: Date[]) => void;
|
|
88
|
+
value?: Date[] | undefined;
|
|
89
|
+
defaultValue?: Date[] | undefined;
|
|
90
|
+
onChange?: ((dates: Date[]) => void) | undefined;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
export type CalendarProps = CalendarBaseProps & (CalendarSingleProps | CalendarRangeProps | CalendarMultipleProps);
|
|
@@ -161,11 +160,11 @@ function useFormatters(locale?: string) {
|
|
|
161
160
|
}
|
|
162
161
|
};
|
|
163
162
|
return {
|
|
164
|
-
title: make({ month: 'long', year: 'numeric' }, (d) => `${EN_MONTHS[d.getMonth()]} ${d.getFullYear()}`),
|
|
165
|
-
month: make({ month: 'short' }, (d) => EN_MONTHS[d.getMonth()].slice(0, 3)),
|
|
166
|
-
weekday: make({ weekday: 'short' }, (d) => EN_DAYS[d.getDay()]),
|
|
167
|
-
weekdayNarrow: make({ weekday: 'narrow' }, (d) => EN_DAYS[d.getDay()]
|
|
168
|
-
weekdayLong: make({ weekday: 'long' }, (d) => EN_DAYS[d.getDay()]),
|
|
163
|
+
title: make({ month: 'long', year: 'numeric' }, (d) => `${EN_MONTHS[d.getMonth()] ?? ''} ${d.getFullYear()}`),
|
|
164
|
+
month: make({ month: 'short' }, (d) => (EN_MONTHS[d.getMonth()] ?? '').slice(0, 3)),
|
|
165
|
+
weekday: make({ weekday: 'short' }, (d) => EN_DAYS[d.getDay()] ?? ''),
|
|
166
|
+
weekdayNarrow: make({ weekday: 'narrow' }, (d) => (EN_DAYS[d.getDay()] ?? '').slice(0, 1)),
|
|
167
|
+
weekdayLong: make({ weekday: 'long' }, (d) => EN_DAYS[d.getDay()] ?? ''),
|
|
169
168
|
full: make({ weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }, (d) => d.toDateString()),
|
|
170
169
|
};
|
|
171
170
|
}, [locale]);
|
|
@@ -206,7 +205,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
206
205
|
accent,
|
|
207
206
|
style,
|
|
208
207
|
} = props as unknown as FlatProps;
|
|
209
|
-
const { colors, radius, font } = useKit();
|
|
208
|
+
const { colors, radius, font, labels } = useKit();
|
|
210
209
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
211
210
|
const tint = accent ?? colors.accent;
|
|
212
211
|
const fmt = useFormatters(locale);
|
|
@@ -244,9 +243,10 @@ export function Calendar(props: CalendarProps) {
|
|
|
244
243
|
|
|
245
244
|
/* month slide transition */
|
|
246
245
|
const [width, setWidth] = useState(0);
|
|
247
|
-
const slide =
|
|
246
|
+
const [slide] = useState(() => new Animated.Value(1)); // native
|
|
248
247
|
const [leaving, setLeaving] = useState<{ month: Date; dir: number } | null>(null);
|
|
249
|
-
|
|
248
|
+
// Which way the last month change went: the incoming grid slides in from that side.
|
|
249
|
+
const [slideDir, setSlideDir] = useState(1);
|
|
250
250
|
const shownRef = useRef(visible);
|
|
251
251
|
const visibleKey = monthIndex(visible);
|
|
252
252
|
useLayoutEffect(() => {
|
|
@@ -254,7 +254,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
254
254
|
if (monthIndex(prev) === visibleKey) return;
|
|
255
255
|
shownRef.current = visible;
|
|
256
256
|
const dir = visibleKey > monthIndex(prev) ? 1 : -1;
|
|
257
|
-
|
|
257
|
+
setSlideDir(dir);
|
|
258
258
|
setLeaving({ month: prev, dir });
|
|
259
259
|
slide.stopAnimation();
|
|
260
260
|
slide.setValue(0);
|
|
@@ -271,7 +271,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
271
271
|
/* days / months / years view */
|
|
272
272
|
const [view, setView] = useState<'days' | 'months' | 'years'>('days');
|
|
273
273
|
const [pickYear, setPickYear] = useState(visible.getFullYear());
|
|
274
|
-
const viewAnim =
|
|
274
|
+
const [viewAnim] = useState(() => new Animated.Value(1)); // native
|
|
275
275
|
const [gridH, setGridH] = useState(0);
|
|
276
276
|
const switchView = (next: 'days' | 'months' | 'years') => {
|
|
277
277
|
if (next !== 'days' && view === 'days') setPickYear(visible.getFullYear());
|
|
@@ -283,7 +283,9 @@ export function Calendar(props: CalendarProps) {
|
|
|
283
283
|
|
|
284
284
|
/* selection */
|
|
285
285
|
const stateRef = useRef({ mode, current, value, onChange, max, visible, goTo });
|
|
286
|
-
|
|
286
|
+
useLayoutEffect(() => {
|
|
287
|
+
stateRef.current = { mode, current, value, onChange, max, visible, goTo };
|
|
288
|
+
});
|
|
287
289
|
const onDayPress = useCallback((d: Date) => {
|
|
288
290
|
const s = stateRef.current;
|
|
289
291
|
let next: CalendarValue;
|
|
@@ -305,18 +307,14 @@ export function Calendar(props: CalendarProps) {
|
|
|
305
307
|
if (monthIndex(d) !== monthIndex(s.visible)) s.goTo(d);
|
|
306
308
|
}, []);
|
|
307
309
|
|
|
308
|
-
const pan =
|
|
309
|
-
() =>
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
},
|
|
317
|
-
}),
|
|
318
|
-
[]
|
|
319
|
-
);
|
|
310
|
+
const pan = usePanResponder({
|
|
311
|
+
onMoveShouldSetPanResponder: (_, g) => Math.abs(g.dx) > 14 && Math.abs(g.dx) > Math.abs(g.dy) * 1.4,
|
|
312
|
+
onPanResponderRelease: (_, g) => {
|
|
313
|
+
const s = stateRef.current;
|
|
314
|
+
if (g.dx < -40 || g.vx < -0.5) s.goTo(addMonths(s.visible, 1));
|
|
315
|
+
else if (g.dx > 40 || g.vx > 0.5) s.goTo(addMonths(s.visible, -1));
|
|
316
|
+
},
|
|
317
|
+
});
|
|
320
318
|
|
|
321
319
|
const weekdays = useMemo(() => {
|
|
322
320
|
const base = new Date(2023, 0, 1); // a Sunday
|
|
@@ -349,7 +347,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
349
347
|
|
|
350
348
|
const W = width || 320;
|
|
351
349
|
// Reduce Motion: months swap with a fade only, no sideways slide.
|
|
352
|
-
const dir = reduce ? 0 :
|
|
350
|
+
const dir = reduce ? 0 : slideDir;
|
|
353
351
|
const prevDisabled = view === 'days' ? monthIndex(visible) - 1 < minM : view === 'months' ? pickYear * 12 - 1 < minM : false;
|
|
354
352
|
const nextDisabled = view === 'days' ? monthIndex(visible) + 1 > maxM : view === 'months' ? (pickYear + 1) * 12 > maxM : false;
|
|
355
353
|
const decadeStart = Math.floor(pickYear / 12) * 12;
|
|
@@ -374,7 +372,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
374
372
|
onPress={monthPicker ? () => switchView(view === 'days' ? 'months' : view === 'months' ? 'years' : 'days') : undefined}
|
|
375
373
|
disabled={!monthPicker}
|
|
376
374
|
accessibilityRole={monthPicker ? 'button' : 'header'}
|
|
377
|
-
accessibilityLabel={monthPicker ? `${title}, ${view === 'days' ?
|
|
375
|
+
accessibilityLabel={monthPicker ? `${title}, ${view === 'days' ? labels.chooseMonth : view === 'months' ? labels.chooseYear : labels.backToDays}` : title}
|
|
378
376
|
hitSlop={6}
|
|
379
377
|
style={({ pressed }) => [styles.titleBtn, { borderRadius: radius.sm, backgroundColor: pressed ? colors.muted : 'transparent' }]}
|
|
380
378
|
>
|
|
@@ -401,12 +399,12 @@ export function Calendar(props: CalendarProps) {
|
|
|
401
399
|
<Pressable
|
|
402
400
|
onPress={() => goTo(today)}
|
|
403
401
|
accessibilityRole="button"
|
|
404
|
-
accessibilityLabel=
|
|
402
|
+
accessibilityLabel={labels.goToToday}
|
|
405
403
|
hitSlop={4}
|
|
406
404
|
style={({ pressed }) => [styles.todayBtn, { borderRadius: radius.capsule(32), backgroundColor: pressed ? colors.secondary : colors.muted }]}
|
|
407
405
|
>
|
|
408
406
|
<Text maxFontSizeMultiplier={FONT_CAP} style={[styles.todayText, ff, { color: tint }]}>
|
|
409
|
-
|
|
407
|
+
{labels.today}
|
|
410
408
|
</Text>
|
|
411
409
|
</Pressable>
|
|
412
410
|
) : null}
|
|
@@ -429,7 +427,7 @@ export function Calendar(props: CalendarProps) {
|
|
|
429
427
|
</Text>
|
|
430
428
|
))}
|
|
431
429
|
</View>
|
|
432
|
-
<View {...pan
|
|
430
|
+
<View {...pan} style={styles.gridClip} onLayout={(e) => setGridH(e.nativeEvent.layout.height)}>
|
|
433
431
|
{leaving ? (
|
|
434
432
|
<Animated.View
|
|
435
433
|
pointerEvents="none"
|
|
@@ -525,16 +523,16 @@ interface MonthGridProps {
|
|
|
525
523
|
fixedWeeks: boolean;
|
|
526
524
|
showOutsideDays: boolean;
|
|
527
525
|
showToday: boolean;
|
|
528
|
-
minDate?: Date;
|
|
529
|
-
maxDate?: Date;
|
|
530
|
-
isDateDisabled?: (date: Date) => boolean;
|
|
531
|
-
markedDates?: CalendarMarks;
|
|
532
|
-
highlightedDates?: Record<string, string
|
|
526
|
+
minDate?: Date | undefined;
|
|
527
|
+
maxDate?: Date | undefined;
|
|
528
|
+
isDateDisabled?: ((date: Date) => boolean) | undefined;
|
|
529
|
+
markedDates?: CalendarMarks | undefined;
|
|
530
|
+
highlightedDates?: Record<string, string> | undefined;
|
|
533
531
|
tiles: boolean;
|
|
534
|
-
singleKey?: string;
|
|
535
|
-
rangeStart?: number;
|
|
536
|
-
rangeEnd?: number;
|
|
537
|
-
manyKeys?: string;
|
|
532
|
+
singleKey?: string | undefined;
|
|
533
|
+
rangeStart?: number | undefined;
|
|
534
|
+
rangeEnd?: number | undefined;
|
|
535
|
+
manyKeys?: string | undefined;
|
|
538
536
|
tint: string;
|
|
539
537
|
fullLabel: (d: Date) => string;
|
|
540
538
|
reduce: boolean;
|
|
@@ -636,8 +634,8 @@ interface DayCellProps {
|
|
|
636
634
|
band: 'none' | 'start' | 'mid' | 'end';
|
|
637
635
|
roundLeft: boolean;
|
|
638
636
|
roundRight: boolean;
|
|
639
|
-
marks?: string | string[];
|
|
640
|
-
highlight?: string;
|
|
637
|
+
marks?: string | string[] | undefined;
|
|
638
|
+
highlight?: string | undefined;
|
|
641
639
|
tile: boolean;
|
|
642
640
|
tint: string;
|
|
643
641
|
reduce: boolean;
|
|
@@ -665,8 +663,8 @@ const DayCell = memo(function DayCell({
|
|
|
665
663
|
const { colors, radius, font } = useKit();
|
|
666
664
|
const ff = font.family ? { fontFamily: font.family } : null;
|
|
667
665
|
const r = tileRadius(radius.md);
|
|
668
|
-
const pop =
|
|
669
|
-
const bandIn =
|
|
666
|
+
const [pop] = useState(() => new Animated.Value(selected ? 1 : 0)); // native: circle scale + opacity
|
|
667
|
+
const [bandIn] = useState(() => new Animated.Value(band === 'none' ? 0 : 1)); // native: band opacity
|
|
670
668
|
useEffect(() => {
|
|
671
669
|
// Reduce Motion: the selection fills in with a quick fade, no scale pop.
|
|
672
670
|
if (reduce) Animated.timing(pop, { toValue: selected ? 1 : 0, duration: 150, useNativeDriver: true }).start();
|
|
@@ -773,14 +771,14 @@ const DayCell = memo(function DayCell({
|
|
|
773
771
|
/* ───────────────────────────────────── bits ───────────────────────────────────── */
|
|
774
772
|
|
|
775
773
|
function NavButton({ dir, disabled, onPress }: { dir: 1 | -1; disabled: boolean; onPress: () => void }) {
|
|
776
|
-
const { colors } = useKit();
|
|
774
|
+
const { colors, labels } = useKit();
|
|
777
775
|
return (
|
|
778
776
|
<Pressable
|
|
779
777
|
onPress={onPress}
|
|
780
778
|
disabled={disabled}
|
|
781
779
|
hitSlop={4}
|
|
782
780
|
accessibilityRole="button"
|
|
783
|
-
accessibilityLabel={dir < 0 ?
|
|
781
|
+
accessibilityLabel={dir < 0 ? labels.previous : labels.next}
|
|
784
782
|
accessibilityState={{ disabled }}
|
|
785
783
|
style={({ pressed }) => [styles.nav, { backgroundColor: pressed ? colors.secondary : colors.muted, opacity: disabled ? 0.3 : 1 }]}
|
|
786
784
|
>
|
|
@@ -798,7 +796,7 @@ function NavButton({ dir, disabled, onPress }: { dir: 1 | -1; disabled: boolean;
|
|
|
798
796
|
}
|
|
799
797
|
|
|
800
798
|
function Chevron({ turned, color, reduce }: { turned: boolean; color: string; reduce: boolean }) {
|
|
801
|
-
const turn =
|
|
799
|
+
const [turn] = useState(() => new Animated.Value(turned ? 1 : 0)); // native
|
|
802
800
|
useEffect(() => {
|
|
803
801
|
if (reduce) {
|
|
804
802
|
turn.setValue(turned ? 1 : 0);
|
|
@@ -839,7 +837,7 @@ const styles = StyleSheet.create({
|
|
|
839
837
|
navs: { flexDirection: 'row', alignItems: 'center', gap: 6 },
|
|
840
838
|
nav: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center' },
|
|
841
839
|
todayBtn: { minHeight: 32, paddingHorizontal: 12, alignItems: 'center', justifyContent: 'center' },
|
|
842
|
-
todayText: { fontSize: 13, fontWeight: '400' },
|
|
840
|
+
todayText: { fontSize: 13, fontWeight: '400', includeFontPadding: false, textAlignVertical: 'center' },
|
|
843
841
|
weekRow: { flexDirection: 'row', marginBottom: 6 },
|
|
844
842
|
weekday: { flex: 1, textAlign: 'center', fontSize: 13, fontWeight: '400', letterSpacing: 0.2 },
|
|
845
843
|
gridClip: { overflow: 'hidden' },
|
|
@@ -858,7 +856,7 @@ const styles = StyleSheet.create({
|
|
|
858
856
|
alignSelf: 'center',
|
|
859
857
|
},
|
|
860
858
|
fill: { borderWidth: 0 },
|
|
861
|
-
day: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'] },
|
|
859
|
+
day: { fontSize: 15, fontWeight: '400', fontVariant: ['tabular-nums'], includeFontPadding: false, textAlignVertical: 'center' },
|
|
862
860
|
dayStrong: { fontWeight: '400' },
|
|
863
861
|
outside: { opacity: 0.4 },
|
|
864
862
|
disabledText: { opacity: 0.35 },
|
|
@@ -867,5 +865,5 @@ const styles = StyleSheet.create({
|
|
|
867
865
|
pickGrid: { flexDirection: 'row', flexWrap: 'wrap', alignContent: 'center', paddingTop: 4 },
|
|
868
866
|
pickCell: { width: '33.333%', height: 56, alignItems: 'center', justifyContent: 'center' },
|
|
869
867
|
pickInner: { minWidth: 76, minHeight: 40, paddingHorizontal: 14, alignItems: 'center', justifyContent: 'center', borderWidth: 1.5 },
|
|
870
|
-
pickText: { fontSize: 15, fontWeight: '400' },
|
|
868
|
+
pickText: { fontSize: 15, fontWeight: '400', includeFontPadding: false, textAlignVertical: 'center' },
|
|
871
869
|
});
|