jfs-components 0.0.21 → 0.0.25

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.
Files changed (174) hide show
  1. package/lib/commonjs/assets/fonts/JioType Var.ttf +0 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +1 -1
  3. package/lib/commonjs/components/Accordion/Accordion.js.map +1 -1
  4. package/lib/commonjs/components/ActionFooter/ActionFooter.js +2 -2
  5. package/lib/commonjs/components/ActionFooter/ActionFooter.js.map +1 -1
  6. package/lib/commonjs/components/AppBar/AppBar.js +2 -2
  7. package/lib/commonjs/components/AppBar/AppBar.js.map +1 -1
  8. package/lib/commonjs/components/Avatar/Avatar.js +2 -2
  9. package/lib/commonjs/components/Avatar/Avatar.js.map +1 -1
  10. package/lib/commonjs/components/Badge/Badge.js +1 -1
  11. package/lib/commonjs/components/Badge/Badge.js.map +1 -1
  12. package/lib/commonjs/components/BottomNav/BottomNav.js +2 -2
  13. package/lib/commonjs/components/BottomNav/BottomNav.js.map +1 -1
  14. package/lib/commonjs/components/BottomNavItem/BottomNavItem.js +2 -2
  15. package/lib/commonjs/components/BottomNavItem/BottomNavItem.js.map +1 -1
  16. package/lib/commonjs/components/Button/Button.js +1 -1
  17. package/lib/commonjs/components/Button/Button.js.map +1 -1
  18. package/lib/commonjs/components/Carousel/Carousel.js +341 -0
  19. package/lib/commonjs/components/Carousel/Carousel.js.map +1 -0
  20. package/lib/commonjs/components/Carousel/Carousel.mdx +154 -0
  21. package/lib/commonjs/components/Disclaimer/Disclaimer.js +1 -1
  22. package/lib/commonjs/components/Disclaimer/Disclaimer.js.map +1 -1
  23. package/lib/commonjs/components/Divider/Divider.js +2 -2
  24. package/lib/commonjs/components/Divider/Divider.js.map +1 -1
  25. package/lib/commonjs/components/Drawer/Drawer.js +10 -3
  26. package/lib/commonjs/components/Drawer/Drawer.js.map +1 -1
  27. package/lib/commonjs/components/FilterBar/FilterBar.js +2 -2
  28. package/lib/commonjs/components/FilterBar/FilterBar.js.map +1 -1
  29. package/lib/commonjs/components/IconButton/IconButton.js +2 -2
  30. package/lib/commonjs/components/IconButton/IconButton.js.map +1 -1
  31. package/lib/commonjs/components/IconCapsule/IconCapsule.js +2 -2
  32. package/lib/commonjs/components/IconCapsule/IconCapsule.js.map +1 -1
  33. package/lib/commonjs/components/LazyList/LazyList.js +2 -2
  34. package/lib/commonjs/components/LazyList/LazyList.js.map +1 -1
  35. package/lib/commonjs/components/ListGroup/ListGroup.js +1 -1
  36. package/lib/commonjs/components/ListGroup/ListGroup.js.map +1 -1
  37. package/lib/commonjs/components/ListItem/ListItem.js +5 -5
  38. package/lib/commonjs/components/ListItem/ListItem.js.map +1 -1
  39. package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +1 -1
  40. package/lib/commonjs/components/MerchantProfile/MerchantProfile.js.map +1 -1
  41. package/lib/commonjs/components/MoneyValue/MoneyValue.js +1 -1
  42. package/lib/commonjs/components/MoneyValue/MoneyValue.js.map +1 -1
  43. package/lib/commonjs/components/NavArrow/NavArrow.js +1 -1
  44. package/lib/commonjs/components/NavArrow/NavArrow.js.map +1 -1
  45. package/lib/commonjs/components/PageTitle/PageTitle.js +1 -1
  46. package/lib/commonjs/components/PageTitle/PageTitle.js.map +1 -1
  47. package/lib/commonjs/components/RadioButton/RadioButton.js +194 -0
  48. package/lib/commonjs/components/RadioButton/RadioButton.js.map +1 -0
  49. package/lib/commonjs/components/RadioButton/RadioButton.mdx +92 -0
  50. package/lib/commonjs/components/Section/Section.js +4 -4
  51. package/lib/commonjs/components/Section/Section.js.map +1 -1
  52. package/lib/commonjs/components/TextInput/TextInput.js +1 -1
  53. package/lib/commonjs/components/TextInput/TextInput.js.map +1 -1
  54. package/lib/commonjs/components/TransactionDetails/TransactionDetails.js +2 -2
  55. package/lib/commonjs/components/TransactionDetails/TransactionDetails.js.map +1 -1
  56. package/lib/commonjs/components/UpiHandle/UpiHandle.js +4 -2
  57. package/lib/commonjs/components/UpiHandle/UpiHandle.js.map +1 -1
  58. package/lib/commonjs/components/index.js +7 -0
  59. package/lib/commonjs/components/index.js.map +1 -1
  60. package/lib/commonjs/design-tokens/figma-variables-resolver.js +9 -3
  61. package/lib/commonjs/design-tokens/figma-variables-resolver.js.map +2 -1
  62. package/lib/commonjs/icons/registry.js +1 -1
  63. package/lib/module/assets/fonts/JioType Var.ttf +0 -0
  64. package/lib/module/components/Accordion/Accordion.js +1 -1
  65. package/lib/module/components/Accordion/Accordion.js.map +1 -1
  66. package/lib/module/components/ActionFooter/ActionFooter.js +2 -2
  67. package/lib/module/components/ActionFooter/ActionFooter.js.map +1 -1
  68. package/lib/module/components/AppBar/AppBar.js +2 -2
  69. package/lib/module/components/AppBar/AppBar.js.map +1 -1
  70. package/lib/module/components/Avatar/Avatar.js +2 -2
  71. package/lib/module/components/Avatar/Avatar.js.map +1 -1
  72. package/lib/module/components/Badge/Badge.js +1 -1
  73. package/lib/module/components/Badge/Badge.js.map +1 -1
  74. package/lib/module/components/BottomNav/BottomNav.js +2 -2
  75. package/lib/module/components/BottomNav/BottomNav.js.map +1 -1
  76. package/lib/module/components/BottomNavItem/BottomNavItem.js +2 -2
  77. package/lib/module/components/BottomNavItem/BottomNavItem.js.map +1 -1
  78. package/lib/module/components/Button/Button.js +1 -1
  79. package/lib/module/components/Button/Button.js.map +1 -1
  80. package/lib/module/components/Carousel/Carousel.js +333 -0
  81. package/lib/module/components/Carousel/Carousel.js.map +1 -0
  82. package/lib/module/components/Carousel/Carousel.mdx +154 -0
  83. package/lib/module/components/Disclaimer/Disclaimer.js +1 -1
  84. package/lib/module/components/Disclaimer/Disclaimer.js.map +1 -1
  85. package/lib/module/components/Divider/Divider.js +2 -2
  86. package/lib/module/components/Divider/Divider.js.map +1 -1
  87. package/lib/module/components/Drawer/Drawer.js +11 -4
  88. package/lib/module/components/Drawer/Drawer.js.map +1 -1
  89. package/lib/module/components/FilterBar/FilterBar.js +2 -2
  90. package/lib/module/components/FilterBar/FilterBar.js.map +1 -1
  91. package/lib/module/components/IconButton/IconButton.js +2 -2
  92. package/lib/module/components/IconButton/IconButton.js.map +1 -1
  93. package/lib/module/components/IconCapsule/IconCapsule.js +2 -2
  94. package/lib/module/components/IconCapsule/IconCapsule.js.map +1 -1
  95. package/lib/module/components/LazyList/LazyList.js +2 -2
  96. package/lib/module/components/LazyList/LazyList.js.map +1 -1
  97. package/lib/module/components/ListGroup/ListGroup.js +1 -1
  98. package/lib/module/components/ListGroup/ListGroup.js.map +1 -1
  99. package/lib/module/components/ListItem/ListItem.js +5 -5
  100. package/lib/module/components/ListItem/ListItem.js.map +1 -1
  101. package/lib/module/components/MerchantProfile/MerchantProfile.js +1 -1
  102. package/lib/module/components/MerchantProfile/MerchantProfile.js.map +1 -1
  103. package/lib/module/components/MoneyValue/MoneyValue.js +1 -1
  104. package/lib/module/components/MoneyValue/MoneyValue.js.map +1 -1
  105. package/lib/module/components/NavArrow/NavArrow.js +1 -1
  106. package/lib/module/components/NavArrow/NavArrow.js.map +1 -1
  107. package/lib/module/components/PageTitle/PageTitle.js +1 -1
  108. package/lib/module/components/PageTitle/PageTitle.js.map +1 -1
  109. package/lib/module/components/RadioButton/RadioButton.js +188 -0
  110. package/lib/module/components/RadioButton/RadioButton.js.map +1 -0
  111. package/lib/module/components/RadioButton/RadioButton.mdx +92 -0
  112. package/lib/module/components/Section/Section.js +4 -4
  113. package/lib/module/components/Section/Section.js.map +1 -1
  114. package/lib/module/components/TextInput/TextInput.js +1 -1
  115. package/lib/module/components/TextInput/TextInput.js.map +1 -1
  116. package/lib/module/components/TransactionDetails/TransactionDetails.js +2 -2
  117. package/lib/module/components/TransactionDetails/TransactionDetails.js.map +1 -1
  118. package/lib/module/components/UpiHandle/UpiHandle.js +4 -2
  119. package/lib/module/components/UpiHandle/UpiHandle.js.map +1 -1
  120. package/lib/module/components/index.js +1 -0
  121. package/lib/module/components/index.js.map +1 -1
  122. package/lib/module/design-tokens/figma-variables-resolver.js +2 -2
  123. package/lib/module/design-tokens/figma-variables-resolver.js.map +1 -2
  124. package/lib/module/icons/registry.js +1 -1
  125. package/lib/typescript/components/ActionFooter/ActionFooter.d.ts.map +1 -1
  126. package/lib/typescript/components/BottomNav/BottomNav.d.ts.map +1 -1
  127. package/lib/typescript/components/Carousel/Carousel.d.ts +48 -0
  128. package/lib/typescript/components/Carousel/Carousel.d.ts.map +1 -0
  129. package/lib/typescript/components/Drawer/Drawer.d.ts.map +1 -1
  130. package/lib/typescript/components/IconButton/IconButton.d.ts +1 -1
  131. package/lib/typescript/components/IconCapsule/IconCapsule.d.ts +1 -1
  132. package/lib/typescript/components/LazyList/LazyList.d.ts.map +1 -1
  133. package/lib/typescript/components/RadioButton/RadioButton.d.ts +30 -0
  134. package/lib/typescript/components/RadioButton/RadioButton.d.ts.map +1 -0
  135. package/lib/typescript/components/Section/Section.d.ts.map +1 -1
  136. package/lib/typescript/components/UpiHandle/UpiHandle.d.ts +4 -2
  137. package/lib/typescript/components/UpiHandle/UpiHandle.d.ts.map +1 -1
  138. package/lib/typescript/components/index.d.ts +1 -0
  139. package/lib/typescript/components/index.d.ts.map +1 -1
  140. package/lib/typescript/icons/registry.d.ts +1 -1
  141. package/package.json +2 -2
  142. package/src/assets/fonts/JioType Var.ttf +0 -0
  143. package/src/components/.token-metadata.json +72 -0
  144. package/src/components/Accordion/Accordion.tsx +1 -1
  145. package/src/components/ActionFooter/ActionFooter.tsx +2 -2
  146. package/src/components/AppBar/AppBar.tsx +2 -2
  147. package/src/components/Avatar/Avatar.tsx +2 -2
  148. package/src/components/Badge/Badge.tsx +1 -1
  149. package/src/components/BottomNav/BottomNav.tsx +2 -2
  150. package/src/components/BottomNavItem/BottomNavItem.tsx +2 -2
  151. package/src/components/Button/Button.tsx +1 -1
  152. package/src/components/Carousel/Carousel.mdx +154 -0
  153. package/src/components/Carousel/Carousel.tsx +472 -0
  154. package/src/components/Disclaimer/Disclaimer.tsx +1 -1
  155. package/src/components/Divider/Divider.tsx +2 -2
  156. package/src/components/Drawer/Drawer.tsx +11 -3
  157. package/src/components/FilterBar/FilterBar.tsx +2 -2
  158. package/src/components/IconButton/IconButton.tsx +2 -2
  159. package/src/components/IconCapsule/IconCapsule.tsx +2 -2
  160. package/src/components/LazyList/LazyList.tsx +2 -2
  161. package/src/components/ListGroup/ListGroup.tsx +1 -1
  162. package/src/components/ListItem/ListItem.tsx +5 -5
  163. package/src/components/MerchantProfile/MerchantProfile.tsx +1 -1
  164. package/src/components/MoneyValue/MoneyValue.tsx +1 -1
  165. package/src/components/NavArrow/NavArrow.tsx +1 -1
  166. package/src/components/PageTitle/PageTitle.tsx +1 -1
  167. package/src/components/RadioButton/RadioButton.mdx +92 -0
  168. package/src/components/RadioButton/RadioButton.tsx +226 -0
  169. package/src/components/Section/Section.tsx +4 -4
  170. package/src/components/TextInput/TextInput.tsx +1 -1
  171. package/src/components/TransactionDetails/TransactionDetails.tsx +2 -2
  172. package/src/components/UpiHandle/UpiHandle.tsx +6 -3
  173. package/src/components/index.ts +1 -0
  174. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,472 @@
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useRef,
5
+ useState,
6
+ useEffect,
7
+ useCallback,
8
+ useMemo,
9
+ } from 'react'
10
+ import {
11
+ View,
12
+ ScrollView,
13
+ Animated,
14
+ Pressable,
15
+ type ViewStyle,
16
+ type StyleProp,
17
+ type NativeSyntheticEvent,
18
+ type NativeScrollEvent,
19
+ type LayoutChangeEvent,
20
+ } from 'react-native'
21
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Context
25
+ // ---------------------------------------------------------------------------
26
+
27
+ interface CarouselContextValue {
28
+ modes: Record<string, any>
29
+ activeIndex: number
30
+ totalItems: number
31
+ goTo: (index: number) => void
32
+ goNext: () => void
33
+ goPrev: () => void
34
+ }
35
+
36
+ const CarouselContext = createContext<CarouselContextValue>({
37
+ modes: {},
38
+ activeIndex: 0,
39
+ totalItems: 0,
40
+ goTo: () => {},
41
+ goNext: () => {},
42
+ goPrev: () => {},
43
+ })
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Types
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export interface CarouselProps {
50
+ /** Content items to display (should be Carousel.Item or any React nodes). */
51
+ children?: React.ReactNode
52
+ /** Modes object for design-token resolution. */
53
+ modes?: Record<string, any>
54
+ /** Enable auto-play. Default: false. */
55
+ autoPlay?: boolean
56
+ /** Auto-play interval in ms. Default: 4000. */
57
+ autoPlayInterval?: number
58
+ /** Show pagination dots. Default: true. */
59
+ showPagination?: boolean
60
+ /** Enable looping (jumps back to first after last). Default: true. */
61
+ loop?: boolean
62
+ /** Gap between items in px. Resolved from token `carousel/gap` or defaults to 12. */
63
+ gap?: number
64
+ /** Width of each carousel item. When undefined, items take full container width. */
65
+ itemWidth?: number
66
+ /** Horizontal padding around the carousel track, allowing peek of adjacent items. Defaults to 0. */
67
+ peekOffset?: number
68
+ /** Called when the active index changes. */
69
+ onIndexChange?: (index: number) => void
70
+ /** Style overrides for the outermost container. */
71
+ style?: StyleProp<ViewStyle>
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Carousel
76
+ // ---------------------------------------------------------------------------
77
+
78
+ export function Carousel({
79
+ children,
80
+ modes = {},
81
+ autoPlay = false,
82
+ autoPlayInterval = 4000,
83
+ showPagination = true,
84
+ loop = true,
85
+ gap: gapProp,
86
+ itemWidth: itemWidthProp,
87
+ peekOffset: peekOffsetProp,
88
+ onIndexChange,
89
+ style,
90
+ }: CarouselProps) {
91
+ // ---- Token resolution ----
92
+ const tokenGap = parseFloat(
93
+ getVariableByName('carousel/gap', modes) || '12',
94
+ )
95
+ const gap = gapProp ?? tokenGap
96
+
97
+ const tokenPeekOffset = parseFloat(
98
+ getVariableByName('carousel/peekOffset', modes) || '0',
99
+ )
100
+ const peekOffset = peekOffsetProp ?? tokenPeekOffset
101
+
102
+ const containerPaddingH = parseFloat(
103
+ getVariableByName('carousel/padding/horizontal', modes) || '0',
104
+ )
105
+ const containerPaddingV = parseFloat(
106
+ getVariableByName('carousel/padding/vertical', modes) || '0',
107
+ )
108
+ const paginationGap = parseFloat(
109
+ getVariableByName('carousel/pagination/gap', modes) || '12',
110
+ )
111
+
112
+ // ---- Refs & state ----
113
+ const scrollRef = useRef<ScrollView>(null)
114
+ const [activeIndex, setActiveIndex] = useState(0)
115
+ const [containerWidth, setContainerWidth] = useState(0)
116
+ const autoPlayTimer = useRef<ReturnType<typeof setInterval> | null>(null)
117
+ const userInteracting = useRef(false)
118
+
119
+ // Flatten children so we can count items
120
+ const items = useMemo(
121
+ () => React.Children.toArray(children).filter(React.isValidElement),
122
+ [children],
123
+ )
124
+
125
+ const totalItems = items.length
126
+
127
+ // Effective item width: provided prop, or full container width minus peek offsets
128
+ const effectiveItemWidth = useMemo(() => {
129
+ if (itemWidthProp != null) return itemWidthProp
130
+ if (containerWidth === 0) return 0
131
+ // Full-width minus peekOffset on each side
132
+ return containerWidth - peekOffset * 2 - containerPaddingH * 2
133
+ }, [itemWidthProp, containerWidth, peekOffset, containerPaddingH])
134
+
135
+ // Snap interval = item width + gap
136
+ const snapInterval = effectiveItemWidth + gap
137
+
138
+ // ---- Navigation helpers ----
139
+ const scrollToIndex = useCallback(
140
+ (index: number, animated = true) => {
141
+ if (totalItems === 0 || snapInterval === 0) return
142
+ let targetIndex = index
143
+ if (loop) {
144
+ targetIndex = ((index % totalItems) + totalItems) % totalItems
145
+ } else {
146
+ targetIndex = Math.max(0, Math.min(index, totalItems - 1))
147
+ }
148
+ scrollRef.current?.scrollTo({
149
+ x: targetIndex * snapInterval,
150
+ animated,
151
+ })
152
+ setActiveIndex(targetIndex)
153
+ onIndexChange?.(targetIndex)
154
+ },
155
+ [totalItems, snapInterval, loop, onIndexChange],
156
+ )
157
+
158
+ const goTo = useCallback(
159
+ (index: number) => scrollToIndex(index),
160
+ [scrollToIndex],
161
+ )
162
+ const goNext = useCallback(
163
+ () => scrollToIndex(activeIndex + 1),
164
+ [scrollToIndex, activeIndex],
165
+ )
166
+ const goPrev = useCallback(
167
+ () => scrollToIndex(activeIndex - 1),
168
+ [scrollToIndex, activeIndex],
169
+ )
170
+
171
+ // ---- Scroll event handler ----
172
+ const handleScroll = useCallback(
173
+ (event: NativeSyntheticEvent<NativeScrollEvent>) => {
174
+ if (snapInterval === 0) return
175
+ const offsetX = event.nativeEvent.contentOffset.x
176
+ const newIndex = Math.round(offsetX / snapInterval)
177
+ const clampedIndex = Math.max(0, Math.min(newIndex, totalItems - 1))
178
+ if (clampedIndex !== activeIndex) {
179
+ setActiveIndex(clampedIndex)
180
+ onIndexChange?.(clampedIndex)
181
+ }
182
+ },
183
+ [snapInterval, totalItems, activeIndex, onIndexChange],
184
+ )
185
+
186
+ // ---- Auto-play ----
187
+ const clearAutoPlay = useCallback(() => {
188
+ if (autoPlayTimer.current != null) {
189
+ clearInterval(autoPlayTimer.current)
190
+ autoPlayTimer.current = null
191
+ }
192
+ }, [])
193
+
194
+ const startAutoPlay = useCallback(() => {
195
+ if (!autoPlay || totalItems <= 1) return
196
+ clearAutoPlay()
197
+ autoPlayTimer.current = setInterval(() => {
198
+ if (!userInteracting.current) {
199
+ setActiveIndex((prev) => {
200
+ const next = loop
201
+ ? (prev + 1) % totalItems
202
+ : Math.min(prev + 1, totalItems - 1)
203
+ scrollRef.current?.scrollTo({
204
+ x: next * snapInterval,
205
+ animated: true,
206
+ })
207
+ onIndexChange?.(next)
208
+ return next
209
+ })
210
+ }
211
+ }, autoPlayInterval)
212
+ }, [
213
+ autoPlay,
214
+ totalItems,
215
+ loop,
216
+ snapInterval,
217
+ autoPlayInterval,
218
+ clearAutoPlay,
219
+ onIndexChange,
220
+ ])
221
+
222
+ useEffect(() => {
223
+ startAutoPlay()
224
+ return clearAutoPlay
225
+ }, [startAutoPlay, clearAutoPlay])
226
+
227
+ const handleScrollBeginDrag = useCallback(() => {
228
+ userInteracting.current = true
229
+ }, [])
230
+
231
+ const handleScrollEndDrag = useCallback(() => {
232
+ userInteracting.current = false
233
+ // Reset autoplay timer after interaction
234
+ if (autoPlay) startAutoPlay()
235
+ }, [autoPlay, startAutoPlay])
236
+
237
+ // ---- Layout ----
238
+ const handleLayout = useCallback((e: LayoutChangeEvent) => {
239
+ setContainerWidth(e.nativeEvent.layout.width)
240
+ }, [])
241
+
242
+ // ---- Context value ----
243
+ const contextValue = useMemo<CarouselContextValue>(
244
+ () => ({
245
+ modes,
246
+ activeIndex,
247
+ totalItems,
248
+ goTo,
249
+ goNext,
250
+ goPrev,
251
+ }),
252
+ [modes, activeIndex, totalItems, goTo, goNext, goPrev],
253
+ )
254
+
255
+ // ---- Render ----
256
+ const outerStyle: ViewStyle = {
257
+ paddingVertical: containerPaddingV,
258
+ }
259
+
260
+ const contentContainerStyle: ViewStyle = {
261
+ paddingHorizontal: peekOffset + containerPaddingH,
262
+ gap,
263
+ // Align items to the start so snap works correctly
264
+ alignItems: 'flex-start',
265
+ }
266
+
267
+ return (
268
+ <CarouselContext.Provider value={contextValue}>
269
+ <View style={[outerStyle, style]} onLayout={handleLayout}>
270
+ <ScrollView
271
+ ref={scrollRef}
272
+ horizontal
273
+ pagingEnabled={false}
274
+ showsHorizontalScrollIndicator={false}
275
+ decelerationRate="fast"
276
+ snapToInterval={snapInterval > 0 ? snapInterval : undefined}
277
+ snapToAlignment="start"
278
+ contentContainerStyle={contentContainerStyle}
279
+ onScroll={handleScroll}
280
+ scrollEventThrottle={16}
281
+ onScrollBeginDrag={handleScrollBeginDrag}
282
+ onScrollEndDrag={handleScrollEndDrag}
283
+
284
+ >
285
+ {items.map((child, index) => {
286
+ const itemStyle: ViewStyle = {
287
+ width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
288
+ }
289
+
290
+ // Pass modes down to children
291
+ const childWithModes = React.isValidElement(child)
292
+ ? React.cloneElement(child as React.ReactElement<any>, {
293
+ modes: {
294
+ ...((child.props as any)?.modes || {}),
295
+ ...modes,
296
+ },
297
+ style: [itemStyle, (child.props as any)?.style],
298
+ })
299
+ : child
300
+
301
+ return (
302
+ <View key={index} style={itemStyle}>
303
+ {childWithModes}
304
+ </View>
305
+ )
306
+ })}
307
+ </ScrollView>
308
+
309
+ {showPagination && totalItems > 1 && (
310
+ <Pagination
311
+ modes={modes}
312
+ style={{ marginTop: paginationGap }}
313
+ />
314
+ )}
315
+ </View>
316
+ </CarouselContext.Provider>
317
+ )
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Carousel.Item
322
+ // ---------------------------------------------------------------------------
323
+
324
+ export interface CarouselItemProps {
325
+ children?: React.ReactNode
326
+ modes?: Record<string, any>
327
+ style?: StyleProp<ViewStyle>
328
+ }
329
+
330
+ /**
331
+ * Optional wrapper for carousel items. Not strictly required — any
332
+ * React node works as a direct child of `<Carousel>`.
333
+ */
334
+ export function Item({ children, modes: _modes, style }: CarouselItemProps) {
335
+ return <View style={style}>{children}</View>
336
+ }
337
+
338
+ // ---------------------------------------------------------------------------
339
+ // Carousel.Pagination
340
+ // ---------------------------------------------------------------------------
341
+
342
+ export interface PaginationProps {
343
+ modes?: Record<string, any>
344
+ style?: StyleProp<ViewStyle>
345
+ }
346
+
347
+ export function Pagination({ modes: propModes, style }: PaginationProps) {
348
+ const { modes: ctxModes, activeIndex, totalItems, goTo } =
349
+ useContext(CarouselContext)
350
+ const modes = propModes || ctxModes || {}
351
+
352
+ // Token resolution for dots
353
+ const dotSize = parseFloat(
354
+ getVariableByName('carousel/pagination/dotSize', modes) || '8',
355
+ )
356
+ const dotActiveWidth = parseFloat(
357
+ getVariableByName('carousel/pagination/dotActiveWidth', modes) || '24',
358
+ )
359
+ const dotGap = parseFloat(
360
+ getVariableByName('carousel/pagination/dotGap', modes) || '8',
361
+ )
362
+ const dotColor =
363
+ (getVariableByName('carousel/pagination/dotColor', modes) as string) ||
364
+ 'rgba(255,255,255,0.35)'
365
+ const dotActiveColor =
366
+ (getVariableByName(
367
+ 'carousel/pagination/dotActiveColor',
368
+ modes,
369
+ ) as string) || '#ffffff'
370
+ const dotRadius = parseFloat(
371
+ getVariableByName('carousel/pagination/dotRadius', modes) || '4',
372
+ )
373
+
374
+ const containerStyle: ViewStyle = {
375
+ flexDirection: 'row',
376
+ justifyContent: 'center',
377
+ alignItems: 'center',
378
+ gap: dotGap,
379
+ }
380
+
381
+ return (
382
+ <View
383
+ style={[containerStyle, style]}
384
+ accessibilityRole="tablist"
385
+ accessibilityLabel={undefined}
386
+ >
387
+ {Array.from({ length: totalItems }).map((_, i) => {
388
+ const isActive = i === activeIndex
389
+ return (
390
+ <Pressable
391
+ key={i}
392
+ onPress={() => goTo(i)}
393
+ accessibilityRole="tab"
394
+ accessibilityState={{ selected: isActive }}
395
+ accessibilityLabel={undefined}
396
+ >
397
+ <AnimatedDot
398
+ isActive={isActive}
399
+ size={dotSize}
400
+ activeWidth={dotActiveWidth}
401
+ radius={dotRadius}
402
+ color={dotColor}
403
+ activeColor={dotActiveColor}
404
+ />
405
+ </Pressable>
406
+ )
407
+ })}
408
+ </View>
409
+ )
410
+ }
411
+
412
+ // ---------------------------------------------------------------------------
413
+ // AnimatedDot (internal)
414
+ // ---------------------------------------------------------------------------
415
+
416
+ interface AnimatedDotProps {
417
+ isActive: boolean
418
+ size: number
419
+ activeWidth: number
420
+ radius: number
421
+ color: string
422
+ activeColor: string
423
+ }
424
+
425
+ function AnimatedDot({
426
+ isActive,
427
+ size,
428
+ activeWidth,
429
+ radius,
430
+ color,
431
+ activeColor,
432
+ }: AnimatedDotProps) {
433
+ const animValue = useRef(new Animated.Value(isActive ? 1 : 0)).current
434
+
435
+ useEffect(() => {
436
+ Animated.timing(animValue, {
437
+ toValue: isActive ? 1 : 0,
438
+ duration: 250,
439
+ useNativeDriver: false,
440
+ }).start()
441
+ }, [isActive, animValue])
442
+
443
+ const width = animValue.interpolate({
444
+ inputRange: [0, 1],
445
+ outputRange: [size, activeWidth],
446
+ })
447
+
448
+ const backgroundColor = animValue.interpolate({
449
+ inputRange: [0, 1],
450
+ outputRange: [color, activeColor],
451
+ })
452
+
453
+ return (
454
+ <Animated.View
455
+ style={{
456
+ width,
457
+ height: size,
458
+ borderRadius: radius,
459
+ backgroundColor,
460
+ }}
461
+ />
462
+ )
463
+ }
464
+
465
+ // ---------------------------------------------------------------------------
466
+ // Attach sub-components
467
+ // ---------------------------------------------------------------------------
468
+
469
+ Carousel.Item = Item
470
+ Carousel.Pagination = Pagination
471
+
472
+ export default Carousel
@@ -83,7 +83,7 @@ function Disclaimer({
83
83
  <View
84
84
  style={[containerStyle, style]}
85
85
  accessibilityRole="text"
86
- accessibilityLabel={defaultAccessibilityLabel}
86
+ accessibilityLabel={undefined}
87
87
  accessibilityHint={accessibilityHint}
88
88
  {...rest}
89
89
  >
@@ -58,7 +58,7 @@ function Divider({
58
58
  direction = 'horizontal',
59
59
  modes = {},
60
60
  style,
61
- accessibilityLabel = 'Divider',
61
+ accessibilityLabel = undefined,
62
62
  }: DividerProps) {
63
63
  // Resolve design tokens
64
64
  const size = getVariableByName('divider/size', modes) ?? 1
@@ -82,7 +82,7 @@ function Divider({
82
82
  <View
83
83
  style={[dividerStyle, style]}
84
84
  accessibilityRole="none"
85
- accessibilityLabel={accessibilityLabel}
85
+ accessibilityLabel={undefined}
86
86
  />
87
87
  )
88
88
  }
@@ -8,6 +8,7 @@ import {
8
8
  } from 'react-native-gesture-handler'
9
9
  import Animated, {
10
10
  runOnJS,
11
+ useAnimatedProps,
11
12
  useAnimatedScrollHandler,
12
13
  useAnimatedStyle,
13
14
  useSharedValue,
@@ -116,6 +117,13 @@ function Drawer({
116
117
  const scrollY = useSharedValue(0)
117
118
  const scrollRef = useRef(null)
118
119
 
120
+ // Dynamically disable top bounce so a downward swipe at the top of
121
+ // the content drags the whole drawer instead of rubber-banding the
122
+ // scroll view. Bottom bounce is preserved when the user has scrolled.
123
+ const animatedScrollProps = useAnimatedProps(() => ({
124
+ bounces: scrollY.value > 1,
125
+ }))
126
+
119
127
  // We need to know if we are currently dragging the drawer or scrolling the content
120
128
  // to prevent conflict.
121
129
  const isDrawerActive = useSharedValue(false)
@@ -287,7 +295,7 @@ function Drawer({
287
295
  ]}
288
296
  accessible={true}
289
297
  accessibilityRole={'dialog' as any}
290
- accessibilityLabel={defaultAccessibilityLabel}
298
+ accessibilityLabel={undefined}
291
299
  accessibilityHint={accessibilityHint || 'Swipe up to expand, swipe down to collapse'}
292
300
  >
293
301
  {/* Handle Area */}
@@ -330,8 +338,8 @@ function Drawer({
330
338
  style={[styles.content, contentStyle]}
331
339
  contentContainerStyle={[{ paddingBottom: paddingBottom + bottomInset, gap: drawerGap, flexDirection: 'column', alignItems: 'stretch' }, contentContainerStyle]}
332
340
  showsVerticalScrollIndicator={showsVerticalScrollIndicator}
333
- bounces={true}
334
- alwaysBounceVertical={true}
341
+ animatedProps={animatedScrollProps}
342
+ alwaysBounceVertical={false}
335
343
  overScrollMode="always"
336
344
  onScroll={useAnimatedScrollHandler((event) => {
337
345
  scrollY.value = event.contentOffset.y
@@ -140,7 +140,7 @@ function FilterBar({
140
140
  {...(onChangeText !== undefined && { onChangeText })}
141
141
  modes={modes}
142
142
  style={fullWidthStyle}
143
- accessibilityLabel={resolvedAccessibilityLabel}
143
+ accessibilityLabel={undefined}
144
144
  {...(accessibilityHint !== undefined && { accessibilityHint })}
145
145
  onFocus={() => setIsFocused(true)}
146
146
  onBlur={() => setIsFocused(false)}
@@ -188,7 +188,7 @@ function FilterBar({
188
188
  <View
189
189
  style={[containerStyle, focusStyle, style]}
190
190
  accessibilityRole="search"
191
- accessibilityLabel={resolvedAccessibilityLabel}
191
+ accessibilityLabel={undefined}
192
192
  accessibilityHint={accessibilityHint}
193
193
  {...rest}
194
194
  >
@@ -69,7 +69,7 @@ type IconButtonProps = SafePressableProps & {
69
69
  * <IconButton modes={{"Appearance": "high"}} onPress={() => console.log('pressed')} />
70
70
  *
71
71
  * // With accessibility label
72
- * <IconButton iconName="ic_search" accessibilityLabel="Search" accessibilityHint="Opens the search screen" />
72
+ * <IconButton iconName="ic_search" accessibilityLabel={undefined} accessibilityHint="Opens the search screen" />
73
73
  * ```
74
74
  */
75
75
  function IconButton({
@@ -157,7 +157,7 @@ function IconButton({
157
157
  return (
158
158
  <Pressable
159
159
  accessibilityRole="button"
160
- accessibilityLabel={defaultAccessibilityLabel}
160
+ accessibilityLabel={undefined}
161
161
  accessibilityHint={accessibilityHint}
162
162
  accessibilityState={{
163
163
  disabled,
@@ -36,7 +36,7 @@ type IconCapsuleProps = {
36
36
  * <IconCapsule modes={{"Appearance": "Secondary"}} />
37
37
  *
38
38
  * // With accessibility label
39
- * <IconCapsule iconName="ic_card" accessibilityLabel="Credit card icon" />
39
+ * <IconCapsule iconName="ic_card" accessibilityLabel={undefined} />
40
40
  * ```
41
41
  */
42
42
  function IconCapsule({
@@ -80,7 +80,7 @@ function IconCapsule({
80
80
  <View
81
81
  style={containerStyle}
82
82
  accessibilityRole={accessibilityRole}
83
- accessibilityLabel={defaultAccessibilityLabel}
83
+ accessibilityLabel={undefined}
84
84
  {...rest}
85
85
  >
86
86
  <Icon
@@ -74,7 +74,7 @@ function LazyList({
74
74
  listGroupsSlot,
75
75
  modes = {},
76
76
  style,
77
- accessibilityLabel = "List",
77
+ accessibilityLabel = undefined,
78
78
  accessibilityHint,
79
79
  ...rest
80
80
  }: LazyListProps) {
@@ -103,7 +103,7 @@ function LazyList({
103
103
  <View
104
104
  style={[containerStyle, style]}
105
105
  accessibilityRole="list"
106
- accessibilityLabel={accessibilityLabel}
106
+ accessibilityLabel={undefined}
107
107
  accessibilityHint={accessibilityHint}
108
108
  {...rest}
109
109
  >
@@ -91,7 +91,7 @@ function ListGroup({
91
91
  <View
92
92
  style={[containerStyle, style]}
93
93
  accessibilityRole="list"
94
- accessibilityLabel={defaultAccessibilityLabel}
94
+ accessibilityLabel={undefined}
95
95
  accessibilityHint={accessibilityHint}
96
96
  {...rest}
97
97
  >
@@ -211,7 +211,7 @@ function ListItem({
211
211
  ? processedLeading.length === 1
212
212
  ? processedLeading[0]
213
213
  : processedLeading
214
- : <IconCapsule modes={modes} accessibilityLabel={defaultAccessibilityLabel} />
214
+ : <IconCapsule modes={modes} accessibilityLabel={undefined} />
215
215
 
216
216
  const renderSupportContent = () => {
217
217
  if (supportSlot) {
@@ -290,7 +290,7 @@ function ListItem({
290
290
  return (
291
291
  <Pressable
292
292
  accessibilityRole="button"
293
- accessibilityLabel={defaultAccessibilityLabel}
293
+ accessibilityLabel={undefined}
294
294
  accessibilityHint={accessibilityHint}
295
295
  accessibilityState={{
296
296
  ...accessibilityState
@@ -312,7 +312,7 @@ function ListItem({
312
312
  return (
313
313
  <View
314
314
  accessibilityRole={undefined}
315
- accessibilityLabel={defaultAccessibilityLabel}
315
+ accessibilityLabel={undefined}
316
316
  accessibilityHint={accessibilityHint}
317
317
  style={[baseContainerStyle, sharedLayoutStyle, style]}
318
318
  {...rest}
@@ -334,7 +334,7 @@ function ListItem({
334
334
  return (
335
335
  <Pressable
336
336
  accessibilityRole="button"
337
- accessibilityLabel={defaultAccessibilityLabel}
337
+ accessibilityLabel={undefined}
338
338
  accessibilityHint={accessibilityHint}
339
339
  accessibilityState={{
340
340
  ...accessibilityState
@@ -356,7 +356,7 @@ function ListItem({
356
356
  return (
357
357
  <View
358
358
  accessibilityRole={undefined}
359
- accessibilityLabel={defaultAccessibilityLabel}
359
+ accessibilityLabel={undefined}
360
360
  accessibilityHint={accessibilityHint}
361
361
  style={[baseContainerStyle, sharedLayoutStyle, style]}
362
362
  {...rest}
@@ -147,7 +147,7 @@ function MerchantProfile({
147
147
  <View
148
148
  style={[containerStyle, style]}
149
149
  accessibilityRole="header"
150
- accessibilityLabel={defaultAccessibilityLabel}
150
+ accessibilityLabel={undefined}
151
151
  >
152
152
  <Avatar
153
153
  style="Image"
@@ -155,7 +155,7 @@ function MoneyValue({
155
155
  <View
156
156
  style={[containerStyle, style]}
157
157
  accessibilityRole="text"
158
- accessibilityLabel={defaultAccessibilityLabel}
158
+ accessibilityLabel={undefined}
159
159
  accessibilityHint={accessibilityHint}
160
160
  {...rest}
161
161
  >
@@ -89,7 +89,7 @@ export default function NavArrow({
89
89
  <View
90
90
  style={containerStyle}
91
91
  accessibilityRole="image"
92
- accessibilityLabel={defaultAccessibilityLabel}
92
+ accessibilityLabel={undefined}
93
93
  {...rest}
94
94
  >
95
95
  <Icon