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.
- package/CHANGELOG.md +5 -0
- package/lib/commonjs/components/CardCTA/CardCTA.js +4 -5
- package/lib/commonjs/components/Carousel/Carousel.js +50 -9
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CardCTA/CardCTA.js +4 -5
- package/lib/module/components/Carousel/Carousel.js +51 -10
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/CardCTA/CardCTA.tsx +3 -4
- package/src/components/Carousel/Carousel.tsx +66 -7
- package/src/icons/registry.ts +1 -1
|
@@ -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:
|
|
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
|
@@ -284,13 +284,12 @@ function CardCTA({
|
|
|
284
284
|
borderColor,
|
|
285
285
|
flexDirection: isRating || isSip ? 'column' : 'row',
|
|
286
286
|
overflow: 'visible',
|
|
287
|
-
//
|
|
288
|
-
//
|
|
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
|
-
//
|
|
416
|
-
|
|
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
|
-
...(
|
|
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:
|
|
500
|
+
flexGrow: 0,
|
|
458
501
|
flexShrink: 0,
|
|
459
|
-
...(
|
|
460
|
-
? {
|
|
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
|
-
{
|
|
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
|
// ---------------------------------------------------------------------------
|
package/src/icons/registry.ts
CHANGED