jfs-components 0.1.50 → 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/.cursor/D2C-FLOW.md +94 -0
- package/.cursor/D2C-SCREEN.md +119 -0
- package/.cursor/commands/D2C.md +101 -0
- package/CHANGELOG.md +18 -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 +4 -2
- 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
|
@@ -9,12 +9,20 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
10
|
var _webPlatformUtils = require("../../utils/web-platform-utils");
|
|
11
11
|
var _reactUtils = require("../../utils/react-utils");
|
|
12
|
+
var _GlassFill = _interopRequireDefault(require("../../utils/GlassFill/GlassFill"));
|
|
12
13
|
var _Icon = _interopRequireDefault(require("../../icons/Icon"));
|
|
13
14
|
var _Skeleton = _interopRequireDefault(require("../../skeleton/Skeleton"));
|
|
14
15
|
var _SkeletonGroup = require("../../skeleton/SkeletonGroup");
|
|
15
16
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
17
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
18
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
19
|
+
/**
|
|
20
|
+
* Figma `Type` property on Button.
|
|
21
|
+
* - `'default'` — solid token-colored pill (hugs content)
|
|
22
|
+
* - `'glass'` — frosted translucent pill backed by {@link GlassFill}
|
|
23
|
+
* - `'fixed'` — solid pill constrained by `button/maxWidth`
|
|
24
|
+
*/
|
|
25
|
+
|
|
18
26
|
// ---------------------------------------------------------------------------
|
|
19
27
|
// Module-scope constants — never re-allocated per render.
|
|
20
28
|
// ---------------------------------------------------------------------------
|
|
@@ -58,28 +66,79 @@ const NO_WRAP_LABEL_STYLE = {
|
|
|
58
66
|
// the most common path (no modes prop) is resolved once for the whole app.
|
|
59
67
|
// ---------------------------------------------------------------------------
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', modes);
|
|
68
|
-
const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', modes);
|
|
69
|
-
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', modes);
|
|
70
|
-
const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', modes);
|
|
71
|
-
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
|
|
72
|
-
const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', modes);
|
|
73
|
-
const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', modes);
|
|
74
|
-
const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', modes);
|
|
75
|
-
const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', modes) ?? 18;
|
|
69
|
+
const BUTTON_STATE_VALUES = new Set(['Idle', 'Hover', 'Pressed', 'Disabled']);
|
|
70
|
+
const SOLID_STATE_COLLECTION = 'Button / State';
|
|
71
|
+
const GLASS_STATE_COLLECTION = 'Button Glass State';
|
|
72
|
+
function resolveIsGlass(type, modes) {
|
|
73
|
+
return type === 'glass' || modes['Button type'] === 'glass';
|
|
74
|
+
}
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Pick Idle/Hover/Pressed/Disabled from the active state collection, falling
|
|
78
|
+
* back to the sibling collection when callers pass the other key (e.g. glass
|
|
79
|
+
* buttons that still send `Button / State`). Never treats a collection name
|
|
80
|
+
* as a mode value.
|
|
81
|
+
*/
|
|
82
|
+
function pickButtonInteractiveState(modes, isGlass) {
|
|
83
|
+
const primary = isGlass ? GLASS_STATE_COLLECTION : SOLID_STATE_COLLECTION;
|
|
84
|
+
const sibling = isGlass ? SOLID_STATE_COLLECTION : GLASS_STATE_COLLECTION;
|
|
85
|
+
const candidate = modes[primary] ?? modes[sibling];
|
|
86
|
+
if (typeof candidate === 'string' && BUTTON_STATE_VALUES.has(candidate)) {
|
|
87
|
+
return candidate;
|
|
88
|
+
}
|
|
89
|
+
return 'Idle';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Write a valid interactive state onto the correct collection for glass/solid. */
|
|
93
|
+
function withButtonInteractiveState(modes, isGlass, state) {
|
|
94
|
+
const collection = isGlass ? GLASS_STATE_COLLECTION : SOLID_STATE_COLLECTION;
|
|
95
|
+
const sibling = isGlass ? SOLID_STATE_COLLECTION : GLASS_STATE_COLLECTION;
|
|
96
|
+
const {
|
|
97
|
+
[sibling]: _dropSibling,
|
|
98
|
+
...rest
|
|
99
|
+
} = modes;
|
|
100
|
+
return {
|
|
101
|
+
...rest,
|
|
102
|
+
[collection]: state
|
|
82
103
|
};
|
|
104
|
+
}
|
|
105
|
+
function resolveButtonTokens(modes, disabled, type) {
|
|
106
|
+
const isGlass = resolveIsGlass(type, modes);
|
|
107
|
+
const isFixed = type === 'fixed';
|
|
108
|
+
|
|
109
|
+
// Inject the Button type mode so token aliases (background/foreground) resolve
|
|
110
|
+
// to the glass palette. Callers can also drive glass solely via modes.
|
|
111
|
+
const effectiveModes = isGlass ? {
|
|
112
|
+
...modes,
|
|
113
|
+
'Button type': 'glass'
|
|
114
|
+
} : modes;
|
|
115
|
+
|
|
116
|
+
// Glass interactive states live on `Button Glass State`; solid on `Button / State`.
|
|
117
|
+
// Map Idle/Hover/Pressed/Disabled values through — never assign a collection name.
|
|
118
|
+
const idleState = disabled && isGlass ? 'Disabled' : pickButtonInteractiveState(effectiveModes, isGlass);
|
|
119
|
+
const idleModes = withButtonInteractiveState(effectiveModes, isGlass, idleState);
|
|
120
|
+
const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('button/background', idleModes);
|
|
121
|
+
const borderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', idleModes);
|
|
122
|
+
const borderWidth = (0, _figmaVariablesResolver.getVariableByName)('button/border/size', idleModes);
|
|
123
|
+
const radius = (0, _figmaVariablesResolver.getVariableByName)('button/radius', idleModes);
|
|
124
|
+
const paddingHorizontal = (0, _figmaVariablesResolver.getVariableByName)('button/padding/horizontal', idleModes);
|
|
125
|
+
const paddingVertical = (0, _figmaVariablesResolver.getVariableByName)('button/padding/vertical', idleModes);
|
|
126
|
+
const gap = (0, _figmaVariablesResolver.getVariableByName)('button/gap', idleModes);
|
|
127
|
+
const maxWidth = (0, _figmaVariablesResolver.getVariableByName)('button/maxWidth', idleModes);
|
|
128
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('button/fontFamily', idleModes);
|
|
129
|
+
const fontWeightValue = (0, _figmaVariablesResolver.getVariableByName)('button/fontWeight', idleModes);
|
|
130
|
+
const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue;
|
|
131
|
+
const lineHeight = (0, _figmaVariablesResolver.getVariableByName)('button/lineHeight', idleModes);
|
|
132
|
+
const fontSize = (0, _figmaVariablesResolver.getVariableByName)('button/fontSize', idleModes);
|
|
133
|
+
const textColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', idleModes);
|
|
134
|
+
const iconSize = (0, _figmaVariablesResolver.getVariableByName)('button/icon/size', idleModes) ?? 18;
|
|
135
|
+
|
|
136
|
+
// Figma `blur/minimal` (px radius) -> GlassFill's 0–100 intensity scale.
|
|
137
|
+
const glassBlurIntensity = Math.max(0, Math.min(100, Math.round(Number((0, _figmaVariablesResolver.getVariableByName)('blur/minimal', idleModes) ?? 29))));
|
|
138
|
+
|
|
139
|
+
// Pressed tokens are always resolved so the Pressable `style` callback /
|
|
140
|
+
// glass overlay can apply the pressed visual without a modes recompute.
|
|
141
|
+
const pressedModes = withButtonInteractiveState(effectiveModes, isGlass, 'Pressed');
|
|
83
142
|
const pressedBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', pressedModes);
|
|
84
143
|
const pressedBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', pressedModes);
|
|
85
144
|
const pressedTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', pressedModes);
|
|
@@ -88,20 +147,22 @@ function resolveButtonTokens(modes, disabled) {
|
|
|
88
147
|
// ~3 token lookups + an object spread per render on every Button.
|
|
89
148
|
let hoverContainer = null;
|
|
90
149
|
let hoverTextColor = null;
|
|
150
|
+
let glassOverlayHover = null;
|
|
91
151
|
if (IS_WEB) {
|
|
92
|
-
const hoverModes =
|
|
93
|
-
...modes,
|
|
94
|
-
'Button / State': 'Hover'
|
|
95
|
-
};
|
|
152
|
+
const hoverModes = withButtonInteractiveState(effectiveModes, isGlass, 'Hover');
|
|
96
153
|
const hoverBg = (0, _figmaVariablesResolver.getVariableByName)('button/background', hoverModes);
|
|
97
154
|
const hoverBorderColor = (0, _figmaVariablesResolver.getVariableByName)('button/border/color', hoverModes);
|
|
98
155
|
hoverTextColor = (0, _figmaVariablesResolver.getVariableByName)('button/foreground', hoverModes);
|
|
99
|
-
|
|
156
|
+
glassOverlayHover = hoverBg;
|
|
157
|
+
// Glass keeps the host transparent; hover only retints the GlassFill overlay.
|
|
158
|
+
hoverContainer = isGlass ? null : {
|
|
100
159
|
backgroundColor: hoverBg,
|
|
101
160
|
borderColor: hoverBorderColor
|
|
102
161
|
};
|
|
103
162
|
}
|
|
104
163
|
return {
|
|
164
|
+
isGlass,
|
|
165
|
+
isFixed,
|
|
105
166
|
container: {
|
|
106
167
|
...containerLayoutStyle,
|
|
107
168
|
paddingHorizontal: paddingHorizontal,
|
|
@@ -109,9 +170,18 @@ function resolveButtonTokens(modes, disabled) {
|
|
|
109
170
|
borderRadius: radius,
|
|
110
171
|
borderWidth: borderWidth ?? 1,
|
|
111
172
|
borderColor: borderColor,
|
|
112
|
-
|
|
173
|
+
// Glass: transparent host so GlassFill shows through. Fixed: clamp width.
|
|
174
|
+
backgroundColor: isGlass ? 'transparent' : backgroundColor,
|
|
113
175
|
gap: gap,
|
|
114
|
-
|
|
176
|
+
// Glass disabled uses Button Glass State=Disabled foreground tokens —
|
|
177
|
+
// don't also apply the solid-button opacity fade.
|
|
178
|
+
opacity: disabled && !isGlass ? 0.5 : 1,
|
|
179
|
+
...(isGlass ? {
|
|
180
|
+
overflow: 'hidden'
|
|
181
|
+
} : null),
|
|
182
|
+
...(isFixed && maxWidth != null ? {
|
|
183
|
+
maxWidth: maxWidth
|
|
184
|
+
} : null)
|
|
115
185
|
},
|
|
116
186
|
baseLabel: {
|
|
117
187
|
color: textColor,
|
|
@@ -121,7 +191,7 @@ function resolveButtonTokens(modes, disabled) {
|
|
|
121
191
|
lineHeight: lineHeight
|
|
122
192
|
},
|
|
123
193
|
hoverContainer,
|
|
124
|
-
pressedContainer: {
|
|
194
|
+
pressedContainer: isGlass ? {} : {
|
|
125
195
|
backgroundColor: pressedBg,
|
|
126
196
|
borderColor: pressedBorderColor
|
|
127
197
|
},
|
|
@@ -131,7 +201,11 @@ function resolveButtonTokens(modes, disabled) {
|
|
|
131
201
|
hoverIconColor: hoverTextColor,
|
|
132
202
|
pressedIconColor: pressedTextColor,
|
|
133
203
|
iconSize,
|
|
134
|
-
accessoryOffset: gap / 2
|
|
204
|
+
accessoryOffset: gap / 2,
|
|
205
|
+
glassOverlayIdle: backgroundColor,
|
|
206
|
+
glassOverlayHover,
|
|
207
|
+
glassOverlayPressed: pressedBg,
|
|
208
|
+
glassBlurIntensity
|
|
135
209
|
};
|
|
136
210
|
}
|
|
137
211
|
|
|
@@ -177,6 +251,9 @@ function ButtonImpl({
|
|
|
177
251
|
leading,
|
|
178
252
|
trailing,
|
|
179
253
|
icon,
|
|
254
|
+
type = 'default',
|
|
255
|
+
glassOverlayColor,
|
|
256
|
+
glassIntensity,
|
|
180
257
|
modes = _reactUtils.EMPTY_MODES,
|
|
181
258
|
onPress,
|
|
182
259
|
disabled = false,
|
|
@@ -201,7 +278,7 @@ function ButtonImpl({
|
|
|
201
278
|
userHandlersRef.current.onPressOut = rest?.onPressOut;
|
|
202
279
|
userHandlersRef.current.onHoverIn = rest?.onHoverIn;
|
|
203
280
|
userHandlersRef.current.onHoverOut = rest?.onHoverOut;
|
|
204
|
-
const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled), [modes, disabled]);
|
|
281
|
+
const tokens = (0, _react.useMemo)(() => resolveButtonTokens(modes, disabled, type), [modes, disabled, type]);
|
|
205
282
|
|
|
206
283
|
// Skeleton context — read unconditionally so React's hook order stays
|
|
207
284
|
// stable. The actual short-circuit return happens AFTER all remaining
|
|
@@ -315,7 +392,25 @@ function ButtonImpl({
|
|
|
315
392
|
modes: modes
|
|
316
393
|
});
|
|
317
394
|
}
|
|
318
|
-
|
|
395
|
+
const body = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
396
|
+
children: [leading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
397
|
+
style: leadingAccessoryStyle,
|
|
398
|
+
children: leading
|
|
399
|
+
}) : null, content, icon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
400
|
+
style: trailingAccessoryStyle,
|
|
401
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
402
|
+
name: icon,
|
|
403
|
+
size: tokens.iconSize,
|
|
404
|
+
color: activeIconColor,
|
|
405
|
+
accessibilityElementsHidden: true,
|
|
406
|
+
importantForAccessibility: "no"
|
|
407
|
+
})
|
|
408
|
+
}) : trailing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
409
|
+
style: trailingAccessoryStyle,
|
|
410
|
+
children: trailing
|
|
411
|
+
}) : null]
|
|
412
|
+
});
|
|
413
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
319
414
|
accessibilityRole: "button",
|
|
320
415
|
accessibilityLabel: undefined,
|
|
321
416
|
...(accessibilityHint !== undefined ? {
|
|
@@ -336,22 +431,19 @@ function ButtonImpl({
|
|
|
336
431
|
unstable_pressDelay: PRESS_DELAY,
|
|
337
432
|
style: styleCallback,
|
|
338
433
|
...webProps,
|
|
339
|
-
children:
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
})
|
|
351
|
-
}
|
|
352
|
-
style: trailingAccessoryStyle,
|
|
353
|
-
children: trailing
|
|
354
|
-
}) : null]
|
|
434
|
+
children: tokens.isGlass ? ({
|
|
435
|
+
pressed
|
|
436
|
+
}) => {
|
|
437
|
+
const overlayColor = glassOverlayColor ?? (pressed && !disabled ? tokens.glassOverlayPressed : isHovered && !disabled && tokens.glassOverlayHover ? tokens.glassOverlayHover : tokens.glassOverlayIdle);
|
|
438
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
439
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_GlassFill.default, {
|
|
440
|
+
tint: "light",
|
|
441
|
+
intensity: glassIntensity ?? tokens.glassBlurIntensity,
|
|
442
|
+
overlayColor: overlayColor,
|
|
443
|
+
androidTintWash: false
|
|
444
|
+
}), body]
|
|
445
|
+
});
|
|
446
|
+
} : body
|
|
355
447
|
});
|
|
356
448
|
}
|
|
357
449
|
const Button = /*#__PURE__*/_react.default.memo(ButtonImpl);
|
|
@@ -22,7 +22,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
22
22
|
* @component
|
|
23
23
|
* @example
|
|
24
24
|
* ```jsx
|
|
25
|
-
* <ButtonGroup modes={{"
|
|
25
|
+
* <ButtonGroup modes={{"Color Mode": "Light"}}>
|
|
26
26
|
* <IconButton iconName="ic_qr_code" />
|
|
27
27
|
* <Button label="Pay" />
|
|
28
28
|
* </ButtonGroup>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.CARD_MEDIA_ASPECT_RATIO = void 0;
|
|
6
7
|
exports.Card = Card;
|
|
7
8
|
exports.default = void 0;
|
|
8
9
|
var _react = _interopRequireWildcard(require("react"));
|
|
@@ -12,117 +13,160 @@ var _reactUtils = require("../../utils/react-utils");
|
|
|
12
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
14
15
|
/**
|
|
15
|
-
* Context
|
|
16
|
-
*
|
|
17
|
-
* needing explicit mode prop passing for every child.
|
|
16
|
+
* Context so `Card.Title` / `Card.SupportText` resolve tokens from the parent
|
|
17
|
+
* card's `modes` without every call site having to pass them explicitly.
|
|
18
18
|
*/
|
|
19
|
-
const CardContext = /*#__PURE__*/(0, _react.createContext)({
|
|
19
|
+
const CardContext = /*#__PURE__*/(0, _react.createContext)({
|
|
20
|
+
modes: _reactUtils.EMPTY_MODES
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/** Canonical variant values. Maps 1:1 to Figma `Property 1`. */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Accepts both the canonical lowercase API and Figma's PascalCase property
|
|
27
|
+
* values so Code Connect / design tooling can pass either.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const normalizeVariant = value => {
|
|
31
|
+
if (value === 'slim' || value === 'Slim') return 'slim';
|
|
32
|
+
return 'default';
|
|
33
|
+
};
|
|
34
|
+
const toNumber = (value, fallback) => {
|
|
35
|
+
if (typeof value === 'number') {
|
|
36
|
+
return Number.isFinite(value) ? value : fallback;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
const parsed = Number(value);
|
|
40
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
41
|
+
}
|
|
42
|
+
return fallback;
|
|
43
|
+
};
|
|
44
|
+
const toFontWeight = (value, fallback) => {
|
|
45
|
+
if (typeof value === 'number') return String(value);
|
|
46
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
return fallback;
|
|
50
|
+
};
|
|
51
|
+
|
|
20
52
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
53
|
+
* Figma `Ratio` default for the media slot (node 766:6258) — 154×116.
|
|
54
|
+
* Kept as a named constant so stories / docs stay aligned with the design.
|
|
55
|
+
*/
|
|
56
|
+
const CARD_MEDIA_ASPECT_RATIO = exports.CARD_MEDIA_ASPECT_RATIO = 154 / 116;
|
|
57
|
+
function resolveCardTokens(modes, mediaAspectRatio) {
|
|
58
|
+
const backgroundColor = (0, _figmaVariablesResolver.getVariableByName)('card/background/color', modes) ?? '#ffffff';
|
|
59
|
+
const borderColor = (0, _figmaVariablesResolver.getVariableByName)('card/border/color', modes) ?? '#ffffff';
|
|
60
|
+
const borderWidth = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/strokeWidth', modes), 0.5);
|
|
61
|
+
const radius = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/radius', modes), 12);
|
|
62
|
+
// Figma root uses itemSpacing=4 (bound conceptually to card/gap). The
|
|
63
|
+
// component-set frames also use SPACE_BETWEEN + fixed heights for gallery
|
|
64
|
+
// layout — we hug content and apply gap only, so consumers aren't stuck
|
|
65
|
+
// with empty distributed space.
|
|
66
|
+
const gap = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/gap', modes), 4);
|
|
67
|
+
const paddingHorizontal = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/padding-horizontal', modes), 0);
|
|
68
|
+
const paddingVertical = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/padding-vertical', modes), 0);
|
|
69
|
+
const contentGap = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/gap', modes), 4);
|
|
70
|
+
const contentPaddingHorizontal = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/horizontal', modes), 12);
|
|
71
|
+
const contentPaddingVertical = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/content/padding/vertical', modes), 12);
|
|
72
|
+
return {
|
|
73
|
+
containerStyle: {
|
|
74
|
+
backgroundColor,
|
|
75
|
+
borderColor,
|
|
76
|
+
borderWidth,
|
|
77
|
+
borderRadius: radius,
|
|
78
|
+
gap,
|
|
79
|
+
paddingHorizontal,
|
|
80
|
+
paddingVertical,
|
|
81
|
+
overflow: 'hidden'
|
|
82
|
+
},
|
|
83
|
+
// Header wrap binds the same content-padding tokens as the content wrap
|
|
84
|
+
// in Figma (12/12) — not a separate header token set.
|
|
85
|
+
headerWrapperStyle: {
|
|
86
|
+
width: '100%',
|
|
87
|
+
flexDirection: 'row',
|
|
88
|
+
alignItems: 'flex-start',
|
|
89
|
+
paddingHorizontal: contentPaddingHorizontal,
|
|
90
|
+
paddingVertical: contentPaddingVertical
|
|
91
|
+
},
|
|
92
|
+
mediaWrapperStyle: {
|
|
93
|
+
width: '100%',
|
|
94
|
+
aspectRatio: mediaAspectRatio,
|
|
95
|
+
overflow: 'hidden'
|
|
96
|
+
},
|
|
97
|
+
contentWrapperStyle: {
|
|
98
|
+
width: '100%',
|
|
99
|
+
gap: contentGap,
|
|
100
|
+
paddingHorizontal: contentPaddingHorizontal,
|
|
101
|
+
paddingVertical: contentPaddingVertical
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* `Card` — Figma node 5739:794 (`Property 1=Default` / `Property 1=Slim`).
|
|
108
|
+
*
|
|
109
|
+
* Slots: optional `header`, optional `media` (Default only), and `children`
|
|
110
|
+
* for the content stack. Typography subcomponents (`Card.Title`,
|
|
111
|
+
* `Card.SupportText`) read tokens via context.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
27
114
|
* ```tsx
|
|
28
|
-
* <Card
|
|
115
|
+
* <Card
|
|
116
|
+
* variant="default"
|
|
117
|
+
* header={<GoldLogo />}
|
|
118
|
+
* media={<Image imageSource={uri} ratio={154 / 116} />}
|
|
119
|
+
* modes={{ AppearanceBrand: 'Neutral' }}
|
|
120
|
+
* >
|
|
29
121
|
* <Card.SupportText>Support text</Card.SupportText>
|
|
30
122
|
* <Card.Title>Title</Card.Title>
|
|
31
123
|
* <Card.SupportText>Support text</Card.SupportText>
|
|
32
124
|
* </Card>
|
|
33
125
|
* ```
|
|
126
|
+
*
|
|
127
|
+
* @component
|
|
34
128
|
*/
|
|
35
129
|
function Card({
|
|
130
|
+
variant: variantProp = 'default',
|
|
36
131
|
header,
|
|
37
132
|
media,
|
|
38
133
|
children,
|
|
39
|
-
modes = _reactUtils.EMPTY_MODES,
|
|
40
|
-
mediaAspectRatio =
|
|
134
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
135
|
+
mediaAspectRatio = CARD_MEDIA_ASPECT_RATIO,
|
|
41
136
|
style,
|
|
42
137
|
onPress,
|
|
43
138
|
disabled,
|
|
44
139
|
accessibilityLabel
|
|
45
140
|
}) {
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const borderWidth = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/strokeWidth', modes)); // Ensure number
|
|
50
|
-
const radius = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/radius', modes));
|
|
51
|
-
const gap = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/gap', modes));
|
|
52
|
-
const paddingHorizontal = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/padding-horizontal', modes));
|
|
53
|
-
const paddingVertical = parseFloat((0, _figmaVariablesResolver.getVariableByName)('card/padding-vertical', modes));
|
|
141
|
+
const variant = normalizeVariant(variantProp);
|
|
142
|
+
const modes = propModes ?? _reactUtils.EMPTY_MODES;
|
|
143
|
+
const tokens = (0, _react.useMemo)(() => resolveCardTokens(modes, mediaAspectRatio), [modes, mediaAspectRatio]);
|
|
54
144
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const mediaWithModes = /*#__PURE__*/(0, _react.isValidElement)(media) ? /*#__PURE__*/(0, _react.cloneElement)(media, {
|
|
62
|
-
modes: {
|
|
63
|
-
...media.props.modes,
|
|
64
|
-
...modes
|
|
65
|
-
}
|
|
66
|
-
}) : media;
|
|
67
|
-
|
|
68
|
-
// Clone header to pass modes if it's a valid element
|
|
69
|
-
const headerWithModes = /*#__PURE__*/(0, _react.isValidElement)(header) ? /*#__PURE__*/(0, _react.cloneElement)(header, {
|
|
70
|
-
modes: {
|
|
71
|
-
...header.props.modes,
|
|
72
|
-
...modes
|
|
73
|
-
}
|
|
74
|
-
}) : header;
|
|
75
|
-
const containerStyle = {
|
|
76
|
-
backgroundColor,
|
|
77
|
-
borderColor,
|
|
78
|
-
borderWidth,
|
|
79
|
-
borderRadius: radius,
|
|
80
|
-
gap,
|
|
81
|
-
paddingHorizontal,
|
|
82
|
-
paddingVertical,
|
|
83
|
-
overflow: 'hidden' // Ensure border radius clips content
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Header wrap uses fixed padding from Figma (no dedicated tokens defined).
|
|
87
|
-
const headerWrapperStyle = {
|
|
88
|
-
width: '100%',
|
|
89
|
-
flexDirection: 'row',
|
|
90
|
-
alignItems: 'flex-start',
|
|
91
|
-
paddingHorizontal: 12,
|
|
92
|
-
paddingVertical: 16
|
|
93
|
-
};
|
|
94
|
-
const mediaWrapperStyle = {
|
|
95
|
-
width: '100%',
|
|
96
|
-
aspectRatio: mediaAspectRatio,
|
|
97
|
-
overflow: 'hidden'
|
|
98
|
-
// Figma has 'bg-[#3a46f7]' on the Ratio definition, but that's a placeholder.
|
|
99
|
-
// We assume the media content handles its own fill, or we can add a placeholder bg if needed.
|
|
100
|
-
// For now, we leave it transparent or rely on media.
|
|
101
|
-
};
|
|
102
|
-
const contentWrapperStyle = {
|
|
103
|
-
width: '100%',
|
|
104
|
-
gap: contentGap,
|
|
105
|
-
paddingHorizontal: contentPaddingHorizontal,
|
|
106
|
-
paddingVertical: contentPaddingVertical
|
|
107
|
-
};
|
|
145
|
+
// Slim suppresses media even if a consumer still passes it — matches Figma
|
|
146
|
+
// Property 1=Slim. Default keeps prior behavior: media renders when provided.
|
|
147
|
+
const showMedia = variant === 'default' && media != null;
|
|
148
|
+
const processedHeader = header != null ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(header), modes) : null;
|
|
149
|
+
const processedMedia = showMedia ? (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(media), modes) : null;
|
|
150
|
+
const processedChildren = (0, _reactUtils.cloneChildrenWithModes)(_react.default.Children.toArray(children ?? null), modes);
|
|
108
151
|
const content = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
109
|
-
children: [
|
|
110
|
-
style: headerWrapperStyle,
|
|
111
|
-
children:
|
|
112
|
-
}),
|
|
113
|
-
style: mediaWrapperStyle,
|
|
114
|
-
children:
|
|
115
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
116
|
-
style: contentWrapperStyle,
|
|
117
|
-
children:
|
|
152
|
+
children: [processedHeader ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
153
|
+
style: tokens.headerWrapperStyle,
|
|
154
|
+
children: processedHeader
|
|
155
|
+
}) : null, processedMedia ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
156
|
+
style: tokens.mediaWrapperStyle,
|
|
157
|
+
children: processedMedia
|
|
158
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
159
|
+
style: tokens.contentWrapperStyle,
|
|
160
|
+
children: processedChildren
|
|
118
161
|
})]
|
|
119
162
|
});
|
|
163
|
+
const rootStyle = [tokens.containerStyle, style];
|
|
120
164
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(CardContext.Provider, {
|
|
121
165
|
value: {
|
|
122
166
|
modes
|
|
123
167
|
},
|
|
124
168
|
children: onPress ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
125
|
-
style:
|
|
169
|
+
style: rootStyle,
|
|
126
170
|
onPress: onPress,
|
|
127
171
|
disabled: disabled,
|
|
128
172
|
accessibilityRole: "button",
|
|
@@ -132,7 +176,7 @@ function Card({
|
|
|
132
176
|
},
|
|
133
177
|
children: content
|
|
134
178
|
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
135
|
-
style:
|
|
179
|
+
style: rootStyle,
|
|
136
180
|
accessibilityLabel: accessibilityLabel,
|
|
137
181
|
children: content
|
|
138
182
|
})
|
|
@@ -144,69 +188,64 @@ function Card({
|
|
|
144
188
|
// ----------------------------------------------------------------------------
|
|
145
189
|
|
|
146
190
|
/**
|
|
147
|
-
* Title
|
|
148
|
-
* Uses tokens: card/title/*
|
|
191
|
+
* Title — tokens: `card/title/*`
|
|
149
192
|
*/
|
|
150
193
|
function Title({
|
|
151
194
|
children,
|
|
152
195
|
style,
|
|
153
|
-
modes: propModes
|
|
196
|
+
modes: propModes,
|
|
197
|
+
numberOfLines = 3
|
|
154
198
|
}) {
|
|
155
199
|
const context = (0, _react.useContext)(CardContext);
|
|
156
|
-
const modes = propModes
|
|
157
|
-
const color = (0, _figmaVariablesResolver.getVariableByName)('card/title/color', modes);
|
|
158
|
-
const fontSize =
|
|
159
|
-
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontFamily', modes);
|
|
160
|
-
const lineHeight =
|
|
161
|
-
|
|
162
|
-
// Handling the potential typo in Figma variables "fonwWeight" vs "fontWeight"
|
|
163
|
-
// We check both keys to be safe.
|
|
164
|
-
const rawFontWeight = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontWeight', modes);
|
|
165
|
-
const fontWeight = typeof rawFontWeight === 'number' ? rawFontWeight.toString() : rawFontWeight;
|
|
200
|
+
const modes = propModes ?? context.modes ?? _reactUtils.EMPTY_MODES;
|
|
201
|
+
const color = (0, _figmaVariablesResolver.getVariableByName)('card/title/color', modes) ?? '#000000';
|
|
202
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/title/fontSize', modes), 16);
|
|
203
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/title/fontFamily', modes) ?? 'JioType Var';
|
|
204
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/title/lineHeight', modes), 18);
|
|
205
|
+
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('card/title/fontWeight', modes), '900');
|
|
166
206
|
const textStyle = {
|
|
167
207
|
color,
|
|
168
208
|
fontSize,
|
|
169
209
|
fontFamily,
|
|
170
210
|
lineHeight,
|
|
171
|
-
fontWeight
|
|
211
|
+
fontWeight
|
|
172
212
|
};
|
|
173
213
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
174
214
|
style: [textStyle, style],
|
|
215
|
+
numberOfLines: numberOfLines > 0 ? numberOfLines : undefined,
|
|
175
216
|
children: children
|
|
176
217
|
});
|
|
177
218
|
}
|
|
178
219
|
|
|
179
220
|
/**
|
|
180
|
-
*
|
|
181
|
-
* Uses tokens: card/supportive-text/*
|
|
221
|
+
* Supportive text — tokens: `card/supportive-text/*`
|
|
182
222
|
*/
|
|
183
223
|
function SupportText({
|
|
184
224
|
children,
|
|
185
225
|
style,
|
|
186
|
-
modes: propModes
|
|
226
|
+
modes: propModes,
|
|
227
|
+
numberOfLines
|
|
187
228
|
}) {
|
|
188
229
|
const context = (0, _react.useContext)(CardContext);
|
|
189
|
-
const modes = propModes
|
|
190
|
-
const color = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/color', modes);
|
|
191
|
-
const fontSize =
|
|
192
|
-
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontFamily', modes);
|
|
193
|
-
const lineHeight =
|
|
194
|
-
const
|
|
195
|
-
const fontWeight = typeof rawFontWeight === 'number' ? rawFontWeight.toString() : rawFontWeight;
|
|
230
|
+
const modes = propModes ?? context.modes ?? _reactUtils.EMPTY_MODES;
|
|
231
|
+
const color = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/color', modes) ?? '#1a1c1f';
|
|
232
|
+
const fontSize = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontSize', modes), 12);
|
|
233
|
+
const fontFamily = (0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontFamily', modes) ?? 'JioType Var';
|
|
234
|
+
const lineHeight = toNumber((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/lineHeight', modes), 16);
|
|
235
|
+
const fontWeight = toFontWeight((0, _figmaVariablesResolver.getVariableByName)('card/supportive-text/fontWeight', modes), '400');
|
|
196
236
|
const textStyle = {
|
|
197
237
|
color,
|
|
198
238
|
fontSize,
|
|
199
239
|
fontFamily,
|
|
200
240
|
lineHeight,
|
|
201
|
-
fontWeight
|
|
241
|
+
fontWeight
|
|
202
242
|
};
|
|
203
243
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
204
244
|
style: [textStyle, style],
|
|
245
|
+
numberOfLines: numberOfLines,
|
|
205
246
|
children: children
|
|
206
247
|
});
|
|
207
248
|
}
|
|
208
|
-
|
|
209
|
-
// Attach sub-components to Card
|
|
210
249
|
Card.Title = Title;
|
|
211
250
|
Card.SupportText = SupportText;
|
|
212
251
|
var _default = exports.default = Card;
|
|
Binary file
|