jfs-components 0.1.36 → 0.1.49

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 (56) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +16 -3
  3. package/lib/commonjs/components/AppBar/AppBar.js +118 -93
  4. package/lib/commonjs/components/CompareTable/CompareTable.js +98 -33
  5. package/lib/commonjs/components/FormField/FormField.js +9 -1
  6. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +4 -14
  7. package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +20 -4
  8. package/lib/commonjs/components/PageHero/PageHero.js +3 -0
  9. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  10. package/lib/commonjs/components/Section/Section.js +3 -1
  11. package/lib/commonjs/components/ValueBackMetric/ValueBackMetric.js +117 -89
  12. package/lib/commonjs/components/index.js +7 -1
  13. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -42204
  14. package/lib/commonjs/design-tokens/figma-modes.generated.js +8 -7
  15. package/lib/commonjs/icons/registry.js +1 -1
  16. package/lib/module/components/Accordion/Accordion.js +16 -3
  17. package/lib/module/components/AppBar/AppBar.js +116 -93
  18. package/lib/module/components/CompareTable/CompareTable.js +99 -34
  19. package/lib/module/components/FormField/FormField.js +9 -1
  20. package/lib/module/components/FullscreenModal/FullscreenModal.js +4 -14
  21. package/lib/module/components/HelloJioInput/HelloJioInput.js +20 -4
  22. package/lib/module/components/PageHero/PageHero.js +3 -0
  23. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +50 -30
  24. package/lib/module/components/Section/Section.js +3 -1
  25. package/lib/module/components/ValueBackMetric/ValueBackMetric.js +120 -93
  26. package/lib/module/components/index.js +1 -1
  27. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -42204
  28. package/lib/module/design-tokens/figma-modes.generated.js +8 -7
  29. package/lib/module/icons/registry.js +1 -1
  30. package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -1
  31. package/lib/typescript/src/components/AppBar/AppBar.d.ts +46 -14
  32. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +29 -12
  33. package/lib/typescript/src/components/HelloJioInput/HelloJioInput.d.ts +6 -4
  34. package/lib/typescript/src/components/PageHero/PageHero.d.ts +1 -0
  35. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +8 -1
  36. package/lib/typescript/src/components/Section/Section.d.ts +2 -1
  37. package/lib/typescript/src/components/ValueBackMetric/ValueBackMetric.d.ts +27 -16
  38. package/lib/typescript/src/components/index.d.ts +1 -1
  39. package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +2 -1
  40. package/lib/typescript/src/icons/registry.d.ts +1 -1
  41. package/package.json +1 -1
  42. package/src/components/Accordion/Accordion.tsx +61 -33
  43. package/src/components/AppBar/AppBar.tsx +154 -124
  44. package/src/components/CompareTable/CompareTable.tsx +138 -52
  45. package/src/components/FormField/FormField.tsx +7 -0
  46. package/src/components/FullscreenModal/FullscreenModal.tsx +5 -14
  47. package/src/components/HelloJioInput/HelloJioInput.tsx +31 -12
  48. package/src/components/PageHero/PageHero.tsx +5 -0
  49. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +63 -30
  50. package/src/components/Section/Section.tsx +121 -156
  51. package/src/components/ValueBackMetric/ValueBackMetric.tsx +178 -104
  52. package/src/components/index.ts +6 -1
  53. package/src/design-tokens/Coin Variables-variables-full.json +1 -42204
  54. package/src/design-tokens/figma-modes.generated.ts +8 -7
  55. package/src/icons/registry.ts +1 -1
  56. package/D2C.md +0 -113
@@ -1,5 +1,13 @@
1
1
  import React, { useState, useMemo, useRef, useCallback } from 'react'
