panelui-native 0.22.6 → 0.28.1
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 +1 -0
- package/lib/module/components/accordion/index.js +8 -8
- package/lib/module/components/accordion/index.js.map +1 -1
- package/lib/module/components/alert/index.js +2 -2
- package/lib/module/components/alert/index.js.map +1 -1
- package/lib/module/components/area-chart/index.js +958 -0
- package/lib/module/components/area-chart/index.js.map +1 -0
- package/lib/module/components/attachment/index.js +2 -1
- package/lib/module/components/attachment/index.js.map +1 -1
- package/lib/module/components/avatar/index.js +4 -4
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/badge/index.js +4 -4
- package/lib/module/components/badge/index.js.map +1 -1
- package/lib/module/components/bar-chart/index.js +994 -0
- package/lib/module/components/bar-chart/index.js.map +1 -0
- package/lib/module/components/bottom-sheet/index.js +213 -24
- package/lib/module/components/bottom-sheet/index.js.map +1 -1
- package/lib/module/components/breadcrumb/index.js +275 -0
- package/lib/module/components/breadcrumb/index.js.map +1 -0
- package/lib/module/components/button/index.js +11 -6
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/calendar/index.js +557 -0
- package/lib/module/components/calendar/index.js.map +1 -0
- package/lib/module/components/carousel/index.js +680 -0
- package/lib/module/components/carousel/index.js.map +1 -0
- package/lib/module/components/chip/index.js +6 -6
- package/lib/module/components/chip/index.js.map +1 -1
- package/lib/module/components/date-picker/index.js +158 -0
- package/lib/module/components/date-picker/index.js.map +1 -0
- package/lib/module/components/dialog/index.js +7 -2
- package/lib/module/components/dialog/index.js.map +1 -1
- package/lib/module/components/direction/index.js +15 -20
- package/lib/module/components/direction/index.js.map +1 -1
- package/lib/module/components/empty-state/index.js +2 -2
- package/lib/module/components/empty-state/index.js.map +1 -1
- package/lib/module/components/field/index.js +5 -5
- package/lib/module/components/field/index.js.map +1 -1
- package/lib/module/components/flow/index.js +3 -3
- package/lib/module/components/flow/index.js.map +1 -1
- package/lib/module/components/frame/index.js +22 -5
- package/lib/module/components/frame/index.js.map +1 -1
- package/lib/module/components/heatmap-chart/index.js +28 -8
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/input-group/index.js +5 -4
- package/lib/module/components/input-group/index.js.map +1 -1
- package/lib/module/components/item/index.js +9 -9
- package/lib/module/components/item/index.js.map +1 -1
- package/lib/module/components/label/index.js +4 -4
- package/lib/module/components/label/index.js.map +1 -1
- package/lib/module/components/line-chart/index.js +165 -184
- package/lib/module/components/line-chart/index.js.map +1 -1
- package/lib/module/components/loader/index.js +758 -0
- package/lib/module/components/loader/index.js.map +1 -0
- package/lib/module/components/map/basemap.js +265 -0
- package/lib/module/components/map/basemap.js.map +1 -0
- package/lib/module/components/map/index.js +850 -0
- package/lib/module/components/map/index.js.map +1 -0
- package/lib/module/components/map/maplibre.js +87 -0
- package/lib/module/components/map/maplibre.js.map +1 -0
- package/lib/module/components/marker/index.js +3 -3
- package/lib/module/components/marker/index.js.map +1 -1
- package/lib/module/components/message/index.js +15 -14
- package/lib/module/components/message/index.js.map +1 -1
- package/lib/module/components/message-scroller/index.js +5 -4
- package/lib/module/components/message-scroller/index.js.map +1 -1
- package/lib/module/components/otp-input/index.js +2 -2
- package/lib/module/components/popover/index.js +39 -7
- package/lib/module/components/popover/index.js.map +1 -1
- package/lib/module/components/progress/index.js +8 -2
- package/lib/module/components/progress/index.js.map +1 -1
- package/lib/module/components/radio-group/index.js +2 -2
- package/lib/module/components/radio-group/index.js.map +1 -1
- package/lib/module/components/ring-chart/index.js +423 -0
- package/lib/module/components/ring-chart/index.js.map +1 -0
- package/lib/module/components/section-rail/index.js +6 -6
- package/lib/module/components/section-rail/index.js.map +1 -1
- package/lib/module/components/select/index.js +10 -4
- package/lib/module/components/select/index.js.map +1 -1
- package/lib/module/components/separator/index.js +76 -22
- package/lib/module/components/separator/index.js.map +1 -1
- package/lib/module/components/shimmer/index.js +10 -1
- package/lib/module/components/shimmer/index.js.map +1 -1
- package/lib/module/components/slider/index.js +19 -5
- package/lib/module/components/slider/index.js.map +1 -1
- package/lib/module/components/steps/index.js +5 -5
- package/lib/module/components/steps/index.js.map +1 -1
- package/lib/module/components/switch/index.js +6 -1
- package/lib/module/components/switch/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +7 -7
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +4 -4
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/components/toast/index.js +2 -2
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/toggle-button/index.js +5 -5
- package/lib/module/components/toggle-button/index.js.map +1 -1
- package/lib/module/components/tooltip/index.js +492 -0
- package/lib/module/components/tooltip/index.js.map +1 -0
- package/lib/module/components/typography/index.js +7 -2
- package/lib/module/components/typography/index.js.map +1 -1
- package/lib/module/hooks/index.js +1 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/use-back-handler.js +37 -0
- package/lib/module/hooks/use-back-handler.js.map +1 -0
- package/lib/module/hooks/use-direction.js +43 -0
- package/lib/module/hooks/use-direction.js.map +1 -0
- package/lib/module/icons/index.js +180 -2
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +12 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/keyboard-avoider.js +2 -1
- package/lib/module/primitives/keyboard-avoider.js.map +1 -1
- package/lib/module/primitives/text.js +55 -11
- package/lib/module/primitives/text.js.map +1 -1
- package/lib/module/utils/chart.js +279 -0
- package/lib/module/utils/chart.js.map +1 -0
- package/lib/module/utils/date.js +386 -0
- package/lib/module/utils/date.js.map +1 -0
- package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
- package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
- package/lib/typescript/src/components/area-chart/index.d.ts +237 -0
- package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
- package/lib/typescript/src/components/bar-chart/index.d.ts +270 -0
- package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/bottom-sheet/index.d.ts +75 -4
- package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
- package/lib/typescript/src/components/breadcrumb/index.d.ts +100 -0
- package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/calendar/index.d.ts +108 -0
- package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/carousel/index.d.ts +179 -0
- package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
- package/lib/typescript/src/components/date-picker/index.d.ts +91 -0
- package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
- package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
- package/lib/typescript/src/components/direction/index.d.ts +11 -10
- package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
- package/lib/typescript/src/components/field/index.d.ts.map +1 -1
- package/lib/typescript/src/components/frame/index.d.ts +17 -0
- package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +22 -3
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
- package/lib/typescript/src/components/item/index.d.ts.map +1 -1
- package/lib/typescript/src/components/label/index.d.ts.map +1 -1
- package/lib/typescript/src/components/line-chart/index.d.ts +42 -2
- package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/loader/index.d.ts +38 -0
- package/lib/typescript/src/components/loader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/basemap.d.ts +76 -0
- package/lib/typescript/src/components/map/basemap.d.ts.map +1 -0
- package/lib/typescript/src/components/map/index.d.ts +367 -0
- package/lib/typescript/src/components/map/index.d.ts.map +1 -0
- package/lib/typescript/src/components/map/maplibre.d.ts +193 -0
- package/lib/typescript/src/components/map/maplibre.d.ts.map +1 -0
- package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message/index.d.ts.map +1 -1
- package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
- package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
- package/lib/typescript/src/components/ring-chart/index.d.ts +156 -0
- package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -0
- package/lib/typescript/src/components/select/index.d.ts.map +1 -1
- package/lib/typescript/src/components/separator/index.d.ts +36 -1
- package/lib/typescript/src/components/separator/index.d.ts.map +1 -1
- package/lib/typescript/src/components/shimmer/index.d.ts.map +1 -1
- package/lib/typescript/src/components/slider/index.d.ts.map +1 -1
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
- package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tooltip/index.d.ts +125 -0
- package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
- package/lib/typescript/src/components/typography/index.d.ts +6 -0
- package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/index.d.ts +1 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
- package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-direction.d.ts +20 -0
- package/lib/typescript/src/hooks/use-direction.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +8 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +14 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
- package/lib/typescript/src/primitives/text.d.ts +20 -0
- package/lib/typescript/src/primitives/text.d.ts.map +1 -1
- package/lib/typescript/src/utils/chart.d.ts +96 -0
- package/lib/typescript/src/utils/chart.d.ts.map +1 -0
- package/lib/typescript/src/utils/date.d.ts +116 -0
- package/lib/typescript/src/utils/date.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/components/accordion/index.tsx +8 -12
- package/src/components/alert/index.tsx +2 -2
- package/src/components/area-chart/index.tsx +1147 -0
- package/src/components/attachment/index.tsx +2 -2
- package/src/components/avatar/index.tsx +4 -4
- package/src/components/badge/index.tsx +6 -6
- package/src/components/bar-chart/index.tsx +1208 -0
- package/src/components/bottom-sheet/index.tsx +282 -21
- package/src/components/breadcrumb/index.tsx +363 -0
- package/src/components/button/index.tsx +12 -8
- package/src/components/calendar/index.tsx +703 -0
- package/src/components/carousel/index.tsx +788 -0
- package/src/components/chip/index.tsx +6 -8
- package/src/components/date-picker/index.tsx +237 -0
- package/src/components/dialog/index.tsx +7 -2
- package/src/components/direction/index.tsx +22 -22
- package/src/components/empty-state/index.tsx +2 -2
- package/src/components/field/index.tsx +3 -5
- package/src/components/flow/index.tsx +3 -3
- package/src/components/frame/index.tsx +22 -7
- package/src/components/heatmap-chart/index.tsx +58 -11
- package/src/components/input-group/index.tsx +5 -4
- package/src/components/item/index.tsx +9 -9
- package/src/components/label/index.tsx +2 -2
- package/src/components/line-chart/index.tsx +194 -223
- package/src/components/loader/index.tsx +812 -0
- package/src/components/map/basemap.ts +304 -0
- package/src/components/map/index.tsx +1116 -0
- package/src/components/map/maplibre.ts +201 -0
- package/src/components/marker/index.tsx +3 -3
- package/src/components/message/index.tsx +15 -14
- package/src/components/message-scroller/index.tsx +5 -4
- package/src/components/otp-input/index.tsx +2 -2
- package/src/components/popover/index.tsx +43 -7
- package/src/components/progress/index.tsx +10 -2
- package/src/components/radio-group/index.tsx +2 -2
- package/src/components/ring-chart/index.tsx +543 -0
- package/src/components/section-rail/index.tsx +6 -6
- package/src/components/select/index.tsx +10 -4
- package/src/components/separator/index.tsx +77 -19
- package/src/components/shimmer/index.tsx +10 -1
- package/src/components/slider/index.tsx +20 -5
- package/src/components/steps/index.tsx +5 -5
- package/src/components/switch/index.tsx +6 -1
- package/src/components/tabs/index.tsx +7 -9
- package/src/components/timeline/index.tsx +4 -4
- package/src/components/toast/index.tsx +2 -2
- package/src/components/toggle-button/index.tsx +5 -7
- package/src/components/tooltip/index.tsx +610 -0
- package/src/components/typography/index.tsx +7 -2
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-back-handler.ts +37 -0
- package/src/hooks/use-direction.ts +45 -0
- package/src/icons/index.tsx +111 -0
- package/src/index.ts +129 -0
- package/src/primitives/keyboard-avoider.tsx +2 -1
- package/src/primitives/text.tsx +53 -8
- package/src/utils/chart.ts +355 -0
- package/src/utils/date.ts +446 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Carousel — a run of slides, one at a time, dragged with a finger.
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* <Carousel loop>
|
|
8
|
+
* <Carousel.Content className="h-56">
|
|
9
|
+
* {photos.map((photo) => (
|
|
10
|
+
* <Carousel.Item key={photo.id}>
|
|
11
|
+
* <Image source={{ uri: photo.uri }} className="h-full w-full" />
|
|
12
|
+
* </Carousel.Item>
|
|
13
|
+
* ))}
|
|
14
|
+
* </Carousel.Content>
|
|
15
|
+
* <Carousel.Controls />
|
|
16
|
+
* </Carousel>
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* ## One shared value, four layouts
|
|
20
|
+
*
|
|
21
|
+
* Everything is driven from a single `progress` — the position in the run, as a
|
|
22
|
+
* fractional index. `2.4` is two-fifths of the way from the third slide to the
|
|
23
|
+
* fourth, and every slide styles itself from its own distance to that number.
|
|
24
|
+
* A pan writes to it, a spring settles it onto a whole number, and the dots
|
|
25
|
+
* read it.
|
|
26
|
+
*
|
|
27
|
+
* That is why the track is a pan gesture rather than a paging `ScrollView`. A
|
|
28
|
+
* scroll view carries its offset natively and would serve `default` well
|
|
29
|
+
* enough, but `coverflow` and `stack` do not lay their slides along a track at
|
|
30
|
+
* all — they hold them in one place and pull them apart with transforms — so
|
|
31
|
+
* there would be nothing for it to scroll. One mechanism all four read beats a
|
|
32
|
+
* native scroller for one of them and something else for the rest.
|
|
33
|
+
*
|
|
34
|
+
* ## What each layout is for
|
|
35
|
+
*
|
|
36
|
+
* - **`default`** — a track. The honest choice for content that is read rather
|
|
37
|
+
* than admired: a row of cards, a gallery, an onboarding run.
|
|
38
|
+
* - **`interactive`** — the run fans out around the middle slide and tilts away
|
|
39
|
+
* on both sides, opening wider while a finger is down. For a small set worth
|
|
40
|
+
* showing off.
|
|
41
|
+
* - **`coverflow`** — the neighbours turn away from you in perspective. Best
|
|
42
|
+
* with art: covers, posters, photographs.
|
|
43
|
+
* - **`stack`** — a deck. The active card is on top with the next two peeking
|
|
44
|
+
* out behind it, and dragging takes the top one away. For cards dealt with
|
|
45
|
+
* one at a time, where the pile is the point.
|
|
46
|
+
*
|
|
47
|
+
* Depth is carried by scale, opacity and z-order rather than by moving slides
|
|
48
|
+
* along z: React Native's transform has `perspective` and `rotateY` but no
|
|
49
|
+
* `translateZ`, so a slide is made to *look* further away rather than put there.
|
|
50
|
+
*/
|
|
51
|
+
import { Children, createContext, forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useState } from 'react';
|
|
52
|
+
import { Pressable, View } from 'react-native';
|
|
53
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
54
|
+
import Animated, { Extrapolation, interpolate, runOnJS, useAnimatedStyle, useReducedMotion, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
55
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "../../icons/index.js";
|
|
56
|
+
import { Text, textChildren } from "../../primitives/text.js";
|
|
57
|
+
import { cn } from "../../utils/cn.js";
|
|
58
|
+
|
|
59
|
+
/** Settles the run onto a whole index. Tuned to stop rather than to bounce. */
|
|
60
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
61
|
+
const SPRING = {
|
|
62
|
+
damping: 22,
|
|
63
|
+
stiffness: 190,
|
|
64
|
+
mass: 0.55
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Fraction of a slide that has to be dragged past for the move to count, and
|
|
69
|
+
* the velocity that carries it there regardless. Together they are what lets a
|
|
70
|
+
* short fast flick and a long slow drag both advance exactly one slide.
|
|
71
|
+
*/
|
|
72
|
+
const SNAP_FRACTION = 0.28;
|
|
73
|
+
const SNAP_VELOCITY = 500;
|
|
74
|
+
|
|
75
|
+
/** How far past the ends the run may be pulled when it does not loop. */
|
|
76
|
+
const OVERSCROLL = 0.4;
|
|
77
|
+
/** Fraction of the finger's travel that lands past an end. */
|
|
78
|
+
const RUBBER = 0.35;
|
|
79
|
+
|
|
80
|
+
/** Slides either side of the active one that `coverflow` still draws. */
|
|
81
|
+
const COVERFLOW_DEPTH = 2;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Gap between coverflow's slides, as a fraction of one slide's length.
|
|
85
|
+
*
|
|
86
|
+
* Proportional rather than a fixed number of points: the neighbours have to
|
|
87
|
+
* clear the middle slide by enough to be read as separate cards, and how much
|
|
88
|
+
* that is depends entirely on how wide the cards are.
|
|
89
|
+
*/
|
|
90
|
+
const COVERFLOW_SPREAD = 0.55;
|
|
91
|
+
|
|
92
|
+
/** Cards behind the top one in `stack`. Two is a pile; five is a mess. */
|
|
93
|
+
const STACK_DEPTH = 2;
|
|
94
|
+
const CarouselContext = /*#__PURE__*/createContext(null);
|
|
95
|
+
function useCarousel(component) {
|
|
96
|
+
const context = useContext(CarouselContext);
|
|
97
|
+
if (!context) {
|
|
98
|
+
throw new Error(`${component} must be used within a <Carousel>`);
|
|
99
|
+
}
|
|
100
|
+
return context;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A slide's own position in the run, handed down by `Content` rather than
|
|
105
|
+
* counted up by each slide.
|
|
106
|
+
*
|
|
107
|
+
* The alternative is for a slide to register itself and be told its number
|
|
108
|
+
* back, which works right up until one is inserted in the middle and every
|
|
109
|
+
* slide after it is wrong until the next render settles.
|
|
110
|
+
*/
|
|
111
|
+
const ItemIndexContext = /*#__PURE__*/createContext(0);
|
|
112
|
+
|
|
113
|
+
/** Position and controls, for a control of your own outside the built-in ones. */
|
|
114
|
+
export function useCarouselState() {
|
|
115
|
+
const {
|
|
116
|
+
index,
|
|
117
|
+
count,
|
|
118
|
+
scrollTo,
|
|
119
|
+
next,
|
|
120
|
+
previous
|
|
121
|
+
} = useCarousel('useCarouselState');
|
|
122
|
+
return {
|
|
123
|
+
index,
|
|
124
|
+
count,
|
|
125
|
+
scrollTo,
|
|
126
|
+
next,
|
|
127
|
+
previous
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Wraps a fractional index into the run. A worklet because the pan needs it
|
|
133
|
+
* per frame on the UI thread, and the imperative handle needs it in JS.
|
|
134
|
+
*/
|
|
135
|
+
function wrap(value, count) {
|
|
136
|
+
'worklet';
|
|
137
|
+
|
|
138
|
+
if (count <= 0) return 0;
|
|
139
|
+
return (value % count + count) % count;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Signed distance from a slide to the current position, taking the short way
|
|
144
|
+
* round when the run loops.
|
|
145
|
+
*
|
|
146
|
+
* Without the wrap the last slide sits `count - 1` away from the first, and
|
|
147
|
+
* every layout throws it off screen at the exact moment it should be sliding
|
|
148
|
+
* in from the other side.
|
|
149
|
+
*/
|
|
150
|
+
function distance(index, progress, count, loop) {
|
|
151
|
+
'worklet';
|
|
152
|
+
|
|
153
|
+
const raw = index - progress;
|
|
154
|
+
if (!loop || count <= 1) return raw;
|
|
155
|
+
const half = count / 2;
|
|
156
|
+
if (raw > half) return raw - count;
|
|
157
|
+
if (raw < -half) return raw + count;
|
|
158
|
+
return raw;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Imperative handle, for driving the run from outside its own controls. */
|
|
162
|
+
|
|
163
|
+
const CarouselRoot = /*#__PURE__*/forwardRef(function CarouselRoot({
|
|
164
|
+
className,
|
|
165
|
+
variant = 'default',
|
|
166
|
+
orientation = 'horizontal',
|
|
167
|
+
loop = false,
|
|
168
|
+
align = 'center',
|
|
169
|
+
itemSize: itemSizeProp,
|
|
170
|
+
autoplay = false,
|
|
171
|
+
autoplayInterval = 4000,
|
|
172
|
+
index: indexProp,
|
|
173
|
+
defaultIndex = 0,
|
|
174
|
+
onIndexChange,
|
|
175
|
+
scrollEnabled = true,
|
|
176
|
+
children,
|
|
177
|
+
...props
|
|
178
|
+
}, ref) {
|
|
179
|
+
const [size, setSize] = useState({
|
|
180
|
+
width: 0,
|
|
181
|
+
height: 0
|
|
182
|
+
});
|
|
183
|
+
const [internalIndex, setInternalIndex] = useState(defaultIndex);
|
|
184
|
+
const [count, setCount] = useState(0);
|
|
185
|
+
const [touched, setTouched] = useState(false);
|
|
186
|
+
const reducedMotion = useReducedMotion();
|
|
187
|
+
const isControlled = indexProp !== undefined;
|
|
188
|
+
const index = isControlled ? indexProp : internalIndex;
|
|
189
|
+
const progress = useSharedValue(defaultIndex);
|
|
190
|
+
const engaged = useSharedValue(0);
|
|
191
|
+
|
|
192
|
+
// A deck is dealt from the top of a pile, so it is dragged sideways whatever
|
|
193
|
+
// the run's own direction is — there is no track for it to travel along.
|
|
194
|
+
const axis = variant === 'stack' ? 'horizontal' : orientation;
|
|
195
|
+
const along = axis === 'horizontal' ? size.width : size.height;
|
|
196
|
+
const itemSize = itemSizeProp ?? along ?? 0;
|
|
197
|
+
const setIndex = useCallback(next => {
|
|
198
|
+
if (!isControlled) setInternalIndex(next);
|
|
199
|
+
onIndexChange?.(next);
|
|
200
|
+
}, [isControlled, onIndexChange]);
|
|
201
|
+
const scrollTo = useCallback(target => {
|
|
202
|
+
if (count <= 0) return;
|
|
203
|
+
const settled = loop ? wrap(target, count) : Math.max(0, Math.min(target, count - 1));
|
|
204
|
+
if (reducedMotion) {
|
|
205
|
+
progress.value = settled;
|
|
206
|
+
} else if (loop) {
|
|
207
|
+
// Spring to the nearest representation of the target rather than to the
|
|
208
|
+
// target itself, so a wrap from the last slide to the first travels one
|
|
209
|
+
// step forward instead of winding all the way back through the run.
|
|
210
|
+
const shortest = progress.value + distance(settled, progress.value, count, true);
|
|
211
|
+
progress.value = withSpring(shortest, SPRING, finished => {
|
|
212
|
+
if (finished) progress.value = wrap(progress.value, count);
|
|
213
|
+
});
|
|
214
|
+
} else {
|
|
215
|
+
progress.value = withSpring(settled, SPRING);
|
|
216
|
+
}
|
|
217
|
+
setIndex(settled);
|
|
218
|
+
}, [count, loop, progress, reducedMotion, setIndex]);
|
|
219
|
+
const next = useCallback(() => scrollTo(index + 1), [index, scrollTo]);
|
|
220
|
+
const previous = useCallback(() => scrollTo(index - 1), [index, scrollTo]);
|
|
221
|
+
useImperativeHandle(ref, () => ({
|
|
222
|
+
next,
|
|
223
|
+
previous,
|
|
224
|
+
scrollTo
|
|
225
|
+
}), [next, previous, scrollTo]);
|
|
226
|
+
|
|
227
|
+
// A controlled index moving underneath us animates rather than jumps.
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (!isControlled || indexProp === undefined || count <= 0) return;
|
|
230
|
+
if (Math.abs(progress.value - indexProp) < 0.001) return;
|
|
231
|
+
progress.value = reducedMotion ? indexProp : withSpring(indexProp, SPRING);
|
|
232
|
+
}, [isControlled, indexProp, count, progress, reducedMotion]);
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
* Autoplay stops for good the first time a finger lands, rather than pausing.
|
|
236
|
+
* Someone who has taken hold of the run is reading it, and having it start
|
|
237
|
+
* moving again a few seconds later is the behaviour everybody hates.
|
|
238
|
+
*/
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
if (!autoplay || touched || count <= 1) return;
|
|
241
|
+
const timer = setInterval(() => scrollTo(index + 1), autoplayInterval);
|
|
242
|
+
return () => clearInterval(timer);
|
|
243
|
+
}, [autoplay, touched, count, index, autoplayInterval, scrollTo]);
|
|
244
|
+
const settle = useCallback(target => {
|
|
245
|
+
setTouched(true);
|
|
246
|
+
scrollTo(target);
|
|
247
|
+
}, [scrollTo]);
|
|
248
|
+
const pan = useMemo(() => Gesture.Pan().enabled(scrollEnabled && count > 1 && itemSize > 0)
|
|
249
|
+
// A carousel inside a scroll view has to let the cross-axis drags
|
|
250
|
+
// through, or the two fight over every diagonal.
|
|
251
|
+
.activeOffsetX(axis === 'horizontal' ? [-10, 10] : [-10000, 10000]).activeOffsetY(axis === 'horizontal' ? [-10000, 10000] : [-10, 10]).onBegin(() => {
|
|
252
|
+
engaged.value = withTiming(1, {
|
|
253
|
+
duration: 160
|
|
254
|
+
});
|
|
255
|
+
}).onUpdate(event => {
|
|
256
|
+
const moved = axis === 'horizontal' ? event.translationX : event.translationY;
|
|
257
|
+
const raw = progress.value - moved / itemSize;
|
|
258
|
+
if (loop) {
|
|
259
|
+
progress.value = raw;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// Off the ends the run follows the finger at a fraction of the
|
|
263
|
+
// distance, so the edge is felt rather than hit.
|
|
264
|
+
const last = count - 1;
|
|
265
|
+
progress.value = raw < 0 ? Math.max(-OVERSCROLL, raw * RUBBER) : raw > last ? Math.min(last + OVERSCROLL, last + (raw - last) * RUBBER) : raw;
|
|
266
|
+
}).onEnd(event => {
|
|
267
|
+
const velocity = axis === 'horizontal' ? event.velocityX : event.velocityY;
|
|
268
|
+
const moved = axis === 'horizontal' ? event.translationX : event.translationY;
|
|
269
|
+
|
|
270
|
+
// The slide it started on, not the one it is nearest now: rounding
|
|
271
|
+
// the current position would let a slow drag that never reached the
|
|
272
|
+
// threshold still count as a move.
|
|
273
|
+
const from = Math.round(progress.value + moved / itemSize);
|
|
274
|
+
const past = Math.abs(moved) / itemSize > SNAP_FRACTION;
|
|
275
|
+
const flicked = Math.abs(velocity) > SNAP_VELOCITY;
|
|
276
|
+
const step = past || flicked ? moved < 0 ? 1 : -1 : 0;
|
|
277
|
+
runOnJS(settle)(from + step);
|
|
278
|
+
}).onFinalize(() => {
|
|
279
|
+
engaged.value = withTiming(0, {
|
|
280
|
+
duration: 220
|
|
281
|
+
});
|
|
282
|
+
}), [scrollEnabled, count, itemSize, axis, engaged, progress, loop, settle]);
|
|
283
|
+
const onLayout = event => {
|
|
284
|
+
const {
|
|
285
|
+
width,
|
|
286
|
+
height
|
|
287
|
+
} = event.nativeEvent.layout;
|
|
288
|
+
setSize(current => Math.abs(current.width - width) < 1 && Math.abs(current.height - height) < 1 ? current : {
|
|
289
|
+
width,
|
|
290
|
+
height
|
|
291
|
+
});
|
|
292
|
+
props.onLayout?.(event);
|
|
293
|
+
};
|
|
294
|
+
const context = useMemo(() => ({
|
|
295
|
+
progress,
|
|
296
|
+
engaged,
|
|
297
|
+
count,
|
|
298
|
+
setCount,
|
|
299
|
+
index,
|
|
300
|
+
scrollTo: settle,
|
|
301
|
+
next,
|
|
302
|
+
previous,
|
|
303
|
+
variant,
|
|
304
|
+
orientation: axis,
|
|
305
|
+
align,
|
|
306
|
+
loop,
|
|
307
|
+
itemSize
|
|
308
|
+
}), [progress, engaged, count, index, settle, next, previous, variant, axis, align, loop, itemSize]);
|
|
309
|
+
return /*#__PURE__*/_jsx(CarouselContext.Provider, {
|
|
310
|
+
value: context,
|
|
311
|
+
children: /*#__PURE__*/_jsx(GestureDetector, {
|
|
312
|
+
gesture: pan,
|
|
313
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
314
|
+
accessibilityRole: "list",
|
|
315
|
+
...props,
|
|
316
|
+
onLayout: onLayout,
|
|
317
|
+
className: cn('w-full', className),
|
|
318
|
+
children: textChildren(children)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
CarouselRoot.displayName = 'Carousel';
|
|
324
|
+
|
|
325
|
+
/* -------------------------------------------------------------------------- */
|
|
326
|
+
/* Track */
|
|
327
|
+
/* -------------------------------------------------------------------------- */
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* The box the slides live in. Give it a height — nothing inside it is in the
|
|
331
|
+
* layout flow, so it has no height of its own to take.
|
|
332
|
+
*
|
|
333
|
+
* It does not move. Every slide places itself from `progress`, which is what
|
|
334
|
+
* lets `default` and `coverflow` be one component with different arithmetic
|
|
335
|
+
* rather than two different trees. The alignment here is the *resting* place
|
|
336
|
+
* every slide is offset from.
|
|
337
|
+
*/
|
|
338
|
+
function CarouselContent({
|
|
339
|
+
className,
|
|
340
|
+
children,
|
|
341
|
+
...props
|
|
342
|
+
}) {
|
|
343
|
+
const {
|
|
344
|
+
setCount,
|
|
345
|
+
variant,
|
|
346
|
+
orientation,
|
|
347
|
+
align
|
|
348
|
+
} = useCarousel('Carousel.Content');
|
|
349
|
+
const slides = Children.count(children);
|
|
350
|
+
const horizontal = orientation === 'horizontal';
|
|
351
|
+
const centred = align === 'center' || variant === 'coverflow' || variant === 'stack';
|
|
352
|
+
useEffect(() => {
|
|
353
|
+
setCount(slides);
|
|
354
|
+
}, [setCount, slides]);
|
|
355
|
+
return /*#__PURE__*/_jsx(View, {
|
|
356
|
+
...props,
|
|
357
|
+
className: cn('w-full overflow-hidden',
|
|
358
|
+
// Absolutely positioned children with no insets still take the
|
|
359
|
+
// parent's alignment in Yoga, which is what puts a slide at rest.
|
|
360
|
+
horizontal ? cn('justify-center', centred ? 'items-center' : 'items-start') : cn('items-center', centred ? 'justify-center' : 'justify-start'), className),
|
|
361
|
+
style: [
|
|
362
|
+
// `perspective` on the container is what makes coverflow's rotation
|
|
363
|
+
// read as depth rather than as a squash.
|
|
364
|
+
variant === 'coverflow' ? {
|
|
365
|
+
transform: [{
|
|
366
|
+
perspective: 1000
|
|
367
|
+
}]
|
|
368
|
+
} : null, props.style],
|
|
369
|
+
children: Children.map(children, (child, index) => /*#__PURE__*/_jsx(ItemIndexContext.Provider, {
|
|
370
|
+
value: index,
|
|
371
|
+
children: child
|
|
372
|
+
}))
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/** One slide. Its transform is whatever the root's `variant` asks for. */
|
|
376
|
+
function CarouselItem({
|
|
377
|
+
className,
|
|
378
|
+
children,
|
|
379
|
+
style,
|
|
380
|
+
...props
|
|
381
|
+
}) {
|
|
382
|
+
const {
|
|
383
|
+
progress,
|
|
384
|
+
engaged,
|
|
385
|
+
count,
|
|
386
|
+
index: active,
|
|
387
|
+
variant,
|
|
388
|
+
orientation,
|
|
389
|
+
loop,
|
|
390
|
+
itemSize
|
|
391
|
+
} = useCarousel('Carousel.Item');
|
|
392
|
+
const index = useContext(ItemIndexContext);
|
|
393
|
+
const horizontal = orientation === 'horizontal';
|
|
394
|
+
|
|
395
|
+
/*
|
|
396
|
+
* In the layouts that pile slides on top of each other, only the one on top
|
|
397
|
+
* takes touches.
|
|
398
|
+
*
|
|
399
|
+
* `zIndex` reorders what is *drawn* but, on iOS, not what is *hit*: hit
|
|
400
|
+
* testing walks the subviews in the order they were added, so the last slide
|
|
401
|
+
* rendered sits in front of every gesture regardless of its z-order — and in
|
|
402
|
+
* a deck that slide is the one at the bottom of the pile, drawn at zero
|
|
403
|
+
* opacity. An invisible card was swallowing every drag.
|
|
404
|
+
*/
|
|
405
|
+
const stacked = variant === 'coverflow' || variant === 'stack';
|
|
406
|
+
const inert = stacked && index !== active;
|
|
407
|
+
const animated = useAnimatedStyle(() => {
|
|
408
|
+
const d = distance(index, progress.value, count, loop);
|
|
409
|
+
const a = Math.abs(d);
|
|
410
|
+
if (variant === 'coverflow') {
|
|
411
|
+
return {
|
|
412
|
+
opacity: a > COVERFLOW_DEPTH ? 0 : Math.max(0, 1 - a * 0.25),
|
|
413
|
+
zIndex: Math.round(100 - a * 10),
|
|
414
|
+
transform: [{
|
|
415
|
+
perspective: 1000
|
|
416
|
+
}, {
|
|
417
|
+
translateX: d * itemSize * COVERFLOW_SPREAD
|
|
418
|
+
},
|
|
419
|
+
// Turned away from the middle and back towards it as it arrives.
|
|
420
|
+
// Interpolated rather than switched, or a slide would snap flat.
|
|
421
|
+
{
|
|
422
|
+
rotateY: `${interpolate(d, [-1, 0, 1], [38, 0, -38], Extrapolation.CLAMP)}deg`
|
|
423
|
+
}, {
|
|
424
|
+
scale: interpolate(a, [0, 1], [1.1, 0.92], Extrapolation.CLAMP)
|
|
425
|
+
}]
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (variant === 'stack') {
|
|
429
|
+
// The pile behind the top card is stepped, not spread: each card back is
|
|
430
|
+
// a little smaller and a little lower. Only the top one is dragged, and
|
|
431
|
+
// it leaves sideways with a tilt.
|
|
432
|
+
const behind = Math.min(Math.max(0, d), STACK_DEPTH);
|
|
433
|
+
const leaving = Math.min(0, d);
|
|
434
|
+
return {
|
|
435
|
+
opacity: d < -1 || d > STACK_DEPTH ? 0 : 1,
|
|
436
|
+
zIndex: Math.round(100 - Math.max(0, d) * 10),
|
|
437
|
+
transform: [{
|
|
438
|
+
translateX: leaving * itemSize * 1.15
|
|
439
|
+
}, {
|
|
440
|
+
translateY: behind * 14
|
|
441
|
+
}, {
|
|
442
|
+
rotate: `${leaving * 12}deg`
|
|
443
|
+
}, {
|
|
444
|
+
scale: 1 - behind * 0.06
|
|
445
|
+
}]
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (variant === 'interactive') {
|
|
449
|
+
/*
|
|
450
|
+
* Two states blended by `engaged`, rather than switched between: at rest
|
|
451
|
+
* the run is a tidy fan, and it opens wider under a finger. Blended, so
|
|
452
|
+
* taking hold of it opens it; switched, it would jump open.
|
|
453
|
+
*/
|
|
454
|
+
const open = engaged.value;
|
|
455
|
+
const spread = d * itemSize;
|
|
456
|
+
const lift = d * 24 * open;
|
|
457
|
+
const inactive = 0.78 - open * 0.15;
|
|
458
|
+
return {
|
|
459
|
+
opacity: a > 3 ? 0 : 1,
|
|
460
|
+
zIndex: Math.round(100 - a * 10),
|
|
461
|
+
transform: [{
|
|
462
|
+
translateX: horizontal ? spread : lift
|
|
463
|
+
}, {
|
|
464
|
+
translateY: horizontal ? lift : spread
|
|
465
|
+
}, {
|
|
466
|
+
rotate: `${d * (5 + open * 15)}deg`
|
|
467
|
+
},
|
|
468
|
+
/*
|
|
469
|
+
* The slide in the middle rests at exactly 1, and the fan's depth
|
|
470
|
+
* comes from the others shrinking rather than from it growing.
|
|
471
|
+
*
|
|
472
|
+
* Scaling it up instead is what made its caption soft. Text is
|
|
473
|
+
* rasterised at its layout size and then scaled by the compositor,
|
|
474
|
+
* so a label on a slide held at 1.05 is drawn at the wrong raster
|
|
475
|
+
* size for as long as it is the active one — which is the whole time
|
|
476
|
+
* it is readable, since every other caption has faded out.
|
|
477
|
+
*/
|
|
478
|
+
{
|
|
479
|
+
scale: interpolate(a, [0, 1], [1, inactive], Extrapolation.CLAMP)
|
|
480
|
+
}]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// `default` — a plain track, each slide one length from the last.
|
|
485
|
+
return {
|
|
486
|
+
transform: [{
|
|
487
|
+
translateX: horizontal ? d * itemSize : 0
|
|
488
|
+
}, {
|
|
489
|
+
translateY: horizontal ? 0 : d * itemSize
|
|
490
|
+
}]
|
|
491
|
+
};
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// `coverflow` and `stack` are sized by whatever is put in them: they are
|
|
495
|
+
// built around a card, and a card that had to be the width of the screen
|
|
496
|
+
// would have nothing to stack behind.
|
|
497
|
+
const sized = variant === 'default' || variant === 'interactive';
|
|
498
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
499
|
+
...props,
|
|
500
|
+
style: [{
|
|
501
|
+
position: 'absolute',
|
|
502
|
+
pointerEvents: inert ? 'none' : 'auto'
|
|
503
|
+
}, sized ? horizontal ? {
|
|
504
|
+
width: itemSize
|
|
505
|
+
} : {
|
|
506
|
+
height: itemSize
|
|
507
|
+
} : null, sized && variant === 'default' ? horizontal ? {
|
|
508
|
+
height: '100%'
|
|
509
|
+
} : {
|
|
510
|
+
width: '100%'
|
|
511
|
+
} : null, animated, style],
|
|
512
|
+
className: cn(className),
|
|
513
|
+
children: textChildren(children)
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* -------------------------------------------------------------------------- */
|
|
518
|
+
/* Controls */
|
|
519
|
+
/* -------------------------------------------------------------------------- */
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* A slide's label, shown only while that slide is the active one.
|
|
523
|
+
*
|
|
524
|
+
* It lives inside the slide rather than beside the run, so it travels with what
|
|
525
|
+
* it names — a caption that stays put while the picture moves belongs to the
|
|
526
|
+
* carousel rather than to the picture.
|
|
527
|
+
*/
|
|
528
|
+
function CarouselCaption({
|
|
529
|
+
className,
|
|
530
|
+
children,
|
|
531
|
+
...props
|
|
532
|
+
}) {
|
|
533
|
+
const {
|
|
534
|
+
progress,
|
|
535
|
+
count,
|
|
536
|
+
loop
|
|
537
|
+
} = useCarousel('Carousel.Caption');
|
|
538
|
+
const index = useContext(ItemIndexContext);
|
|
539
|
+
|
|
540
|
+
/*
|
|
541
|
+
* Gone well before the next slide arrives, rather than fading across the
|
|
542
|
+
* whole step. The layouts that use a caption overlap their slides, so a
|
|
543
|
+
* caption still at a third of its opacity halfway through a drag is a line
|
|
544
|
+
* of grey text sitting on top of the neighbouring picture — which reads as a
|
|
545
|
+
* rendering fault rather than as a transition.
|
|
546
|
+
*/
|
|
547
|
+
/*
|
|
548
|
+
* Opacity only. A scale here would compound with the slide's own — the
|
|
549
|
+
* caption is inside it — and the text would be resampled twice, once for
|
|
550
|
+
* each. The slide already carries all the movement this needs to read as
|
|
551
|
+
* belonging to the picture.
|
|
552
|
+
*/
|
|
553
|
+
const animated = useAnimatedStyle(() => {
|
|
554
|
+
const a = Math.abs(distance(index, progress.value, count, loop));
|
|
555
|
+
return {
|
|
556
|
+
opacity: interpolate(a, [0, 0.3], [1, 0], Extrapolation.CLAMP)
|
|
557
|
+
};
|
|
558
|
+
});
|
|
559
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
560
|
+
...props,
|
|
561
|
+
style: [animated, props.style],
|
|
562
|
+
className: cn(className),
|
|
563
|
+
children: textChildren(children, text => /*#__PURE__*/_jsx(Text, {
|
|
564
|
+
size: "xs",
|
|
565
|
+
weight: "semibold",
|
|
566
|
+
className: "text-center",
|
|
567
|
+
children: text
|
|
568
|
+
}))
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* One dot per slide, the active one drawn as a bar.
|
|
573
|
+
*
|
|
574
|
+
* Length rather than colour alone carries the position: a row that differs only
|
|
575
|
+
* in opacity is unreadable at a glance, and invisible to anyone who cannot
|
|
576
|
+
* separate the two greys.
|
|
577
|
+
*/
|
|
578
|
+
function CarouselDots({
|
|
579
|
+
className,
|
|
580
|
+
orientation = 'horizontal',
|
|
581
|
+
interactive = true,
|
|
582
|
+
...props
|
|
583
|
+
}) {
|
|
584
|
+
const {
|
|
585
|
+
count,
|
|
586
|
+
index,
|
|
587
|
+
scrollTo
|
|
588
|
+
} = useCarousel('Carousel.Dots');
|
|
589
|
+
const horizontal = orientation === 'horizontal';
|
|
590
|
+
if (count <= 1) return null;
|
|
591
|
+
return /*#__PURE__*/_jsx(View, {
|
|
592
|
+
accessibilityRole: "tablist",
|
|
593
|
+
...props,
|
|
594
|
+
className: cn('items-center gap-1', horizontal ? 'flex-row' : 'flex-col', className),
|
|
595
|
+
children: Array.from({
|
|
596
|
+
length: count
|
|
597
|
+
}, (_unused, dot) => {
|
|
598
|
+
const active = dot === index;
|
|
599
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
600
|
+
disabled: !interactive,
|
|
601
|
+
onPress: () => scrollTo(dot),
|
|
602
|
+
hitSlop: 6,
|
|
603
|
+
accessibilityRole: "tab",
|
|
604
|
+
accessibilityLabel: `Slide ${dot + 1} of ${count}`,
|
|
605
|
+
accessibilityState: {
|
|
606
|
+
selected: active
|
|
607
|
+
},
|
|
608
|
+
className: cn('rounded-full', horizontal ? active ? 'h-1 w-4' : 'h-1 w-1' : active ? 'h-4 w-1' : 'h-1 w-1', active ? 'bg-foreground' : 'bg-foreground/30')
|
|
609
|
+
}, dot);
|
|
610
|
+
})
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
/** Body of the two arrows — they differ only in icon, label and direction. */
|
|
614
|
+
function makeArrow(direction) {
|
|
615
|
+
const name = direction === 'next' ? 'Next' : 'Previous';
|
|
616
|
+
function Arrow({
|
|
617
|
+
className,
|
|
618
|
+
children,
|
|
619
|
+
...props
|
|
620
|
+
}) {
|
|
621
|
+
const {
|
|
622
|
+
count,
|
|
623
|
+
index,
|
|
624
|
+
loop,
|
|
625
|
+
next,
|
|
626
|
+
previous
|
|
627
|
+
} = useCarousel(`Carousel.${name}`);
|
|
628
|
+
// Without a loop the ends are dead. An arrow that stays live and does
|
|
629
|
+
// nothing is worse than one that says it cannot.
|
|
630
|
+
const disabled = !loop && (direction === 'next' ? index >= count - 1 : index <= 0);
|
|
631
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
632
|
+
disabled: disabled,
|
|
633
|
+
onPress: direction === 'next' ? next : previous,
|
|
634
|
+
hitSlop: 8,
|
|
635
|
+
accessibilityRole: "button",
|
|
636
|
+
accessibilityLabel: direction === 'next' ? 'Next slide' : 'Previous slide',
|
|
637
|
+
accessibilityState: {
|
|
638
|
+
disabled
|
|
639
|
+
},
|
|
640
|
+
className: cn('items-center justify-center rounded-full p-1', disabled ? 'opacity-30' : 'active:bg-foreground/10', className),
|
|
641
|
+
...props,
|
|
642
|
+
children: children ?? (direction === 'next' ? /*#__PURE__*/_jsx(ChevronRightIcon, {
|
|
643
|
+
size: 14
|
|
644
|
+
}) : /*#__PURE__*/_jsx(ChevronLeftIcon, {
|
|
645
|
+
size: 14
|
|
646
|
+
}))
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
Arrow.displayName = `Carousel.${name}`;
|
|
650
|
+
return Arrow;
|
|
651
|
+
}
|
|
652
|
+
const CarouselPrevious = makeArrow('previous');
|
|
653
|
+
const CarouselNext = makeArrow('next');
|
|
654
|
+
/**
|
|
655
|
+
* The arrows and the dots in one pill.
|
|
656
|
+
*
|
|
657
|
+
* Together rather than scattered, because they answer the same question — where
|
|
658
|
+
* am I in this, and how do I move — and a bar that reads as one object can sit
|
|
659
|
+
* over the content instead of taking a strip of the layout for itself.
|
|
660
|
+
*/
|
|
661
|
+
function CarouselControls({
|
|
662
|
+
className,
|
|
663
|
+
...props
|
|
664
|
+
}) {
|
|
665
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
666
|
+
...props,
|
|
667
|
+
className: cn('flex-row items-center justify-center gap-2 self-center rounded-full border border-border bg-background/80 px-1.5 py-0.5', className),
|
|
668
|
+
children: [/*#__PURE__*/_jsx(CarouselPrevious, {}), /*#__PURE__*/_jsx(CarouselDots, {}), /*#__PURE__*/_jsx(CarouselNext, {})]
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
export const Carousel = Object.assign(CarouselRoot, {
|
|
672
|
+
Content: CarouselContent,
|
|
673
|
+
Item: CarouselItem,
|
|
674
|
+
Caption: CarouselCaption,
|
|
675
|
+
Dots: CarouselDots,
|
|
676
|
+
Previous: CarouselPrevious,
|
|
677
|
+
Next: CarouselNext,
|
|
678
|
+
Controls: CarouselControls
|
|
679
|
+
});
|
|
680
|
+
//# sourceMappingURL=index.js.map
|