jfs-components 0.1.28 → 0.1.30
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 +15 -2
- package/lib/commonjs/components/CompareTable/CompareTable.js +191 -25
- package/lib/commonjs/components/ContentSheet/ContentSheet.js +66 -6
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +179 -54
- package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +7 -1
- package/lib/commonjs/components/Table/Table.js +27 -7
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/CompareTable/CompareTable.js +191 -26
- package/lib/module/components/ContentSheet/ContentSheet.js +69 -9
- package/lib/module/components/MoneyValue/MoneyValue.js +182 -57
- package/lib/module/components/PdpCcCard/PdpCcCard.js +7 -1
- package/lib/module/components/Table/Table.js +27 -7
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +21 -1
- package/lib/typescript/src/components/ContentSheet/ContentSheet.d.ts +14 -1
- package/lib/typescript/src/components/MoneyValue/MoneyValue.d.ts +10 -1
- package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +25 -1
- package/lib/typescript/src/components/Table/Table.d.ts +9 -1
- package/lib/typescript/src/icons/registry.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/CompareTable/CompareTable.tsx +224 -30
- package/src/components/ContentSheet/ContentSheet.tsx +88 -8
- package/src/components/MoneyValue/MoneyValue.tsx +216 -65
- package/src/components/PdpCcCard/PdpCcCard.tsx +36 -1
- package/src/components/Table/Table.tsx +29 -4
- package/src/icons/registry.ts +1 -1
|
@@ -61,6 +61,8 @@ function MoneyValue({
|
|
|
61
61
|
negative,
|
|
62
62
|
focused = false,
|
|
63
63
|
hidden = false,
|
|
64
|
+
editable = false,
|
|
65
|
+
onValueChange,
|
|
64
66
|
modes = _reactUtils.EMPTY_MODES,
|
|
65
67
|
style,
|
|
66
68
|
valueStyle,
|
|
@@ -72,24 +74,51 @@ function MoneyValue({
|
|
|
72
74
|
...rest
|
|
73
75
|
}) {
|
|
74
76
|
// Auto-detect negative from value and compute display value
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} = (0, _react.useMemo)(() => {
|
|
79
|
-
const stringValue = String(value);
|
|
80
|
-
const trimmed = stringValue.trim();
|
|
81
|
-
const valueIsNegative = trimmed.startsWith('-') || typeof value === 'number' && value < 0;
|
|
77
|
+
const stringValue = String(value);
|
|
78
|
+
const trimmed = stringValue.trim();
|
|
79
|
+
const valueIsNegative = trimmed.startsWith('-') || typeof value === 'number' && value < 0;
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
// Strip leading minus sign for display (we show it separately)
|
|
82
|
+
const absoluteValue = trimmed.startsWith('-') ? trimmed.slice(1) : trimmed;
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
// Use explicit negative prop if provided, otherwise use auto-detected
|
|
85
|
+
const showNegative = negative !== undefined ? negative : valueIsNegative;
|
|
86
|
+
const isNegative = hidden ? false : showNegative;
|
|
87
|
+
const displayValue = hidden ? '•••' : absoluteValue;
|
|
88
|
+
|
|
89
|
+
// Inline editing state
|
|
90
|
+
const [isEditing, setIsEditing] = (0, _react.useState)(false);
|
|
91
|
+
const [editBuffer, setEditBuffer] = (0, _react.useState)('');
|
|
92
|
+
const inputRef = (0, _react.useRef)(null);
|
|
93
|
+
// Always-current snapshot of the display value for callbacks to reference.
|
|
94
|
+
const displayValueRef = (0, _react.useRef)(displayValue);
|
|
95
|
+
displayValueRef.current = displayValue;
|
|
96
|
+
const enterEditMode = (0, _react.useCallback)(() => {
|
|
97
|
+
setIsEditing(true);
|
|
98
|
+
setEditBuffer(displayValueRef.current);
|
|
99
|
+
// Focus the input on next frame so the keyboard opens
|
|
100
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
101
|
+
}, []);
|
|
102
|
+
const commitEdit = (0, _react.useCallback)(() => {
|
|
103
|
+
setIsEditing(false);
|
|
104
|
+
const resolved = editBuffer || '0';
|
|
105
|
+
if (resolved !== displayValueRef.current) {
|
|
106
|
+
onValueChange?.(resolved);
|
|
107
|
+
}
|
|
108
|
+
}, [editBuffer, onValueChange]);
|
|
109
|
+
const handleChangeText = (0, _react.useCallback)(text => {
|
|
110
|
+
// Allow only digits and at most one decimal point
|
|
111
|
+
const filtered = text.replace(/[^0-9.]/g, '');
|
|
112
|
+
const parts = filtered.split('.');
|
|
113
|
+
const clean = parts.length > 2 ? parts[0] + '.' + parts.slice(1).join('') : filtered;
|
|
114
|
+
setEditBuffer(clean);
|
|
115
|
+
}, []);
|
|
116
|
+
const handlePress = (0, _react.useCallback)(event => {
|
|
117
|
+
if (editable) {
|
|
118
|
+
enterEditMode();
|
|
119
|
+
}
|
|
120
|
+
onPress?.(event);
|
|
121
|
+
}, [editable, enterEditMode, onPress]);
|
|
93
122
|
|
|
94
123
|
// Resolve typography and layout tokens from Figma
|
|
95
124
|
const textColor = (0, _figmaVariablesResolver.getVariableByName)('moneyValue/text/color', modes);
|
|
@@ -150,44 +179,140 @@ function MoneyValue({
|
|
|
150
179
|
cursorOpacity.setValue(0);
|
|
151
180
|
}
|
|
152
181
|
}, [focused, cursorOpacity]);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
|
|
183
|
+
// Keyboard avoidance — lift the component when the keyboard would cover it.
|
|
184
|
+
// Follows the same pattern as ActionFooter (uses Keyboard.addListener).
|
|
185
|
+
// Resets when editing ends or the keyboard hides.
|
|
186
|
+
const rootRef = (0, _react.useRef)(null);
|
|
187
|
+
const keyboardLift = (0, _react.useRef)(new _reactNative.Animated.Value(0)).current;
|
|
188
|
+
const rootWindowYRef = (0, _react.useRef)(0);
|
|
189
|
+
const rootMeasuredHeightRef = (0, _react.useRef)(0);
|
|
190
|
+
const measureRoot = (0, _react.useCallback)(() => {
|
|
191
|
+
rootRef.current?.measureInWindow((_x, y, _w, h) => {
|
|
192
|
+
rootWindowYRef.current = y;
|
|
193
|
+
rootMeasuredHeightRef.current = h;
|
|
194
|
+
});
|
|
195
|
+
}, []);
|
|
196
|
+
const handleRootLayout = (0, _react.useCallback)(_e => {
|
|
197
|
+
// measureInWindow gives the component's position relative to the window,
|
|
198
|
+
// which is what we need for keyboard overlap calculation. onLayout fires
|
|
199
|
+
// after the component mounts and whenever its size changes.
|
|
200
|
+
measureRoot();
|
|
201
|
+
}, [measureRoot]);
|
|
202
|
+
(0, _react.useEffect)(() => {
|
|
203
|
+
if (!editable) return;
|
|
204
|
+
const showSub = _reactNative.Keyboard.addListener('keyboardDidShow', e => {
|
|
205
|
+
measureRoot();
|
|
206
|
+
if (!isEditing) return;
|
|
207
|
+
const kbHeight = e.endCoordinates.height;
|
|
208
|
+
const {
|
|
209
|
+
height: screenH
|
|
210
|
+
} = require('react-native').Dimensions.get('window');
|
|
211
|
+
const componentBottom = rootWindowYRef.current + rootMeasuredHeightRef.current;
|
|
212
|
+
const visibleBottom = screenH - kbHeight;
|
|
213
|
+
const overlap = componentBottom - visibleBottom + 12;
|
|
214
|
+
if (overlap > 0) {
|
|
215
|
+
_reactNative.Animated.timing(keyboardLift, {
|
|
216
|
+
toValue: -overlap,
|
|
217
|
+
duration: e.duration ?? 250,
|
|
218
|
+
useNativeDriver: true
|
|
219
|
+
}).start();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
const hideSub = _reactNative.Keyboard.addListener('keyboardDidHide', () => {
|
|
223
|
+
_reactNative.Animated.timing(keyboardLift, {
|
|
224
|
+
toValue: 0,
|
|
225
|
+
duration: 150,
|
|
226
|
+
useNativeDriver: true
|
|
227
|
+
}).start();
|
|
228
|
+
});
|
|
229
|
+
return () => {
|
|
230
|
+
showSub.remove();
|
|
231
|
+
hideSub.remove();
|
|
232
|
+
};
|
|
233
|
+
}, [editable, isEditing, keyboardLift, measureRoot]);
|
|
234
|
+
|
|
235
|
+
// Reset lift when editing ends
|
|
236
|
+
(0, _react.useEffect)(() => {
|
|
237
|
+
if (!isEditing) {
|
|
238
|
+
keyboardLift.setValue(0);
|
|
239
|
+
}
|
|
240
|
+
}, [isEditing, keyboardLift]);
|
|
241
|
+
const handleBlur = (0, _react.useCallback)(() => {
|
|
242
|
+
commitEdit();
|
|
243
|
+
}, [commitEdit]);
|
|
244
|
+
const handleSubmitEditing = (0, _react.useCallback)(() => {
|
|
245
|
+
commitEdit();
|
|
246
|
+
}, [commitEdit]);
|
|
247
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
248
|
+
style: {
|
|
249
|
+
transform: [{
|
|
250
|
+
translateY: keyboardLift
|
|
251
|
+
}]
|
|
252
|
+
},
|
|
253
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
254
|
+
ref: rootRef,
|
|
255
|
+
onLayout: handleRootLayout,
|
|
256
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
|
|
257
|
+
style: [containerStyle, style],
|
|
258
|
+
accessibilityRole: "text",
|
|
259
|
+
accessibilityLabel: undefined,
|
|
260
|
+
accessibilityHint: accessibilityHint,
|
|
261
|
+
onPress: handlePress,
|
|
262
|
+
disabled: !onPress && !editable,
|
|
263
|
+
...rest,
|
|
264
|
+
children: [isNegative && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
265
|
+
style: [currencyTextStyle, negativeSignStyle],
|
|
266
|
+
accessibilityElementsHidden: true,
|
|
267
|
+
importantForAccessibility: "no",
|
|
268
|
+
children: "-"
|
|
269
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
270
|
+
style: [currencyTextStyle, currencyStyle],
|
|
271
|
+
accessibilityElementsHidden: true,
|
|
272
|
+
importantForAccessibility: "no",
|
|
273
|
+
children: resolvedCurrency
|
|
274
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
275
|
+
style: {
|
|
276
|
+
flexDirection: 'row',
|
|
277
|
+
alignItems: 'center'
|
|
278
|
+
},
|
|
279
|
+
children: isEditing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
280
|
+
ref: inputRef,
|
|
281
|
+
value: editBuffer,
|
|
282
|
+
onChangeText: handleChangeText,
|
|
283
|
+
onBlur: handleBlur,
|
|
284
|
+
onSubmitEditing: handleSubmitEditing,
|
|
285
|
+
keyboardType: "decimal-pad",
|
|
286
|
+
returnKeyType: "done",
|
|
287
|
+
selectTextOnFocus: true,
|
|
288
|
+
style: [valueTextStyle, valueStyle, {
|
|
289
|
+
padding: 0,
|
|
290
|
+
margin: 0,
|
|
291
|
+
// On web a thin border helps the cursor appear precisely
|
|
292
|
+
...(_reactNative.Platform.OS === 'web' ? {
|
|
293
|
+
outlineStyle: 'none'
|
|
294
|
+
} : {})
|
|
295
|
+
}],
|
|
296
|
+
accessibilityLabel: accessibilityLabel || 'Edit value'
|
|
297
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
298
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
299
|
+
style: [valueTextStyle, valueStyle],
|
|
300
|
+
accessibilityElementsHidden: true,
|
|
301
|
+
importantForAccessibility: "no",
|
|
302
|
+
children: displayValue
|
|
303
|
+
}), focused && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
304
|
+
style: {
|
|
305
|
+
opacity: cursorOpacity,
|
|
306
|
+
width: 2,
|
|
307
|
+
height: valueFontSize * 1.1,
|
|
308
|
+
backgroundColor: textColor,
|
|
309
|
+
marginLeft: 2
|
|
310
|
+
}
|
|
311
|
+
})]
|
|
312
|
+
})
|
|
313
|
+
})]
|
|
314
|
+
})
|
|
315
|
+
})
|
|
191
316
|
});
|
|
192
317
|
}
|
|
193
318
|
var _default = exports.default = MoneyValue;
|
|
@@ -45,6 +45,9 @@ function PdpCcCard({
|
|
|
45
45
|
media,
|
|
46
46
|
title = 'Title',
|
|
47
47
|
subtitle = 'Subtitle',
|
|
48
|
+
numberOfLines,
|
|
49
|
+
disableTruncation,
|
|
50
|
+
singleLine,
|
|
48
51
|
metrics = DEFAULT_METRICS,
|
|
49
52
|
buttonLabel = 'button',
|
|
50
53
|
buttonIcon = 'ic_add_circle',
|
|
@@ -96,7 +99,10 @@ function PdpCcCard({
|
|
|
96
99
|
title: title,
|
|
97
100
|
subtitle: subtitle,
|
|
98
101
|
textAlign: "Center",
|
|
99
|
-
modes: modes
|
|
102
|
+
modes: modes,
|
|
103
|
+
numberOfLines: numberOfLines,
|
|
104
|
+
disableTruncation: disableTruncation,
|
|
105
|
+
singleLine: singleLine
|
|
100
106
|
}), metrics.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
101
107
|
style: styles.metricsRow,
|
|
102
108
|
children: metrics.map((metric, index) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.default.Fragment, {
|
|
@@ -9,12 +9,22 @@ exports.TableHeader = TableHeader;
|
|
|
9
9
|
exports.TableHeaderCell = TableHeaderCell;
|
|
10
10
|
exports.TableRow = TableRow;
|
|
11
11
|
exports.default = void 0;
|
|
12
|
-
var _react =
|
|
12
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
13
13
|
var _reactNative = require("react-native");
|
|
14
14
|
var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
|
|
15
15
|
var _reactUtils = require("../../utils/react-utils");
|
|
16
16
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
-
function
|
|
17
|
+
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); }
|
|
18
|
+
/**
|
|
19
|
+
* Carries the root `Table`'s `columnWidth` down to every cell so that, when it
|
|
20
|
+
* is set, all columns (header + body, data-driven or composed) share the same
|
|
21
|
+
* fixed width without each cell having to repeat the prop. Cells still honour
|
|
22
|
+
* an explicit `width` prop over this context value. `undefined` (the default,
|
|
23
|
+
* when no `Table` ancestor is present, or `columnWidth` is unset) leaves cells
|
|
24
|
+
* to flex — preserving the original behaviour.
|
|
25
|
+
*/
|
|
26
|
+
const TableContext = /*#__PURE__*/(0, _react.createContext)(undefined);
|
|
27
|
+
|
|
18
28
|
/* -------------------------------------------------------------------------------------------------
|
|
19
29
|
* Shared types & token resolution
|
|
20
30
|
* -----------------------------------------------------------------------------------------------*/
|
|
@@ -98,6 +108,10 @@ function TableCell({
|
|
|
98
108
|
style
|
|
99
109
|
}) {
|
|
100
110
|
const t = resolveTableTokens(modes);
|
|
111
|
+
// A cell's width resolves in priority order: an explicit `width` prop, then
|
|
112
|
+
// the root Table's `columnWidth` (via context), then fall back to flex.
|
|
113
|
+
const ctxColumnWidth = (0, _react.useContext)(TableContext);
|
|
114
|
+
const effectiveWidth = width ?? ctxColumnWidth;
|
|
101
115
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
102
116
|
style: [{
|
|
103
117
|
backgroundColor: t.cellBg,
|
|
@@ -109,8 +123,8 @@ function TableCell({
|
|
|
109
123
|
borderColor: t.borderColor,
|
|
110
124
|
borderBottomWidth: isLastRow ? 0 : t.borderSize,
|
|
111
125
|
borderRightWidth: isLastColumn ? 0 : t.borderSize,
|
|
112
|
-
...(
|
|
113
|
-
width
|
|
126
|
+
...(effectiveWidth != null ? {
|
|
127
|
+
width: effectiveWidth
|
|
114
128
|
} : {
|
|
115
129
|
flex,
|
|
116
130
|
minWidth: 0
|
|
@@ -138,6 +152,8 @@ function TableHeaderCell({
|
|
|
138
152
|
style
|
|
139
153
|
}) {
|
|
140
154
|
const t = resolveTableTokens(modes);
|
|
155
|
+
const ctxColumnWidth = (0, _react.useContext)(TableContext);
|
|
156
|
+
const effectiveWidth = width ?? ctxColumnWidth;
|
|
141
157
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
142
158
|
style: [{
|
|
143
159
|
backgroundColor: t.headerBg,
|
|
@@ -146,8 +162,8 @@ function TableHeaderCell({
|
|
|
146
162
|
justifyContent: alignToJustify(align),
|
|
147
163
|
paddingHorizontal: t.headerPaddingH,
|
|
148
164
|
paddingVertical: t.headerPaddingV,
|
|
149
|
-
...(
|
|
150
|
-
width
|
|
165
|
+
...(effectiveWidth != null ? {
|
|
166
|
+
width: effectiveWidth
|
|
151
167
|
} : {
|
|
152
168
|
flex,
|
|
153
169
|
minWidth: 0
|
|
@@ -262,6 +278,7 @@ function Table({
|
|
|
262
278
|
getRowKey,
|
|
263
279
|
onRowPress,
|
|
264
280
|
scrollable = false,
|
|
281
|
+
columnWidth,
|
|
265
282
|
accessibilityLabel,
|
|
266
283
|
modes = _reactUtils.EMPTY_MODES,
|
|
267
284
|
style
|
|
@@ -305,7 +322,10 @@ function Table({
|
|
|
305
322
|
}, !scrollable && {
|
|
306
323
|
width: '100%'
|
|
307
324
|
}, style],
|
|
308
|
-
children:
|
|
325
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(TableContext.Provider, {
|
|
326
|
+
value: columnWidth,
|
|
327
|
+
children: body
|
|
328
|
+
})
|
|
309
329
|
});
|
|
310
330
|
if (scrollable) {
|
|
311
331
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
|