jfs-components 0.1.56 → 0.1.57

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.
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-07-27T20:32:46.426Z
7
+ * Generated: 2026-07-27T20:45:13.986Z
8
8
  */
9
9
  export declare const iconRegistry: Record<string, {
10
10
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfs-components",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "React Native Jio Finance Components Library",
5
5
  "author": "sunshuaiqi@gmail.com",
6
6
  "license": "MIT",
@@ -284,13 +284,12 @@ function CardCTA({
284
284
  borderColor,
285
285
  flexDirection: isRating || isSip ? 'column' : 'row',
286
286
  overflow: 'visible',
287
- // Rating cards fill equal-height carousel slots and pin the footer down
288
- // when free space exists; intrinsic height is unchanged when not stretched.
287
+ // Pin footer to the bottom when a parent (e.g. equal-height Carousel)
288
+ // assigns an explicit height. Do not flexGrow that unbounded-grows
289
+ // inside horizontal ScrollViews and blows cards to infinite height.
289
290
  ...(isRating
290
291
  ? {
291
- alignSelf: 'stretch',
292
292
  justifyContent: 'space-between' as const,
293
- flexGrow: 1,
294
293
  }
295
294
  : {}),
296
295
  }
@@ -12,6 +12,7 @@ import {
12
12
  ScrollView,
13
13
  Animated,
14
14
  Pressable,
15
+ StyleSheet,
15
16
  type ViewStyle,
16
17
  type StyleProp,
17
18
  type NativeSyntheticEvent,
@@ -210,6 +211,44 @@ export function Carousel({
210
211
  return containerWidth - containerPaddingH * 4
211
212
  }, [itemWidthProp, itemInset, containerWidth, containerPaddingH, isNumbered])
212
213
 
214
+ // Equal-height: measure each slide's intrinsic height, then lock every slot
215
+ // to the tallest. Do NOT use contentContainerStyle.alignItems:'stretch' or
216
+ // height:'100%' without a bounded cross-axis — in a horizontal ScrollView
217
+ // that resolves to an unbounded/infinite height.
218
+ const slideHeightsRef = useRef<Record<number, number>>({})
219
+ const [equalizedHeight, setEqualizedHeight] = useState<number | undefined>()
220
+ const equalizedHeightRef = useRef<number | undefined>(undefined)
221
+ equalizedHeightRef.current = equalizedHeight
222
+
223
+ useEffect(() => {
224
+ slideHeightsRef.current = {}
225
+ equalizedHeightRef.current = undefined
226
+ setEqualizedHeight(undefined)
227
+ }, [totalItems, effectiveItemWidth, equalHeight])
228
+
229
+ const handleSlideLayout = useCallback(
230
+ (index: number) => (event: LayoutChangeEvent) => {
231
+ if (!equalHeight) return
232
+ // Only measure while unconstrained. After lock, the slot height is forced
233
+ // and onLayout would just echo equalizedHeight.
234
+ if (equalizedHeightRef.current != null) return
235
+
236
+ const height = Math.ceil(event.nativeEvent.layout.height)
237
+ if (height <= 0) return
238
+
239
+ const previous = slideHeightsRef.current[index] ?? 0
240
+ if (height <= previous) return
241
+
242
+ slideHeightsRef.current[index] = height
243
+ const measured = items.map((_, i) => slideHeightsRef.current[i])
244
+ if (measured.some(value => value == null)) return
245
+
246
+ const next = Math.max(...(measured as number[]))
247
+ setEqualizedHeight(next)
248
+ },
249
+ [equalHeight, items],
250
+ )
251
+
213
252
  // Snap interval = item width + gap
214
253
  const snapInterval = effectiveItemWidth + gap
215
254
 
@@ -412,8 +451,9 @@ export function Carousel({
412
451
  const contentContainerStyle: ViewStyle = {
413
452
  paddingHorizontal: effectivePaddingH,
414
453
  gap,
415
- // Stretch so every slide matches the tallest (default). Opt out with equalHeight={false}.
416
- alignItems: equalHeight ? 'stretch' : 'flex-start',
454
+ // Always flex-start on the cross axis. Stretch + unbounded ScrollView
455
+ // height is what caused infinite-tall slides.
456
+ alignItems: 'flex-start',
417
457
  }
418
458
 
419
459
  return (
@@ -435,6 +475,9 @@ export function Carousel({
435
475
  onMomentumScrollEnd={finishProgrammaticScroll}
436
476
  >
437
477
  {items.map((child, index) => {
478
+ const hasEqualizedHeight =
479
+ equalHeight && equalizedHeight != null && equalizedHeight > 0
480
+
438
481
  // Strict slot box: width must be honored; never grow or shrink with
439
482
  // content, and clip anything that misbehaves (e.g. a child whose
440
483
  // inner flex layout would otherwise leak into the next slot on
@@ -445,7 +488,7 @@ export function Carousel({
445
488
  flexShrink: 0,
446
489
  flexBasis: effectiveItemWidth > 0 ? effectiveItemWidth : 'auto',
447
490
  overflow: 'hidden',
448
- ...(equalHeight ? { alignSelf: 'stretch' } : {}),
491
+ ...(hasEqualizedHeight ? { height: equalizedHeight } : {}),
449
492
  }
450
493
 
451
494
  // The cloned style forces the child's outer node to also honor the
@@ -454,10 +497,10 @@ export function Carousel({
454
497
  const childOverrideStyle: ViewStyle = {
455
498
  width: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
456
499
  maxWidth: effectiveItemWidth > 0 ? effectiveItemWidth : undefined,
457
- flexGrow: equalHeight ? 1 : 0,
500
+ flexGrow: 0,
458
501
  flexShrink: 0,
459
- ...(equalHeight
460
- ? { alignSelf: 'stretch', height: '100%' as const }
502
+ ...(hasEqualizedHeight
503
+ ? { height: '100%' as const, alignSelf: 'stretch' }
461
504
  : {}),
462
505
  }
463
506
 
@@ -474,7 +517,17 @@ export function Carousel({
474
517
 
475
518
  return (
476
519
  <View key={index} style={slotStyle}>
477
- {childWithModes}
520
+ {/*
521
+ Measure intrinsic height on an unconstrained inner wrapper.
522
+ After equalization the outer slot is height-locked and this
523
+ inner fills it — further onLayouts are ignored.
524
+ */}
525
+ <View
526
+ style={hasEqualizedHeight ? styles.equalizedFill : undefined}
527
+ onLayout={handleSlideLayout(index)}
528
+ >
529
+ {childWithModes}
530
+ </View>
478
531
  </View>
479
532
  )
480
533
  })}
@@ -667,6 +720,12 @@ function AnimatedDot({
667
720
  )
668
721
  }
669
722
 
723
+ const styles = StyleSheet.create({
724
+ equalizedFill: {
725
+ flex: 1,
726
+ },
727
+ })
728
+
670
729
  // ---------------------------------------------------------------------------
671
730
  // Attach sub-components
672
731
  // ---------------------------------------------------------------------------
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-07-27T20:32:46.426Z
7
+ * Generated: 2026-07-27T20:45:13.986Z
8
8
  */
9
9
 
10
10
  // Icon name to SVG data mapping