jfs-components 0.1.51 → 0.1.54
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 +11 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +319 -135
- package/lib/commonjs/components/Badge/Badge.js +20 -1
- package/lib/commonjs/components/Button/Button.js +139 -47
- package/lib/commonjs/components/ButtonGroup/ButtonGroup.js +1 -1
- package/lib/commonjs/components/Card/Card.js +151 -112
- package/lib/commonjs/components/Card/assets/gold-logo-mark.png +0 -0
- package/lib/commonjs/components/CategoryCard/CategoryCard.js +97 -63
- package/lib/commonjs/components/CategoryCard/assets/sample.png +0 -0
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +59 -10
- package/lib/commonjs/components/ExpandableCheckbox/ExpandableCheckbox.js +132 -49
- package/lib/commonjs/components/HelloJioInput/HelloJioInput.js +56 -14
- package/lib/commonjs/components/IconCapsule/IconCapsule.js +1 -1
- package/lib/commonjs/components/MetricData/MetricData.js +26 -13
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +1 -1
- package/lib/commonjs/components/Nudge/Nudge.js +1 -1
- package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +47 -91
- package/lib/commonjs/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
- package/lib/commonjs/components/PortfolioHero/PortfolioHero.js +1 -1
- package/lib/commonjs/components/Slider/Slider.js +3 -3
- package/lib/commonjs/components/TextInput/TextInput.js +26 -8
- package/lib/commonjs/components/Tooltip/Tooltip.js +3 -5
- package/lib/commonjs/components/index.js +7 -1
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +13 -13
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/ActionFooter/ActionFooter.js +322 -138
- package/lib/module/components/Badge/Badge.js +20 -1
- package/lib/module/components/Button/Button.js +140 -48
- package/lib/module/components/ButtonGroup/ButtonGroup.js +1 -1
- package/lib/module/components/Card/Card.js +152 -114
- package/lib/module/components/Card/assets/gold-logo-mark.png +0 -0
- package/lib/module/components/CategoryCard/CategoryCard.js +97 -63
- package/lib/module/components/CategoryCard/assets/sample.png +0 -0
- package/lib/module/components/CheckboxItem/CheckboxItem.js +59 -10
- package/lib/module/components/ExpandableCheckbox/ExpandableCheckbox.js +135 -52
- package/lib/module/components/HelloJioInput/HelloJioInput.js +56 -14
- package/lib/module/components/IconCapsule/IconCapsule.js +1 -1
- package/lib/module/components/MetricData/MetricData.js +26 -14
- package/lib/module/components/MoneyValue/MoneyValue.js +1 -1
- package/lib/module/components/Nudge/Nudge.js +1 -1
- package/lib/module/components/PdpCcCard/PdpCcCard.js +48 -92
- package/lib/module/components/PlanComparisonCard/PlanComparisonCard.js +52 -45
- package/lib/module/components/PortfolioHero/PortfolioHero.js +1 -1
- package/lib/module/components/Slider/Slider.js +3 -3
- package/lib/module/components/TextInput/TextInput.js +27 -9
- package/lib/module/components/Tooltip/Tooltip.js +3 -5
- package/lib/module/components/index.js +1 -1
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/design-tokens/figma-modes.generated.js +13 -13
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/ActionFooter/ActionFooter.d.ts +50 -21
- package/lib/typescript/src/components/Button/Button.d.ts +24 -1
- package/lib/typescript/src/components/ButtonGroup/ButtonGroup.d.ts +2 -2
- package/lib/typescript/src/components/Card/Card.d.ts +54 -25
- package/lib/typescript/src/components/CategoryCard/CategoryCard.d.ts +29 -12
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +27 -9
- package/lib/typescript/src/components/ExpandableCheckbox/ExpandableCheckbox.d.ts +37 -12
- package/lib/typescript/src/components/IconCapsule/IconCapsule.d.ts +1 -1
- package/lib/typescript/src/components/MetricData/MetricData.d.ts +7 -5
- package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +16 -6
- package/lib/typescript/src/components/PlanComparisonCard/PlanComparisonCard.d.ts +9 -2
- package/lib/typescript/src/components/index.d.ts +2 -2
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/ActionFooter/ActionFooter.tsx +375 -141
- package/src/components/Badge/Badge.tsx +21 -1
- package/src/components/Button/Button.tsx +192 -44
- package/src/components/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/components/Card/Card.tsx +370 -212
- package/src/components/Card/assets/gold-logo-mark.png +0 -0
- package/src/components/CategoryCard/CategoryCard.tsx +130 -84
- package/src/components/CategoryCard/assets/sample.png +0 -0
- package/src/components/CheckboxItem/CheckboxItem.tsx +72 -12
- package/src/components/ExpandableCheckbox/ExpandableCheckbox.tsx +186 -67
- package/src/components/HelloJioInput/HelloJioInput.tsx +58 -17
- package/src/components/IconCapsule/IconCapsule.tsx +1 -1
- package/src/components/MetricData/MetricData.tsx +39 -22
- package/src/components/MoneyValue/MoneyValue.tsx +1 -1
- package/src/components/Nudge/Nudge.tsx +1 -1
- package/src/components/PdpCcCard/PdpCcCard.tsx +41 -92
- package/src/components/PlanComparisonCard/PlanComparisonCard.tsx +73 -58
- package/src/components/PortfolioHero/PortfolioHero.tsx +1 -1
- package/src/components/Slider/Slider.tsx +3 -3
- package/src/components/TextInput/TextInput.tsx +26 -9
- package/src/components/Tooltip/Tooltip.tsx +3 -5
- package/src/components/index.ts +9 -2
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/design-tokens/figma-modes.generated.ts +13 -13
- package/src/icons/registry.ts +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import React, { useMemo } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
|
-
Text,
|
|
5
4
|
Pressable,
|
|
6
5
|
StyleSheet,
|
|
7
6
|
type ViewStyle,
|
|
8
|
-
type TextStyle,
|
|
9
7
|
type StyleProp,
|
|
10
8
|
type ImageSourcePropType,
|
|
11
9
|
} from 'react-native'
|
|
@@ -16,19 +14,29 @@ import Divider from '../Divider/Divider'
|
|
|
16
14
|
import Button from '../Button/Button'
|
|
17
15
|
import Image from '../Image/Image'
|
|
18
16
|
import Icon from '../Icon/Icon'
|
|
17
|
+
import MetricData from '../MetricData/MetricData'
|
|
19
18
|
import type { Modes } from '../../design-tokens'
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* A single metric column inside the card's stats row: a small title, a bold
|
|
23
|
-
* value and an optional muted caption.
|
|
22
|
+
* value (optionally preceded by an icon) and an optional muted caption.
|
|
24
23
|
*/
|
|
25
24
|
export interface PdpCcCardMetric {
|
|
26
25
|
/** Small label rendered above the value (e.g. `"Weight"`). */
|
|
27
26
|
title?: string
|
|
28
|
-
/**
|
|
29
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The prominent value (e.g. `"24K"`). Accepts a string or any node
|
|
29
|
+
* (e.g. a `MoneyValue`) for richer composition.
|
|
30
|
+
*/
|
|
31
|
+
value?: string | React.ReactNode
|
|
30
32
|
/** Optional muted caption rendered below the value. */
|
|
31
33
|
caption?: string
|
|
34
|
+
/**
|
|
35
|
+
* Optional glyph shown to the left of the value. Pass a registry icon name
|
|
36
|
+
* (e.g. `'ic_card'`) or a custom node. Opt-in — omit to keep the legacy
|
|
37
|
+
* text-only metric column.
|
|
38
|
+
*/
|
|
39
|
+
icon?: string | React.ReactNode
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
export interface PdpCcCardProps {
|
|
@@ -77,7 +85,7 @@ export interface PdpCcCardProps {
|
|
|
77
85
|
/**
|
|
78
86
|
* The metric columns rendered in the stats row. Vertical dividers are
|
|
79
87
|
* inserted automatically between adjacent metrics. Defaults to two sample
|
|
80
|
-
* metrics.
|
|
88
|
+
* metrics (with leading icons) matching the Figma default.
|
|
81
89
|
*/
|
|
82
90
|
metrics?: PdpCcCardMetric[]
|
|
83
91
|
/** CTA button label. Defaults to `"button"`. */
|
|
@@ -115,8 +123,9 @@ export interface PdpCcCardProps {
|
|
|
115
123
|
* `imageSource` for the default image or `media` for a full slot override.
|
|
116
124
|
* - **Title** — a centered headline + subtitle rendered through the shared
|
|
117
125
|
* {@link Title} component (`title/*`, `pageSubtitle/*` tokens).
|
|
118
|
-
* - **Metrics** — a row of {@link PdpCcCardMetric} columns
|
|
119
|
-
*
|
|
126
|
+
* - **Metrics** — a row of {@link PdpCcCardMetric} columns composed via
|
|
127
|
+
* {@link MetricData} (title / optional icon + value / caption) separated by
|
|
128
|
+
* vertical `Divider`s (`metricdata/*`, `icon/*` tokens).
|
|
120
129
|
* - **CTA** — a small tonal {@link Button} (`Button / Size: S`,
|
|
121
130
|
* `AppearanceBrand: Secondary`, `Emphasis: Medium`) with a leading icon.
|
|
122
131
|
*
|
|
@@ -179,12 +188,14 @@ function PdpCcCard({
|
|
|
179
188
|
<Button
|
|
180
189
|
label={buttonLabel}
|
|
181
190
|
modes={ctaModes}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
{...(onButtonPress !== undefined ? { onPress: onButtonPress } : null)}
|
|
192
|
+
{...(buttonIcon
|
|
193
|
+
? {
|
|
194
|
+
leading: (
|
|
195
|
+
<Icon iconName={buttonIcon} size={tokens.buttonIconSize} color={buttonForeground} />
|
|
196
|
+
),
|
|
197
|
+
}
|
|
198
|
+
: null)}
|
|
188
199
|
/>
|
|
189
200
|
)
|
|
190
201
|
|
|
@@ -197,9 +208,9 @@ function PdpCcCard({
|
|
|
197
208
|
subtitle={subtitle}
|
|
198
209
|
textAlign="Center"
|
|
199
210
|
modes={modes}
|
|
200
|
-
numberOfLines
|
|
201
|
-
disableTruncation
|
|
202
|
-
singleLine
|
|
211
|
+
{...(numberOfLines !== undefined ? { numberOfLines } : null)}
|
|
212
|
+
{...(disableTruncation !== undefined ? { disableTruncation } : null)}
|
|
213
|
+
{...(singleLine !== undefined ? { singleLine } : null)}
|
|
203
214
|
/>
|
|
204
215
|
|
|
205
216
|
{metrics.length > 0 ? (
|
|
@@ -209,7 +220,14 @@ function PdpCcCard({
|
|
|
209
220
|
{index > 0 ? (
|
|
210
221
|
<Divider direction="vertical" modes={modes} style={styles.metricDivider} />
|
|
211
222
|
) : null}
|
|
212
|
-
<
|
|
223
|
+
<MetricData
|
|
224
|
+
{...(metric.title !== undefined ? { title: metric.title } : null)}
|
|
225
|
+
{...(metric.value !== undefined ? { value: metric.value } : null)}
|
|
226
|
+
{...(metric.caption !== undefined ? { caption: metric.caption } : null)}
|
|
227
|
+
{...(metric.icon !== undefined ? { icon: metric.icon } : null)}
|
|
228
|
+
modes={modes}
|
|
229
|
+
style={styles.metricCell}
|
|
230
|
+
/>
|
|
213
231
|
</React.Fragment>
|
|
214
232
|
))}
|
|
215
233
|
</View>
|
|
@@ -244,20 +262,6 @@ function PdpCcCard({
|
|
|
244
262
|
)
|
|
245
263
|
}
|
|
246
264
|
|
|
247
|
-
// ---------------------------------------------------------------------------
|
|
248
|
-
// Metricdata — internal metric column (Figma node 5352:256)
|
|
249
|
-
// ---------------------------------------------------------------------------
|
|
250
|
-
|
|
251
|
-
function Metricdata({ metric, tokens }: { metric: PdpCcCardMetric; tokens: ResolvedTokens }) {
|
|
252
|
-
return (
|
|
253
|
-
<View style={tokens.metric}>
|
|
254
|
-
{metric.title != null ? <Text style={tokens.metricTitle}>{metric.title}</Text> : null}
|
|
255
|
-
{metric.value != null ? <Text style={tokens.metricValue}>{metric.value}</Text> : null}
|
|
256
|
-
{metric.caption != null ? <Text style={tokens.metricCaption}>{metric.caption}</Text> : null}
|
|
257
|
-
</View>
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
265
|
// ---------------------------------------------------------------------------
|
|
262
266
|
// Tokens / static styles
|
|
263
267
|
// ---------------------------------------------------------------------------
|
|
@@ -266,10 +270,6 @@ interface ResolvedTokens {
|
|
|
266
270
|
container: ViewStyle
|
|
267
271
|
imageRadius: number
|
|
268
272
|
buttonIconSize: number
|
|
269
|
-
metric: ViewStyle
|
|
270
|
-
metricTitle: TextStyle
|
|
271
|
-
metricValue: TextStyle
|
|
272
|
-
metricCaption: TextStyle
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function asNum(raw: unknown, fallback: number): number {
|
|
@@ -294,25 +294,6 @@ function resolveTokens(modes: Modes): ResolvedTokens {
|
|
|
294
294
|
const imageRadius = asNum(getVariableByName('image/radius', modes), 8)
|
|
295
295
|
const buttonIconSize = asNum(getVariableByName('button/iconSize', modes), 16)
|
|
296
296
|
|
|
297
|
-
const metricGap = asNum(getVariableByName('metricdata/gap', modes), 4)
|
|
298
|
-
const metricPadH = asNum(getVariableByName('metricdata/padding/horizontal', modes), 10)
|
|
299
|
-
const metricPadV = asNum(getVariableByName('metricdata/padding/vertical', modes), 10)
|
|
300
|
-
|
|
301
|
-
const titleColor = asStr(getVariableByName('metricdata/title/color', modes), '#000000')
|
|
302
|
-
const titleSize = asNum(getVariableByName('metricdata/title/fontsize', modes), 12)
|
|
303
|
-
const titleFamily = asStr(getVariableByName('metricdata/title/fontfamily', modes), 'JioType Var')
|
|
304
|
-
const titleWeight = asStr(getVariableByName('metricdata/title/fontweight', modes), '400')
|
|
305
|
-
|
|
306
|
-
const valueColor = asStr(getVariableByName('metricdata/value/color', modes), '#000000')
|
|
307
|
-
const valueSize = asNum(getVariableByName('metricdata/value/fontsize', modes), 20)
|
|
308
|
-
const valueFamily = asStr(getVariableByName('metricdata/value/fontfamily', modes), 'JioType Var')
|
|
309
|
-
const valueWeight = asStr(getVariableByName('metricdata/value/fontweight', modes), '700')
|
|
310
|
-
|
|
311
|
-
const captionColor = asStr(getVariableByName('metricdata/caption/color', modes), '#777777')
|
|
312
|
-
const captionSize = asNum(getVariableByName('metricdata/caption/fontsize', modes), 12)
|
|
313
|
-
const captionFamily = asStr(getVariableByName('metricdata/caption/fontfamily', modes), 'JioType Var')
|
|
314
|
-
const captionWeight = asStr(getVariableByName('metricdata/caption/fontweight', modes), '500')
|
|
315
|
-
|
|
316
297
|
return {
|
|
317
298
|
container: {
|
|
318
299
|
backgroundColor: background,
|
|
@@ -325,47 +306,12 @@ function resolveTokens(modes: Modes): ResolvedTokens {
|
|
|
325
306
|
},
|
|
326
307
|
imageRadius,
|
|
327
308
|
buttonIconSize,
|
|
328
|
-
metric: {
|
|
329
|
-
flex: 1,
|
|
330
|
-
gap: metricGap,
|
|
331
|
-
paddingHorizontal: metricPadH,
|
|
332
|
-
paddingVertical: metricPadV,
|
|
333
|
-
alignItems: 'center',
|
|
334
|
-
justifyContent: 'center',
|
|
335
|
-
},
|
|
336
|
-
metricTitle: {
|
|
337
|
-
color: titleColor,
|
|
338
|
-
fontSize: titleSize,
|
|
339
|
-
fontFamily: titleFamily,
|
|
340
|
-
fontWeight: titleWeight as TextStyle['fontWeight'],
|
|
341
|
-
lineHeight: Math.round(titleSize * 1.2),
|
|
342
|
-
textAlign: 'center',
|
|
343
|
-
includeFontPadding: false as any,
|
|
344
|
-
},
|
|
345
|
-
metricValue: {
|
|
346
|
-
color: valueColor,
|
|
347
|
-
fontSize: valueSize,
|
|
348
|
-
fontFamily: valueFamily,
|
|
349
|
-
fontWeight: valueWeight as TextStyle['fontWeight'],
|
|
350
|
-
lineHeight: Math.round(valueSize * 1.2),
|
|
351
|
-
textAlign: 'center',
|
|
352
|
-
includeFontPadding: false as any,
|
|
353
|
-
},
|
|
354
|
-
metricCaption: {
|
|
355
|
-
color: captionColor,
|
|
356
|
-
fontSize: captionSize,
|
|
357
|
-
fontFamily: captionFamily,
|
|
358
|
-
fontWeight: captionWeight as TextStyle['fontWeight'],
|
|
359
|
-
lineHeight: Math.round(captionSize * 1.2),
|
|
360
|
-
textAlign: 'center',
|
|
361
|
-
includeFontPadding: false as any,
|
|
362
|
-
},
|
|
363
309
|
}
|
|
364
310
|
}
|
|
365
311
|
|
|
366
312
|
const DEFAULT_METRICS: PdpCcCardMetric[] = [
|
|
367
|
-
{ title: 'Title', value: 'Value', caption: 'caption' },
|
|
368
|
-
{ title: 'Title', value: 'Value', caption: 'caption' },
|
|
313
|
+
{ title: 'Title', value: 'Value', caption: 'caption', icon: 'ic_card' },
|
|
314
|
+
{ title: 'Title', value: 'Value', caption: 'caption', icon: 'ic_card' },
|
|
369
315
|
]
|
|
370
316
|
|
|
371
317
|
const styles = StyleSheet.create({
|
|
@@ -379,6 +325,9 @@ const styles = StyleSheet.create({
|
|
|
379
325
|
flexDirection: 'row',
|
|
380
326
|
alignItems: 'stretch',
|
|
381
327
|
},
|
|
328
|
+
metricCell: {
|
|
329
|
+
flex: 1,
|
|
330
|
+
},
|
|
382
331
|
metricDivider: {
|
|
383
332
|
alignSelf: 'center',
|
|
384
333
|
height: '70%',
|
|
@@ -73,6 +73,12 @@ export type PlanComparisonCardProps = {
|
|
|
73
73
|
columns?: PlanComparisonColumn[];
|
|
74
74
|
/** Feature rows compared across the plan columns. */
|
|
75
75
|
rows?: PlanComparisonRow[];
|
|
76
|
+
/**
|
|
77
|
+
* Slot that replaces the default comparison table (Figma `Slot`). When
|
|
78
|
+
* omitted, the table is built from `columns` + `rows`. Header still
|
|
79
|
+
* renders from `columns`. `modes` are cascaded into slot children.
|
|
80
|
+
*/
|
|
81
|
+
children?: React.ReactNode;
|
|
76
82
|
/** Design token modes for theming (e.g. `{ "Color Mode": "Light" }`). */
|
|
77
83
|
modes?: Modes;
|
|
78
84
|
/** Override the outer container style. */
|
|
@@ -88,6 +94,7 @@ const DEFAULT_ROWS: PlanComparisonRow[] = [
|
|
|
88
94
|
{ label: 'JioPoints multiplier', values: ['1x', '1.25x'] },
|
|
89
95
|
{ label: 'Cashback', showInfo: true, values: [false, 'Upto ₹5000'] },
|
|
90
96
|
{ label: 'Bonus JioGold', showInfo: true, values: [false, '1%'] },
|
|
97
|
+
{ label: 'Bonus JioSilver', showInfo: true, values: [false, '1%'] },
|
|
91
98
|
];
|
|
92
99
|
|
|
93
100
|
/** Keeps every text layer on a single line. */
|
|
@@ -112,13 +119,15 @@ const planColumnStyle: ViewStyle = {
|
|
|
112
119
|
* current plan against one or more alternative plans across a set of feature
|
|
113
120
|
* rows. Implementation of Figma node `4498:2968` (`PlanComparisonCard`).
|
|
114
121
|
*
|
|
115
|
-
*
|
|
122
|
+
* Structure: header (from `columns`) + Slot (default table from `rows`, or
|
|
123
|
+
* custom `children`). Columns use a 1.8fr / 1fr flex ratio (label vs plan).
|
|
116
124
|
*
|
|
117
125
|
* @component
|
|
118
126
|
*/
|
|
119
127
|
function PlanComparisonCard({
|
|
120
128
|
columns = DEFAULT_COLUMNS,
|
|
121
129
|
rows = DEFAULT_ROWS,
|
|
130
|
+
children,
|
|
122
131
|
modes = EMPTY_MODES,
|
|
123
132
|
style,
|
|
124
133
|
}: PlanComparisonCardProps) {
|
|
@@ -234,6 +243,65 @@ function PlanComparisonCard({
|
|
|
234
243
|
return cloneChildrenWithModes(value, modes);
|
|
235
244
|
};
|
|
236
245
|
|
|
246
|
+
const defaultTable = (
|
|
247
|
+
<View
|
|
248
|
+
style={{
|
|
249
|
+
width: '100%',
|
|
250
|
+
backgroundColor: tableBackground,
|
|
251
|
+
borderWidth: tableBorderSize,
|
|
252
|
+
borderColor: tableBorderColor,
|
|
253
|
+
borderRadius: tableRadius,
|
|
254
|
+
overflow: 'hidden',
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
{rows.map((row, rowIndex) => {
|
|
258
|
+
const isLast = rowIndex === rows.length - 1;
|
|
259
|
+
const showInfo = row.showInfo || row.onInfoPress != null;
|
|
260
|
+
return (
|
|
261
|
+
<View
|
|
262
|
+
key={row.key ?? `${row.label}-${rowIndex}`}
|
|
263
|
+
style={[
|
|
264
|
+
rowStyle,
|
|
265
|
+
{
|
|
266
|
+
borderBottomWidth: isLast ? 0 : cellBorderSize,
|
|
267
|
+
borderBottomColor: cellBorderColor,
|
|
268
|
+
},
|
|
269
|
+
]}
|
|
270
|
+
>
|
|
271
|
+
<View style={[labelColumnStyle, labelCellStyle]}>
|
|
272
|
+
<Text style={labelTextStyle}>{row.label}</Text>
|
|
273
|
+
{showInfo &&
|
|
274
|
+
(row.onInfoPress ? (
|
|
275
|
+
<Pressable
|
|
276
|
+
onPress={row.onInfoPress}
|
|
277
|
+
accessibilityRole="button"
|
|
278
|
+
accessibilityLabel={`More information about ${row.label}`}
|
|
279
|
+
hitSlop={8}
|
|
280
|
+
>
|
|
281
|
+
<Icon name="ic_info" size={iconSize} color={iconColor} />
|
|
282
|
+
</Pressable>
|
|
283
|
+
) : (
|
|
284
|
+
<Icon name="ic_info" size={iconSize} color={iconColor} />
|
|
285
|
+
))}
|
|
286
|
+
</View>
|
|
287
|
+
|
|
288
|
+
{columns.map((column, colIndex) => (
|
|
289
|
+
<View
|
|
290
|
+
key={column.label ?? colIndex}
|
|
291
|
+
style={[planColumnStyle, valueCellStyle]}
|
|
292
|
+
>
|
|
293
|
+
{renderValue(
|
|
294
|
+
row.values?.[colIndex],
|
|
295
|
+
`${rowIndex}-${colIndex}`,
|
|
296
|
+
)}
|
|
297
|
+
</View>
|
|
298
|
+
))}
|
|
299
|
+
</View>
|
|
300
|
+
);
|
|
301
|
+
})}
|
|
302
|
+
</View>
|
|
303
|
+
);
|
|
304
|
+
|
|
237
305
|
return (
|
|
238
306
|
<View style={[{ gap, width: '100%' }, style]}>
|
|
239
307
|
{/* Header row — same 1.8fr / 1fr grid as the table */}
|
|
@@ -253,63 +321,10 @@ function PlanComparisonCard({
|
|
|
253
321
|
))}
|
|
254
322
|
</View>
|
|
255
323
|
|
|
256
|
-
{/*
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
backgroundColor: tableBackground,
|
|
261
|
-
borderWidth: tableBorderSize,
|
|
262
|
-
borderColor: tableBorderColor,
|
|
263
|
-
borderRadius: tableRadius,
|
|
264
|
-
overflow: 'hidden',
|
|
265
|
-
}}
|
|
266
|
-
>
|
|
267
|
-
{rows.map((row, rowIndex) => {
|
|
268
|
-
const isLast = rowIndex === rows.length - 1;
|
|
269
|
-
const showInfo = row.showInfo || row.onInfoPress != null;
|
|
270
|
-
return (
|
|
271
|
-
<View
|
|
272
|
-
key={row.key ?? `${row.label}-${rowIndex}`}
|
|
273
|
-
style={[
|
|
274
|
-
rowStyle,
|
|
275
|
-
{
|
|
276
|
-
borderBottomWidth: isLast ? 0 : cellBorderSize,
|
|
277
|
-
borderBottomColor: cellBorderColor,
|
|
278
|
-
},
|
|
279
|
-
]}
|
|
280
|
-
>
|
|
281
|
-
<View style={[labelColumnStyle, labelCellStyle]}>
|
|
282
|
-
<Text style={labelTextStyle}>{row.label}</Text>
|
|
283
|
-
{showInfo &&
|
|
284
|
-
(row.onInfoPress ? (
|
|
285
|
-
<Pressable
|
|
286
|
-
onPress={row.onInfoPress}
|
|
287
|
-
accessibilityRole="button"
|
|
288
|
-
accessibilityLabel={`More information about ${row.label}`}
|
|
289
|
-
hitSlop={8}
|
|
290
|
-
>
|
|
291
|
-
<Icon name="ic_info" size={iconSize} color={iconColor} />
|
|
292
|
-
</Pressable>
|
|
293
|
-
) : (
|
|
294
|
-
<Icon name="ic_info" size={iconSize} color={iconColor} />
|
|
295
|
-
))}
|
|
296
|
-
</View>
|
|
297
|
-
|
|
298
|
-
{columns.map((column, colIndex) => (
|
|
299
|
-
<View
|
|
300
|
-
key={column.label ?? colIndex}
|
|
301
|
-
style={[planColumnStyle, valueCellStyle]}
|
|
302
|
-
>
|
|
303
|
-
{renderValue(
|
|
304
|
-
row.values?.[colIndex],
|
|
305
|
-
`${rowIndex}-${colIndex}`,
|
|
306
|
-
)}
|
|
307
|
-
</View>
|
|
308
|
-
))}
|
|
309
|
-
</View>
|
|
310
|
-
);
|
|
311
|
-
})}
|
|
312
|
-
</View>
|
|
324
|
+
{/* Figma Slot — custom children replace the default table */}
|
|
325
|
+
{children != null
|
|
326
|
+
? cloneChildrenWithModes(children, modes)
|
|
327
|
+
: defaultTable}
|
|
313
328
|
</View>
|
|
314
329
|
);
|
|
315
330
|
}
|
|
@@ -547,9 +547,9 @@ function resolveTokens(modes: Modes): ResolvedTokens {
|
|
|
547
547
|
const tooltipBackground = asStr(getVariableByName('tooltip/background', modes), '#0f0d0a')
|
|
548
548
|
const tooltipPaddingH = asNum(getVariableByName('tooltip/padding/horizontal', modes), 12)
|
|
549
549
|
const tooltipPaddingV = asNum(getVariableByName('tooltip/padding/vertical', modes), 8)
|
|
550
|
-
//
|
|
551
|
-
const tooltipRadius =
|
|
552
|
-
const tooltipMaxWidth =
|
|
550
|
+
// Figma Tooltip binds bare local `radius` / `maxWidth` (not tooltip/*).
|
|
551
|
+
const tooltipRadius = Number(getVariableByName('radius', modes))
|
|
552
|
+
const tooltipMaxWidth = Number(getVariableByName('maxWidth', modes))
|
|
553
553
|
const tipWidth = asNum(getVariableByName('tooltip/tipItem/width', modes), 16)
|
|
554
554
|
const tipHeight = asNum(getVariableByName('tooltip/tipItem/height', modes), 8)
|
|
555
555
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { forwardRef, useRef, useState } from 'react'
|
|
2
|
-
import { Platform, Pressable, View, TextInput as RNTextInput, type StyleProp, type ViewStyle, type TextInputProps as RNTextInputProps, type TextStyle } from 'react-native'
|
|
2
|
+
import { Platform, Pressable, StyleSheet, View, TextInput as RNTextInput, type StyleProp, type ViewStyle, type TextInputProps as RNTextInputProps, type TextStyle } from 'react-native'
|
|
3
3
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
|
|
4
4
|
import Icon from '../../icons/Icon'
|
|
5
5
|
import { EMPTY_MODES, cloneChildrenWithModes, mergeRefs } from '../../utils/react-utils'
|
|
@@ -178,28 +178,45 @@ const TextInputBase = forwardRef<RNTextInput, TextInputProps>(function TextInput
|
|
|
178
178
|
const hoverStyle: ViewStyle = isHovered ? { opacity: 0.95 } : {}
|
|
179
179
|
const focusContainerStyle: ViewStyle = isFocused ? { borderColor: '#222', borderWidth: 1 } : {}
|
|
180
180
|
|
|
181
|
+
// ⚠️ DO NOT change to an unconditional `lineHeight`, and do NOT put
|
|
182
|
+
// `minHeight: lineHeight` on this single-line input on iOS. Same contract as
|
|
183
|
+
// FormField / SuggestiveSearch — read before "simplifying".
|
|
184
|
+
//
|
|
185
|
+
// facebook/react-native#39145 / #28012: on iOS, a single-line RN TextInput
|
|
186
|
+
// (UITextField) with `lineHeight` > font metrics puts ALL the extra space
|
|
187
|
+
// ABOVE the glyphs, so placeholder/value sink. Android/Web distribute evenly.
|
|
188
|
+
// RN's documented workaround (#39145) and #56774: omit `lineHeight` on
|
|
189
|
+
// single-line iOS inputs — UITextField centers natively — and let the
|
|
190
|
+
// wrapper (`alignItems: 'center'` + padding/icon) own vertical layout.
|
|
191
|
+
// Pinning `lineHeight` to `fontSize` is NOT a substitute; it clips custom
|
|
192
|
+
// font metrics and floats typed text above center.
|
|
181
193
|
const textInputStyle: TextStyle = {
|
|
182
194
|
flex: 1,
|
|
183
195
|
color: textColor,
|
|
184
196
|
fontSize,
|
|
185
|
-
// iOS-only quirk: a single-line RNTextInput with an explicit `lineHeight`
|
|
186
|
-
// larger than `fontSize` puts all the extra space ABOVE the glyphs, so the
|
|
187
|
-
// text/placeholder sits below center (iOS native font-metrics bug — see
|
|
188
|
-
// facebook/react-native#39145 / #28012). The container centers via
|
|
189
|
-
// `alignItems: 'center'` + `minHeight`, so omit lineHeight on iOS
|
|
190
|
-
// (UITextField centers natively) and keep it on Android/Web where it's fine.
|
|
191
197
|
...(Platform.OS === 'ios' ? null : { lineHeight }),
|
|
192
198
|
fontFamily,
|
|
193
199
|
fontWeight,
|
|
194
200
|
padding: 0,
|
|
195
201
|
margin: 0,
|
|
196
|
-
minHeight: lineHeight,
|
|
197
202
|
// Remove focus outline for React Native Web
|
|
198
203
|
outlineStyle: 'none' as any,
|
|
199
204
|
outlineWidth: 0,
|
|
200
205
|
outlineColor: 'transparent',
|
|
201
206
|
}
|
|
202
207
|
|
|
208
|
+
// On iOS, strip any consumer `inputStyle.lineHeight` so it can't re-break
|
|
209
|
+
// the native centering contract above.
|
|
210
|
+
const resolvedInputStyle: StyleProp<TextStyle> =
|
|
211
|
+
Platform.OS === 'ios' && inputStyle != null
|
|
212
|
+
? (() => {
|
|
213
|
+
const flat = StyleSheet.flatten(inputStyle) as TextStyle | undefined
|
|
214
|
+
if (!flat || flat.lineHeight == null) return inputStyle
|
|
215
|
+
const { lineHeight: _ignoredLineHeight, ...restStyle } = flat
|
|
216
|
+
return restStyle
|
|
217
|
+
})()
|
|
218
|
+
: inputStyle
|
|
219
|
+
|
|
203
220
|
// Determine leading element: use provided leading, or default to icon from leadingIconName
|
|
204
221
|
const leadingElement = leading || (
|
|
205
222
|
<Icon name={leadingIconName} size={iconSize} color={iconColor} />
|
|
@@ -266,7 +283,7 @@ const TextInputBase = forwardRef<RNTextInput, TextInputProps>(function TextInput
|
|
|
266
283
|
placeholderTextColor={placeholderColor}
|
|
267
284
|
onFocus={handleFocus}
|
|
268
285
|
onBlur={handleBlur}
|
|
269
|
-
style={[textInputStyle,
|
|
286
|
+
style={[textInputStyle, resolvedInputStyle]}
|
|
270
287
|
{...rest}
|
|
271
288
|
/>
|
|
272
289
|
{processedTrailing && (
|
|
@@ -262,14 +262,12 @@ export function TooltipContent({
|
|
|
262
262
|
const insets = useSafeAreaInsets()
|
|
263
263
|
const { width: windowWidth, height: windowHeight } = Dimensions.get('window')
|
|
264
264
|
|
|
265
|
-
// Tokens
|
|
265
|
+
// Tokens — Figma binds Output/Tooltip `radius` + `maxWidth` (not tooltip/*).
|
|
266
266
|
const bgColor = (getVariableByName('tooltip/background', modes) as string)
|
|
267
267
|
const paddingH = Number(getVariableByName('tooltip/padding/horizontal', modes))
|
|
268
268
|
const paddingV = Number(getVariableByName('tooltip/padding/vertical', modes))
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const radius = Number(getVariableByName('tooltip/radius', modes) ?? 8)
|
|
272
|
-
const maxWidthToken = Number(getVariableByName('tooltip/maxWidth', modes) ?? 280)
|
|
269
|
+
const radius = Number(getVariableByName('radius', modes))
|
|
270
|
+
const maxWidthToken = Number(getVariableByName('maxWidth', modes))
|
|
273
271
|
const arrowWidth = Number(getVariableByName('tooltip/tipItem/width', modes))
|
|
274
272
|
const arrowHeight = Number(getVariableByName('tooltip/tipItem/height', modes))
|
|
275
273
|
const labelColor = (getVariableByName('tooltip/label/color', modes) as string)
|
package/src/components/index.ts
CHANGED
|
@@ -14,8 +14,15 @@ export { default as BenefitCard, type BenefitCardProps, type BenefitCardItem } f
|
|
|
14
14
|
export { default as BottomNav } from './BottomNav/BottomNav'
|
|
15
15
|
export { default as BottomNavItem } from './BottomNavItem/BottomNavItem'
|
|
16
16
|
export { default as BrandChip, type BrandChipProps } from './BrandChip/BrandChip'
|
|
17
|
-
export { default as Button, type ButtonProps } from './Button/Button'
|
|
18
|
-
export {
|
|
17
|
+
export { default as Button, type ButtonProps, type ButtonType } from './Button/Button'
|
|
18
|
+
export {
|
|
19
|
+
default as Card,
|
|
20
|
+
type CardProps,
|
|
21
|
+
type CardTextProps,
|
|
22
|
+
type CardVariant,
|
|
23
|
+
type CardVariantInput,
|
|
24
|
+
CARD_MEDIA_ASPECT_RATIO,
|
|
25
|
+
} from './Card/Card'
|
|
19
26
|
export { default as CardAdvisory, type CardAdvisoryProps } from './CardAdvisory/CardAdvisory'
|
|
20
27
|
export {
|
|
21
28
|
default as CardBankAccount,
|