jfs-components 0.0.77 → 0.0.79

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 (87) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/lib/commonjs/components/Accordion/Accordion.js +55 -55
  3. package/lib/commonjs/components/ActionFooter/ActionFooter.js +48 -2
  4. package/lib/commonjs/components/Attached/Attached.js +144 -0
  5. package/lib/commonjs/components/Card/Card.js +25 -2
  6. package/lib/commonjs/components/Checkbox/Checkbox.js +21 -9
  7. package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -16
  8. package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +167 -0
  9. package/lib/commonjs/components/FormField/FormField.js +14 -1
  10. package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +353 -0
  11. package/lib/commonjs/components/ListItem/ListItem.js +46 -24
  12. package/lib/commonjs/components/MessageField/MessageField.js +318 -0
  13. package/lib/commonjs/components/NavArrow/NavArrow.js +58 -17
  14. package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +328 -0
  15. package/lib/commonjs/components/Slot/Slot.js +73 -0
  16. package/lib/commonjs/components/Stepper/Step.js +47 -60
  17. package/lib/commonjs/components/Stepper/StepLabel.js +40 -10
  18. package/lib/commonjs/components/Stepper/Stepper.js +15 -17
  19. package/lib/commonjs/components/SuggestiveSearch/SuggestiveSearch.js +487 -0
  20. package/lib/commonjs/components/TextInput/TextInput.js +16 -1
  21. package/lib/commonjs/components/Title/Title.js +10 -2
  22. package/lib/commonjs/components/index.js +49 -0
  23. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  24. package/lib/commonjs/icons/registry.js +1 -1
  25. package/lib/module/components/Accordion/Accordion.js +56 -56
  26. package/lib/module/components/ActionFooter/ActionFooter.js +50 -4
  27. package/lib/module/components/Attached/Attached.js +139 -0
  28. package/lib/module/components/Card/Card.js +25 -2
  29. package/lib/module/components/Checkbox/Checkbox.js +22 -10
  30. package/lib/module/components/DropdownInput/DropdownInput.js +30 -16
  31. package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +161 -0
  32. package/lib/module/components/FormField/FormField.js +16 -3
  33. package/lib/module/components/FullscreenModal/FullscreenModal.js +348 -0
  34. package/lib/module/components/ListItem/ListItem.js +46 -24
  35. package/lib/module/components/MessageField/MessageField.js +313 -0
  36. package/lib/module/components/NavArrow/NavArrow.js +59 -18
  37. package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +322 -0
  38. package/lib/module/components/Slot/Slot.js +68 -0
  39. package/lib/module/components/Stepper/Step.js +48 -61
  40. package/lib/module/components/Stepper/StepLabel.js +40 -10
  41. package/lib/module/components/Stepper/Stepper.js +15 -17
  42. package/lib/module/components/SuggestiveSearch/SuggestiveSearch.js +481 -0
  43. package/lib/module/components/TextInput/TextInput.js +17 -2
  44. package/lib/module/components/Title/Title.js +10 -2
  45. package/lib/module/components/index.js +7 -0
  46. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  47. package/lib/module/icons/registry.js +1 -1
  48. package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -20
  49. package/lib/typescript/src/components/Attached/Attached.d.ts +61 -0
  50. package/lib/typescript/src/components/Card/Card.d.ts +9 -2
  51. package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +63 -0
  52. package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +99 -0
  53. package/lib/typescript/src/components/ListItem/ListItem.d.ts +15 -5
  54. package/lib/typescript/src/components/MessageField/MessageField.d.ts +81 -0
  55. package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +10 -5
  56. package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +64 -0
  57. package/lib/typescript/src/components/Slot/Slot.d.ts +52 -0
  58. package/lib/typescript/src/components/Stepper/Step.d.ts +4 -1
  59. package/lib/typescript/src/components/Stepper/StepLabel.d.ts +4 -1
  60. package/lib/typescript/src/components/Stepper/Stepper.d.ts +3 -1
  61. package/lib/typescript/src/components/SuggestiveSearch/SuggestiveSearch.d.ts +123 -0
  62. package/lib/typescript/src/components/index.d.ts +10 -3
  63. package/lib/typescript/src/icons/registry.d.ts +1 -1
  64. package/package.json +1 -1
  65. package/src/components/Accordion/Accordion.tsx +113 -73
  66. package/src/components/ActionFooter/ActionFooter.tsx +56 -4
  67. package/src/components/Attached/Attached.tsx +181 -0
  68. package/src/components/Card/Card.tsx +28 -1
  69. package/src/components/Checkbox/Checkbox.tsx +22 -9
  70. package/src/components/DropdownInput/DropdownInput.tsx +67 -39
  71. package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +237 -0
  72. package/src/components/FormField/FormField.tsx +19 -3
  73. package/src/components/FullscreenModal/FullscreenModal.tsx +414 -0
  74. package/src/components/ListItem/ListItem.tsx +55 -25
  75. package/src/components/MessageField/MessageField.tsx +543 -0
  76. package/src/components/NavArrow/NavArrow.tsx +81 -17
  77. package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +426 -0
  78. package/src/components/Slot/Slot.tsx +91 -0
  79. package/src/components/Stepper/Step.tsx +52 -51
  80. package/src/components/Stepper/StepLabel.tsx +46 -9
  81. package/src/components/Stepper/Stepper.tsx +20 -15
  82. package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +756 -0
  83. package/src/components/TextInput/TextInput.tsx +14 -1
  84. package/src/components/Title/Title.tsx +13 -2
  85. package/src/components/index.ts +10 -3
  86. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  87. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+
