jfs-components 0.1.48 → 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.
- package/CHANGELOG.md +1 -1
- package/lib/commonjs/components/Section/Section.js +3 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +8 -7
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Section/Section.js +3 -1
- package/lib/module/design-tokens/figma-modes.generated.js +8 -7
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Section/Section.d.ts +2 -1
- package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +2 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Section/Section.tsx +121 -156
- package/src/design-tokens/figma-modes.generated.ts +8 -7
- package/src/icons/registry.ts +1 -1
- package/D2C.md +0 -113
|
@@ -36,6 +36,7 @@ type BentoToggleRenderState = {
|
|
|
36
36
|
type SectionBentoProps = {
|
|
37
37
|
navSlot?: React.ReactNode;
|
|
38
38
|
upiSlot?: React.ReactNode;
|
|
39
|
+
upi?: boolean;
|
|
39
40
|
modes?: Modes;
|
|
40
41
|
style?: StyleProp<ViewStyle>;
|
|
41
42
|
accessibilityLabel?: string;
|
|
@@ -82,6 +83,6 @@ type SectionBentoProps = {
|
|
|
82
83
|
*/
|
|
83
84
|
renderToggle?: (state: BentoToggleRenderState) => React.ReactNode;
|
|
84
85
|
} & React.ComponentProps<typeof View>;
|
|
85
|
-
declare function SectionBento({ navSlot, upiSlot, modes, style, accessibilityLabel: _accessibilityLabel, accessibilityHint, collapsedCount, defaultExpanded, expanded: controlledExpanded, onExpandedChange, toggleMoreLabel, toggleLessLabel, toggleMoreIcon, toggleLessIcon, renderToggle, ...rest }: SectionBentoProps): import("react/jsx-runtime").JSX.Element;
|
|
86
|
+
declare function SectionBento({ navSlot, upiSlot, upi, modes, style, accessibilityLabel: _accessibilityLabel, accessibilityHint, collapsedCount, defaultExpanded, expanded: controlledExpanded, onExpandedChange, toggleMoreLabel, toggleLessLabel, toggleMoreIcon, toggleLessIcon, renderToggle, ...rest }: SectionBentoProps): import("react/jsx-runtime").JSX.Element;
|
|
86
87
|
export default Section;
|
|
87
88
|
//# sourceMappingURL=Section.d.ts.map
|
|
@@ -46,7 +46,7 @@ export declare const FIGMA_MODES: {
|
|
|
46
46
|
readonly "Calendar Glyph / Output": readonly ["Default"];
|
|
47
47
|
readonly "Calendar Glyph State": readonly ["Idle", "notSaved", "saved"];
|
|
48
48
|
readonly "Card / Output": readonly ["Default"];
|
|
49
|
-
readonly "Card
|
|
49
|
+
readonly "Card Container": readonly ["Default", "Plain"];
|
|
50
50
|
readonly "Card Feedback / Output": readonly ["Default"];
|
|
51
51
|
readonly "Card Tab / output": readonly ["Default"];
|
|
52
52
|
readonly "Card Tab State": readonly ["Default", "Active"];
|
|
@@ -231,6 +231,7 @@ export declare const FIGMA_MODES: {
|
|
|
231
231
|
readonly "Slot gap": readonly ["S", "M", "L", "XL", "XS"];
|
|
232
232
|
readonly "special button": readonly ["Default"];
|
|
233
233
|
readonly "Spinner / Output": readonly ["Default"];
|
|
234
|
+
readonly "Stack Context": readonly ["Root", "Nested"];
|
|
234
235
|
readonly "statGroup / Output": readonly ["Default"];
|
|
235
236
|
readonly "StatItem / Output": readonly ["Default"];
|
|
236
237
|
readonly "StatItem Label Position ": readonly ["Top", "Bottom"];
|
|
@@ -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-
|
|
7
|
+
* Generated: 2026-07-25T08:19:21.865Z
|
|
8
8
|
*/
|
|
9
9
|
export declare const iconRegistry: Record<string, {
|
|
10
10
|
path: string;
|
package/package.json
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import React, { useState, useMemo, useRef, useCallback } from 'react'
|
|
2
|
-
import {
|
|
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
|
-
|
|
271
|
-
|
|
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 =
|
|
413
|
-
const slotGap =
|
|
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
|
-
|
|
632
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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
|
-
|
|
@@ -63,7 +63,7 @@ export const FIGMA_MODES = {
|
|
|
63
63
|
"Calendar Glyph / Output": ["Default"],
|
|
64
64
|
"Calendar Glyph State": ["Idle", "notSaved", "saved"],
|
|
65
65
|
"Card / Output": ["Default"],
|
|
66
|
-
"Card
|
|
66
|
+
"Card Container": ["Default", "Plain"],
|
|
67
67
|
"Card Feedback / Output": ["Default"],
|
|
68
68
|
"Card Tab / output": ["Default"],
|
|
69
69
|
"Card Tab State": ["Default", "Active"],
|
|
@@ -248,6 +248,7 @@ export const FIGMA_MODES = {
|
|
|
248
248
|
"Slot gap": ["S", "M", "L", "XL", "XS"],
|
|
249
249
|
"special button": ["Default"],
|
|
250
250
|
"Spinner / Output": ["Default"],
|
|
251
|
+
"Stack Context": ["Root", "Nested"],
|
|
251
252
|
"statGroup / Output": ["Default"],
|
|
252
253
|
"StatItem / Output": ["Default"],
|
|
253
254
|
"StatItem Label Position ": ["Top", "Bottom"],
|
|
@@ -304,7 +305,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
304
305
|
"Accordion": ["Accordion States"],
|
|
305
306
|
"AccordionCheckbox": ["Color Mode", "context 10", "Profile Card Appearance"],
|
|
306
307
|
"AccountCard": ["AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "Emphasis", "Page type"],
|
|
307
|
-
"ActionFooter": ["Color Mode", "context5"],
|
|
308
|
+
"ActionFooter": ["Color Mode", "Context", "context5", "Slot gap"],
|
|
308
309
|
"ActionTile": ["AppearanceBrand", "AppearanceSystem", "Color Mode", "Context", "Emphasis", "Icon Capsule Size", "Page type", "Semantic Intent"],
|
|
309
310
|
"Additem": ["AppearanceBrand", "AppearanceSystem", "Color Mode", "Context", "Emphasis", "Icon Capsule Size", "Page type", "Semantic Intent"],
|
|
310
311
|
"AllocationComparisonChart": ["Appearance / DataViz", "Color Mode", "Emphasis / DataViz"],
|
|
@@ -322,7 +323,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
322
323
|
"BrandChip": ["Avatar Size", "Badge Size", "Color Mode", "context 10", "Context4", "Profile Card Appearance"],
|
|
323
324
|
"BubbleChart": ["Appearance / DataViz", "Color Mode", "Context", "context 10", "Emphasis / DataViz", "Profile Card Appearance", "Text Appearance"],
|
|
324
325
|
"Button": ["AppearanceBrand", "AppearanceSystem", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "Emphasis", "Page type", "Profile Card Appearance", "Semantic Intent"],
|
|
325
|
-
"Card": ["AppearanceBrand", "AppearanceSystem", "
|
|
326
|
+
"Card": ["AppearanceBrand", "AppearanceSystem", "Card Container", "Color Mode", "Gap", "Page type", "Semantic Intent"],
|
|
326
327
|
"CardAdvisory": ["AppearanceBrand", "AppearanceSystem", "Button / Size", "Button / State", "circularProgressBar Size", "Color Mode", "Context", "context 10", "Emphasis", "Nudge padding", "Page type", "Profile Card Appearance", "Semantic Intent", "Slot gap"],
|
|
327
328
|
"CardBankAccount": ["AppearanceBrand", "AppearanceSystem", "Avatar Size", "Badge Size", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "Context4", "context5", "Emphasis", "List Item Style", "Page type", "Profile Card Appearance", "Semantic Intent", "Text Appearance", "Text Sizes", "Weight"],
|
|
328
329
|
"CardCTA": ["AppearanceBrand", "AppearanceSystem", "Avatar Size", "Badge Size", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "context 8", "Context4", "Emphasis", "Icon Capsule Size", "MediaBlock", "Page type", "Profile Card Appearance", "Semantic Intent"],
|
|
@@ -341,7 +342,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
341
342
|
"CircularProgressBar": ["AppearanceBrand", "AppearanceSystem", "circularProgressBar Size", "Color Mode", "Emphasis", "Semantic Intent"],
|
|
342
343
|
"CircularRating": ["AppearanceBrand", "AppearanceSystem", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "Emphasis", "Nudge padding", "Page type", "Profile Card Appearance", "Semantic Intent", "Slot gap"],
|
|
343
344
|
"ClusterBubble": ["Appearance / DataViz", "Color Mode", "Context", "context 10", "Emphasis / DataViz", "Profile Card Appearance", "Text Appearance"],
|
|
344
|
-
"CompareTable": ["Accordion States", "AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "Emphasis", "Page type", "Radius"
|
|
345
|
+
"CompareTable": ["Accordion States", "AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "Emphasis", "Page type", "Radius"],
|
|
345
346
|
"ComparisonBar": ["AppearanceBrand", "AppearanceSystem", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "Emphasis", "Icon Capsule Size", "Page type", "Profile Card Appearance", "Radius", "Semantic Intent"],
|
|
346
347
|
"ContentSheet": ["Color Mode", "Page type"],
|
|
347
348
|
"CoverageBarComparison": ["Appearance / DataViz", "Color Mode", "Emphasis / DataViz"],
|
|
@@ -366,7 +367,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
366
367
|
"HelloJioInput": ["AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "Emphasis", "Hello Jio Input Size", "Hello Jio Input State", "Page type"],
|
|
367
368
|
"HeroSection": ["AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "context 9", "context7", "Emphasis", "FormField States", "InputState", "Page type", "Status"],
|
|
368
369
|
"HoldingsCard": ["AppearanceBrand", "Color Mode"],
|
|
369
|
-
"HStack": ["Context", "Padding", "Page type", "Slot gap"],
|
|
370
|
+
"HStack": ["Context", "Padding", "Page type", "Slot gap", "Stack Context"],
|
|
370
371
|
"Icon": ["AppearanceBrand", "AppearanceSystem", "Badge Size", "Color Mode", "context 10", "Context4", "Emphasis", "Page type", "Profile Card Appearance", "Semantic Intent"],
|
|
371
372
|
"IconButton": ["AppearanceBrand", "Button / Size", "Button / State", "Color Mode", "Emphasis", "Page type"],
|
|
372
373
|
"IconCapsule": ["AppearanceBrand", "AppearanceSystem", "Color Mode", "Context", "Emphasis", "Icon Capsule Size", "Page type", "Semantic Intent"],
|
|
@@ -393,7 +394,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
393
394
|
"PaymentFeedback": ["AppearanceBrand", "AppearanceSystem", "Color Mode", "Context", "Emphasis", "Icon Capsule Size", "Page type", "Semantic Intent"],
|
|
394
395
|
"PdpCcCard": ["AppearanceBrand", "AppearanceSystem", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "context7", "Emphasis", "Page type", "Profile Card Appearance", "Radius", "Semantic Intent"],
|
|
395
396
|
"PlanComparisonCard": ["Color Mode"],
|
|
396
|
-
"Popup": ["Color Mode", "Context", "Padding", "Page type", "Slot gap"],
|
|
397
|
+
"Popup": ["Color Mode", "Context", "Padding", "Page type", "Slot gap", "Stack Context"],
|
|
397
398
|
"PortfolioHero": ["Avatar Size", "Badge Size", "Color Mode", "context 10", "Context3", "Context4", "Profile Card Appearance"],
|
|
398
399
|
"ProductLabel": ["Avatar Size", "Badge Size", "Color Mode", "context 10", "Context4", "Profile Card Appearance"],
|
|
399
400
|
"ProductMerchandisingCard": ["AppearanceBrand", "AppearanceSystem", "Avatar Size", "Badge Size", "Button / Size", "Button / State", "Color Mode", "Context", "context 10", "Context4", "Emphasis", "Page type", "Profile Card Appearance", "Semantic Intent"],
|
|
@@ -434,7 +435,7 @@ export const FIGMA_COMPONENT_MODES: Record<string, readonly string[]> = {
|
|
|
434
435
|
"TransactionStatus": ["Transaction Status"],
|
|
435
436
|
"UpiHandle": ["Color Mode", "context 10", "Profile Card Appearance", "UPI Handle Image"],
|
|
436
437
|
"ValueBackMetric": ["AppearanceBrand", "AppearanceSystem", "Badge Size", "Color Mode", "Context", "context 10", "Context4", "Emphasis", "Page type", "Profile Card Appearance", "Semantic Intent", "Text Appearance", "Text Sizes", "Weight"],
|
|
437
|
-
"VStack": ["Context", "Padding", "Page type", "Slot gap"],
|
|
438
|
+
"VStack": ["Context", "Padding", "Page type", "Slot gap", "Stack Context"],
|
|
438
439
|
};
|
|
439
440
|
|
|
440
441
|
/**
|