jfs-components 0.1.51 → 0.1.55
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/.cursor/D2C-SCREEN.md +2 -1
- package/.cursor/commands/D2C.md +2 -1
- package/CHANGELOG.md +17 -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/CircularProgressBarDoted/CircularProgressBarDoted.js +1 -1
- 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/CircularProgressBarDoted/CircularProgressBarDoted.js +1 -1
- 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/CircularProgressBarDoted/CircularProgressBarDoted.tsx +1 -1
- 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
|
@@ -10,13 +10,21 @@ import GlassFill from '../../utils/GlassFill/GlassFill';
|
|
|
10
10
|
import IconButton from '../IconButton/IconButton';
|
|
11
11
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
12
|
const IS_WEB = Platform.OS === 'web';
|
|
13
|
-
|
|
13
|
+
/** Figma collection name — must match `figma-modes.generated.ts` exactly. */
|
|
14
|
+
const STATE_COLLECTION = 'Hello Jio Input State';
|
|
14
15
|
|
|
15
16
|
/** Default IconButton modes matching the Figma send control. */
|
|
16
17
|
const DEFAULT_SEND_BUTTON_MODES = Object.freeze({
|
|
17
18
|
AppearanceBrand: 'Primary',
|
|
18
19
|
'Button / Size': 'S'
|
|
19
20
|
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Figma `Hello Jio Input State` only exposes Idle | Active. The component-set
|
|
24
|
+
* variant IdleJioPlus is glass chrome on top of Idle tokens — never a mode
|
|
25
|
+
* value for this collection.
|
|
26
|
+
*/
|
|
27
|
+
|
|
20
28
|
const toNumber = (value, fallback) => {
|
|
21
29
|
if (typeof value === 'number') {
|
|
22
30
|
return Number.isFinite(value) ? value : fallback;
|
|
@@ -32,6 +40,29 @@ const toFontWeight = (value, fallback) => {
|
|
|
32
40
|
if (typeof value === 'string') return value;
|
|
33
41
|
return fallback;
|
|
34
42
|
};
|
|
43
|
+
|
|
44
|
+
/** True when a CSS/RN color string already carries a non-opaque alpha channel. */
|
|
45
|
+
const hasAlphaChannel = color => {
|
|
46
|
+
const trimmed = color.trim();
|
|
47
|
+
if (/^#[0-9a-fA-F]{8}$/.test(trimmed)) {
|
|
48
|
+
const alpha = parseInt(trimmed.slice(7, 9), 16);
|
|
49
|
+
return alpha < 255;
|
|
50
|
+
}
|
|
51
|
+
if (/^#[0-9a-fA-F]{4}$/.test(trimmed)) {
|
|
52
|
+
const alphaNibble = parseInt(trimmed.slice(3, 4), 16);
|
|
53
|
+
return alphaNibble < 15;
|
|
54
|
+
}
|
|
55
|
+
const func = trimmed.match(/^(rgba|hsla)\(/i);
|
|
56
|
+
if (func) {
|
|
57
|
+
const parts = trimmed.slice(trimmed.indexOf('(') + 1, -1).split(',');
|
|
58
|
+
if (parts.length === 4) {
|
|
59
|
+
const alpha = Number(parts[3].trim());
|
|
60
|
+
return Number.isFinite(alpha) && alpha < 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
|
|
35
66
|
/**
|
|
36
67
|
* Fallback values from the Figma `HelloJioInput` component when local token
|
|
37
68
|
* JSON has not been synced yet. Mode-driven `getVariableByName` wins when
|
|
@@ -51,14 +82,11 @@ const STATE_FALLBACKS = {
|
|
|
51
82
|
iconColor: '#24262b'
|
|
52
83
|
},
|
|
53
84
|
IdleJioPlus: {
|
|
54
|
-
//
|
|
55
|
-
// `
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// translucent glass fallbacks the sibling GlassFill consumers use
|
|
60
|
-
// (FavoriteToggle `#ffffff33`, NumberPagination `rgba(141,141,141,0.4)`),
|
|
61
|
-
// this fallback carries alpha so the frost tints the blurred content.
|
|
85
|
+
// Frosted IdleJioPlus fill. Figma paints `helloJioInput/background` over
|
|
86
|
+
// `glass/minimal` blur; the synced token is often opaque `#f5f5f5`, which
|
|
87
|
+
// would hide the blur. Match sibling GlassFill consumers (FavoriteToggle
|
|
88
|
+
// `#ffffff33`, NumberPagination `rgba(141,141,141,0.4)`) with alpha so the
|
|
89
|
+
// frost tints the blurred content.
|
|
62
90
|
background: '#f5f5f566',
|
|
63
91
|
borderColor: '#f5f5f5',
|
|
64
92
|
labelColor: '#545961',
|
|
@@ -88,6 +116,11 @@ function resolveHelloJioInputTokens(modes, visualState) {
|
|
|
88
116
|
const blurMinimal = toNumber(getVariableByName('blur/minimal', modes), 29);
|
|
89
117
|
const blurIntensity = Math.max(0, Math.min(100, Math.round(blurMinimal)));
|
|
90
118
|
const useGlass = visualState === 'IdleJioPlus';
|
|
119
|
+
|
|
120
|
+
// Figma IdleJioPlus paints `helloJioInput/background` over `glass/minimal`
|
|
121
|
+
// blur. The exported token is often opaque `#f5f5f5` (alpha lost in sync),
|
|
122
|
+
// which would fully hide the blur — keep a translucent frost when needed.
|
|
123
|
+
const glassOverlay = useGlass ? hasAlphaChannel(background) ? background : STATE_FALLBACKS.IdleJioPlus.background : background;
|
|
91
124
|
return {
|
|
92
125
|
containerStyle: {
|
|
93
126
|
position: 'relative',
|
|
@@ -133,7 +166,8 @@ function resolveHelloJioInputTokens(modes, visualState) {
|
|
|
133
166
|
iconColor,
|
|
134
167
|
iconSize,
|
|
135
168
|
blurIntensity,
|
|
136
|
-
useGlass
|
|
169
|
+
useGlass,
|
|
170
|
+
glassOverlay
|
|
137
171
|
};
|
|
138
172
|
}
|
|
139
173
|
|
|
@@ -180,12 +214,21 @@ const HelloJioInput = /*#__PURE__*/forwardRef(function HelloJioInput({
|
|
|
180
214
|
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
181
215
|
const isControlled = value !== undefined;
|
|
182
216
|
const currentValue = isControlled ? value : uncontrolledValue;
|
|
217
|
+
|
|
218
|
+
// Component-set visual vs token mode:
|
|
219
|
+
// - Idle / Active map 1:1 onto `Hello Jio Input State`
|
|
220
|
+
// - IdleJioPlus is Figma chrome (glass) on Idle tokens — never pass
|
|
221
|
+
// "IdleJioPlus" into the state collection (that mode does not exist)
|
|
183
222
|
const visualState = isFocused ? 'Active' : jioPlus ? 'IdleJioPlus' : 'Idle';
|
|
223
|
+
const tokenState = isFocused ? 'Active' : 'Idle';
|
|
184
224
|
const modes = useMemo(() => ({
|
|
185
225
|
...globalModes,
|
|
186
226
|
...propModes,
|
|
187
|
-
[STATE_COLLECTION]:
|
|
188
|
-
}), [globalModes, propModes,
|
|
227
|
+
[STATE_COLLECTION]: tokenState
|
|
228
|
+
}), [globalModes, propModes, tokenState]);
|
|
229
|
+
|
|
230
|
+
// Defaults first; caller/`modes` may still override Size/Appearance.
|
|
231
|
+
// Custom chrome that must ignore parent modes belongs in `trailing`.
|
|
189
232
|
const sendButtonModes = useMemo(() => ({
|
|
190
233
|
...DEFAULT_SEND_BUTTON_MODES,
|
|
191
234
|
...modes
|
|
@@ -233,12 +276,11 @@ const HelloJioInput = /*#__PURE__*/forwardRef(function HelloJioInput({
|
|
|
233
276
|
const containerStyleArray = [tokens.containerStyle, hoverStyle, disabled ? {
|
|
234
277
|
opacity: 0.5
|
|
235
278
|
} : null, style];
|
|
236
|
-
const glassOverlay = getVariableByName('helloJioInput/background', modes) ?? STATE_FALLBACKS.IdleJioPlus.background;
|
|
237
279
|
const inner = /*#__PURE__*/_jsxs(_Fragment, {
|
|
238
280
|
children: [tokens.useGlass ? /*#__PURE__*/_jsx(GlassFill, {
|
|
239
281
|
tint: "light",
|
|
240
282
|
intensity: tokens.blurIntensity,
|
|
241
|
-
overlayColor: glassOverlay,
|
|
283
|
+
overlayColor: tokens.glassOverlay,
|
|
242
284
|
androidTintWash: false
|
|
243
285
|
}) : null, processedLeading ? /*#__PURE__*/_jsx(View, {
|
|
244
286
|
accessibilityElementsHidden: true,
|
|
@@ -44,7 +44,7 @@ function resolveIconCapsuleTokens(modes) {
|
|
|
44
44
|
* @param {Object} props - Component props
|
|
45
45
|
* @param {string} [props.iconName="ic_card"] - The name of the icon to display from the icon registry
|
|
46
46
|
* @param {UnifiedSource} [props.source] - Fallback source (remote URI, inline SVG XML, `require()` asset, SVG React component, or React element). Used when `iconName` is missing or unknown. Tinted with the mode-resolved icon color so it follows design tokens just like a built-in icon.
|
|
47
|
-
* @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"
|
|
47
|
+
* @param {Object} [props.modes={}] - Mode configuration for design tokens (e.g., {"AppearanceBrand": "Primary"})
|
|
48
48
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers
|
|
49
49
|
* @param {string} [props.accessibilityRole] - Accessibility role (defaults to "image" for decorative icons)
|
|
50
50
|
*
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
4
|
import { View, Text } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
6
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
7
7
|
import Icon from '../Icon/Icon';
|
|
8
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
9
|
const LINE_HEIGHT_RATIO = 1.2;
|
|
10
|
-
const ICON_SIZE = 18;
|
|
11
10
|
// Figma renders the value-row icon with no surrounding padding; the shared Icon
|
|
12
11
|
// component pulls padding from `icon/padding/*` tokens, so we zero it here.
|
|
13
12
|
const ICON_PADDING_RESET = {
|
|
@@ -20,9 +19,10 @@ const ICON_PADDING_RESET = {
|
|
|
20
19
|
* MetricData — a compact, centered metric block.
|
|
21
20
|
*
|
|
22
21
|
* Stacks a small `title`, a prominent `value` (with an optional leading
|
|
23
|
-
* `icon`), and a muted `caption`. Typography, colours,
|
|
24
|
-
* resolve from the `metricdata/*` design tokens
|
|
25
|
-
*
|
|
22
|
+
* `icon` in a `value wrap` row), and a muted `caption`. Typography, colours,
|
|
23
|
+
* gaps and padding resolve from the `metricdata/*` design tokens; the leading
|
|
24
|
+
* glyph uses `icon/*`. `title`, `caption` and `icon` are optional — omit
|
|
25
|
+
* `icon` to keep the legacy text-only layout.
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```tsx
|
|
@@ -42,10 +42,16 @@ function MetricData({
|
|
|
42
42
|
captionStyle,
|
|
43
43
|
accessibilityLabel
|
|
44
44
|
}) {
|
|
45
|
+
// Figma's Metricdata instance seeds Secondary on the nested Icon so the
|
|
46
|
+
// default glyph is brand purple (`#5d00b5`). Caller `modes` still win.
|
|
47
|
+
const iconModes = useMemo(() => ({
|
|
48
|
+
AppearanceBrand: 'Secondary',
|
|
49
|
+
...modes
|
|
50
|
+
}), [modes]);
|
|
45
51
|
const titleColor = getVariableByName('metricdata/title/color', modes) ?? '#000000';
|
|
46
52
|
const titleFontFamily = getVariableByName('metricdata/title/fontfamily', modes) ?? 'JioType Var';
|
|
47
53
|
const titleFontSize = getVariableByName('metricdata/title/fontsize', modes) ?? 12;
|
|
48
|
-
const titleFontWeight = String(getVariableByName('metricdata/title/fontweight', modes) ??
|
|
54
|
+
const titleFontWeight = String(getVariableByName('metricdata/title/fontweight', modes) ?? 500);
|
|
49
55
|
const valueColor = getVariableByName('metricdata/value/color', modes) ?? '#000000';
|
|
50
56
|
const valueFontFamily = getVariableByName('metricdata/value/fontfamily', modes) ?? 'JioType Var';
|
|
51
57
|
const valueFontSize = getVariableByName('metricdata/value/fontsize', modes) ?? 20;
|
|
@@ -53,11 +59,16 @@ function MetricData({
|
|
|
53
59
|
const captionColor = getVariableByName('metricdata/caption/color', modes) ?? '#777777';
|
|
54
60
|
const captionFontFamily = getVariableByName('metricdata/caption/fontfamily', modes) ?? 'JioType Var';
|
|
55
61
|
const captionFontSize = getVariableByName('metricdata/caption/fontsize', modes) ?? 12;
|
|
56
|
-
const captionFontWeight = String(getVariableByName('metricdata/caption/fontweight', modes) ??
|
|
62
|
+
const captionFontWeight = String(getVariableByName('metricdata/caption/fontweight', modes) ?? 400);
|
|
57
63
|
const gap = getVariableByName('metricdata/gap', modes) ?? 4;
|
|
58
64
|
const valueWrapGap = getVariableByName('metricdata/valueWrap/gap', modes) ?? 4;
|
|
59
65
|
const paddingHorizontal = getVariableByName('metricdata/padding/horizontal', modes) ?? 10;
|
|
60
66
|
const paddingVertical = getVariableByName('metricdata/padding/vertical', modes) ?? 10;
|
|
67
|
+
|
|
68
|
+
// Resolve via getVariableByName so extract-tokens picks up the new bindings;
|
|
69
|
+
// Icon reads the same tokens when color/size props are omitted.
|
|
70
|
+
const iconColor = getVariableByName('icon/color', iconModes) ?? '#5d00b5';
|
|
71
|
+
const iconSize = getVariableByName('icon/size', iconModes) ?? 18;
|
|
61
72
|
const containerStyle = {
|
|
62
73
|
flexDirection: 'column',
|
|
63
74
|
alignItems: 'center',
|
|
@@ -92,11 +103,12 @@ function MetricData({
|
|
|
92
103
|
};
|
|
93
104
|
const iconNode = icon == null ? null : typeof icon === 'string' ? /*#__PURE__*/_jsx(Icon, {
|
|
94
105
|
iconName: icon,
|
|
95
|
-
size:
|
|
96
|
-
color:
|
|
97
|
-
modes:
|
|
106
|
+
size: iconSize,
|
|
107
|
+
color: iconColor,
|
|
108
|
+
modes: iconModes,
|
|
98
109
|
style: ICON_PADDING_RESET
|
|
99
|
-
}) : icon;
|
|
110
|
+
}) : cloneChildrenWithModes(icon, iconModes);
|
|
111
|
+
const showValueWrap = iconNode != null || value !== undefined;
|
|
100
112
|
const resolvedLabel = accessibilityLabel ?? ([title, typeof value === 'string' ? value : undefined, caption].filter(Boolean).join(', ') || undefined);
|
|
101
113
|
return /*#__PURE__*/_jsxs(View, {
|
|
102
114
|
style: [containerStyle, style],
|
|
@@ -107,7 +119,7 @@ function MetricData({
|
|
|
107
119
|
children: [title !== undefined && title !== '' ? /*#__PURE__*/_jsx(Text, {
|
|
108
120
|
style: [titleTextStyle, titleStyle],
|
|
109
121
|
children: title
|
|
110
|
-
}) : null, /*#__PURE__*/_jsxs(View, {
|
|
122
|
+
}) : null, showValueWrap ? /*#__PURE__*/_jsxs(View, {
|
|
111
123
|
style: {
|
|
112
124
|
flexDirection: 'row',
|
|
113
125
|
alignItems: 'center',
|
|
@@ -118,7 +130,7 @@ function MetricData({
|
|
|
118
130
|
style: [valueTextStyle, valueStyle],
|
|
119
131
|
children: value
|
|
120
132
|
}) : value]
|
|
121
|
-
}), caption !== undefined && caption !== '' ? /*#__PURE__*/_jsx(Text, {
|
|
133
|
+
}) : null, caption !== undefined && caption !== '' ? /*#__PURE__*/_jsx(Text, {
|
|
122
134
|
style: [captionTextStyle, captionStyle],
|
|
123
135
|
children: caption
|
|
124
136
|
}) : null]
|
|
@@ -176,7 +176,7 @@ function MoneyValue({
|
|
|
176
176
|
}, [focused, cursorOpacity]);
|
|
177
177
|
|
|
178
178
|
// Keyboard avoidance — lift the component when the keyboard would cover it.
|
|
179
|
-
//
|
|
179
|
+
// Uses Keyboard.addListener + measureInWindow overlap math (field-local).
|
|
180
180
|
// Resets when editing ends or the keyboard hides.
|
|
181
181
|
const rootRef = useRef(null);
|
|
182
182
|
const keyboardLift = useRef(new Animated.Value(0)).current;
|
|
@@ -15,7 +15,7 @@ function toFontWeight(value, fallback) {
|
|
|
15
15
|
}
|
|
16
16
|
function resolveNudgeTokens(modes) {
|
|
17
17
|
const background = getVariableByName('nudge/background', modes);
|
|
18
|
-
const radius = getVariableByName('nudge/
|
|
18
|
+
const radius = getVariableByName('nudge/borderRadius', modes);
|
|
19
19
|
const paddingH = getVariableByName('nudge/padding/horizontal', modes);
|
|
20
20
|
const paddingV = getVariableByName('nudge/padding/vertical', modes);
|
|
21
21
|
const gap = getVariableByName('nudge/gap', modes);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
|
-
import { View,
|
|
4
|
+
import { View, Pressable, StyleSheet } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
7
7
|
import Title from '../Title/Title';
|
|
@@ -9,10 +9,11 @@ import Divider from '../Divider/Divider';
|
|
|
9
9
|
import Button from '../Button/Button';
|
|
10
10
|
import Image from '../Image/Image';
|
|
11
11
|
import Icon from '../Icon/Icon';
|
|
12
|
+
import MetricData from '../MetricData/MetricData';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* A single metric column inside the card's stats row: a small title, a bold
|
|
15
|
-
* value and an optional muted caption.
|
|
16
|
+
* value (optionally preceded by an icon) and an optional muted caption.
|
|
16
17
|
*/
|
|
17
18
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
19
|
/**
|
|
@@ -25,8 +26,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
25
26
|
* `imageSource` for the default image or `media` for a full slot override.
|
|
26
27
|
* - **Title** — a centered headline + subtitle rendered through the shared
|
|
27
28
|
* {@link Title} component (`title/*`, `pageSubtitle/*` tokens).
|
|
28
|
-
* - **Metrics** — a row of {@link PdpCcCardMetric} columns
|
|
29
|
-
*
|
|
29
|
+
* - **Metrics** — a row of {@link PdpCcCardMetric} columns composed via
|
|
30
|
+
* {@link MetricData} (title / optional icon + value / caption) separated by
|
|
31
|
+
* vertical `Divider`s (`metricdata/*`, `icon/*` tokens).
|
|
30
32
|
* - **CTA** — a small tonal {@link Button} (`Button / Size: S`,
|
|
31
33
|
* `AppearanceBrand: Secondary`, `Emphasis: Medium`) with a leading icon.
|
|
32
34
|
*
|
|
@@ -78,12 +80,16 @@ function PdpCcCard({
|
|
|
78
80
|
const ctaNode = button ? cloneChildrenWithModes(button, ctaModes) : /*#__PURE__*/_jsx(Button, {
|
|
79
81
|
label: buttonLabel,
|
|
80
82
|
modes: ctaModes,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
...(onButtonPress !== undefined ? {
|
|
84
|
+
onPress: onButtonPress
|
|
85
|
+
} : null),
|
|
86
|
+
...(buttonIcon ? {
|
|
87
|
+
leading: /*#__PURE__*/_jsx(Icon, {
|
|
88
|
+
iconName: buttonIcon,
|
|
89
|
+
size: tokens.buttonIconSize,
|
|
90
|
+
color: buttonForeground
|
|
91
|
+
})
|
|
92
|
+
} : null)
|
|
87
93
|
});
|
|
88
94
|
const content = /*#__PURE__*/_jsxs(_Fragment, {
|
|
89
95
|
children: [/*#__PURE__*/_jsx(View, {
|
|
@@ -94,9 +100,15 @@ function PdpCcCard({
|
|
|
94
100
|
subtitle: subtitle,
|
|
95
101
|
textAlign: "Center",
|
|
96
102
|
modes: modes,
|
|
97
|
-
numberOfLines
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
...(numberOfLines !== undefined ? {
|
|
104
|
+
numberOfLines
|
|
105
|
+
} : null),
|
|
106
|
+
...(disableTruncation !== undefined ? {
|
|
107
|
+
disableTruncation
|
|
108
|
+
} : null),
|
|
109
|
+
...(singleLine !== undefined ? {
|
|
110
|
+
singleLine
|
|
111
|
+
} : null)
|
|
100
112
|
}), metrics.length > 0 ? /*#__PURE__*/_jsx(View, {
|
|
101
113
|
style: styles.metricsRow,
|
|
102
114
|
children: metrics.map((metric, index) => /*#__PURE__*/_jsxs(React.Fragment, {
|
|
@@ -104,9 +116,21 @@ function PdpCcCard({
|
|
|
104
116
|
direction: "vertical",
|
|
105
117
|
modes: modes,
|
|
106
118
|
style: styles.metricDivider
|
|
107
|
-
}) : null, /*#__PURE__*/_jsx(
|
|
108
|
-
metric
|
|
109
|
-
|
|
119
|
+
}) : null, /*#__PURE__*/_jsx(MetricData, {
|
|
120
|
+
...(metric.title !== undefined ? {
|
|
121
|
+
title: metric.title
|
|
122
|
+
} : null),
|
|
123
|
+
...(metric.value !== undefined ? {
|
|
124
|
+
value: metric.value
|
|
125
|
+
} : null),
|
|
126
|
+
...(metric.caption !== undefined ? {
|
|
127
|
+
caption: metric.caption
|
|
128
|
+
} : null),
|
|
129
|
+
...(metric.icon !== undefined ? {
|
|
130
|
+
icon: metric.icon
|
|
131
|
+
} : null),
|
|
132
|
+
modes: modes,
|
|
133
|
+
style: styles.metricCell
|
|
110
134
|
})]
|
|
111
135
|
}, index))
|
|
112
136
|
}) : null, showButton ? ctaNode : null]
|
|
@@ -133,29 +157,6 @@ function PdpCcCard({
|
|
|
133
157
|
});
|
|
134
158
|
}
|
|
135
159
|
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
// Metricdata — internal metric column (Figma node 5352:256)
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
|
|
140
|
-
function Metricdata({
|
|
141
|
-
metric,
|
|
142
|
-
tokens
|
|
143
|
-
}) {
|
|
144
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
145
|
-
style: tokens.metric,
|
|
146
|
-
children: [metric.title != null ? /*#__PURE__*/_jsx(Text, {
|
|
147
|
-
style: tokens.metricTitle,
|
|
148
|
-
children: metric.title
|
|
149
|
-
}) : null, metric.value != null ? /*#__PURE__*/_jsx(Text, {
|
|
150
|
-
style: tokens.metricValue,
|
|
151
|
-
children: metric.value
|
|
152
|
-
}) : null, metric.caption != null ? /*#__PURE__*/_jsx(Text, {
|
|
153
|
-
style: tokens.metricCaption,
|
|
154
|
-
children: metric.caption
|
|
155
|
-
}) : null]
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
160
|
// ---------------------------------------------------------------------------
|
|
160
161
|
// Tokens / static styles
|
|
161
162
|
// ---------------------------------------------------------------------------
|
|
@@ -178,21 +179,6 @@ function resolveTokens(modes) {
|
|
|
178
179
|
const gap = asNum(getVariableByName('PDPcccard/gap', modes), 12);
|
|
179
180
|
const imageRadius = asNum(getVariableByName('image/radius', modes), 8);
|
|
180
181
|
const buttonIconSize = asNum(getVariableByName('button/iconSize', modes), 16);
|
|
181
|
-
const metricGap = asNum(getVariableByName('metricdata/gap', modes), 4);
|
|
182
|
-
const metricPadH = asNum(getVariableByName('metricdata/padding/horizontal', modes), 10);
|
|
183
|
-
const metricPadV = asNum(getVariableByName('metricdata/padding/vertical', modes), 10);
|
|
184
|
-
const titleColor = asStr(getVariableByName('metricdata/title/color', modes), '#000000');
|
|
185
|
-
const titleSize = asNum(getVariableByName('metricdata/title/fontsize', modes), 12);
|
|
186
|
-
const titleFamily = asStr(getVariableByName('metricdata/title/fontfamily', modes), 'JioType Var');
|
|
187
|
-
const titleWeight = asStr(getVariableByName('metricdata/title/fontweight', modes), '400');
|
|
188
|
-
const valueColor = asStr(getVariableByName('metricdata/value/color', modes), '#000000');
|
|
189
|
-
const valueSize = asNum(getVariableByName('metricdata/value/fontsize', modes), 20);
|
|
190
|
-
const valueFamily = asStr(getVariableByName('metricdata/value/fontfamily', modes), 'JioType Var');
|
|
191
|
-
const valueWeight = asStr(getVariableByName('metricdata/value/fontweight', modes), '700');
|
|
192
|
-
const captionColor = asStr(getVariableByName('metricdata/caption/color', modes), '#777777');
|
|
193
|
-
const captionSize = asNum(getVariableByName('metricdata/caption/fontsize', modes), 12);
|
|
194
|
-
const captionFamily = asStr(getVariableByName('metricdata/caption/fontfamily', modes), 'JioType Var');
|
|
195
|
-
const captionWeight = asStr(getVariableByName('metricdata/caption/fontweight', modes), '500');
|
|
196
182
|
return {
|
|
197
183
|
container: {
|
|
198
184
|
backgroundColor: background,
|
|
@@ -204,52 +190,19 @@ function resolveTokens(modes) {
|
|
|
204
190
|
justifyContent: 'center'
|
|
205
191
|
},
|
|
206
192
|
imageRadius,
|
|
207
|
-
buttonIconSize
|
|
208
|
-
metric: {
|
|
209
|
-
flex: 1,
|
|
210
|
-
gap: metricGap,
|
|
211
|
-
paddingHorizontal: metricPadH,
|
|
212
|
-
paddingVertical: metricPadV,
|
|
213
|
-
alignItems: 'center',
|
|
214
|
-
justifyContent: 'center'
|
|
215
|
-
},
|
|
216
|
-
metricTitle: {
|
|
217
|
-
color: titleColor,
|
|
218
|
-
fontSize: titleSize,
|
|
219
|
-
fontFamily: titleFamily,
|
|
220
|
-
fontWeight: titleWeight,
|
|
221
|
-
lineHeight: Math.round(titleSize * 1.2),
|
|
222
|
-
textAlign: 'center',
|
|
223
|
-
includeFontPadding: false
|
|
224
|
-
},
|
|
225
|
-
metricValue: {
|
|
226
|
-
color: valueColor,
|
|
227
|
-
fontSize: valueSize,
|
|
228
|
-
fontFamily: valueFamily,
|
|
229
|
-
fontWeight: valueWeight,
|
|
230
|
-
lineHeight: Math.round(valueSize * 1.2),
|
|
231
|
-
textAlign: 'center',
|
|
232
|
-
includeFontPadding: false
|
|
233
|
-
},
|
|
234
|
-
metricCaption: {
|
|
235
|
-
color: captionColor,
|
|
236
|
-
fontSize: captionSize,
|
|
237
|
-
fontFamily: captionFamily,
|
|
238
|
-
fontWeight: captionWeight,
|
|
239
|
-
lineHeight: Math.round(captionSize * 1.2),
|
|
240
|
-
textAlign: 'center',
|
|
241
|
-
includeFontPadding: false
|
|
242
|
-
}
|
|
193
|
+
buttonIconSize
|
|
243
194
|
};
|
|
244
195
|
}
|
|
245
196
|
const DEFAULT_METRICS = [{
|
|
246
197
|
title: 'Title',
|
|
247
198
|
value: 'Value',
|
|
248
|
-
caption: 'caption'
|
|
199
|
+
caption: 'caption',
|
|
200
|
+
icon: 'ic_card'
|
|
249
201
|
}, {
|
|
250
202
|
title: 'Title',
|
|
251
203
|
value: 'Value',
|
|
252
|
-
caption: 'caption'
|
|
204
|
+
caption: 'caption',
|
|
205
|
+
icon: 'ic_card'
|
|
253
206
|
}];
|
|
254
207
|
const styles = StyleSheet.create({
|
|
255
208
|
mediaSlot: {
|
|
@@ -262,6 +215,9 @@ const styles = StyleSheet.create({
|
|
|
262
215
|
flexDirection: 'row',
|
|
263
216
|
alignItems: 'stretch'
|
|
264
217
|
},
|
|
218
|
+
metricCell: {
|
|
219
|
+
flex: 1
|
|
220
|
+
},
|
|
265
221
|
metricDivider: {
|
|
266
222
|
alignSelf: 'center',
|
|
267
223
|
height: '70%'
|
|
@@ -39,6 +39,10 @@ const DEFAULT_ROWS = [{
|
|
|
39
39
|
label: 'Bonus JioGold',
|
|
40
40
|
showInfo: true,
|
|
41
41
|
values: [false, '1%']
|
|
42
|
+
}, {
|
|
43
|
+
label: 'Bonus JioSilver',
|
|
44
|
+
showInfo: true,
|
|
45
|
+
values: [false, '1%']
|
|
42
46
|
}];
|
|
43
47
|
|
|
44
48
|
/** Keeps every text layer on a single line. */
|
|
@@ -63,13 +67,15 @@ const planColumnStyle = {
|
|
|
63
67
|
* current plan against one or more alternative plans across a set of feature
|
|
64
68
|
* rows. Implementation of Figma node `4498:2968` (`PlanComparisonCard`).
|
|
65
69
|
*
|
|
66
|
-
*
|
|
70
|
+
* Structure: header (from `columns`) + Slot (default table from `rows`, or
|
|
71
|
+
* custom `children`). Columns use a 1.8fr / 1fr flex ratio (label vs plan).
|
|
67
72
|
*
|
|
68
73
|
* @component
|
|
69
74
|
*/
|
|
70
75
|
function PlanComparisonCard({
|
|
71
76
|
columns = DEFAULT_COLUMNS,
|
|
72
77
|
rows = DEFAULT_ROWS,
|
|
78
|
+
children,
|
|
73
79
|
modes = EMPTY_MODES,
|
|
74
80
|
style
|
|
75
81
|
}) {
|
|
@@ -166,6 +172,50 @@ function PlanComparisonCard({
|
|
|
166
172
|
}
|
|
167
173
|
return cloneChildrenWithModes(value, modes);
|
|
168
174
|
};
|
|
175
|
+
const defaultTable = /*#__PURE__*/_jsx(View, {
|
|
176
|
+
style: {
|
|
177
|
+
width: '100%',
|
|
178
|
+
backgroundColor: tableBackground,
|
|
179
|
+
borderWidth: tableBorderSize,
|
|
180
|
+
borderColor: tableBorderColor,
|
|
181
|
+
borderRadius: tableRadius,
|
|
182
|
+
overflow: 'hidden'
|
|
183
|
+
},
|
|
184
|
+
children: rows.map((row, rowIndex) => {
|
|
185
|
+
const isLast = rowIndex === rows.length - 1;
|
|
186
|
+
const showInfo = row.showInfo || row.onInfoPress != null;
|
|
187
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
188
|
+
style: [rowStyle, {
|
|
189
|
+
borderBottomWidth: isLast ? 0 : cellBorderSize,
|
|
190
|
+
borderBottomColor: cellBorderColor
|
|
191
|
+
}],
|
|
192
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
193
|
+
style: [labelColumnStyle, labelCellStyle],
|
|
194
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
195
|
+
style: labelTextStyle,
|
|
196
|
+
children: row.label
|
|
197
|
+
}), showInfo && (row.onInfoPress ? /*#__PURE__*/_jsx(Pressable, {
|
|
198
|
+
onPress: row.onInfoPress,
|
|
199
|
+
accessibilityRole: "button",
|
|
200
|
+
accessibilityLabel: `More information about ${row.label}`,
|
|
201
|
+
hitSlop: 8,
|
|
202
|
+
children: /*#__PURE__*/_jsx(Icon, {
|
|
203
|
+
name: "ic_info",
|
|
204
|
+
size: iconSize,
|
|
205
|
+
color: iconColor
|
|
206
|
+
})
|
|
207
|
+
}) : /*#__PURE__*/_jsx(Icon, {
|
|
208
|
+
name: "ic_info",
|
|
209
|
+
size: iconSize,
|
|
210
|
+
color: iconColor
|
|
211
|
+
}))]
|
|
212
|
+
}), columns.map((column, colIndex) => /*#__PURE__*/_jsx(View, {
|
|
213
|
+
style: [planColumnStyle, valueCellStyle],
|
|
214
|
+
children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
|
|
215
|
+
}, column.label ?? colIndex))]
|
|
216
|
+
}, row.key ?? `${row.label}-${rowIndex}`);
|
|
217
|
+
})
|
|
218
|
+
});
|
|
169
219
|
return /*#__PURE__*/_jsxs(View, {
|
|
170
220
|
style: [{
|
|
171
221
|
gap,
|
|
@@ -184,50 +234,7 @@ function PlanComparisonCard({
|
|
|
184
234
|
children: column.label
|
|
185
235
|
})
|
|
186
236
|
}, column.label ?? index))]
|
|
187
|
-
}),
|
|
188
|
-
style: {
|
|
189
|
-
width: '100%',
|
|
190
|
-
backgroundColor: tableBackground,
|
|
191
|
-
borderWidth: tableBorderSize,
|
|
192
|
-
borderColor: tableBorderColor,
|
|
193
|
-
borderRadius: tableRadius,
|
|
194
|
-
overflow: 'hidden'
|
|
195
|
-
},
|
|
196
|
-
children: rows.map((row, rowIndex) => {
|
|
197
|
-
const isLast = rowIndex === rows.length - 1;
|
|
198
|
-
const showInfo = row.showInfo || row.onInfoPress != null;
|
|
199
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
200
|
-
style: [rowStyle, {
|
|
201
|
-
borderBottomWidth: isLast ? 0 : cellBorderSize,
|
|
202
|
-
borderBottomColor: cellBorderColor
|
|
203
|
-
}],
|
|
204
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
205
|
-
style: [labelColumnStyle, labelCellStyle],
|
|
206
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
207
|
-
style: labelTextStyle,
|
|
208
|
-
children: row.label
|
|
209
|
-
}), showInfo && (row.onInfoPress ? /*#__PURE__*/_jsx(Pressable, {
|
|
210
|
-
onPress: row.onInfoPress,
|
|
211
|
-
accessibilityRole: "button",
|
|
212
|
-
accessibilityLabel: `More information about ${row.label}`,
|
|
213
|
-
hitSlop: 8,
|
|
214
|
-
children: /*#__PURE__*/_jsx(Icon, {
|
|
215
|
-
name: "ic_info",
|
|
216
|
-
size: iconSize,
|
|
217
|
-
color: iconColor
|
|
218
|
-
})
|
|
219
|
-
}) : /*#__PURE__*/_jsx(Icon, {
|
|
220
|
-
name: "ic_info",
|
|
221
|
-
size: iconSize,
|
|
222
|
-
color: iconColor
|
|
223
|
-
}))]
|
|
224
|
-
}), columns.map((column, colIndex) => /*#__PURE__*/_jsx(View, {
|
|
225
|
-
style: [planColumnStyle, valueCellStyle],
|
|
226
|
-
children: renderValue(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
|
|
227
|
-
}, column.label ?? colIndex))]
|
|
228
|
-
}, row.key ?? `${row.label}-${rowIndex}`);
|
|
229
|
-
})
|
|
230
|
-
})]
|
|
237
|
+
}), children != null ? cloneChildrenWithModes(children, modes) : defaultTable]
|
|
231
238
|
});
|
|
232
239
|
}
|
|
233
240
|
export default PlanComparisonCard;
|
|
@@ -386,9 +386,9 @@ function resolveTokens(modes) {
|
|
|
386
386
|
const tooltipBackground = asStr(getVariableByName('tooltip/background', modes), '#0f0d0a');
|
|
387
387
|
const tooltipPaddingH = asNum(getVariableByName('tooltip/padding/horizontal', modes), 12);
|
|
388
388
|
const tooltipPaddingV = asNum(getVariableByName('tooltip/padding/vertical', modes), 8);
|
|
389
|
-
//
|
|
390
|
-
const tooltipRadius =
|
|
391
|
-
const tooltipMaxWidth =
|
|
389
|
+
// Figma Tooltip binds bare local `radius` / `maxWidth` (not tooltip/*).
|
|
390
|
+
const tooltipRadius = Number(getVariableByName('radius', modes));
|
|
391
|
+
const tooltipMaxWidth = Number(getVariableByName('maxWidth', modes));
|
|
392
392
|
const tipWidth = asNum(getVariableByName('tooltip/tipItem/width', modes), 16);
|
|
393
393
|
const tipHeight = asNum(getVariableByName('tooltip/tipItem/height', modes), 8);
|
|
394
394
|
const tooltipLabelColor = asStr(getVariableByName('tooltip/label/color', modes), '#ffffff');
|