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
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
11
|
+
var _Accordion = _interopRequireDefault(require("../Accordion/Accordion"));
|
|
12
|
+
var _Image = _interopRequireDefault(require("../Image/Image"));
|
|
13
|
+
var _IconButton = _interopRequireDefault(require("../IconButton/IconButton"));
|
|
14
|
+
var _Icon = _interopRequireDefault(require("../../icons/Icon"));
|
|
15
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
/**
|
|
18
|
+
* Brand link colour used by the selection-card CTA. The Figma node resolves
|
|
19
|
+
* `button/foreground` to this value, but no reproducible token mode currently
|
|
20
|
+
* produces it (the published token store is out of sync), so it is pinned here
|
|
21
|
+
* to stay faithful to the design.
|
|
22
|
+
*/
|
|
23
|
+
const BRAND_LINK_COLOR = '#5d00b5';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Selection-card close control (Figma node 5568:3492). It is an `IconButton`
|
|
27
|
+
* pinned to the card's top-right corner. The Figma node resolves its
|
|
28
|
+
* `iconButton/background` and border to fully transparent (`#ebebed00` /
|
|
29
|
+
* `#ffffff00`) — values no token mode reproduces — so they are overridden here.
|
|
30
|
+
* Size (40px) and icon are left to the `IconButton`'s own token resolution
|
|
31
|
+
* (padding 11 · 2 + icon 18).
|
|
32
|
+
*/
|
|
33
|
+
/** Add-a-card icon button fill + icon (Figma node 5568:3494 direct overrides). */
|
|
34
|
+
const ADD_BUTTON_BG = '#545961';
|
|
35
|
+
const ADD_BUTTON_ICON_COLOR = '#ffffff';
|
|
36
|
+
const closeButtonStyle = {
|
|
37
|
+
position: 'absolute',
|
|
38
|
+
top: 1,
|
|
39
|
+
right: 0,
|
|
40
|
+
backgroundColor: 'transparent',
|
|
41
|
+
borderColor: 'transparent'
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Keeps every text layer on a single line on web. */
|
|
45
|
+
const NO_WRAP_TEXT = {
|
|
46
|
+
flexShrink: 0,
|
|
47
|
+
...(_reactNative.Platform.OS === 'web' ? {
|
|
48
|
+
whiteSpace: 'nowrap'
|
|
49
|
+
} : {})
|
|
50
|
+
};
|
|
51
|
+
const toWeight = w => typeof w === 'number' ? `${w}` : w;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A single product/plan column rendered as a selection card at the top of the
|
|
55
|
+
* table. The card order maps 1:1 to each {@link CompareTableRow}'s `values`.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Value rendered inside a comparison cell.
|
|
60
|
+
* - `string` / `number` → rendered as value text.
|
|
61
|
+
* - `false` → renders the muted "not available" cross icon.
|
|
62
|
+
* - any React element → rendered as-is (e.g. a `Badge`, `MoneyValue`, icon…).
|
|
63
|
+
* - `null` / `undefined` / `true` → empty cell.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A collapsible section. Each section renders as an `Accordion` whose body is a
|
|
68
|
+
* comparison table that spans the same columns as the selection cards.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
const DEFAULT_COLUMNS = [{
|
|
72
|
+
label: 'Plan A',
|
|
73
|
+
actionLabel: 'View details',
|
|
74
|
+
onRemove: () => {}
|
|
75
|
+
}, {
|
|
76
|
+
label: 'Plan B',
|
|
77
|
+
actionLabel: 'View details',
|
|
78
|
+
onRemove: () => {}
|
|
79
|
+
}];
|
|
80
|
+
const DEFAULT_SECTIONS = [{
|
|
81
|
+
title: 'Fees & charges',
|
|
82
|
+
header: 'Annual fees',
|
|
83
|
+
defaultExpanded: true,
|
|
84
|
+
rows: [{
|
|
85
|
+
values: ['₹0', '₹499']
|
|
86
|
+
}, {
|
|
87
|
+
values: ['Free', 'Up to ₹1,300']
|
|
88
|
+
}, {
|
|
89
|
+
values: ['1x', '1.25x']
|
|
90
|
+
}]
|
|
91
|
+
}];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* CompareTable renders a product comparison surface: a row of selection cards
|
|
95
|
+
* (one per column) followed by one or more collapsible sections, each holding a
|
|
96
|
+
* comparison table whose cells line up beneath their column.
|
|
97
|
+
*
|
|
98
|
+
* The selection card is an internal sub-component and is intentionally not
|
|
99
|
+
* exported — it should only be used through `CompareTable`. Implementation of
|
|
100
|
+
* Figma node `5568:3496` (`Compare table`) with sub-component `5568:3487`
|
|
101
|
+
* (`selectioncard`).
|
|
102
|
+
*
|
|
103
|
+
* @component
|
|
104
|
+
*/
|
|
105
|
+
function CompareTable({
|
|
106
|
+
columns = DEFAULT_COLUMNS,
|
|
107
|
+
sections = DEFAULT_SECTIONS,
|
|
108
|
+
onAddColumn,
|
|
109
|
+
addColumnLabel = 'Add a card',
|
|
110
|
+
maxColumns = 4,
|
|
111
|
+
modes = _reactUtils.EMPTY_MODES,
|
|
112
|
+
style
|
|
113
|
+
}) {
|
|
114
|
+
// --- selection card tokens ------------------------------------------------
|
|
115
|
+
const cardBg = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/background/color', modes) ?? '#ffffff';
|
|
116
|
+
const cardBorderColor = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/border/color', modes) ?? '#eeeeee';
|
|
117
|
+
const cardBorderSize = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/border/size', modes) ?? 1;
|
|
118
|
+
const cardGap = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/gap', modes) ?? 12;
|
|
119
|
+
const cardPaddingH = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/padding/horizontal', modes) ?? 20;
|
|
120
|
+
const cardPaddingV = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/padding/vertical', modes) ?? 20;
|
|
121
|
+
const cardLabelColor = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/label/color', modes) ?? '#000000';
|
|
122
|
+
const cardLabelFontSize = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/label/fontsize', modes) ?? 14;
|
|
123
|
+
const cardLabelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/label/fontfamily', modes) ?? 'JioType Var';
|
|
124
|
+
const cardLabelFontWeight = (0, _figmaVariablesResolver.getVariableByName)('selectionCard/label/fontweight', modes) ?? 700;
|
|
125
|
+
const imageRadius = (0, _figmaVariablesResolver.getVariableByName)('image/radius', modes) ?? 8;
|
|
126
|
+
|
|
127
|
+
// --- add-a-card icon button tokens ---------------------------------------
|
|
128
|
+
// padding/size/border come from the shared iconButton tokens; the filled
|
|
129
|
+
// background (#545961) and white icon are the Figma node's direct overrides
|
|
130
|
+
// (no token mode reproduces them), so they are pinned to match the design.
|
|
131
|
+
const addBorderColor = (0, _figmaVariablesResolver.getVariableByName)('iconButton/border/color', modes) ?? 'rgba(255,255,255,0)';
|
|
132
|
+
const addBorderSize = (0, _figmaVariablesResolver.getVariableByName)('iconButton/border/size', modes) ?? 1;
|
|
133
|
+
const addPadding = (0, _figmaVariablesResolver.getVariableByName)('iconButton/padding', modes) ?? 11;
|
|
134
|
+
const addIconSize = (0, _figmaVariablesResolver.getVariableByName)('iconButton/icon/size', modes) ?? 18;
|
|
135
|
+
const addBg = ADD_BUTTON_BG;
|
|
136
|
+
const addIconColor = ADD_BUTTON_ICON_COLOR;
|
|
137
|
+
|
|
138
|
+
// --- table header tokens --------------------------------------------------
|
|
139
|
+
const headerBg = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/background/color', modes) ?? '#f5f5f5';
|
|
140
|
+
const headerLabelColor = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/label/color', modes) ?? '#000000';
|
|
141
|
+
const headerFontSize = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/label/fontsize', modes) ?? 14;
|
|
142
|
+
const headerFontFamily = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/label/fontfamily', modes) ?? 'JioType Var';
|
|
143
|
+
const headerFontWeight = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/label/fontweight', modes) ?? 600;
|
|
144
|
+
const headerPaddingH = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/padding/horizontal', modes) ?? 12;
|
|
145
|
+
const headerPaddingV = (0, _figmaVariablesResolver.getVariableByName)('comparetableHeader/padding/vertical', modes) ?? 12;
|
|
146
|
+
|
|
147
|
+
// --- table cell tokens ----------------------------------------------------
|
|
148
|
+
const cellBorderColor = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/border/color', modes) ?? '#eeeeee';
|
|
149
|
+
const cellBorderSize = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/border/size', modes) ?? 1;
|
|
150
|
+
const cellBg = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/background/color', modes) ?? '#ffffff';
|
|
151
|
+
const cellLabelColor = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/label/color', modes) ?? '#000000';
|
|
152
|
+
const cellFontSize = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/label/fontsize', modes) ?? 14;
|
|
153
|
+
const cellFontFamily = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/label/fontfamily', modes) ?? 'JioType Var';
|
|
154
|
+
const cellFontWeight = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/label/fontweight', modes) ?? 400;
|
|
155
|
+
const cellPaddingH = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/padding/horizontal', modes) ?? 12;
|
|
156
|
+
const cellPaddingV = (0, _figmaVariablesResolver.getVariableByName)('compareTableCell/padding/vertical', modes) ?? 12;
|
|
157
|
+
const columnCount = columns.length;
|
|
158
|
+
const showAddCard = onAddColumn != null && columnCount < maxColumns;
|
|
159
|
+
const cardLabelStyle = {
|
|
160
|
+
...NO_WRAP_TEXT,
|
|
161
|
+
color: cardLabelColor,
|
|
162
|
+
fontSize: cardLabelFontSize,
|
|
163
|
+
fontFamily: cardLabelFontFamily,
|
|
164
|
+
fontWeight: toWeight(cardLabelFontWeight),
|
|
165
|
+
lineHeight: cardLabelFontSize * 1.1,
|
|
166
|
+
textAlign: 'center'
|
|
167
|
+
};
|
|
168
|
+
const linkStyle = {
|
|
169
|
+
...NO_WRAP_TEXT,
|
|
170
|
+
color: BRAND_LINK_COLOR,
|
|
171
|
+
fontFamily: cardLabelFontFamily,
|
|
172
|
+
fontSize: 14,
|
|
173
|
+
fontWeight: '700',
|
|
174
|
+
lineHeight: 22
|
|
175
|
+
};
|
|
176
|
+
const headerTextStyle = {
|
|
177
|
+
color: headerLabelColor,
|
|
178
|
+
fontSize: headerFontSize,
|
|
179
|
+
fontFamily: headerFontFamily,
|
|
180
|
+
fontWeight: toWeight(headerFontWeight)
|
|
181
|
+
};
|
|
182
|
+
const cellTextStyle = {
|
|
183
|
+
color: cellLabelColor,
|
|
184
|
+
fontSize: cellFontSize,
|
|
185
|
+
fontFamily: cellFontFamily,
|
|
186
|
+
fontWeight: toWeight(cellFontWeight)
|
|
187
|
+
};
|
|
188
|
+
const cardStyle = {
|
|
189
|
+
flex: 1,
|
|
190
|
+
minWidth: 0,
|
|
191
|
+
minHeight: 147,
|
|
192
|
+
backgroundColor: cardBg,
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
justifyContent: 'center',
|
|
195
|
+
gap: cardGap,
|
|
196
|
+
paddingHorizontal: cardPaddingH,
|
|
197
|
+
paddingVertical: cardPaddingV
|
|
198
|
+
};
|
|
199
|
+
const renderCard = (column, index) => {
|
|
200
|
+
const isLast = index === columnCount - 1 && !showAddCard;
|
|
201
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
202
|
+
style: [cardStyle, {
|
|
203
|
+
borderBottomWidth: cardBorderSize,
|
|
204
|
+
borderBottomColor: cardBorderColor,
|
|
205
|
+
borderRightWidth: isLast ? 0 : cardBorderSize,
|
|
206
|
+
borderRightColor: cardBorderColor
|
|
207
|
+
}],
|
|
208
|
+
children: [column.imageSource != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
|
|
209
|
+
imageSource: column.imageSource,
|
|
210
|
+
width: 66,
|
|
211
|
+
height: 44,
|
|
212
|
+
borderRadius: imageRadius,
|
|
213
|
+
resizeMode: "cover",
|
|
214
|
+
accessibilityLabel: column.label
|
|
215
|
+
}), column.label != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
216
|
+
style: cardLabelStyle,
|
|
217
|
+
numberOfLines: 2,
|
|
218
|
+
children: column.label
|
|
219
|
+
}), column.actionLabel != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
220
|
+
onPress: column.onActionPress,
|
|
221
|
+
accessibilityRole: "button",
|
|
222
|
+
accessibilityLabel: column.actionLabel,
|
|
223
|
+
hitSlop: 6,
|
|
224
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
225
|
+
style: linkStyle,
|
|
226
|
+
children: column.actionLabel
|
|
227
|
+
})
|
|
228
|
+
}), column.onRemove != null &&
|
|
229
|
+
/*#__PURE__*/
|
|
230
|
+
// Figma node 5568:3492 — an Icon Button (not a capsule):
|
|
231
|
+
// padding 11 + icon 18 → 40px circle, transparent background,
|
|
232
|
+
// absolutely pinned to the card's top-right (right:0, top:1).
|
|
233
|
+
(0, _jsxRuntime.jsx)(_IconButton.default, {
|
|
234
|
+
iconName: "ic_close",
|
|
235
|
+
onPress: column.onRemove,
|
|
236
|
+
modes: modes,
|
|
237
|
+
accessibilityLabel: `Remove ${column.label ?? 'column'}`,
|
|
238
|
+
style: closeButtonStyle
|
|
239
|
+
})]
|
|
240
|
+
}, column.key ?? column.label ?? index);
|
|
241
|
+
};
|
|
242
|
+
const renderAddCard = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
243
|
+
onPress: onAddColumn,
|
|
244
|
+
accessibilityRole: "button",
|
|
245
|
+
accessibilityLabel: addColumnLabel,
|
|
246
|
+
style: [cardStyle, {
|
|
247
|
+
borderBottomWidth: cardBorderSize,
|
|
248
|
+
borderBottomColor: cardBorderColor
|
|
249
|
+
}],
|
|
250
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
251
|
+
style: {
|
|
252
|
+
width: addPadding * 2 + addIconSize,
|
|
253
|
+
height: addPadding * 2 + addIconSize,
|
|
254
|
+
borderRadius: 9999,
|
|
255
|
+
borderWidth: addBorderSize,
|
|
256
|
+
borderColor: addBorderColor,
|
|
257
|
+
backgroundColor: addBg,
|
|
258
|
+
alignItems: 'center',
|
|
259
|
+
justifyContent: 'center'
|
|
260
|
+
},
|
|
261
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
262
|
+
name: "ic_add",
|
|
263
|
+
size: addIconSize,
|
|
264
|
+
color: addIconColor,
|
|
265
|
+
accessibilityElementsHidden: true,
|
|
266
|
+
importantForAccessibility: "no"
|
|
267
|
+
})
|
|
268
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
269
|
+
style: cardLabelStyle,
|
|
270
|
+
numberOfLines: 1,
|
|
271
|
+
children: addColumnLabel
|
|
272
|
+
})]
|
|
273
|
+
}, "__add_card__");
|
|
274
|
+
const renderCellContent = (value, cellKey) => {
|
|
275
|
+
if (value === false) {
|
|
276
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
|
|
277
|
+
name: "ic_close",
|
|
278
|
+
size: cellFontSize + 2,
|
|
279
|
+
color: cellBorderColor
|
|
280
|
+
}, cellKey);
|
|
281
|
+
}
|
|
282
|
+
if (value === null || value === undefined || value === true) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
286
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
287
|
+
style: cellTextStyle,
|
|
288
|
+
children: value
|
|
289
|
+
}, cellKey);
|
|
290
|
+
}
|
|
291
|
+
return (0, _reactUtils.cloneChildrenWithModes)(value, modes);
|
|
292
|
+
};
|
|
293
|
+
const renderTable = section => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
294
|
+
style: {
|
|
295
|
+
width: '100%',
|
|
296
|
+
borderWidth: cellBorderSize,
|
|
297
|
+
borderColor: cellBorderColor,
|
|
298
|
+
overflow: 'hidden'
|
|
299
|
+
},
|
|
300
|
+
children: [section.header != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
301
|
+
style: {
|
|
302
|
+
backgroundColor: headerBg,
|
|
303
|
+
paddingHorizontal: headerPaddingH,
|
|
304
|
+
paddingVertical: headerPaddingV,
|
|
305
|
+
borderBottomWidth: cellBorderSize,
|
|
306
|
+
borderBottomColor: cellBorderColor
|
|
307
|
+
},
|
|
308
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
309
|
+
style: headerTextStyle,
|
|
310
|
+
children: section.header
|
|
311
|
+
})
|
|
312
|
+
}), section.rows.map((row, rowIndex) => {
|
|
313
|
+
const isLastRow = rowIndex === section.rows.length - 1;
|
|
314
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
315
|
+
style: {
|
|
316
|
+
flexDirection: 'row',
|
|
317
|
+
width: '100%'
|
|
318
|
+
},
|
|
319
|
+
children: Array.from({
|
|
320
|
+
length: columnCount
|
|
321
|
+
}).map((_, colIndex) => {
|
|
322
|
+
const isLastCol = colIndex === columnCount - 1;
|
|
323
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
324
|
+
style: {
|
|
325
|
+
flex: 1,
|
|
326
|
+
minWidth: 0,
|
|
327
|
+
backgroundColor: cellBg,
|
|
328
|
+
justifyContent: 'center',
|
|
329
|
+
paddingHorizontal: cellPaddingH,
|
|
330
|
+
paddingVertical: cellPaddingV,
|
|
331
|
+
borderRightWidth: isLastCol ? 0 : cellBorderSize,
|
|
332
|
+
borderRightColor: cellBorderColor,
|
|
333
|
+
borderBottomWidth: isLastRow ? 0 : cellBorderSize,
|
|
334
|
+
borderBottomColor: cellBorderColor
|
|
335
|
+
},
|
|
336
|
+
children: renderCellContent(row.values?.[colIndex], `${rowIndex}-${colIndex}`)
|
|
337
|
+
}, colIndex);
|
|
338
|
+
})
|
|
339
|
+
}, row.key ?? rowIndex);
|
|
340
|
+
})]
|
|
341
|
+
});
|
|
342
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
343
|
+
style: [{
|
|
344
|
+
width: '100%',
|
|
345
|
+
backgroundColor: cardBg,
|
|
346
|
+
borderWidth: 1,
|
|
347
|
+
borderColor: '#e8e8e8',
|
|
348
|
+
overflow: 'hidden'
|
|
349
|
+
}, style],
|
|
350
|
+
children: [(columnCount > 0 || showAddCard) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
351
|
+
style: {
|
|
352
|
+
flexDirection: 'row',
|
|
353
|
+
width: '100%'
|
|
354
|
+
},
|
|
355
|
+
children: [columns.map(renderCard), showAddCard && renderAddCard()]
|
|
356
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
357
|
+
style: {
|
|
358
|
+
width: '100%'
|
|
359
|
+
},
|
|
360
|
+
children: sections.map((section, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_Accordion.default, {
|
|
361
|
+
title: section.title,
|
|
362
|
+
defaultExpanded: section.defaultExpanded ?? index === 0,
|
|
363
|
+
modes: modes,
|
|
364
|
+
style: {
|
|
365
|
+
paddingHorizontal: headerPaddingH
|
|
366
|
+
},
|
|
367
|
+
children: renderTable(section)
|
|
368
|
+
}, section.key ?? section.title ?? index))
|
|
369
|
+
})]
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
var _default = exports.default = CompareTable;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
10
|
+
var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
|
|
11
|
+
var _reactUtils = require("../../utils/react-utils");
|
|
12
|
+
var _webPlatformUtils = require("../../utils/web-platform-utils");
|
|
13
|
+
var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
14
|
+
var _Image = _interopRequireDefault(require("../Image/Image"));
|
|
15
|
+
var _IconCapsule = _interopRequireDefault(require("../IconCapsule/IconCapsule"));
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
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
|
+
* A single slot in the {@link ComparisonBar}. Each item is either empty (the
|
|
21
|
+
* "Add" state — a tappable `+` capsule) or filled with an image (the
|
|
22
|
+
* "Image Added" state — the image plus a dismiss capsule in the corner).
|
|
23
|
+
*
|
|
24
|
+
* The presence of {@link ComparisonBarItem.imageSource} is what toggles the
|
|
25
|
+
* state: provide a source to show the image, leave it `undefined`/`null` to
|
|
26
|
+
* show the empty add slot. This keeps the component fully controlled — the
|
|
27
|
+
* `ComparisonBar` never owns the image state itself, so the consumer decides
|
|
28
|
+
* (e.g. after opening a file/asset picker) when and how an item flips between
|
|
29
|
+
* the two states.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const ITEM_WIDTH = 45;
|
|
33
|
+
const ITEM_HEIGHT = 44;
|
|
34
|
+
function resolveTokens(modes) {
|
|
35
|
+
const cardGap = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/gap', modes) ?? 12;
|
|
36
|
+
const cardPadH = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/padding/horizontal', modes) ?? 12;
|
|
37
|
+
const cardPadV = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/padding/vertical', modes) ?? 10;
|
|
38
|
+
const cardRadius = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/radius', modes) ?? 12;
|
|
39
|
+
const cardBackground = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/background', modes) ?? '#ffffff';
|
|
40
|
+
const cardBorderColor = (0, _figmaVariablesResolver.getVariableByName)('compareFloatCard/border/color', modes) ?? '#f5f5f5';
|
|
41
|
+
const itemPadH = (0, _figmaVariablesResolver.getVariableByName)('compareCardItem/padding/horizontal', modes) ?? 6;
|
|
42
|
+
const itemPadV = (0, _figmaVariablesResolver.getVariableByName)('compareCardItem/padding/vertical', modes) ?? 8;
|
|
43
|
+
const itemRadius = (0, _figmaVariablesResolver.getVariableByName)('compareCardItem/radius', modes) ?? 8;
|
|
44
|
+
const itemBackground = (0, _figmaVariablesResolver.getVariableByName)('compareCardItem/background', modes) ?? '#ebebed';
|
|
45
|
+
const imageRadius = (0, _figmaVariablesResolver.getVariableByName)('image/radius', modes) ?? 8;
|
|
46
|
+
return {
|
|
47
|
+
card: {
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
gap: cardGap,
|
|
51
|
+
paddingHorizontal: cardPadH,
|
|
52
|
+
paddingVertical: cardPadV,
|
|
53
|
+
borderRadius: cardRadius,
|
|
54
|
+
borderWidth: 1,
|
|
55
|
+
borderColor: cardBorderColor,
|
|
56
|
+
backgroundColor: cardBackground,
|
|
57
|
+
alignSelf: 'flex-start'
|
|
58
|
+
},
|
|
59
|
+
item: {
|
|
60
|
+
width: ITEM_WIDTH,
|
|
61
|
+
height: ITEM_HEIGHT,
|
|
62
|
+
borderRadius: itemRadius,
|
|
63
|
+
backgroundColor: itemBackground,
|
|
64
|
+
paddingHorizontal: itemPadH,
|
|
65
|
+
paddingVertical: itemPadV,
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
justifyContent: 'center'
|
|
68
|
+
},
|
|
69
|
+
itemImageState: {
|
|
70
|
+
paddingHorizontal: itemPadH,
|
|
71
|
+
paddingVertical: itemPadV
|
|
72
|
+
},
|
|
73
|
+
imageRadius
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Internal slot renderer for {@link ComparisonBar}. Intentionally NOT exported
|
|
78
|
+
* — it is meaningless outside of a `ComparisonBar` (its layout, sizing and
|
|
79
|
+
* remove affordance all assume the surrounding card) and is kept private so
|
|
80
|
+
* the public surface stays a single, cohesive component.
|
|
81
|
+
*/
|
|
82
|
+
function Additem({
|
|
83
|
+
item,
|
|
84
|
+
index,
|
|
85
|
+
tokens,
|
|
86
|
+
addCapsuleModes,
|
|
87
|
+
closeCapsuleModes,
|
|
88
|
+
onPress,
|
|
89
|
+
onRemove
|
|
90
|
+
}) {
|
|
91
|
+
const hasImage = item.imageSource != null && item.imageSource !== '';
|
|
92
|
+
const id = item.id ?? index;
|
|
93
|
+
const addWebProps = (0, _webPlatformUtils.usePressableWebSupport)({
|
|
94
|
+
restProps: {},
|
|
95
|
+
onPress: () => onPress?.(id, index),
|
|
96
|
+
disabled: false,
|
|
97
|
+
accessibilityLabel: item.accessibilityLabel ?? 'Add item to comparison'
|
|
98
|
+
});
|
|
99
|
+
const removeWebProps = (0, _webPlatformUtils.usePressableWebSupport)({
|
|
100
|
+
restProps: {},
|
|
101
|
+
onPress: () => onRemove?.(id, index),
|
|
102
|
+
disabled: false,
|
|
103
|
+
accessibilityLabel: item.accessibilityLabel ?? 'Remove item from comparison'
|
|
104
|
+
});
|
|
105
|
+
if (!hasImage) {
|
|
106
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
107
|
+
style: tokens.item,
|
|
108
|
+
accessibilityRole: "button",
|
|
109
|
+
accessibilityLabel: item.accessibilityLabel ?? 'Add item to comparison',
|
|
110
|
+
onPress: () => onPress?.(id, index),
|
|
111
|
+
...addWebProps,
|
|
112
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconCapsule.default, {
|
|
113
|
+
iconName: "ic_add",
|
|
114
|
+
modes: addCapsuleModes,
|
|
115
|
+
style: ADD_CAPSULE_STYLE
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Mobile-first: the entire filled slot is the remove target, not just the
|
|
121
|
+
// tiny close capsule (which a fingertip struggles to hit). The capsule stays
|
|
122
|
+
// purely as a visual affordance and is marked non-interactive so it never
|
|
123
|
+
// intercepts the press from the surrounding Pressable.
|
|
124
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
125
|
+
style: [tokens.item, tokens.itemImageState],
|
|
126
|
+
accessibilityRole: "button",
|
|
127
|
+
accessibilityLabel: item.accessibilityLabel ?? 'Remove item from comparison',
|
|
128
|
+
onPress: () => onRemove?.(id, index),
|
|
129
|
+
...removeWebProps,
|
|
130
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Image.default, {
|
|
131
|
+
imageSource: item.imageSource,
|
|
132
|
+
width: "100%",
|
|
133
|
+
height: "100%",
|
|
134
|
+
borderRadius: tokens.imageRadius,
|
|
135
|
+
resizeMode: "cover",
|
|
136
|
+
accessibilityLabel: item.accessibilityLabel ?? 'Comparison item image'
|
|
137
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
138
|
+
style: CLOSE_CAPSULE_WRAPPER_STYLE,
|
|
139
|
+
pointerEvents: "none",
|
|
140
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_IconCapsule.default, {
|
|
141
|
+
iconName: "ic_close",
|
|
142
|
+
modes: closeCapsuleModes
|
|
143
|
+
})
|
|
144
|
+
})]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// The Add capsule is the transparent IconCapsule variant from Figma (the gray
|
|
149
|
+
// item box is the visible surface); its size/icon come from the resolved
|
|
150
|
+
// `Icon Capsule Size: S` tokens, we only flatten the background/border here.
|
|
151
|
+
const ADD_CAPSULE_STYLE = {
|
|
152
|
+
backgroundColor: 'transparent',
|
|
153
|
+
borderColor: 'transparent'
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Positions the dismiss IconCapsule in the slot's top-right corner. The capsule
|
|
157
|
+
// itself (size/background/icon) is fully token-driven via `closeCapsuleModes`.
|
|
158
|
+
const CLOSE_CAPSULE_WRAPPER_STYLE = {
|
|
159
|
+
position: 'absolute',
|
|
160
|
+
top: 1,
|
|
161
|
+
right: 1
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Mode overrides applied on top of the consumer's `modes` for each capsule.
|
|
165
|
+
// These mirror the Figma component's IconCapsule variant selections so the
|
|
166
|
+
// sizing and colours come from design tokens instead of magic numbers.
|
|
167
|
+
const ADD_CAPSULE_MODE_OVERRIDES = {
|
|
168
|
+
'Icon Capsule Size': 'XS'
|
|
169
|
+
};
|
|
170
|
+
const CLOSE_CAPSULE_MODE_OVERRIDES = {
|
|
171
|
+
AppearanceBrand: 'Neutral',
|
|
172
|
+
Emphasis: 'Medium',
|
|
173
|
+
'Icon Capsule Size': 'XS'
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* ComparisonBar — a floating card that lets a user assemble a set of items to
|
|
178
|
+
* compare, then trigger the comparison.
|
|
179
|
+
*
|
|
180
|
+
* Each slot is fully controlled via its `imageSource`: an empty slot shows a
|
|
181
|
+
* tappable `+` (the "Add" state) and a filled slot shows the image with a
|
|
182
|
+
* dismiss capsule (the "Image Added" state). The component never sources or
|
|
183
|
+
* stores images itself — when an empty slot is pressed it fires `onItemPress`
|
|
184
|
+
* with the item's id/index so the consumer can open whatever picker is
|
|
185
|
+
* appropriate and then update that item's `imageSource` to flip its state.
|
|
186
|
+
* Tapping a filled slot (anywhere on it — a mobile-friendly hit target, with
|
|
187
|
+
* the dismiss capsule as a visual affordance) fires `onItemRemove` so the
|
|
188
|
+
* consumer can clear the source again.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```tsx
|
|
192
|
+
* const [items, setItems] = useState<ComparisonBarItem[]>([
|
|
193
|
+
* { id: 'a' }, { id: 'b' }, { id: 'c' }, { id: 'd' },
|
|
194
|
+
* ])
|
|
195
|
+
*
|
|
196
|
+
* <ComparisonBar
|
|
197
|
+
* items={items}
|
|
198
|
+
* onItemPress={async (id) => {
|
|
199
|
+
* const uri = await openImagePicker()
|
|
200
|
+
* setItems(prev => prev.map(it => it.id === id ? { ...it, imageSource: uri } : it))
|
|
201
|
+
* }}
|
|
202
|
+
* onItemRemove={(id) =>
|
|
203
|
+
* setItems(prev => prev.map(it => it.id === id ? { ...it, imageSource: null } : it))
|
|
204
|
+
* }
|
|
205
|
+
* onCompare={runComparison}
|
|
206
|
+
* />
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
function ComparisonBar({
|
|
210
|
+
items,
|
|
211
|
+
onItemPress,
|
|
212
|
+
onItemRemove,
|
|
213
|
+
onCompare,
|
|
214
|
+
compareLabel = 'Compare',
|
|
215
|
+
compareDisabled,
|
|
216
|
+
disableCompareWhenEmpty = true,
|
|
217
|
+
modes: propModes = _reactUtils.EMPTY_MODES,
|
|
218
|
+
style
|
|
219
|
+
}) {
|
|
220
|
+
const {
|
|
221
|
+
modes: globalModes
|
|
222
|
+
} = (0, _JFSThemeProvider.useTokens)();
|
|
223
|
+
const modes = (0, _react.useMemo)(() => globalModes === _reactUtils.EMPTY_MODES && propModes === _reactUtils.EMPTY_MODES ? _reactUtils.EMPTY_MODES : {
|
|
224
|
+
...globalModes,
|
|
225
|
+
...propModes
|
|
226
|
+
}, [globalModes, propModes]);
|
|
227
|
+
const tokens = (0, _react.useMemo)(() => resolveTokens(modes), [modes]);
|
|
228
|
+
|
|
229
|
+
// Capsule modes = consumer modes + this component's fixed IconCapsule variant
|
|
230
|
+
// selections. Memoized so each `Additem`'s `IconCapsule` keeps a stable
|
|
231
|
+
// `modes` identity and hits the resolver cache.
|
|
232
|
+
const addCapsuleModes = (0, _react.useMemo)(() => ({
|
|
233
|
+
...modes,
|
|
234
|
+
...ADD_CAPSULE_MODE_OVERRIDES
|
|
235
|
+
}), [modes]);
|
|
236
|
+
const closeCapsuleModes = (0, _react.useMemo)(() => ({
|
|
237
|
+
...modes,
|
|
238
|
+
...CLOSE_CAPSULE_MODE_OVERRIDES
|
|
239
|
+
}), [modes]);
|
|
240
|
+
|
|
241
|
+
// An explicit `compareDisabled` always wins (functional disable — the tap is
|
|
242
|
+
// truly blocked, not merely dimmed). Otherwise auto-disable while no slot has
|
|
243
|
+
// an image, since there is nothing to compare yet.
|
|
244
|
+
const hasAnyImage = (0, _react.useMemo)(() => items.some(it => it.imageSource != null && it.imageSource !== ''), [items]);
|
|
245
|
+
const isCompareDisabled = compareDisabled ?? (disableCompareWhenEmpty && !hasAnyImage);
|
|
246
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
247
|
+
style: [tokens.card, style],
|
|
248
|
+
children: [items.map((item, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Additem, {
|
|
249
|
+
item: item,
|
|
250
|
+
index: index,
|
|
251
|
+
tokens: tokens,
|
|
252
|
+
addCapsuleModes: addCapsuleModes,
|
|
253
|
+
closeCapsuleModes: closeCapsuleModes,
|
|
254
|
+
onPress: onItemPress,
|
|
255
|
+
onRemove: onItemRemove
|
|
256
|
+
}, item.id ?? index)), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
257
|
+
label: compareLabel,
|
|
258
|
+
modes: modes,
|
|
259
|
+
disabled: isCompareDisabled,
|
|
260
|
+
...(onCompare !== undefined ? {
|
|
261
|
+
onPress: onCompare
|
|
262
|
+
} : {})
|
|
263
|
+
})]
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
var _default = exports.default = /*#__PURE__*/_react.default.memo(ComparisonBar);
|