jfs-components 0.1.2 → 0.1.8
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 +29 -0
- package/lib/commonjs/components/AmountInput/AmountInput.js +8 -5
- package/lib/commonjs/components/BenefitCard/BenefitCard.js +231 -0
- package/lib/commonjs/components/CcCard/CcCard.js +470 -0
- package/lib/commonjs/components/Checkbox/Checkbox.js +4 -3
- package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +4 -3
- package/lib/commonjs/components/CompareTable/CompareTable.js +372 -0
- package/lib/commonjs/components/ComparisonBar/ComparisonBar.js +266 -0
- package/lib/commonjs/components/DropdownInput/DropdownInput.js +35 -3
- package/lib/commonjs/components/FormField/FormField.js +4 -3
- package/lib/commonjs/components/InputSearch/InputSearch.js +6 -4
- package/lib/commonjs/components/NoteInput/NoteInput.js +6 -5
- package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +273 -0
- package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.js +263 -0
- package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.web.js +116 -0
- package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +353 -0
- package/lib/commonjs/components/ProjectionMarker/ProjectionMarker.js +161 -0
- package/lib/commonjs/components/Radio/Radio.js +5 -5
- package/lib/commonjs/components/Slider/Slider.js +473 -0
- package/lib/commonjs/components/TextInput/TextInput.js +13 -8
- package/lib/commonjs/components/TextSegment/TextSegment.js +118 -0
- package/lib/commonjs/components/index.js +63 -0
- package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/commonjs/design-tokens/figma-modes.generated.js +38 -9
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/commonjs/utils/react-utils.js +22 -0
- package/lib/module/components/AmountInput/AmountInput.js +6 -4
- package/lib/module/components/BenefitCard/BenefitCard.js +225 -0
- package/lib/module/components/CcCard/CcCard.js +464 -0
- package/lib/module/components/Checkbox/Checkbox.js +5 -4
- package/lib/module/components/CheckboxItem/CheckboxItem.js +5 -4
- package/lib/module/components/CompareTable/CompareTable.js +367 -0
- package/lib/module/components/ComparisonBar/ComparisonBar.js +260 -0
- package/lib/module/components/DropdownInput/DropdownInput.js +36 -4
- package/lib/module/components/FormField/FormField.js +5 -4
- package/lib/module/components/InputSearch/InputSearch.js +6 -4
- package/lib/module/components/NoteInput/NoteInput.js +7 -6
- package/lib/module/components/PdpCcCard/PdpCcCard.js +267 -0
- package/lib/module/components/ProductMerchandisingCard/GlassFill.js +257 -0
- package/lib/module/components/ProductMerchandisingCard/GlassFill.web.js +111 -0
- package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +347 -0
- package/lib/module/components/ProjectionMarker/ProjectionMarker.js +156 -0
- package/lib/module/components/Radio/Radio.js +5 -4
- package/lib/module/components/Slider/Slider.js +468 -0
- package/lib/module/components/TextInput/TextInput.js +15 -10
- package/lib/module/components/TextSegment/TextSegment.js +113 -0
- package/lib/module/components/index.js +9 -0
- package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
- package/lib/module/design-tokens/figma-modes.generated.js +38 -9
- package/lib/module/icons/registry.js +1 -1
- package/lib/module/utils/react-utils.js +21 -0
- package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +3 -2
- package/lib/typescript/src/components/BenefitCard/BenefitCard.d.ts +93 -0
- package/lib/typescript/src/components/CcCard/CcCard.d.ts +137 -0
- package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +3 -2
- package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +2 -2
- package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +88 -0
- package/lib/typescript/src/components/ComparisonBar/ComparisonBar.d.ts +118 -0
- package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +20 -1
- package/lib/typescript/src/components/FormField/FormField.d.ts +2 -2
- package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +23 -2
- package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +19 -2
- package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +84 -0
- package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.d.ts +56 -0
- package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.web.d.ts +27 -0
- package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +81 -0
- package/lib/typescript/src/components/ProjectionMarker/ProjectionMarker.d.ts +82 -0
- package/lib/typescript/src/components/Radio/Radio.d.ts +3 -2
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +2 -2
- package/lib/typescript/src/components/Slider/Slider.d.ts +99 -0
- package/lib/typescript/src/components/TextInput/TextInput.d.ts +9 -29
- package/lib/typescript/src/components/TextSegment/TextSegment.d.ts +100 -0
- package/lib/typescript/src/components/index.d.ts +10 -1
- package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +22 -2
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/lib/typescript/src/utils/react-utils.d.ts +10 -0
- package/package.json +2 -1
- package/src/components/AmountInput/AmountInput.tsx +7 -5
- package/src/components/BenefitCard/BenefitCard.tsx +309 -0
- package/src/components/CcCard/CcCard.tsx +598 -0
- package/src/components/Checkbox/Checkbox.tsx +5 -4
- package/src/components/CheckboxItem/CheckboxItem.tsx +5 -4
- package/src/components/CompareTable/CompareTable.tsx +477 -0
- package/src/components/ComparisonBar/ComparisonBar.tsx +356 -0
- package/src/components/DropdownInput/DropdownInput.tsx +55 -3
- package/src/components/FormField/FormField.tsx +5 -4
- package/src/components/InputSearch/InputSearch.tsx +8 -5
- package/src/components/NoteInput/NoteInput.tsx +8 -6
- package/src/components/PdpCcCard/PdpCcCard.tsx +356 -0
- package/src/components/ProductMerchandisingCard/GlassFill.tsx +276 -0
- package/src/components/ProductMerchandisingCard/GlassFill.web.tsx +127 -0
- package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +423 -0
- package/src/components/ProjectionMarker/ProjectionMarker.tsx +277 -0
- package/src/components/Radio/Radio.tsx +5 -4
- package/src/components/Slider/Slider.tsx +628 -0
- package/src/components/TextInput/TextInput.tsx +15 -11
- package/src/components/TextSegment/TextSegment.tsx +166 -0
- package/src/components/index.ts +10 -1
- package/src/design-tokens/Coin Variables-variables-full.json +1 -1
- package/src/design-tokens/figma-modes.generated.ts +38 -9
- package/src/icons/registry.ts +1 -1
- package/src/utils/react-utils.ts +23 -0
- package/lib/typescript/scripts/extract-component-tokens.d.ts +0 -9
- package/lib/typescript/scripts/generate-component-docs.d.ts +0 -9
- package/lib/typescript/scripts/generate-icon-registry.d.ts +0 -3
- package/lib/typescript/scripts/generate-mode-types.d.ts +0 -2
- package/lib/typescript/scripts/retype-modes.d.cts +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Dimensions, Modal, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
6
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
@@ -16,6 +16,12 @@ const IS_WEB = Platform.OS === 'web';
|
|
|
16
16
|
// Types
|
|
17
17
|
// ---------------------------------------------------------------------------
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Imperative handle exposed via `ref`. Lets the consumer drive the dropdown
|
|
21
|
+
* from outside — e.g. close it when an Android hardware/system button is
|
|
22
|
+
* pressed, or open it programmatically from a sibling control.
|
|
23
|
+
*/
|
|
24
|
+
|
|
19
25
|
// ---------------------------------------------------------------------------
|
|
20
26
|
// Token resolution
|
|
21
27
|
// ---------------------------------------------------------------------------
|
|
@@ -119,7 +125,7 @@ function collectOptionsFromChildren(children) {
|
|
|
119
125
|
// Component
|
|
120
126
|
// ---------------------------------------------------------------------------
|
|
121
127
|
|
|
122
|
-
function DropdownInput({
|
|
128
|
+
const DropdownInput = /*#__PURE__*/forwardRef(function DropdownInput({
|
|
123
129
|
label,
|
|
124
130
|
placeholder = 'Select an option',
|
|
125
131
|
items,
|
|
@@ -150,7 +156,7 @@ function DropdownInput({
|
|
|
150
156
|
accessibilityHint,
|
|
151
157
|
onFocus,
|
|
152
158
|
onBlur
|
|
153
|
-
}) {
|
|
159
|
+
}, ref) {
|
|
154
160
|
// ---------------- Modes ----------------
|
|
155
161
|
const {
|
|
156
162
|
modes: globalModes
|
|
@@ -264,6 +270,32 @@ function DropdownInput({
|
|
|
264
270
|
measure();
|
|
265
271
|
}, [measure]);
|
|
266
272
|
|
|
273
|
+
// ---------------- Imperative handle ----------------
|
|
274
|
+
useImperativeHandle(ref, () => ({
|
|
275
|
+
open: () => {
|
|
276
|
+
if (isDisabled || isReadOnly) return;
|
|
277
|
+
measure();
|
|
278
|
+
setOpenState(true);
|
|
279
|
+
},
|
|
280
|
+
close: closeMenu,
|
|
281
|
+
toggle: () => {
|
|
282
|
+
if (isDisabled || isReadOnly) return;
|
|
283
|
+
measure();
|
|
284
|
+
toggleMenu();
|
|
285
|
+
},
|
|
286
|
+
focus: () => {
|
|
287
|
+
;
|
|
288
|
+
triggerRef.current?.focus?.();
|
|
289
|
+
},
|
|
290
|
+
blur: () => {
|
|
291
|
+
;
|
|
292
|
+
triggerRef.current?.blur?.();
|
|
293
|
+
},
|
|
294
|
+
measureInWindow: callback => {
|
|
295
|
+
triggerRef.current?.measureInWindow(callback);
|
|
296
|
+
}
|
|
297
|
+
}), [isDisabled, isReadOnly, measure, setOpenState, closeMenu, toggleMenu]);
|
|
298
|
+
|
|
267
299
|
// ---------------- Popup positioning ----------------
|
|
268
300
|
const [menuSize, setMenuSize] = useState(null);
|
|
269
301
|
const handleMenuLayout = useCallback(e => {
|
|
@@ -558,7 +590,7 @@ function DropdownInput({
|
|
|
558
590
|
})
|
|
559
591
|
})]
|
|
560
592
|
});
|
|
561
|
-
}
|
|
593
|
+
});
|
|
562
594
|
const webNoOutline = {
|
|
563
595
|
outlineStyle: 'none',
|
|
564
596
|
outlineWidth: 0,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import React, { forwardRef, useCallback, useMemo, useState } from 'react';
|
|
4
4
|
import { View, Text, TextInput as RNTextInput } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { useTokens } from '../../design-tokens/JFSThemeProvider';
|
|
@@ -156,7 +156,7 @@ function firstError(error) {
|
|
|
156
156
|
// Component
|
|
157
157
|
// ---------------------------------------------------------------------------
|
|
158
158
|
|
|
159
|
-
function FormField({
|
|
159
|
+
const FormField = /*#__PURE__*/forwardRef(function FormField({
|
|
160
160
|
label,
|
|
161
161
|
placeholder,
|
|
162
162
|
value,
|
|
@@ -184,7 +184,7 @@ function FormField({
|
|
|
184
184
|
accessibilityLabel,
|
|
185
185
|
accessibilityHint,
|
|
186
186
|
testID
|
|
187
|
-
}) {
|
|
187
|
+
}, ref) {
|
|
188
188
|
// -- Form context integration -------------------------------------------
|
|
189
189
|
const formCtx = useFormContext();
|
|
190
190
|
const formError = name && formCtx ? firstError(formCtx.validationErrors[name]) : undefined;
|
|
@@ -341,6 +341,7 @@ function FormField({
|
|
|
341
341
|
importantForAccessibility: "no",
|
|
342
342
|
children: processedLeading
|
|
343
343
|
}), /*#__PURE__*/_jsx(RNTextInput, {
|
|
344
|
+
ref: ref,
|
|
344
345
|
style: [inputTextStyles, inputTextStyle],
|
|
345
346
|
value: value ?? '',
|
|
346
347
|
onChangeText: handleChangeText,
|
|
@@ -370,5 +371,5 @@ function FormField({
|
|
|
370
371
|
modes: modes
|
|
371
372
|
})]
|
|
372
373
|
});
|
|
373
|
-
}
|
|
374
|
+
});
|
|
374
375
|
export default FormField;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
3
|
+
import React, { forwardRef, useState } from 'react';
|
|
4
4
|
import { View, Text, Pressable } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
@@ -48,7 +48,7 @@ function SupportText({
|
|
|
48
48
|
})]
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
const InputSearch = /*#__PURE__*/forwardRef(function InputSearch({
|
|
52
52
|
supportText = true,
|
|
53
53
|
supportTextLabel = "Support Text",
|
|
54
54
|
supportTextIcon = "ic_info",
|
|
@@ -63,7 +63,7 @@ export default function InputSearch({
|
|
|
63
63
|
trailing,
|
|
64
64
|
inputStyle,
|
|
65
65
|
...rest
|
|
66
|
-
}) {
|
|
66
|
+
}, ref) {
|
|
67
67
|
const [isFocused, setIsFocused] = useState(false);
|
|
68
68
|
|
|
69
69
|
// Hardcode InputState based on the state prop, ignoring any external InputState passed in modes
|
|
@@ -118,6 +118,7 @@ export default function InputSearch({
|
|
|
118
118
|
gap: formFieldGap
|
|
119
119
|
}, containerStyle],
|
|
120
120
|
children: [/*#__PURE__*/_jsx(TextInput, {
|
|
121
|
+
ref: ref,
|
|
121
122
|
placeholder: placeholder,
|
|
122
123
|
value: value || '',
|
|
123
124
|
onChangeText: onChangeText || (() => {}),
|
|
@@ -137,4 +138,5 @@ export default function InputSearch({
|
|
|
137
138
|
modes: componentModes
|
|
138
139
|
})]
|
|
139
140
|
});
|
|
140
|
-
}
|
|
141
|
+
});
|
|
142
|
+
export default InputSearch;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState, useRef } from 'react';
|
|
3
|
+
import React, { forwardRef, useState, useRef } from 'react';
|
|
4
4
|
import { View, TextInput as RNTextInput, Text, Pressable } 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, mergeRefs } from '../../utils/react-utils';
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
/**
|
|
9
9
|
* NoteInput component representing an interactive "Add note" badge style field.
|
|
10
10
|
* Allows the user to click, clears the placeholder text, and shows a blinking cursor when focused.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const NoteInput = /*#__PURE__*/forwardRef(function NoteInput({
|
|
13
13
|
value = '',
|
|
14
14
|
placeholder = 'Add note',
|
|
15
15
|
onChangeText,
|
|
@@ -20,7 +20,7 @@ export default function NoteInput({
|
|
|
20
20
|
onFocus,
|
|
21
21
|
onBlur,
|
|
22
22
|
...rest
|
|
23
|
-
}) {
|
|
23
|
+
}, ref) {
|
|
24
24
|
const [internalFocused, setInternalFocused] = useState(false);
|
|
25
25
|
const inputRef = useRef(null);
|
|
26
26
|
|
|
@@ -92,7 +92,7 @@ export default function NoteInput({
|
|
|
92
92
|
importantForAccessibility: "no",
|
|
93
93
|
children: internalFocused ? value || ' ' : value || placeholder
|
|
94
94
|
}), /*#__PURE__*/_jsx(RNTextInput, {
|
|
95
|
-
ref: inputRef,
|
|
95
|
+
ref: mergeRefs(inputRef, ref),
|
|
96
96
|
value: value,
|
|
97
97
|
onChangeText: onChangeText,
|
|
98
98
|
placeholder: internalFocused ? '' : placeholder,
|
|
@@ -112,4 +112,5 @@ export default function NoteInput({
|
|
|
112
112
|
})]
|
|
113
113
|
})
|
|
114
114
|
});
|
|
115
|
-
}
|
|
115
|
+
});
|
|
116
|
+
export default NoteInput;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
import { View, Text, Pressable, StyleSheet } from 'react-native';
|
|
5
|
+
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
7
|
+
import Title from '../Title/Title';
|
|
8
|
+
import Divider from '../Divider/Divider';
|
|
9
|
+
import Button from '../Button/Button';
|
|
10
|
+
import Image from '../Image/Image';
|
|
11
|
+
import Icon from '../Icon/Icon';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A single metric column inside the card's stats row: a small title, a bold
|
|
15
|
+
* value and an optional muted caption.
|
|
16
|
+
*/
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
|
+
/**
|
|
19
|
+
* PdpCcCard — Figma node 5352:935 ("PDP cc card").
|
|
20
|
+
*
|
|
21
|
+
* A centered white product/PDP card composed from the shared design-system
|
|
22
|
+
* primitives so it stays in sync with the rest of the library:
|
|
23
|
+
*
|
|
24
|
+
* - **Media** — a top image slot (`Image`, rounded via `image/radius`). Pass
|
|
25
|
+
* `imageSource` for the default image or `media` for a full slot override.
|
|
26
|
+
* - **Title** — a centered headline + subtitle rendered through the shared
|
|
27
|
+
* {@link Title} component (`title/*`, `pageSubtitle/*` tokens).
|
|
28
|
+
* - **Metrics** — a row of {@link PdpCcCardMetric} columns (title / value /
|
|
29
|
+
* caption) separated by vertical `Divider`s (`metricdata/*` tokens).
|
|
30
|
+
* - **CTA** — a small tonal {@link Button} (`Button / Size: S`,
|
|
31
|
+
* `AppearanceBrand: Secondary`, `Emphasis: Medium`) with a leading icon.
|
|
32
|
+
*
|
|
33
|
+
* All defaults can be overridden via `modes`.
|
|
34
|
+
*/
|
|
35
|
+
function PdpCcCard({
|
|
36
|
+
imageSource,
|
|
37
|
+
imageWidth = 100,
|
|
38
|
+
imageHeight = 60,
|
|
39
|
+
media,
|
|
40
|
+
title = 'Title',
|
|
41
|
+
subtitle = 'Subtitle',
|
|
42
|
+
metrics = DEFAULT_METRICS,
|
|
43
|
+
buttonLabel = 'button',
|
|
44
|
+
buttonIcon = 'ic_add_circle',
|
|
45
|
+
onButtonPress,
|
|
46
|
+
button,
|
|
47
|
+
showButton = true,
|
|
48
|
+
onPress,
|
|
49
|
+
width = 344,
|
|
50
|
+
modes = EMPTY_MODES,
|
|
51
|
+
style,
|
|
52
|
+
accessibilityLabel
|
|
53
|
+
}) {
|
|
54
|
+
const tokens = useMemo(() => resolveTokens(modes), [modes]);
|
|
55
|
+
|
|
56
|
+
// The CTA uses the brand "Secondary" appearance at "Medium" emphasis (lilac
|
|
57
|
+
// fill + purple label) and the small size — matching the design. A
|
|
58
|
+
// consumer-supplied `modes` value still wins via spread order.
|
|
59
|
+
const ctaModes = useMemo(() => ({
|
|
60
|
+
AppearanceBrand: 'Secondary',
|
|
61
|
+
Emphasis: 'Medium',
|
|
62
|
+
'Button / Size': 'S',
|
|
63
|
+
...modes
|
|
64
|
+
}), [modes]);
|
|
65
|
+
const buttonForeground = useMemo(() => asStr(getVariableByName('button/foreground', ctaModes), '#5d00b5'), [ctaModes]);
|
|
66
|
+
const mediaNode = media ? cloneChildrenWithModes(media, modes) : /*#__PURE__*/_jsx(Image, {
|
|
67
|
+
imageSource: imageSource,
|
|
68
|
+
width: imageWidth,
|
|
69
|
+
height: imageHeight,
|
|
70
|
+
borderRadius: tokens.imageRadius,
|
|
71
|
+
resizeMode: "cover",
|
|
72
|
+
accessibilityElementsHidden: true,
|
|
73
|
+
importantForAccessibility: "no"
|
|
74
|
+
});
|
|
75
|
+
const ctaNode = button ? cloneChildrenWithModes(button, ctaModes) : /*#__PURE__*/_jsx(Button, {
|
|
76
|
+
label: buttonLabel,
|
|
77
|
+
modes: ctaModes,
|
|
78
|
+
onPress: onButtonPress,
|
|
79
|
+
leading: buttonIcon ? /*#__PURE__*/_jsx(Icon, {
|
|
80
|
+
iconName: buttonIcon,
|
|
81
|
+
size: tokens.buttonIconSize,
|
|
82
|
+
color: buttonForeground
|
|
83
|
+
}) : undefined
|
|
84
|
+
});
|
|
85
|
+
const content = /*#__PURE__*/_jsxs(_Fragment, {
|
|
86
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
87
|
+
style: styles.mediaSlot,
|
|
88
|
+
children: mediaNode
|
|
89
|
+
}), /*#__PURE__*/_jsx(Title, {
|
|
90
|
+
title: title,
|
|
91
|
+
subtitle: subtitle,
|
|
92
|
+
textAlign: "Center",
|
|
93
|
+
modes: modes
|
|
94
|
+
}), metrics.length > 0 ? /*#__PURE__*/_jsx(View, {
|
|
95
|
+
style: styles.metricsRow,
|
|
96
|
+
children: metrics.map((metric, index) => /*#__PURE__*/_jsxs(React.Fragment, {
|
|
97
|
+
children: [index > 0 ? /*#__PURE__*/_jsx(Divider, {
|
|
98
|
+
direction: "vertical",
|
|
99
|
+
modes: modes,
|
|
100
|
+
style: styles.metricDivider
|
|
101
|
+
}) : null, /*#__PURE__*/_jsx(Metricdata, {
|
|
102
|
+
metric: metric,
|
|
103
|
+
tokens: tokens
|
|
104
|
+
})]
|
|
105
|
+
}, index))
|
|
106
|
+
}) : null, showButton ? ctaNode : null]
|
|
107
|
+
});
|
|
108
|
+
const containerStyle = useMemo(() => ({
|
|
109
|
+
...tokens.container,
|
|
110
|
+
width
|
|
111
|
+
}), [tokens.container, width]);
|
|
112
|
+
if (onPress) {
|
|
113
|
+
return /*#__PURE__*/_jsx(Pressable, {
|
|
114
|
+
style: ({
|
|
115
|
+
pressed
|
|
116
|
+
}) => [containerStyle, pressed ? styles.pressed : null, style],
|
|
117
|
+
accessibilityRole: "button",
|
|
118
|
+
accessibilityLabel: accessibilityLabel ?? title,
|
|
119
|
+
onPress: onPress,
|
|
120
|
+
children: content
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return /*#__PURE__*/_jsx(View, {
|
|
124
|
+
style: [containerStyle, style],
|
|
125
|
+
accessibilityLabel: accessibilityLabel,
|
|
126
|
+
children: content
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Metricdata — internal metric column (Figma node 5352:256)
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
function Metricdata({
|
|
135
|
+
metric,
|
|
136
|
+
tokens
|
|
137
|
+
}) {
|
|
138
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
139
|
+
style: tokens.metric,
|
|
140
|
+
children: [metric.title != null ? /*#__PURE__*/_jsx(Text, {
|
|
141
|
+
style: tokens.metricTitle,
|
|
142
|
+
children: metric.title
|
|
143
|
+
}) : null, metric.value != null ? /*#__PURE__*/_jsx(Text, {
|
|
144
|
+
style: tokens.metricValue,
|
|
145
|
+
children: metric.value
|
|
146
|
+
}) : null, metric.caption != null ? /*#__PURE__*/_jsx(Text, {
|
|
147
|
+
style: tokens.metricCaption,
|
|
148
|
+
children: metric.caption
|
|
149
|
+
}) : null]
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Tokens / static styles
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
function asNum(raw, fallback) {
|
|
158
|
+
const n = typeof raw === 'number' ? raw : parseFloat(raw);
|
|
159
|
+
return Number.isFinite(n) ? n : fallback;
|
|
160
|
+
}
|
|
161
|
+
function asStr(raw, fallback) {
|
|
162
|
+
return raw != null ? String(raw) : fallback;
|
|
163
|
+
}
|
|
164
|
+
function resolveTokens(modes) {
|
|
165
|
+
// NOTE: token names are passed as string literals DIRECTLY to
|
|
166
|
+
// getVariableByName so the `extract-component-tokens` script can statically
|
|
167
|
+
// collect them for the generated docs. Do not refactor these into a helper
|
|
168
|
+
// that receives the name as a variable.
|
|
169
|
+
const background = asStr(getVariableByName('PDPcccard/bg/color', modes), '#ffffff');
|
|
170
|
+
const paddingHorizontal = asNum(getVariableByName('PDPcccard/padding/horizontal', modes), 16);
|
|
171
|
+
const paddingVertical = asNum(getVariableByName('PDPcccard/padding/vertical', modes), 20);
|
|
172
|
+
const gap = asNum(getVariableByName('PDPcccard/gap', modes), 12);
|
|
173
|
+
const imageRadius = asNum(getVariableByName('image/radius', modes), 8);
|
|
174
|
+
const buttonIconSize = asNum(getVariableByName('button/iconSize', modes), 16);
|
|
175
|
+
const metricGap = asNum(getVariableByName('metricdata/gap', modes), 4);
|
|
176
|
+
const metricPadH = asNum(getVariableByName('metricdata/padding/horizontal', modes), 10);
|
|
177
|
+
const metricPadV = asNum(getVariableByName('metricdata/padding/vertical', modes), 10);
|
|
178
|
+
const titleColor = asStr(getVariableByName('metricdata/title/color', modes), '#000000');
|
|
179
|
+
const titleSize = asNum(getVariableByName('metricdata/title/fontsize', modes), 12);
|
|
180
|
+
const titleFamily = asStr(getVariableByName('metricdata/title/fontfamily', modes), 'JioType Var');
|
|
181
|
+
const titleWeight = asStr(getVariableByName('metricdata/title/fontweight', modes), '400');
|
|
182
|
+
const valueColor = asStr(getVariableByName('metricdata/value/color', modes), '#000000');
|
|
183
|
+
const valueSize = asNum(getVariableByName('metricdata/value/fontsize', modes), 20);
|
|
184
|
+
const valueFamily = asStr(getVariableByName('metricdata/value/fontfamily', modes), 'JioType Var');
|
|
185
|
+
const valueWeight = asStr(getVariableByName('metricdata/value/fontweight', modes), '700');
|
|
186
|
+
const captionColor = asStr(getVariableByName('metricdata/caption/color', modes), '#777777');
|
|
187
|
+
const captionSize = asNum(getVariableByName('metricdata/caption/fontsize', modes), 12);
|
|
188
|
+
const captionFamily = asStr(getVariableByName('metricdata/caption/fontfamily', modes), 'JioType Var');
|
|
189
|
+
const captionWeight = asStr(getVariableByName('metricdata/caption/fontweight', modes), '500');
|
|
190
|
+
return {
|
|
191
|
+
container: {
|
|
192
|
+
backgroundColor: background,
|
|
193
|
+
paddingHorizontal,
|
|
194
|
+
paddingVertical,
|
|
195
|
+
gap,
|
|
196
|
+
flexDirection: 'column',
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
justifyContent: 'center'
|
|
199
|
+
},
|
|
200
|
+
imageRadius,
|
|
201
|
+
buttonIconSize,
|
|
202
|
+
metric: {
|
|
203
|
+
flex: 1,
|
|
204
|
+
gap: metricGap,
|
|
205
|
+
paddingHorizontal: metricPadH,
|
|
206
|
+
paddingVertical: metricPadV,
|
|
207
|
+
alignItems: 'center',
|
|
208
|
+
justifyContent: 'center'
|
|
209
|
+
},
|
|
210
|
+
metricTitle: {
|
|
211
|
+
color: titleColor,
|
|
212
|
+
fontSize: titleSize,
|
|
213
|
+
fontFamily: titleFamily,
|
|
214
|
+
fontWeight: titleWeight,
|
|
215
|
+
lineHeight: Math.round(titleSize * 1.2),
|
|
216
|
+
textAlign: 'center',
|
|
217
|
+
includeFontPadding: false
|
|
218
|
+
},
|
|
219
|
+
metricValue: {
|
|
220
|
+
color: valueColor,
|
|
221
|
+
fontSize: valueSize,
|
|
222
|
+
fontFamily: valueFamily,
|
|
223
|
+
fontWeight: valueWeight,
|
|
224
|
+
lineHeight: Math.round(valueSize * 1.2),
|
|
225
|
+
textAlign: 'center',
|
|
226
|
+
includeFontPadding: false
|
|
227
|
+
},
|
|
228
|
+
metricCaption: {
|
|
229
|
+
color: captionColor,
|
|
230
|
+
fontSize: captionSize,
|
|
231
|
+
fontFamily: captionFamily,
|
|
232
|
+
fontWeight: captionWeight,
|
|
233
|
+
lineHeight: Math.round(captionSize * 1.2),
|
|
234
|
+
textAlign: 'center',
|
|
235
|
+
includeFontPadding: false
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const DEFAULT_METRICS = [{
|
|
240
|
+
title: 'Title',
|
|
241
|
+
value: 'Value',
|
|
242
|
+
caption: 'caption'
|
|
243
|
+
}, {
|
|
244
|
+
title: 'Title',
|
|
245
|
+
value: 'Value',
|
|
246
|
+
caption: 'caption'
|
|
247
|
+
}];
|
|
248
|
+
const styles = StyleSheet.create({
|
|
249
|
+
mediaSlot: {
|
|
250
|
+
alignSelf: 'stretch',
|
|
251
|
+
alignItems: 'center',
|
|
252
|
+
justifyContent: 'center'
|
|
253
|
+
},
|
|
254
|
+
metricsRow: {
|
|
255
|
+
alignSelf: 'stretch',
|
|
256
|
+
flexDirection: 'row',
|
|
257
|
+
alignItems: 'stretch'
|
|
258
|
+
},
|
|
259
|
+
metricDivider: {
|
|
260
|
+
alignSelf: 'center',
|
|
261
|
+
height: '70%'
|
|
262
|
+
},
|
|
263
|
+
pressed: {
|
|
264
|
+
opacity: 0.92
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
export default PdpCcCard;
|