3
+ import React, { useState, useCallback } from 'react';
4
+ import { View, Text, Pressable, Platform } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
7
+ import Icon from '../../icons/Icon';
8
+
9
+ /**
10
+ * A single plan column header (the label column has no header of its own).
11
+ */
12
+
13
+ /**
14
+ * Value rendered inside a plan cell.
15
+ * - `string` / `number` → rendered as value text.
16
+ * - `false` → renders the muted "not available" cross icon.
17
+ * - any React node → rendered as-is (e.g. a `Badge`, `MoneyValue`, icon…).
18
+ * - `null` / `undefined` / `true` → empty cell.
19
+ */
20
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
21
+ const DEFAULT_COLUMNS = [{
22
+ label: 'Your plan'
23
+ }, {
24
+ label: 'JioFinance+',
25
+ brand: true
26
+ }];
27
+ const DEFAULT_ROWS = [{
28
+ label: 'JioPoints multiplier',
29
+ values: ['1x', '1.25x']
30
+ }, {
31
+ label: 'Cashback',
32
+ showInfo: true,
33
+ values: [false, 'Upto ₹5000']
34
+ }, {
35
+ label: 'Bonus JioGold',
36
+ showInfo: true,
37
+ values: [false, '1%']
38
+ }];
39
+
40
+ /**
41
+ * PlanComparisonCard renders a compact comparison table that pits the user's
42
+ * current plan against one or more alternative plans across a set of feature
43
+ * rows. Implementation of Figma node `4498:2968` (`PlanComparisonCard`).
44
+ *
45
+ * The leading column holds feature labels (with an optional info icon); every
46
+ * other column maps to a plan in `columns`. Each cell value can be plain text,
47
+ * a "not available" cross (`false`), or any custom React node.
48
+ *
49
+ * @component
50
+ * @example
51
+ * ```tsx
52
+ * <PlanComparisonCard
53
+ * columns={[{ label: 'Your plan' }, { label: 'JioFinance+', brand: true }]}
54
+ * rows={[
55
+ * { label: 'JioPoints multiplier', values: ['1x', '1.25x'] },
56
+ * { label: 'Cashback', showInfo: true, values: [false, 'Upto ₹5000'] },
57
+ * ]}
58
+ * />
59
+ * ```
60
+ */
61
+ /** Keeps every text layer on a single line; columns grow to fit content. */
62
+ const NO_WRAP_TEXT = {
63
+ flexShrink: 0,
64
+ ...(Platform.OS === 'web' ? {
65
+ whiteSpace: 'nowrap'
66
+ } : {})
67
+ };
68
+ function PlanComparisonCard({
69
+ columns = DEFAULT_COLUMNS,
70
+ rows = DEFAULT_ROWS,
71
+ labelColumnFlex = 0,
72
+ modes = EMPTY_MODES,
73
+ style
74
+ }) {
75
+ /** Natural widths from header labels (plan columns only). */
76
+ const [headerWidths, setHeaderWidths] = useState([]);
77
+ /** Natural widths from table body columns. */
78
+ const [bodyWidths, setBodyWidths] = useState([]);
79
+ const setMeasuredWidth = useCallback((setter, index, width) => {
80
+ setter(prev => {
81
+ if (prev[index] === width) return prev;
82
+ const next = [...prev];
83
+ next[index] = width;
84
+ return next;
85
+ });
86
+ }, []);
87
+ const onHeaderColumnLayout = useCallback((index, event) => {
88
+ setMeasuredWidth(setHeaderWidths, index, event.nativeEvent.layout.width);
89
+ }, [setMeasuredWidth]);
90
+ const onBodyColumnLayout = useCallback((index, event) => {
91
+ setMeasuredWidth(setBodyWidths, index, event.nativeEvent.layout.width);
92
+ }, [setMeasuredWidth]);
93
+
94
+ /**
95
+ * Shared width for header + body cells in a column (max of natural header
96
+ * label vs body content). No columnGap between columns — gaps would shift
97
+ * headers relative to the flush table grid below.
98
+ */
99
+ const columnWidthStyle = index => {
100
+ const width = Math.max(headerWidths[index] ?? 0, bodyWidths[index] ?? 0);
101
+ if (width > 0) {
102
+ return {
103
+ width,
104
+ minWidth: width,
105
+ flexShrink: 0,
106
+ flexGrow: 0
107
+ };
108
+ }
109
+ return {
110
+ flexShrink: 0,
111
+ flexGrow: 0
112
+ };
113
+ };
114
+
115
+ // Container
116
+ const gap = getVariableByName('planComparisonCard/gap', modes) ?? 16;
117
+
118
+ // Header
119
+ const headerFg = getVariableByName('planComparisonCard/header/fg', modes) ?? '#ffffff';
120
+ const headerBrandFg = getVariableByName('planComparisonCard/header/brand/fg', modes) ?? '#cea15a';
121
+ const headerFontSize = getVariableByName('planComparisonCard/header/fontSize', modes) ?? 14;
122
+ const headerFontFamily = getVariableByName('planComparisonCard/header/fontFamily', modes) ?? 'JioType Var';
123
+ const headerLineHeight = getVariableByName('planComparisonCard/header/lineHeight', modes) ?? 18;
124
+ const headerFontWeight = getVariableByName('planComparisonCard/header/fontWeight', modes) ?? '500';
125
+
126
+ // Table
127
+ const tableBackground = getVariableByName('planComparisonCard/tableRow/background', modes) ?? '#141414';
128
+ const tableRadius = getVariableByName('planComparisonCard/tableRow/radius', modes) ?? 16;
129
+ const tableBorderSize = getVariableByName('planComparisonCard/tableRow/border/size', modes) ?? 1;
130
+ const tableBorderColor = getVariableByName('planComparisonCard/tableRow/border/color', modes) ?? '#1e1a14';
131
+
132
+ // Cell
133
+ const cellPadding = getVariableByName('planComparisonCard/tableCell/padding', modes) ?? 12;
134
+ const cellGap = getVariableByName('planComparisonCard/tableCell/gap', modes) ?? 2;
135
+ const cellMinHeight = getVariableByName('planComparisonCard/tableCell/height', modes) ?? 46;
136
+ const cellBorderSize = getVariableByName('planComparisonCard/tableCell/border/size', modes) ?? 1;
137
+ const cellBorderColor = getVariableByName('planComparisonCard/tableCell/border/color', modes) ?? '#1e1a14';
138
+
139
+ // Cell label
140
+ const labelColor = getVariableByName('planComparisonCard/tableCell/label/color', modes) ?? '#ffffff';
141
+ const labelDisabledColor = getVariableByName('planComparisonCard/tableCell/label/disabled/color', modes) ?? '#91949c';
142
+ const labelFontSize = getVariableByName('planComparisonCard/tableCell/label/fontSize', modes) ?? 12;
143
+ const labelFontFamily = getVariableByName('planComparisonCard/tableCell/label/fontFamily', modes) ?? 'JioType Var';
144
+ const labelLineHeight = getVariableByName('planComparisonCard/tableCell/label/lineHeight', modes) ?? 16;
145
+ const labelFontWeight = getVariableByName('planComparisonCard/tableCell/label/fontWeight', modes) ?? '400';
146
+
147
+ // Cell value
148
+ const valueColor = getVariableByName('planComparisonCard/tableCell/value/color', modes) ?? '#ffffff';
149
+ const valueFontSize = getVariableByName('planComparisonCard/tableCell/value/fontSize', modes) ?? 12;
150
+ const valueFontFamily = getVariableByName('planComparisonCard/tableCell/value/fontFamily', modes) ?? 'JioType Var';
151
+ const valueLineHeight = getVariableByName('planComparisonCard/tableCell/value/lineHeight', modes) ?? 16;
152
+ const valueFontWeight = getVariableByName('planComparisonCard/tableCell/value/fontWeight', modes) ?? '500';
153
+
154
+ // Icon
155
+ const iconColor = getVariableByName('planComparisonCard/icon/color', modes) ?? '#ffffff';
156
+ const iconSize = getVariableByName('planComparisonCard/icon/size', modes) ?? 16;
157
+ const toWeight = w => typeof w === 'number' ? `${w}` : w;
158
+ const headerTextStyle = {
159
+ ...NO_WRAP_TEXT,
160
+ fontFamily: headerFontFamily,
161
+ fontSize: headerFontSize,
162
+ lineHeight: headerLineHeight,
163
+ fontWeight: toWeight(headerFontWeight),
164
+ textAlign: 'center'
165
+ };
166
+ const labelTextStyle = {
167
+ ...NO_WRAP_TEXT,
168
+ color: labelColor,
169
+ fontFamily: labelFontFamily,
170
+ fontSize: labelFontSize,
171
+ lineHeight: labelLineHeight,
172
+ fontWeight: toWeight(labelFontWeight)
173
+ };
174
+ const valueTextStyle = {
175
+ ...NO_WRAP_TEXT,
176
+ color: valueColor,
177
+ fontFamily: valueFontFamily,
178
+ fontSize: valueFontSize,
179
+ lineHeight: valueLineHeight,
180
+ fontWeight: toWeight(valueFontWeight),
181
+ textAlign: 'center'
182
+ };
183
+ const planHeaderColumnStyle = {
184
+ alignItems: 'center',
185
+ justifyContent: 'center'
186
+ };
187
+ const renderValue = (value, cellKey) => {
188
+ // "Not available" → muted cross icon.
189
+ if (value === false) {
190
+ return /*#__PURE__*/_jsx(Icon, {
191
+ name: "ic_close",
192
+ size: iconSize,
193
+ color: labelDisabledColor
194
+ }, cellKey);
195
+ }
196
+ // Empty cell.
197
+ if (value === null || value === undefined || value === true) {
198
+ return null;
199
+ }
200
+ // Text content.
201
+ if (typeof value === 'string' || typeof value === 'number') {
202
+ return /*#__PURE__*/_jsx(Text, {
203
+ style: valueTextStyle,
204
+ children: value
205
+ }, cellKey);
206
+ }
207
+ // Custom node — forward modes so themed children stay in sync.
208
+ return cloneChildrenWithModes(value, modes);
209
+ };
210
+ const labelCellStyle = {
211
+ flexDirection: 'row',
212
+ alignItems: 'center',
213
+ gap: cellGap,
214
+ padding: cellPadding,
215
+ minHeight: cellMinHeight,
216
+ flexShrink: 0
217
+ };
218
+ const valueCellStyle = {
219
+ flexDirection: 'row',
220
+ alignItems: 'center',
221
+ justifyContent: 'center',
222
+ padding: cellPadding,
223
+ minHeight: cellMinHeight,
224
+ flexShrink: 0
225
+ };
226
+ return /*#__PURE__*/_jsxs(View, {
227
+ style: [{
228
+ gap,
229
+ alignSelf: 'flex-start'
230
+ }, style],
231
+ children: [/*#__PURE__*/_jsxs(View, {
232
+ style: {
233
+ flexDirection: 'row',
234
+ alignItems: 'flex-end'
235
+ },
236
+ children: [/*#__PURE__*/_jsx(View, {
237
+ style: [columnWidthStyle(0), labelColumnFlex > 0 ? {
238
+ flexGrow: labelColumnFlex
239
+ } : undefined]
240
+ }), columns.map((column, index) => {
241
+ const colIndex = index + 1;
242
+ return /*#__PURE__*/_jsx(View, {
243
+ onLayout: e => onHeaderColumnLayout(colIndex, e),
244
+ style: [columnWidthStyle(colIndex), planHeaderColumnStyle],
245
+ children: /*#__PURE__*/_jsx(Text, {
246
+ style: [headerTextStyle, {
247
+ color: column.brand ? headerBrandFg : headerFg,
248
+ alignSelf: 'center'
249
+ }],
250
+ children: column.label
251
+ })
252
+ }, column.label ?? index);
253
+ })]
254
+ }), /*#__PURE__*/_jsxs(View, {
255
+ style: {
256
+ flexDirection: 'row',
257
+ alignSelf: 'flex-start',
258
+ backgroundColor: tableBackground,
259
+ borderWidth: tableBorderSize,
260
+ borderColor: tableBorderColor,
261
+ borderRadius: tableRadius,
262
+ overflow: 'hidden'
263
+ },
264
+ children: [/*#__PURE__*/_jsx(View, {
265
+ onLayout: e => onBodyColumnLayout(0, e),
266
+ style: [columnWidthStyle(0), labelColumnFlex > 0 ? {
267
+ flexGrow: labelColumnFlex
268
+ } : undefined],
269
+ children: rows.map((row, rowIndex) => {
270
+ const isLast = rowIndex === rows.length - 1;
271
+ const showInfo = row.showInfo || row.onInfoPress != null;
272
+ return /*#__PURE__*/_jsxs(View, {
273
+ style: [labelCellStyle, {
274
+ borderBottomWidth: isLast ? 0 : cellBorderSize,
275
+ borderBottomColor: cellBorderColor
276
+ }],
277
+ children: [/*#__PURE__*/_jsx(Text, {
278
+ style: labelTextStyle,
279
+ children: row.label
280
+ }), showInfo && (row.onInfoPress ? /*#__PURE__*/_jsx(Pressable, {
281
+ onPress: row.onInfoPress,
282
+ accessibilityRole: "button",
283
+ accessibilityLabel: `More information about ${row.label}`,
284
+ hitSlop: 8,
285
+ children: /*#__PURE__*/_jsx(Icon, {
286
+ name: "ic_info",
287
+ size: iconSize,
288
+ color: iconColor
289
+ })
290
+ }) : /*#__PURE__*/_jsx(Icon, {
291
+ name: "ic_info",
292
+ size: iconSize,
293
+ color: iconColor
294
+ }))]
295
+ }, row.key ?? `${row.label}-${rowIndex}`);
296
+ })
297
+ }), columns.map((column, colIndex) => {
298
+ const colIndexWidth = colIndex + 1;
299
+ return /*#__PURE__*/_jsx(View, {
300
+ onLayout: e => onBodyColumnLayout(colIndexWidth, e),
301
+ style: [columnWidthStyle(colIndexWidth), planHeaderColumnStyle],
302
+ children: rows.map((row, rowIndex) => {
303
+ const isLast = rowIndex === rows.length - 1;
304
+ return /*#__PURE__*/_jsx(View, {
305
+ style: [valueCellStyle, {
306
+ borderBottomWidth: isLast ? 0 : cellBorderSize,
307
+ borderBottomColor: cellBorderColor
308
+ }],
309
+ children: /*#__PURE__*/_jsx(View, {
310
+ style: {
311
+ flexShrink: 0
312
+ },
313
+ children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
314
+ })
315
+ }, row.key ?? `${row.label}-${rowIndex}`);
316
+ })
317
+ }, column.label ?? colIndex);
318
+ })]
319
+ })]
320
+ });
321
+ }
322
+ export default PlanComparisonCard;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ import React, { useMemo } from 'react';
4
+ import { View } from 'react-native';
5
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
6
+ import { useTokens } from '../../design-tokens/JFSThemeProvider';
7
+ import { cloneChildrenWithModes, EMPTY_MODES } from '../../utils/react-utils';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ /**
10
+ * Slot — a token-driven layout container for grouped slot content.
11
+ *
12
+ * Use `Slot` instead of a raw `View` when you need a vertical or horizontal
13
+ * stack with design-token gap spacing and automatic `modes` propagation to
14
+ * children. Typical usage is nesting a column of actions inside a
15
+ * direction-locked parent such as `ActionFooter`:
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <ActionFooter modes={modes}>
20
+ * <Slot layoutDirection="vertical" modes={modes}>
21
+ * <Button label="Continue" modes={primaryModes} />
22
+ * <Disclaimer disclaimer="Terms apply." modes={modes} />
23
+ * </Slot>
24
+ * </ActionFooter>
25
+ * ```
26
+ */
27
+ function Slot({
28
+ children,
29
+ layoutDirection = 'vertical',
30
+ alignCrossAxis,
31
+ justifyMainAxis,
32
+ modes: propModes = EMPTY_MODES,
33
+ style,
34
+ ...rest
35
+ }) {
36
+ const {
37
+ modes: globalModes
38
+ } = useTokens();
39
+ const modes = useMemo(() => ({
40
+ ...globalModes,
41
+ ...propModes
42
+ }), [globalModes, propModes]);
43
+ const {
44
+ containerStyle,
45
+ processedChildren
46
+ } = useMemo(() => {
47
+ const gap = getVariableByName('slot/gap', modes) ?? 8;
48
+ const isHorizontal = layoutDirection === 'horizontal';
49
+ const container = {
50
+ flexDirection: isHorizontal ? 'row' : 'column',
51
+ alignItems: alignCrossAxis ?? (isHorizontal ? 'flex-start' : 'stretch'),
52
+ justifyContent: justifyMainAxis ?? (isHorizontal ? 'center' : undefined),
53
+ alignSelf: 'stretch',
54
+ gap
55
+ };
56
+ const processed = children ? cloneChildrenWithModes(children, modes) : null;
57
+ return {
58
+ containerStyle: container,
59
+ processedChildren: processed
60
+ };
61
+ }, [children, modes, layoutDirection, alignCrossAxis, justifyMainAxis]);
62
+ return /*#__PURE__*/_jsx(View, {
63
+ style: [containerStyle, style],
64
+ ...rest,
65
+ children: processedChildren
66
+ });
67
+ }
68
+ export default /*#__PURE__*/React.memo(Slot);
@@ -5,56 +5,52 @@ import { View, Text } from 'react-native';
5
5
  import Svg, { Path } from 'react-native-svg';
