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.
- package/CHANGELOG.md +28 -0
- package/lib/commonjs/components/Accordion/Accordion.js +55 -55
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +48 -2
- package/lib/commonjs/components/Attached/Attached.js +144 -0
- package/lib/commonjs/components/Card/Card.js +25 -2
- package/lib/commonjs/components/Checkbox/Checkbox.js +21 -9
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +30 -16
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +167 -0
- package/lib/commonjs/components/FormField/FormField.js +14 -1
- package/lib/commonjs/components/FullscreenModal/FullscreenModal.js +353 -0
- package/lib/commonjs/components/ListItem/ListItem.js +46 -24
- package/lib/commonjs/components/MessageField/MessageField.js +318 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js +58 -17
- package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +328 -0
- package/lib/commonjs/components/Slot/Slot.js +73 -0
- package/lib/commonjs/components/Stepper/Step.js +47 -60
- package/lib/commonjs/components/Stepper/StepLabel.js +40 -10
- package/lib/commonjs/components/Stepper/Stepper.js +15 -17
- package/lib/commonjs/components/SuggestiveSearch/SuggestiveSearch.js +487 -0
- package/lib/commonjs/components/TextInput/TextInput.js +16 -1
- package/lib/commonjs/components/Title/Title.js +10 -2
- package/lib/commonjs/components/index.js +49 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/Accordion/Accordion.js +56 -56
- package/lib/module/components/ActionFooter/ActionFooter.js +50 -4
- package/lib/module/components/Attached/Attached.js +139 -0
- package/lib/module/components/Card/Card.js +25 -2
- package/lib/module/components/Checkbox/Checkbox.js +22 -10
- package/lib/module/components/DropdownInput/DropdownInput.js +30 -16
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +161 -0
- package/lib/module/components/FormField/FormField.js +16 -3
- package/lib/module/components/FullscreenModal/FullscreenModal.js +348 -0
- package/lib/module/components/ListItem/ListItem.js +46 -24
- package/lib/module/components/MessageField/MessageField.js +313 -0
- package/lib/module/components/NavArrow/NavArrow.js +59 -18
- package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +322 -0
- package/lib/module/components/Slot/Slot.js +68 -0
- package/lib/module/components/Stepper/Step.js +48 -61
- package/lib/module/components/Stepper/StepLabel.js +40 -10
- package/lib/module/components/Stepper/Stepper.js +15 -17
- package/lib/module/components/SuggestiveSearch/SuggestiveSearch.js +481 -0
- package/lib/module/components/TextInput/TextInput.js +17 -2
- package/lib/module/components/Title/Title.js +10 -2
- package/lib/module/components/index.js +7 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/Accordion/Accordion.d.ts +14 -20
- package/lib/typescript/src/components/Attached/Attached.d.ts +61 -0
- package/lib/typescript/src/components/Card/Card.d.ts +9 -2
- package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +63 -0
- package/lib/typescript/src/components/FullscreenModal/FullscreenModal.d.ts +99 -0
- package/lib/typescript/src/components/ListItem/ListItem.d.ts +15 -5
- package/lib/typescript/src/components/MessageField/MessageField.d.ts +81 -0
- package/lib/typescript/src/components/NavArrow/NavArrow.d.ts +10 -5
- package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +64 -0
- package/lib/typescript/src/components/Slot/Slot.d.ts +52 -0
- package/lib/typescript/src/components/Stepper/Step.d.ts +4 -1
- package/lib/typescript/src/components/Stepper/StepLabel.d.ts +4 -1
- package/lib/typescript/src/components/Stepper/Stepper.d.ts +3 -1
- package/lib/typescript/src/components/SuggestiveSearch/SuggestiveSearch.d.ts +123 -0
- package/lib/typescript/src/components/index.d.ts +10 -3
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.tsx +113 -73
- package/src/components/ActionFooter/ActionFooter.tsx +56 -4
- package/src/components/Attached/Attached.tsx +181 -0
- package/src/components/Card/Card.tsx +28 -1
- package/src/components/Checkbox/Checkbox.tsx +22 -9
- package/src/components/DropdownInput/DropdownInput.tsx +67 -39
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +237 -0
- package/src/components/FormField/FormField.tsx +19 -3
- package/src/components/FullscreenModal/FullscreenModal.tsx +414 -0
- package/src/components/ListItem/ListItem.tsx +55 -25
- package/src/components/MessageField/MessageField.tsx +543 -0
- package/src/components/NavArrow/NavArrow.tsx +81 -17
- package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +426 -0
- package/src/components/Slot/Slot.tsx +91 -0
- package/src/components/Stepper/Step.tsx +52 -51
- package/src/components/Stepper/StepLabel.tsx +46 -9
- package/src/components/Stepper/Stepper.tsx +20 -15
- package/src/components/SuggestiveSearch/SuggestiveSearch.tsx +756 -0
- package/src/components/TextInput/TextInput.tsx +14 -1
- package/src/components/Title/Title.tsx +13 -2
- package/src/components/index.ts +10 -3
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- 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)) ||
|
|
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) || '#
|
|
29
|
-
const indicatorBorderColor = getVariableByName('stepIndicator/border/color', modes) || '#
|
|
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) || '#
|
|
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
|
-
|
|
85
|
-
...connectorStyle
|
|
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:
|
|
95
|
-
height:
|
|
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:
|
|
110
|
-
height:
|
|
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:
|
|
125
|
-
height:
|
|
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:
|
|
167
|
-
|
|
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:
|
|
180
|
-
title
|
|
181
|
-
|
|
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
|
-
})
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
}),
|
|
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
|
}
|