2
- import { View, Text, Pressable, Platform, type StyleProp, type ViewStyle, type PressableStateCallbackType } from 'react-native'
2
+ import {
3
+ View,
4
+ Text,
5
+ Pressable,
6
+ Platform,
7
+ type StyleProp,
8
+ type ViewStyle,
9
+ type PressableStateCallbackType,
10
+ } from 'react-native'
3
11
  import Animated, {
4
12
  Easing,
5
13
  FadeInUp,
@@ -94,16 +102,16 @@ const SLOT_GRID_HEIGHT_DURATION_MS = 280
94
102
  const SLOT_GRID_EASING = Easing.out(Easing.cubic)
95
103
 
96
104
  type SlotGridProps = {
97
- items: React.ReactNode[];
98
- gap: number;
99
- maxColumns?: number;
105
+ items: React.ReactNode[]
106
+ gap: number
107
+ maxColumns?: number
100
108
  /**
101
109
  * If set, cells whose index is `>= animateExtrasFromIndex` are wrapped in
102
110
  * `Animated.View` with staggered FadeInUp/FadeOutUp builders so that they
103
111
  * fade in (and reverse-fade out) when they mount or unmount. Cells below
104
112
  * this threshold render as plain `<View>` and are unaffected.
105
113
  */
106
- animateExtrasFromIndex?: number;
114
+ animateExtrasFromIndex?: number
107
115
  /**
108
116
  * If true, the rows container animates its height via an explicit
109
117
  * `useSharedValue` + `withTiming` (ease-out cubic, no overshoot) driven by
@@ -111,7 +119,7 @@ type SlotGridProps = {
111
119
  * to clip mid-animation. Cells inside always render at their natural size
112
120
  * — they are *never* resized during the transition. Default false.
113
121
  */
114
- animateContainerLayout?: boolean;
122
+ animateContainerLayout?: boolean
115
123
  }
116
124
 
117
125
  const slotGridRowFlowStyle: ViewStyle = {
@@ -150,12 +158,10 @@ const SlotGrid = React.memo(function SlotGrid({
150
158
  for (const w of widths.values()) {
151
159
  if (w > newMax) newMax = w
152
160
  }
153
- setFirstRowMaxWidth((prev) =>
154
- prev !== null && Math.abs(prev - newMax) < 0.5 ? prev : newMax
155
- )
161
+ setFirstRowMaxWidth((prev) => (prev !== null && Math.abs(prev - newMax) < 0.5 ? prev : newMax))
156
162
  }
157
163
  },
158
- [firstRowSize]
164
+ [firstRowSize],
159
165
  )
160
166
 
161
167
  const cellWidth = firstRowMaxWidth
@@ -175,7 +181,7 @@ const SlotGrid = React.memo(function SlotGrid({
175
181
  // animated-height clip springs to the new total).
176
182
  const cellStyle = useMemo<ViewStyle | undefined>(
177
183
  () => (cellWidth !== null ? { width: cellWidth } : undefined),
178
- [cellWidth]
184
+ [cellWidth],
179
185
  )
180
186
 
181
187
  // `space-between` distributes any leftover row space as inter-cell gap, so
@@ -188,15 +194,11 @@ const SlotGrid = React.memo(function SlotGrid({
188
194
  // Resolve the threshold once. When undefined we treat it as
189
195
  // Number.POSITIVE_INFINITY so the per-cell branch always picks the plain
190
196
  // `<View>` path.
191
- const extrasThreshold = animationsEnabled
192
- ? (animateExtrasFromIndex as number)
193
- : Number.POSITIVE_INFINITY
197
+ const extrasThreshold = animationsEnabled ? (animateExtrasFromIndex as number) : Number.POSITIVE_INFINITY
194
198
  // Total count of "extra" cells currently rendered. Used to compute the
195
199
  // reverse-stagger delay for the exiting animation so that the last cell
196
200
  // leaves first.
197
- const extrasCount = animationsEnabled
198
- ? Math.max(0, totalItems - extrasThreshold)
199
- : 0
201
+ const extrasCount = animationsEnabled ? Math.max(0, totalItems - extrasThreshold) : 0
200
202
 
201
203
  const useAnimatedContainer = animateContainerLayout === true
202
204
 
@@ -236,12 +238,10 @@ const SlotGrid = React.memo(function SlotGrid({
236
238
  reduceMotion: ReduceMotion.System,
237
239
  })
238
240
  },
239
- [animatedHeight]
241
+ [animatedHeight],
240
242
  )
241
243
  const animatedHeightStyle = useAnimatedStyle(() =>
242
- animatedHeight.value < 0
243
- ? {}
244
- : { height: animatedHeight.value, overflow: 'hidden' as const }
244
+ animatedHeight.value < 0 ? {} : { height: animatedHeight.value, overflow: 'hidden' as const },
245
245
  )
246
246
 
247
247
  const rowsChildren = (
@@ -258,26 +258,14 @@ const SlotGrid = React.memo(function SlotGrid({
258
258
  const onLayoutHandler =
259
259
  firstRowMaxWidth === null && itemIndex < firstRowSize
260
260
  ? (e: { nativeEvent: { layout: { width: number } } }) =>
261
- handleFirstRowItemLayout(
262
- itemIndex,
263
- e.nativeEvent.layout.width
264
- )
261
+ handleFirstRowItemLayout(itemIndex, e.nativeEvent.layout.width)
265
262
  : undefined
266
263
 
267
264
  if (itemIndex >= extrasThreshold) {
268
265
  const extraOrdinal = itemIndex - extrasThreshold
269
- const enterStaggerSteps = Math.min(
270
- extraOrdinal,
271
- SLOT_GRID_STAGGER_CAP
272
- )
273
- const reverseOrdinal = Math.max(
274
- 0,
275
- extrasCount - 1 - extraOrdinal
276
- )
277
- const exitStaggerSteps = Math.min(
278
- reverseOrdinal,
279
- SLOT_GRID_STAGGER_CAP
280
- )
266
+ const enterStaggerSteps = Math.min(extraOrdinal, SLOT_GRID_STAGGER_CAP)
267
+ const reverseOrdinal = Math.max(0, extrasCount - 1 - extraOrdinal)
268
+ const exitStaggerSteps = Math.min(reverseOrdinal, SLOT_GRID_STAGGER_CAP)
281
269
  const entering = FadeInUp.duration(SLOT_GRID_ENTER_DURATION_MS)
282
270
  .easing(SLOT_GRID_EASING)
283
271
  .delay(enterStaggerSteps * SLOT_GRID_ENTER_STAGGER_MS)
@@ -300,20 +288,14 @@ const SlotGrid = React.memo(function SlotGrid({
300
288
  }
301
289
 
302
290
  return (
303
- <View
304
- key={itemIndex}
305
- {...(onLayoutHandler ? { onLayout: onLayoutHandler } : null)}
306
- style={cellStyle}
307
- >
291
+ <View key={itemIndex} {...(onLayoutHandler ? { onLayout: onLayoutHandler } : null)} style={cellStyle}>
308
292
  {child}
309
293
  </View>
310
294
  )
311
295
  })}
312
296
  {cellWidth !== null &&
313
297
  spacersNeeded > 0 &&
314
- Array.from({ length: spacersNeeded }, (_, i) => (
315
- <View key={`spacer-${i}`} style={cellStyle} />
316
- ))}
298
+ Array.from({ length: spacersNeeded }, (_, i) => <View key={`spacer-${i}`} style={cellStyle} />)}
317
299
  </View>
318
300
  )
319
301
  })}
@@ -351,28 +333,28 @@ function slotGridPropsAreEqual(prev: SlotGridProps, next: SlotGridProps) {
351
333
  }
352
334
 
353
335
  type SectionProps = {
354
- title?: string;
355
- supportText?: string;
356
- showSupportText?: boolean;
357
- slot?: React.ReactNode;
336
+ title?: string
337
+ supportText?: string
338
+ showSupportText?: boolean
339
+ slot?: React.ReactNode
358
340
  /** Layout direction of the slot's children. Defaults to 'row' (horizontal). */
359
- slotDirection?: 'row' | 'column';
360
- modes?: Modes;
361
- onPress?: () => void;
362
- style?: StyleProp<ViewStyle>;
363
- accessibilityLabel?: string;
364
- accessibilityHint?: string;
341
+ slotDirection?: 'row' | 'column'
342
+ modes?: Modes
343
+ onPress?: () => void
344
+ style?: StyleProp<ViewStyle>
345
+ accessibilityLabel?: string
346
+ accessibilityHint?: string
365
347
  /**
366
348
  * Web-specific accessibility props (only used on web platform)
367
349
  */
368
- webAccessibilityProps?: WebAccessibilityProps;
350
+ webAccessibilityProps?: WebAccessibilityProps
369
351
  /**
370
352
  * When `true`, disables text truncation: the trailing ellipsis (`…`) is never
371
353
  * shown and any `numberOfLines` clamp is ignored, so the text wraps to as many
372
354
  * lines as it needs even when horizontal space is tight.
373
355
  */
374
- disableTruncation?: boolean;
375
- } & React.ComponentProps<typeof View>;
356
+ disableTruncation?: boolean
357
+ } & React.ComponentProps<typeof View>
376
358
 
377
359
  /**
378
360
  * Section component that mirrors the Figma "Section" component.
@@ -399,18 +381,18 @@ type SectionProps = {
399
381
  * @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
400
382
  */
401
383
  interface SectionTokens {
402
- containerStyle: ViewStyle;
403
- headerStyle: ViewStyle;
404
- titleStyle: ViewStyle;
405
- supportTextStyle: ViewStyle;
406
- sectionGap: number;
407
- slotGap: number;
384
+ containerStyle: ViewStyle
385
+ headerStyle: ViewStyle
386
+ titleStyle: ViewStyle
387
+ supportTextStyle: ViewStyle
388
+ sectionGap: number
389
+ slotGap: number
408
390
  }
409
391
 
410
392
  function resolveSectionTokens(modes: Modes): SectionTokens {
411
393
  const backgroundColor = getVariableByName('section/background/color', modes)
412
- const sectionGap = (getVariableByName('section/gap', modes)) as number
413
- const slotGap = (getVariableByName('slot/gap', modes)) as number
394
+ const sectionGap = getVariableByName('section/gap', modes) as number
395
+ const slotGap = getVariableByName('slot/gap', modes) as number
414
396
  const paddingHorizontal = getVariableByName('section/padding/horizontal', modes)
415
397
  const paddingVertical = getVariableByName('section/padding/vertical', modes)
416
398
  const radius = getVariableByName('section/radius', modes)
@@ -424,10 +406,7 @@ function resolveSectionTokens(modes: Modes): SectionTokens {
424
406
  const titleLineHeight = getVariableByName('section/title/lineHeight', modes)
425
407
  const titleFontFamily = getVariableByName('section/title/fontFamily', modes)
426
408
  const titleFontWeightRaw = getVariableByName('section/title/fontWeight', modes)
427
- const titleFontWeight =
428
- typeof titleFontWeightRaw === 'number'
429
- ? titleFontWeightRaw.toString()
430
- : titleFontWeightRaw
409
+ const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw
431
410
 
432
411
  const supportTextColor = getVariableByName('section/supportText/color', modes)
433
412
  const supportTextFontSize = getVariableByName('section/supportText/fontSize', modes)
@@ -435,9 +414,7 @@ function resolveSectionTokens(modes: Modes): SectionTokens {
435
414
  const supportTextFontFamily = getVariableByName('section/supportText/fontFamily', modes)
436
415
  const supportTextFontWeightRaw = getVariableByName('section/supportText/fontWeight', modes)
437
416
  const supportTextFontWeight =
438
- typeof supportTextFontWeightRaw === 'number'
439
- ? supportTextFontWeightRaw.toString()
440
- : supportTextFontWeightRaw
417
+ typeof supportTextFontWeightRaw === 'number' ? supportTextFontWeightRaw.toString() : supportTextFontWeightRaw
441
418
 
442
419
  return {
443
420
  containerStyle: {
@@ -540,9 +517,7 @@ function Section({
540
517
  >
541
518
  {title}
542
519
  </Text>
543
- {onPress && (
544
- <NavArrow direction="Forward" modes={modes} />
545
- )}
520
+ {onPress && <NavArrow direction="Forward" modes={modes} />}
546
521
  </View>
547
522
  {showSupportText && (
548
523
  <Text
@@ -592,13 +567,10 @@ function Section({
592
567
  isHeaderHovered ? headerHoverStyle : null,
593
568
  isHeaderFocused ? headerFocusStyle : null,
594
569
  ],
595
- [tokens.headerStyle, isHeaderHovered, isHeaderFocused]
570
+ [tokens.headerStyle, isHeaderHovered, isHeaderFocused],
596
571
  )
597
572
 
598
- const containerStyleArray = useMemo(
599
- () => [tokens.containerStyle, style],
600
- [tokens.containerStyle, style]
601
- )
573
+ const containerStyleArray = useMemo(() => [tokens.containerStyle, style], [tokens.containerStyle, style])
602
574
 
603
575
  return (
604
576
  <View
@@ -627,21 +599,27 @@ function Section({
627
599
  {headerContent}
628
600
  </Pressable>
629
601
  ) : (
630
- <View style={tokens.headerStyle}>
631
- {headerContent}
632
- </View>
602
+ <View style={tokens.headerStyle}>{headerContent}</View>
603
+ )}
604
+ {slot && (
605
+ <SectionSlot
606
+ slot={slot}
607
+ modes={modes}
608
+ direction={slotDirection}
609
+ rowGap={tokens.sectionGap}
610
+ columnGap={tokens.slotGap}
611
+ />
633
612
  )}
634
- {slot && <SectionSlot slot={slot} modes={modes} direction={slotDirection} rowGap={tokens.sectionGap} columnGap={tokens.slotGap} />}
635
613
  </View>
636
614
  )
637
615
  }
638
616
 
639
617
  type SectionSlotProps = {
640
- slot: React.ReactNode;
641
- modes: Modes;
642
- direction: 'row' | 'column';
643
- rowGap: number;
644
- columnGap: number;
618
+ slot: React.ReactNode
619
+ modes: Modes
620
+ direction: 'row' | 'column'
621
+ rowGap: number
622
+ columnGap: number
645
623
  }
646
624
 
647
625
  /**
@@ -651,15 +629,9 @@ type SectionSlotProps = {
651
629
  * without re-walking the slot tree via `cloneChildrenWithModes`.
652
630
  */
653
631
  function SectionSlot({ slot, modes, direction, rowGap, columnGap }: SectionSlotProps) {
654
- const processed = useMemo(
655
- () => cloneChildrenWithModes(flattenChildren(slot), modes),
656
- [slot, modes]
657
- )
632
+ const processed = useMemo(() => cloneChildrenWithModes(flattenChildren(slot), modes), [slot, modes])
658
633
 
659
- const columnContainerStyle = useMemo<ViewStyle>(
660
- () => ({ flexDirection: 'column', gap: columnGap }),
661
- [columnGap]
662
- )
634
+ const columnContainerStyle = useMemo<ViewStyle>(() => ({ flexDirection: 'column', gap: columnGap }), [columnGap])
663
635
 
664
636
  if (direction === 'row') {
665
637
  return <SlotGrid items={processed} gap={rowGap} />
@@ -668,21 +640,22 @@ function SectionSlot({ slot, modes, direction, rowGap, columnGap }: SectionSlotP
668
640
  }
669
641
 
670
642
  type BentoToggleRenderState = {
671
- expanded: boolean;
672
- toggle: () => void;
643
+ expanded: boolean
644
+ toggle: () => void
673
645
  }
674
646
 
675
647
  type SectionBentoProps = {
676
- navSlot?: React.ReactNode;
677
- upiSlot?: React.ReactNode;
678
- modes?: Modes;
679
- style?: StyleProp<ViewStyle>;
680
- accessibilityLabel?: string;
681
- accessibilityHint?: string;
648
+ navSlot?: React.ReactNode
649
+ upiSlot?: React.ReactNode
650
+ upi?: boolean
651
+ modes?: Modes
652
+ style?: StyleProp<ViewStyle>
653
+ accessibilityLabel?: string
654
+ accessibilityHint?: string
682
655
  /**
683
656
  * Web-specific accessibility props (only used on web platform)
684
657
  */
685
- webAccessibilityProps?: WebAccessibilityProps;
658
+ webAccessibilityProps?: WebAccessibilityProps
686
659
  /**
687
660
  * Total cell count visible when collapsed (real items + the toggle cell).
688
661
  * Defaults to {@link SLOT_GRID_MAX_COLUMNS} (4) so the collapsed state fills
@@ -690,37 +663,37 @@ type SectionBentoProps = {
690
663
  * disabled (no toggle injected, no animation wrappers — identical to the
691
664
  * legacy behavior for back-compat).
692
665
  */
693
- collapsedCount?: number;
666
+ collapsedCount?: number
694
667
  /**
695
668
  * Uncontrolled initial expanded state. Ignored when `expanded` is provided.
696
669
  * Defaults to `false`.
697
670
  */
698
- defaultExpanded?: boolean;
671
+ defaultExpanded?: boolean
699
672
  /**
700
673
  * Controlled expanded state. When provided, `onExpandedChange` should also
701
674
  * be provided so the component can request changes.
702
675
  */
703
- expanded?: boolean;
676
+ expanded?: boolean
704
677
  /**
705
678
  * Called when the user taps the toggle. Required in controlled mode; ignored
706
679
  * in uncontrolled mode unless you want to observe the change.
707
680
  */
708
- onExpandedChange?: (next: boolean) => void;
681
+ onExpandedChange?: (next: boolean) => void
709
682
  /** Label shown on the toggle cell when collapsed. Default `'More'`. */
710
- toggleMoreLabel?: string;
683
+ toggleMoreLabel?: string
711
684
  /** Label shown on the toggle cell when expanded. Default `'Less'`. */
712
- toggleLessLabel?: string;
685
+ toggleLessLabel?: string
713
686
  /** Icon name shown on the toggle when collapsed. Default `'ic_chevron_down'`. */
714
- toggleMoreIcon?: string;
687
+ toggleMoreIcon?: string
715
688
  /** Icon name shown on the toggle when expanded. Default `'ic_chevron_up'`. */
716
- toggleLessIcon?: string;
689
+ toggleLessIcon?: string
717
690
  /**
718
691
  * Escape hatch: render a custom toggle cell instead of the default ListItem.
719
692
  * The provided node is rendered in the toggle's grid slot. Wire `toggle()` to
720
693
  * any tap interaction inside it. Height + per-cell animations still apply.
721
694
  */
722
- renderToggle?: (state: BentoToggleRenderState) => React.ReactNode;
723
- } & React.ComponentProps<typeof View>;
695
+ renderToggle?: (state: BentoToggleRenderState) => React.ReactNode
696
+ } & React.ComponentProps<typeof View>
724
697
 
725
698
  /**
726
699
  * Section.Bento component that mirrors the Figma "Section.Bento" component.
@@ -739,6 +712,7 @@ type SectionBentoProps = {
739
712
  * @param {Object} props
740
713
  * @param {React.ReactNode} [props.navSlot] - Optional custom slot for navigation items (Figma Slot "Nav wrap")
741
714
  * @param {React.ReactNode} [props.upiSlot] - Optional custom slot for UPI handles (Figma Slot "UPI wrap")
715
+ * @param {boolean} [props.upi=true] - Toggles the UPI wrap (Figma boolean property "upi")
742
716
  * @param {Object} [props.modes={}] - Modes object passed to `getVariableByName` for all design tokens
743
717
  * @param {Object} [props.style] - Optional container style overrides
744
718
  * @param {string} [props.accessibilityLabel] - Accessibility label for the section
@@ -749,6 +723,7 @@ const sectionBentoUpiRowStyle: ViewStyle = { flexDirection: 'row', gap: 8 }
749
723
  function SectionBento({
750
724
  navSlot,
751
725
  upiSlot,
726
+ upi = true,
752
727
  modes = EMPTY_MODES,
753
728
  style,
754
729
  // Same rationale as Section: accepted on the type but unused internally.
@@ -779,17 +754,17 @@ function SectionBento({
779
754
  borderRadius: radius,
780
755
  gap,
781
756
  }),
782
- [backgroundColor, paddingHorizontal, paddingVertical, radius, gap]
757
+ [backgroundColor, paddingHorizontal, paddingVertical, radius, gap],
783
758
  )
784
759
 
785
760
  const processedNavSlot = useMemo(
786
761
  () => (navSlot ? cloneChildrenWithModes(flattenChildren(navSlot), modes) : null),
787
- [navSlot, modes]
762
+ [navSlot, modes],
788
763
  )
789
764
 
790
765
  const processedUpiSlot = useMemo(
791
766
  () => (upiSlot ? cloneChildrenWithModes(flattenChildren(upiSlot), modes) : null),
792
- [upiSlot, modes]
767
+ [upiSlot, modes],
793
768
  )
794
769
 
795
770
  // `canExpand` is true iff there are strictly more real items than fit into
@@ -823,24 +798,22 @@ function SectionBento({
823
798
  return allRealItems
824
799
  }
825
800
  // Leave the last collapsed slot for the toggle cell.
826
- const visibleRealItems = expanded
827
- ? allRealItems
828
- : allRealItems.slice(0, collapsedCount - 1)
829
-
830
- const toggleNode = renderToggle
831
- ? renderToggle({ expanded, toggle })
832
- : (
833
- <DefaultBentoToggle
834
- expanded={expanded}
835
- onPress={toggle}
836
- modes={modes}
837
- moreLabel={toggleMoreLabel}
838
- lessLabel={toggleLessLabel}
839
- moreIcon={toggleMoreIcon}
840
- lessIcon={toggleLessIcon}
841
- extraCount={allRealItems.length - (collapsedCount - 1)}
842
- />
843
- )
801
+ const visibleRealItems = expanded ? allRealItems : allRealItems.slice(0, collapsedCount - 1)
802
+
803
+ const toggleNode = renderToggle ? (
804
+ renderToggle({ expanded, toggle })
805
+ ) : (
806
+ <DefaultBentoToggle
807
+ expanded={expanded}
808
+ onPress={toggle}
809
+ modes={modes}
810
+ moreLabel={toggleMoreLabel}
811
+ lessLabel={toggleLessLabel}
812
+ moreIcon={toggleMoreIcon}
813
+ lessIcon={toggleLessIcon}
814
+ extraCount={allRealItems.length - (collapsedCount - 1)}
815
+ />
816
+ )
844
817
 
845
818
  return [...visibleRealItems, toggleNode]
846
819
  }, [
@@ -877,11 +850,7 @@ function SectionBento({
877
850
  : null)}
878
851
  />
879
852
  )}
880
- {processedUpiSlot && (
881
- <View style={sectionBentoUpiRowStyle}>
882
- {processedUpiSlot}
883
- </View>
884
- )}
853
+ {upi && processedUpiSlot && <View style={sectionBentoUpiRowStyle}>{processedUpiSlot}</View>}
885
854
  </View>
886
855
  )
887
856
  }
@@ -897,15 +866,15 @@ function SectionBento({
897
866
  // shared-value-driven rotation would lose its anchor across toggles.
898
867
  // ---------------------------------------------------------------------------
899
868
  type DefaultBentoToggleProps = {
900
- expanded: boolean;
901
- onPress: () => void;
902
- modes: Modes;
903
- moreLabel: string;
904
- lessLabel: string;
905
- moreIcon: string;
906
- lessIcon: string;
869
+ expanded: boolean
870
+ onPress: () => void
871
+ modes: Modes
872
+ moreLabel: string
873
+ lessLabel: string
874
+ moreIcon: string
875
+ lessIcon: string
907
876
  /** How many additional actions become visible when expanding. Used in the a11y hint. */
908
- extraCount: number;
877
+ extraCount: number
909
878
  }
910
879
 
911
880
  function DefaultBentoToggle({
@@ -921,10 +890,7 @@ function DefaultBentoToggle({
921
890
  const label = expanded ? lessLabel : moreLabel
922
891
  const iconName = expanded ? lessIcon : moreIcon
923
892
  const accessibilityState = useMemo(() => ({ expanded }), [expanded])
924
- const webAccessibilityProps = useMemo(
925
- () => ({ ariaExpanded: expanded }),
926
- [expanded]
927
- )
893
+ const webAccessibilityProps = useMemo(() => ({ ariaExpanded: expanded }), [expanded])
928
894
  const accessibilityHint = expanded
929
895
  ? `Hides ${extraCount} additional ${extraCount === 1 ? 'action' : 'actions'}`
930
896
  : `Shows ${extraCount} additional ${extraCount === 1 ? 'action' : 'actions'}`
@@ -948,4 +914,3 @@ function DefaultBentoToggle({
948
914
  Section.Bento = SectionBento
949
915
 
950
916
  export default Section
951
-