6
6
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
7
7
  import { StepLabel } from './StepLabel';
8
- import { EMPTY_MODES } from '../../utils/react-utils';
8
+ import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
9
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  export function Step({
11
11
  children,
12
12
  modes = EMPTY_MODES,
13
13
  style,
14
14
  index = 0,
15
+ showLine = true,
15
16
  connectorStyle,
16
17
  title,
17
18
  supportingText,
19
+ metaText,
18
20
  subtitle = true,
21
+ meta = true,
19
22
  status = 'number'
20
23
  }) {
21
- // Container styles
22
24
  const minHeight = Number(getVariableByName('steperItem/minHeight', modes)) || 52;
23
- const gap = Number(getVariableByName('steperItem/gap', modes)) || 8;
24
-
25
- // Indicator dimensions and styles
26
- const indicatorSize = Number(getVariableByName('stepIndicator/size', modes)) || 24;
25
+ const gap = Number(getVariableByName('steperItem/gap', modes)) || 16;
26
+ const indicatorSize = Number(getVariableByName('stepIndicator/size', modes)) || 36;
27
27
  const indicatorRadius = Number(getVariableByName('stepIndicator/radius', modes)) || 9999;
28
- const indicatorBg = getVariableByName('stepIndicator/background', modes) || '#5c00b5';
29
- const indicatorBorderColor = getVariableByName('stepIndicator/border/color', modes) || '#5c00b5';
28
+ const indicatorBg = getVariableByName('stepIndicator/background', modes) || '#5d00b5';
29
+ const indicatorBorderColor = getVariableByName('stepIndicator/border/color', modes) || '#5d00b5';
30
30
  const indicatorBorderSize = Number(getVariableByName('stepIndicator/border/size', modes)) || 1;
31
31
  const iconColor = getVariableByName('stepIndicator/icon/color', modes) || '#ffffff';
32
-
33
- // Label styles (for number)
32
+ const stepStatusSize = Number(getVariableByName('stepStatus/size', modes)) || 18;
34
33
  const labelFontSize = Number(getVariableByName('stepIndicator/label/fontSize', modes)) || 12;
35
34
  const labelFontFamily = getVariableByName('stepIndicator/label/fontFamily', modes) || undefined;
36
35
  const labelLineHeight = Number(getVariableByName('stepIndicator/label/lineHeight', modes)) || 14;
37
36
  const labelFontWeight = getVariableByName('stepIndicator/label/fontWeight', modes) || '500';
38
-
39
- // Vertical line styles
40
37
  const lineSize = Number(getVariableByName('steperItem/lineSize', modes)) || 2;
41
- const lineColor = getVariableByName('steperItem/line', modes) || '#5c00b5';
38
+ const lineColor = getVariableByName('steperItem/line', modes) || '#5d00b5';
42
39
  const badgeWrapGap = Number(getVariableByName('steperItem/badgeWrap/gap', modes)) || 2;
43
40
  const containerStyle = {
44
41
  flexDirection: 'row',
45
42
  minHeight,
43
+ gap,
46
44
  ...style
47
45
  };
48
-
49
- // FIX: This wrapper should NOT expand. It should be exact width of the indicator.
50
46
  const indicatorWrapperStyle = {
51
47
  flexDirection: 'column',
52
48
  alignItems: 'center',
53
49
  width: indicatorSize,
54
50
  flexGrow: 0,
55
- // Do NOT grow
56
51
  flexShrink: 0,
57
- gap: badgeWrapGap
52
+ gap: badgeWrapGap,
53
+ alignSelf: 'stretch'
58
54
  };
59
55
  const indicatorStyle = {
60
56
  width: indicatorSize,
@@ -67,6 +63,12 @@ export function Step({
67
63
  justifyContent: 'center',
68
64
  overflow: 'hidden'
69
65
  };
66
+ const stepStatusContainerStyle = {
67
+ width: stepStatusSize,
68
+ height: stepStatusSize,
69
+ alignItems: 'center',
70
+ justifyContent: 'center'
71
+ };
70
72
  const labelStyle = {
71
73
  color: iconColor,
72
74
  fontSize: labelFontSize,
@@ -75,24 +77,19 @@ export function Step({
75
77
  lineHeight: labelLineHeight,
76
78
  textAlign: 'center'
77
79
  };
78
-
79
- // Combine base line style with injected connectorStyle
80
80
  const lineStyle = {
81
81
  width: lineSize,
82
82
  backgroundColor: lineColor,
83
83
  flexGrow: 1,
84
- // Line should take up remaining vertical space in this column
85
- ...connectorStyle // Allow overriding display, color, etc.
84
+ minHeight: 1,
85
+ ...connectorStyle
86
86
  };
87
-
88
- // Helper for icons
89
87
  const renderIcon = () => {
90
88
  switch (status) {
91
89
  case 'complete':
92
- // Checkmark
93
90
  return /*#__PURE__*/_jsx(Svg, {
94
- width: 12,
95
- height: 12,
91
+ width: stepStatusSize,
92
+ height: stepStatusSize,
96
93
  viewBox: "0 0 24 24",
97
94
  fill: "none",
98
95
  children: /*#__PURE__*/_jsx(Path, {
@@ -104,10 +101,9 @@ export function Step({
104
101
  })
105
102
  });
106
103
  case 'error':
107
- // X mark
108
104
  return /*#__PURE__*/_jsx(Svg, {
109
- width: 12,
110
- height: 12,
105
+ width: stepStatusSize,
106
+ height: stepStatusSize,
111
107
  viewBox: "0 0 24 24",
112
108
  fill: "none",
113
109
  children: /*#__PURE__*/_jsx(Path, {
@@ -119,10 +115,9 @@ export function Step({
119
115
  })
120
116
  });
121
117
  case 'warning':
122
- // Exclamation / Triangle
123
118
  return /*#__PURE__*/_jsx(Svg, {
124
- width: 12,
125
- height: 12,
119
+ width: stepStatusSize,
120
+ height: stepStatusSize,
126
121
  viewBox: "0 0 24 24",
127
122
  fill: "none",
128
123
  children: /*#__PURE__*/_jsx(Path, {
@@ -141,46 +136,38 @@ export function Step({
141
136
  });
142
137
  }
143
138
  };
144
-
145
- // Clone children to pass modes if they are StepLabel
146
- const childrenWithProps = React.Children.map(children, child => {
147
- if (/*#__PURE__*/React.isValidElement(child)) {
148
- return /*#__PURE__*/React.cloneElement(child, {
149
- modes
150
- });
151
- }
152
- return child;
153
- });
154
-
155
- // Determine if we have content to pad
156
- // Default padding bottom to 16 if line is shown, but also respect connectorStyle display
157
- // If display is none, we probably don't want the padding bottom either.
158
- const isLineHidden = connectorStyle?.display === 'none';
159
- const contentPaddingBottom = isLineHidden ? 0 : 16;
139
+ const hasSlotChildren = React.Children.count(children) > 0;
160
140
  return /*#__PURE__*/_jsxs(View, {
161
141
  style: containerStyle,
162
142
  children: [/*#__PURE__*/_jsxs(View, {
163
143
  style: indicatorWrapperStyle,
164
144
  children: [/*#__PURE__*/_jsx(View, {
165
145
  style: indicatorStyle,
166
- children: renderIcon()
167
- }), /*#__PURE__*/_jsx(View, {
146
+ children: /*#__PURE__*/_jsx(View, {
147
+ style: stepStatusContainerStyle,
148
+ children: renderIcon()
149
+ })
150
+ }), showLine ? /*#__PURE__*/_jsx(View, {
168
151
  style: lineStyle
169
- })]
170
- }), /*#__PURE__*/_jsx(View, {
171
- style: {
172
- width: gap
173
- }
152
+ }) : null]
174
153
  }), /*#__PURE__*/_jsx(View, {
175
154
  style: {
176
- flex: 1,
177
- paddingBottom: contentPaddingBottom
155
+ flex: 1
178
156
  },
179
- children: title ? /*#__PURE__*/_jsx(StepLabel, {
180
- title: title,
181
- supportingText: subtitle ? supportingText || undefined : undefined,
157
+ children: hasSlotChildren ? cloneChildrenWithModes(children, modes) : /*#__PURE__*/_jsx(StepLabel, {
158
+ ...(title !== undefined ? {
159
+ title
160
+ } : {}),
161
+ ...(supportingText !== undefined ? {
162
+ supportingText
163
+ } : {}),
164
+ ...(metaText !== undefined ? {
165
+ metaText
166
+ } : {}),
167
+ subtitle: subtitle,
168
+ meta: meta,
182
169
  modes: modes
183
- }) : childrenWithProps
170
+ })
184
171
  })]
185
172
  });
186
173
  }
@@ -8,24 +8,42 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  export function StepLabel({
9
9
  title = 'Stepper Item',
10
10
  supportingText,
11
+ metaText,
12
+ subtitle = true,
13
+ meta = true,
11
14
  modes = EMPTY_MODES,
12
15
  style
13
16
  }) {
14
- // Title styles
15
17
  const titleColor = getVariableByName('steperItem/title/color', modes) || '#0d0d0f';
16
18
  const titleFontSize = Number(getVariableByName('steperItem/title/fontSize', modes)) || 14;
17
19
  const titleFontFamily = getVariableByName('steperItem/title/fontFamily', modes) || undefined;
18
20
  const titleLineHeight = Number(getVariableByName('steperItem/title/lineHeight', modes)) || 18;
19
21
  const titleFontWeight = getVariableByName('steperItem/title/fontWeight', modes) || '700';
20
22
 
21
- // Subtitle styles
22
- const subtitleColor = getVariableByName('steperItem/subtitle/color', modes) || '#3d4047';
23
- const subtitleFontSize = Number(getVariableByName('steperItem/subtitle/fontSize', modes)) || 12;
24
- const subtitleFontFamily = getVariableByName('steperItem/subtitle/fontFamily', modes) || undefined;
25
- const subtitleLineHeight = Number(getVariableByName('steperItem/subtitle/lineHeight', modes)) || 16;
26
- const subtitleFontWeight = getVariableByName('steperItem/subtitle/fontWeight', modes) || '400';
27
-
28
- // Layout gap
23
+ // The Subtitle (supportingText) and Meta both default to the "Neutral"
24
+ // AppearanceBrand. A caller-supplied `AppearanceBrand` still wins (it is
25
+ // spread after the default), so each remains overridable. The Title keeps
26
+ // its own appearance resolution.
27
+ const subtitleModes = {
28
+ AppearanceBrand: 'Neutral',
29
+ ...modes
30
+ };
31
+ const metaModes = {
32
+ AppearanceBrand: 'Neutral',
33
+ ...modes
34
+ };
35
+ const subtitleColor = getVariableByName('steperItem/subtitle/color', subtitleModes) || '#3d4047';
36
+ const subtitleFontSize = Number(getVariableByName('steperItem/subtitle/fontSize', subtitleModes)) || 12;
37
+ const subtitleFontFamily = getVariableByName('steperItem/subtitle/fontFamily', subtitleModes) || undefined;
38
+ const subtitleLineHeight = Number(getVariableByName('steperItem/subtitle/lineHeight', subtitleModes)) || 16;
39
+ const subtitleFontWeight = getVariableByName('steperItem/subtitle/fontWeight', subtitleModes) || '400';
40
+ const metaColor = getVariableByName('steperItem/meta/color', metaModes) || '#f7ab21';
41
+ const metaFontSize = Number(getVariableByName('steperItem/meta/fontSize', metaModes)) || 10;
42
+ // The Figma variable is authored as "fontFamily Copy" (with a space + suffix).
43
+ // Match the literal Figma name to avoid a missing-variable warning.
44
+ const metaFontFamily = getVariableByName('steperItem/meta/fontFamily Copy', metaModes) || undefined;
45
+ const metaLineHeight = Number(getVariableByName('steperItem/meta/lineHeight', metaModes)) || 12;
46
+ const metaFontWeight = getVariableByName('steperItem/meta/fontWeight', metaModes) || '700';
29
47
  const textGap = Number(getVariableByName('steperItem/textWrap/gap', modes)) || 2;
30
48
  const titleStyle = {
31
49
  color: titleColor,
@@ -41,6 +59,15 @@ export function StepLabel({
41
59
  fontWeight: subtitleFontWeight,
42
60
  lineHeight: subtitleLineHeight
43
61
  };
62
+ const metaStyle = {
63
+ color: metaColor,
64
+ fontSize: metaFontSize,
65
+ fontFamily: metaFontFamily,
66
+ fontWeight: metaFontWeight,
67
+ lineHeight: metaLineHeight
68
+ };
69
+ const showSubtitle = subtitle && !!supportingText;
70
+ const showMeta = meta && !!metaText;
44
71
  return /*#__PURE__*/_jsxs(View, {
45
72
  style: [{
46
73
  gap: textGap,
@@ -49,9 +76,12 @@ export function StepLabel({
49
76
  children: [/*#__PURE__*/_jsx(Text, {
50
77
  style: titleStyle,
51
78
  children: title
52
- }), supportingText ? /*#__PURE__*/_jsx(Text, {
79
+ }), showSubtitle ? /*#__PURE__*/_jsx(Text, {
53
80
  style: subtitleStyle,
54
81
  children: supportingText
82
+ }) : null, showMeta ? /*#__PURE__*/_jsx(Text, {
83
+ style: metaStyle,
84
+ children: metaText
55
85
  }) : null]
56
86
  });
57
87
  }