jfs-components 0.0.73 → 0.0.74

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +23 -6
  2. package/lib/commonjs/components/AccountCard/AccountCard.js +247 -0
  3. package/lib/commonjs/components/AppBar/AppBar.js +17 -11
  4. package/lib/commonjs/components/CardBankAccount/CardBankAccount.js +18 -2
  5. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +40 -25
  6. package/lib/commonjs/components/Dropdown/Dropdown.js +214 -0
  7. package/lib/commonjs/components/DropdownInput/DropdownInput.js +542 -0
  8. package/lib/commonjs/components/FormField/FormField.js +328 -178
  9. package/lib/commonjs/components/LottieIntroBlock/LottieIntroBlock.js +150 -0
  10. package/lib/commonjs/components/PageHero/PageHero.js +153 -0
  11. package/lib/commonjs/components/PoweredByLabel/PoweredByLabel.js +135 -0
  12. package/lib/commonjs/components/PoweredByLabel/finvu.png +0 -0
  13. package/lib/commonjs/components/Text/Text.js +9 -2
  14. package/lib/commonjs/components/Tooltip/Tooltip.js +34 -27
  15. package/lib/commonjs/components/index.js +60 -0
  16. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  17. package/lib/commonjs/icons/registry.js +1 -1
  18. package/lib/module/components/AccountCard/AccountCard.js +241 -0
  19. package/lib/module/components/AppBar/AppBar.js +17 -11
  20. package/lib/module/components/CardBankAccount/CardBankAccount.js +17 -2
  21. package/lib/module/components/CheckboxItem/CheckboxItem.js +41 -26
  22. package/lib/module/components/Dropdown/Dropdown.js +206 -0
  23. package/lib/module/components/DropdownInput/DropdownInput.js +536 -0
  24. package/lib/module/components/FormField/FormField.js +330 -180
  25. package/lib/module/components/LottieIntroBlock/LottieIntroBlock.js +144 -0
  26. package/lib/module/components/PageHero/PageHero.js +147 -0
  27. package/lib/module/components/PoweredByLabel/PoweredByLabel.js +130 -0
  28. package/lib/module/components/PoweredByLabel/finvu.png +0 -0
  29. package/lib/module/components/Text/Text.js +9 -2
  30. package/lib/module/components/Tooltip/Tooltip.js +34 -27
  31. package/lib/module/components/index.js +7 -1
  32. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  33. package/lib/module/icons/registry.js +1 -1
  34. package/lib/typescript/src/components/AccountCard/AccountCard.d.ts +81 -0
  35. package/lib/typescript/src/components/CardBankAccount/CardBankAccount.d.ts +9 -2
  36. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +18 -2
  37. package/lib/typescript/src/components/Dropdown/Dropdown.d.ts +62 -0
  38. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +107 -0
  39. package/lib/typescript/src/components/FormField/FormField.d.ts +76 -19
  40. package/lib/typescript/src/components/LottieIntroBlock/LottieIntroBlock.d.ts +58 -0
  41. package/lib/typescript/src/components/PageHero/PageHero.d.ts +53 -0
  42. package/lib/typescript/src/components/PoweredByLabel/PoweredByLabel.d.ts +70 -0
  43. package/lib/typescript/src/components/Text/Text.d.ts +12 -2
  44. package/lib/typescript/src/components/Tooltip/Tooltip.d.ts +13 -2
  45. package/lib/typescript/src/components/index.d.ts +7 -1
  46. package/lib/typescript/src/icons/registry.d.ts +1 -1
  47. package/package.json +1 -3
  48. package/src/components/AccountCard/AccountCard.tsx +376 -0
  49. package/src/components/AppBar/AppBar.tsx +25 -14
  50. package/src/components/CardBankAccount/CardBankAccount.tsx +29 -3
  51. package/src/components/CheckboxItem/CheckboxItem.tsx +65 -30
  52. package/src/components/Dropdown/Dropdown.tsx +331 -0
  53. package/src/components/DropdownInput/DropdownInput.tsx +819 -0
  54. package/src/components/FormField/FormField.tsx +542 -215
  55. package/src/components/LottieIntroBlock/LottieIntroBlock.tsx +202 -0
  56. package/src/components/PageHero/PageHero.tsx +200 -0
  57. package/src/components/PoweredByLabel/PoweredByLabel.tsx +221 -0
  58. package/src/components/PoweredByLabel/finvu.png +0 -0
  59. package/src/components/Text/Text.tsx +24 -3
  60. package/src/components/Tooltip/Tooltip.tsx +50 -25
  61. package/src/components/index.ts +15 -1
  62. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  63. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,542 @@
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 _reactNativeSafeAreaContext = require("react-native-safe-area-context");
10
+ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
11
+ var _JFSThemeProvider = require("../../design-tokens/JFSThemeProvider");
12
+ var _reactUtils = require("../../utils/react-utils");
13
+ var _Icon = _interopRequireDefault(require("../../icons/Icon"));
14
+ var _SupportText = _interopRequireDefault(require("../SupportText/SupportText"));
15
+ var _Dropdown = _interopRequireWildcard(require("../Dropdown/Dropdown"));
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
+ const IS_WEB = _reactNative.Platform.OS === 'web';
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Types
23
+ // ---------------------------------------------------------------------------
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Token resolution
27
+ // ---------------------------------------------------------------------------
28
+
29
+ function useChevronTokens(modes) {
30
+ return (0, _react.useMemo)(() => {
31
+ const iconSize = parseInt((0, _figmaVariablesResolver.getVariableByName)('input/iconSize', modes), 10) || 32;
32
+ const iconColor = (0, _figmaVariablesResolver.getVariableByName)('iconButton/icon/color', modes) || '#0f0d0a';
33
+ return {
34
+ iconSize,
35
+ iconColor
36
+ };
37
+ }, [modes]);
38
+ }
39
+ function useFormFieldTokens(modes) {
40
+ return (0, _react.useMemo)(() => {
41
+ const labelColor = (0, _figmaVariablesResolver.getVariableByName)('formField/label/color', modes) || '#0c0d10';
42
+ const labelFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontFamily', modes) || 'JioType Var';
43
+ const labelFontSize = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/label/fontSize', modes), 10) || 14;
44
+ const labelLineHeight = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/label/lineHeight', modes), 10) || 17;
45
+ const labelFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/label/fontWeight', modes) || '500';
46
+ const gap = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/gap', modes), 10) || 8;
47
+ const inputPaddingH = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/padding/horizontal', modes), 10) || 12;
48
+ const inputGap = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/gap', modes), 10) || 8;
49
+ const inputRadius = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/radius', modes), 10) || 8;
50
+ const inputBackground = (0, _figmaVariablesResolver.getVariableByName)('formField/input/background', modes) || '#ffffff';
51
+ const inputFontSize = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontSize', modes), 10) || 16;
52
+ const inputLineHeight = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/label/lineHeight', modes), 10) || 45;
53
+ const inputFontFamily = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontFamily', modes) || 'JioType Var';
54
+ const inputFontWeight = (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/fontWeight', modes) || '400';
55
+ const inputTextColor = (0, _figmaVariablesResolver.getVariableByName)('states/formField/input/label/color', modes) || (0, _figmaVariablesResolver.getVariableByName)('formField/input/label/color', modes) || '#24262b';
56
+ const inputBorderColor = (0, _figmaVariablesResolver.getVariableByName)('states/formField/input/border/color', modes) || (0, _figmaVariablesResolver.getVariableByName)('formField/input/border/color', modes) || '#b5b6b7';
57
+ const inputBorderSize = parseInt((0, _figmaVariablesResolver.getVariableByName)('formField/input/border/size', modes), 10) || 1;
58
+ return {
59
+ labelColor,
60
+ labelFontFamily,
61
+ labelFontSize,
62
+ labelLineHeight,
63
+ labelFontWeight,
64
+ gap,
65
+ inputPaddingH,
66
+ inputGap,
67
+ inputRadius,
68
+ inputBackground,
69
+ inputFontSize,
70
+ inputLineHeight,
71
+ inputFontFamily,
72
+ inputFontWeight,
73
+ inputTextColor,
74
+ inputBorderColor,
75
+ inputBorderSize
76
+ };
77
+ }, [modes]);
78
+ }
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Helpers
82
+ // ---------------------------------------------------------------------------
83
+
84
+ /**
85
+ * Collect every option this DropdownInput knows about, in render order, from
86
+ * both `items` and `children` slots. Used for keyboard navigation, lookups
87
+ * of the selected option, and accessibility labels.
88
+ */
89
+ function collectOptionsFromChildren(children) {
90
+ const out = [];
91
+ (0, _reactUtils.flattenChildren)(children).forEach(child => {
92
+ if (! /*#__PURE__*/_react.default.isValidElement(child)) return;
93
+ if (child.type !== _Dropdown.DropdownItem) return;
94
+ const childProps = child.props;
95
+ const {
96
+ value,
97
+ label,
98
+ disabled
99
+ } = childProps;
100
+ if (value == null) return;
101
+ if (typeof value !== 'string' && typeof value !== 'number') return;
102
+ if (typeof label !== 'string') return;
103
+ const opt = {
104
+ value: value,
105
+ label
106
+ };
107
+ if (disabled != null) opt.disabled = disabled;
108
+ out.push(opt);
109
+ });
110
+ return out;
111
+ }
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Component
115
+ // ---------------------------------------------------------------------------
116
+
117
+ function DropdownInput({
118
+ label,
119
+ placeholder = 'Select an option',
120
+ items,
121
+ value,
122
+ defaultValue = null,
123
+ onValueChange,
124
+ children,
125
+ renderValue,
126
+ open,
127
+ defaultOpen = false,
128
+ onOpenChange,
129
+ placement = 'bottom',
130
+ isRequired = false,
131
+ isDisabled = false,
132
+ isInvalid = false,
133
+ isReadOnly = false,
134
+ supportText,
135
+ errorMessage,
136
+ menuMaxHeight = 240,
137
+ menuOffset = 4,
138
+ matchTriggerWidth = true,
139
+ closeOnBackdropPress = true,
140
+ modes: propModes = _reactUtils.EMPTY_MODES,
141
+ style,
142
+ inputStyle,
143
+ menuStyle,
144
+ accessibilityLabel,
145
+ accessibilityHint,
146
+ onFocus,
147
+ onBlur
148
+ }) {
149
+ // ---------------- Modes ----------------
150
+ const {
151
+ modes: globalModes
152
+ } = (0, _JFSThemeProvider.useTokens)();
153
+ const baseModes = (0, _react.useMemo)(() => ({
154
+ ...globalModes,
155
+ ...propModes
156
+ }), [globalModes, propModes]);
157
+
158
+ // ---------------- Open state ----------------
159
+ const isControlledOpen = open !== undefined;
160
+ const [internalOpen, setInternalOpen] = (0, _react.useState)(defaultOpen);
161
+ const isOpen = (isControlledOpen ? open : internalOpen) && !isDisabled && !isReadOnly;
162
+ const setOpenState = (0, _react.useCallback)(next => {
163
+ if (!isControlledOpen) setInternalOpen(next);
164
+ onOpenChange?.(next);
165
+ }, [isControlledOpen, onOpenChange]);
166
+ const closeMenu = (0, _react.useCallback)(() => setOpenState(false), [setOpenState]);
167
+ const toggleMenu = (0, _react.useCallback)(() => setOpenState(!isOpen), [isOpen, setOpenState]);
168
+
169
+ // ---------------- Value state ----------------
170
+ const isControlledValue = value !== undefined;
171
+ const [internalValue, setInternalValue] = (0, _react.useState)(defaultValue);
172
+ const currentValue = isControlledValue ? value : internalValue;
173
+
174
+ // Combine items + children-derived options into a single lookup table so
175
+ // selecting via either API surfaces the same option metadata.
176
+ const childOptions = (0, _react.useMemo)(() => collectOptionsFromChildren(children), [children]);
177
+ const allOptions = (0, _react.useMemo)(() => [...(items ?? []), ...childOptions], [items, childOptions]);
178
+ const selectedOption = (0, _react.useMemo)(() => allOptions.find(o => o.value === currentValue), [allOptions, currentValue]);
179
+ const handleSelect = (0, _react.useCallback)(selectedValue => {
180
+ if (typeof selectedValue !== 'string' && typeof selectedValue !== 'number') {
181
+ // Items without a meaningful value just close the menu.
182
+ closeMenu();
183
+ return;
184
+ }
185
+ const option = allOptions.find(o => o.value === selectedValue);
186
+ if (option?.disabled) return;
187
+ if (!isControlledValue) setInternalValue(selectedValue);
188
+ onValueChange?.(selectedValue, option);
189
+ closeMenu();
190
+ }, [allOptions, closeMenu, isControlledValue, onValueChange]);
191
+
192
+ // ---------------- Token modes (with state cascade) ----------------
193
+ const modes = (0, _react.useMemo)(() => ({
194
+ ...baseModes,
195
+ 'FormField States': isInvalid ? 'Error' : isReadOnly ? 'Read Only' : isOpen ? 'Active' : baseModes['FormField States'] || 'Idle'
196
+ }), [baseModes, isInvalid, isReadOnly, isOpen]);
197
+ const tokens = useFormFieldTokens(modes);
198
+ const chevron = useChevronTokens(modes);
199
+
200
+ // ---------------- Layout / measurement ----------------
201
+ const triggerRef = (0, _react.useRef)(null);
202
+ const [triggerRect, setTriggerRect] = (0, _react.useState)(null);
203
+ const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
204
+ const measure = (0, _react.useCallback)(() => {
205
+ if (!triggerRef.current) return;
206
+ triggerRef.current.measureInWindow((x, y, width, height) => {
207
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height)) {
208
+ return;
209
+ }
210
+ setTriggerRect(prev => {
211
+ if (!prev || Math.abs(prev.x - x) > 0.5 || Math.abs(prev.y - y) > 0.5 || prev.width !== width || prev.height !== height) {
212
+ return {
213
+ x,
214
+ y,
215
+ width,
216
+ height
217
+ };
218
+ }
219
+ return prev;
220
+ });
221
+ });
222
+ }, []);
223
+
224
+ // Keep the trigger rect in sync while the menu is open (handles scroll,
225
+ // window resize, etc.). One rAF tick per frame is enough; we bail early
226
+ // if the rect hasn't changed so React doesn't re-render unnecessarily.
227
+ (0, _react.useEffect)(() => {
228
+ if (!isOpen) return;
229
+ let raf = 0;
230
+ const loop = () => {
231
+ measure();
232
+ raf = requestAnimationFrame(loop);
233
+ };
234
+ loop();
235
+ return () => {
236
+ if (raf) cancelAnimationFrame(raf);
237
+ };
238
+ }, [isOpen, measure]);
239
+ const handleTriggerLayout = (0, _react.useCallback)(_e => {
240
+ measure();
241
+ }, [measure]);
242
+
243
+ // ---------------- Popup positioning ----------------
244
+ const [menuSize, setMenuSize] = (0, _react.useState)(null);
245
+ const handleMenuLayout = (0, _react.useCallback)(e => {
246
+ const {
247
+ width,
248
+ height
249
+ } = e.nativeEvent.layout;
250
+ setMenuSize(prev => {
251
+ if (!prev || prev.width !== width || prev.height !== height) {
252
+ return {
253
+ width,
254
+ height
255
+ };
256
+ }
257
+ return prev;
258
+ });
259
+ }, []);
260
+ const {
261
+ width: windowWidth,
262
+ height: windowHeight
263
+ } = _reactNative.Dimensions.get('window');
264
+ const computedPlacement = (0, _react.useMemo)(() => {
265
+ if (!triggerRect) return placement === 'top' ? 'top' : 'bottom';
266
+ const spaceBelow = windowHeight - (triggerRect.y + triggerRect.height) - insets.bottom;
267
+ const spaceAbove = triggerRect.y - insets.top;
268
+ const desiredHeight = Math.min(menuSize?.height ?? menuMaxHeight, menuMaxHeight);
269
+ const needed = desiredHeight + menuOffset + 8;
270
+ if (placement === 'top') {
271
+ return spaceAbove >= needed || spaceAbove >= spaceBelow ? 'top' : 'bottom';
272
+ }
273
+ if (placement === 'bottom') {
274
+ return spaceBelow >= needed || spaceBelow >= spaceAbove ? 'bottom' : 'top';
275
+ }
276
+ return spaceBelow >= needed || spaceBelow >= spaceAbove ? 'bottom' : 'top';
277
+ }, [triggerRect, placement, windowHeight, menuSize?.height, menuMaxHeight, menuOffset, insets.top, insets.bottom]);
278
+ const popupStyle = (0, _react.useMemo)(() => {
279
+ if (!triggerRect) {
280
+ return {
281
+ position: 'absolute',
282
+ opacity: 0,
283
+ top: 0,
284
+ left: 0
285
+ };
286
+ }
287
+ const screenPadding = 8;
288
+ const width = matchTriggerWidth ? triggerRect.width : undefined;
289
+ const intrinsicWidth = menuSize?.width ?? triggerRect.width;
290
+ const finalWidth = width ?? intrinsicWidth;
291
+ let leftPos = triggerRect.x;
292
+ const maxLeft = windowWidth - insets.right - finalWidth - screenPadding;
293
+ const minLeft = insets.left + screenPadding;
294
+ if (leftPos > maxLeft) leftPos = maxLeft;
295
+ if (leftPos < minLeft) leftPos = minLeft;
296
+ let topPos;
297
+ if (computedPlacement === 'top') {
298
+ const desiredHeight = menuSize?.height ?? menuMaxHeight;
299
+ topPos = triggerRect.y - desiredHeight - menuOffset;
300
+ if (topPos < insets.top + screenPadding) {
301
+ topPos = insets.top + screenPadding;
302
+ }
303
+ } else {
304
+ topPos = triggerRect.y + triggerRect.height + menuOffset;
305
+ }
306
+ const style = {
307
+ position: 'absolute',
308
+ top: topPos,
309
+ left: leftPos
310
+ };
311
+ if (width != null) style.width = width;
312
+ // Hide first frame before measurement to avoid the popup flashing in
313
+ // the wrong place. menuSize becomes truthy after the first layout.
314
+ if (menuSize == null) style.opacity = 0;
315
+ return style;
316
+ }, [triggerRect, computedPlacement, menuSize, menuOffset, menuMaxHeight, matchTriggerWidth, windowWidth, insets.top, insets.left, insets.right]);
317
+
318
+ // Reset menu size when closing so the next open re-measures (handles items
319
+ // changing while the menu was closed).
320
+ (0, _react.useEffect)(() => {
321
+ if (!isOpen) setMenuSize(null);
322
+ }, [isOpen]);
323
+
324
+ // ---------------- Styles ----------------
325
+ const labelTextStyle = {
326
+ color: tokens.labelColor,
327
+ fontFamily: tokens.labelFontFamily,
328
+ fontSize: tokens.labelFontSize,
329
+ lineHeight: tokens.labelLineHeight,
330
+ fontWeight: tokens.labelFontWeight
331
+ };
332
+ const requiredIndicatorStyle = {
333
+ ...labelTextStyle,
334
+ color: '#d93d3d'
335
+ };
336
+ const wrapperStyle = {
337
+ gap: tokens.gap,
338
+ opacity: isDisabled ? 0.5 : 1,
339
+ width: '100%'
340
+ };
341
+
342
+ // Focus ring uses the resolved input border color from FormField States so
343
+ // active/error look consistent with TextInput-based FormField. We also lift
344
+ // border weight to 2 when "Active" to read as a focus ring.
345
+ const inputRowStyle = {
346
+ flexDirection: 'row',
347
+ alignItems: 'center',
348
+ backgroundColor: tokens.inputBackground,
349
+ borderColor: tokens.inputBorderColor,
350
+ borderWidth: isOpen ? Math.max(tokens.inputBorderSize, 1) : tokens.inputBorderSize,
351
+ borderRadius: tokens.inputRadius,
352
+ paddingHorizontal: tokens.inputPaddingH,
353
+ paddingVertical: 0,
354
+ gap: tokens.inputGap,
355
+ minHeight: tokens.inputLineHeight
356
+ };
357
+ const valueTextStyle = {
358
+ flex: 1,
359
+ color: tokens.inputTextColor,
360
+ fontFamily: tokens.inputFontFamily,
361
+ fontSize: tokens.inputFontSize,
362
+ lineHeight: tokens.inputLineHeight,
363
+ fontWeight: tokens.inputFontWeight,
364
+ paddingVertical: 0
365
+ };
366
+ const placeholderColor = '#888a8d';
367
+
368
+ // ---------------- Support text ----------------
369
+ const supportStatus = isInvalid ? 'Error' : 'Neutral';
370
+ const supportLabel = isInvalid && errorMessage ? errorMessage : supportText;
371
+
372
+ // ---------------- Accessibility ----------------
373
+ const resolvedA11yLabel = accessibilityLabel || label || placeholder || 'Dropdown';
374
+ const a11yProps = {
375
+ accessibilityRole: 'combobox',
376
+ accessibilityLabel: resolvedA11yLabel,
377
+ accessibilityState: {
378
+ disabled: isDisabled,
379
+ expanded: isOpen
380
+ }
381
+ };
382
+ if (accessibilityHint) a11yProps.accessibilityHint = accessibilityHint;
383
+
384
+ // ---------------- Items rendering ----------------
385
+ const renderItems = (0, _react.useCallback)(() => {
386
+ const itemNodes = [];
387
+ if (items && items.length > 0) {
388
+ items.forEach(opt => {
389
+ const isSelected = opt.value === currentValue;
390
+ itemNodes.push(/*#__PURE__*/(0, _jsxRuntime.jsx)(_Dropdown.DropdownItem, {
391
+ value: opt.value,
392
+ label: opt.label,
393
+ selected: isSelected,
394
+ disabled: opt.disabled ?? false,
395
+ leading: opt.leading,
396
+ trailing: opt.trailing,
397
+ onPress: handleSelect,
398
+ modes: modes
399
+ }, `item-${opt.value}`));
400
+ });
401
+ }
402
+ if (children) {
403
+ // Inject `selected` and `onPress` into child DropdownItems so the
404
+ // consumer doesn't have to wire selection by hand. Existing
405
+ // `onPress` handlers on a child are preserved and called after our
406
+ // selection logic runs.
407
+ (0, _reactUtils.flattenChildren)(children).forEach((child, idx) => {
408
+ if (! /*#__PURE__*/_react.default.isValidElement(child)) {
409
+ itemNodes.push(child);
410
+ return;
411
+ }
412
+ if (child.type === _Dropdown.DropdownItem) {
413
+ const original = child.props;
414
+ const isSelected = original.value === currentValue;
415
+ const composedOnPress = v => {
416
+ original.onPress?.(v);
417
+ handleSelect(v);
418
+ };
419
+ itemNodes.push(/*#__PURE__*/_react.default.cloneElement(child, {
420
+ key: child.key ?? `child-${idx}`,
421
+ selected: isSelected,
422
+ onPress: composedOnPress,
423
+ modes: {
424
+ ...modes,
425
+ ...(original.modes || {})
426
+ }
427
+ }));
428
+ } else {
429
+ itemNodes.push(child);
430
+ }
431
+ });
432
+ }
433
+ return itemNodes;
434
+ }, [items, children, currentValue, handleSelect, modes]);
435
+
436
+ // ---------------- Render ----------------
437
+ const hasValue = selectedOption != null;
438
+ const displayLabel = hasValue ? selectedOption.label : placeholder;
439
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
440
+ style: [wrapperStyle, style],
441
+ pointerEvents: isDisabled ? 'none' : 'auto',
442
+ children: [label != null && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
443
+ style: styles.labelRow,
444
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
445
+ style: labelTextStyle,
446
+ children: label
447
+ }), isRequired && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
448
+ style: requiredIndicatorStyle,
449
+ children: " *"
450
+ })]
451
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
452
+ ref: triggerRef,
453
+ onLayout: handleTriggerLayout,
454
+ onPress: () => {
455
+ if (isDisabled || isReadOnly) return;
456
+ measure();
457
+ toggleMenu();
458
+ },
459
+ ...(onFocus ? {
460
+ onFocus
461
+ } : {}),
462
+ ...(onBlur ? {
463
+ onBlur
464
+ } : {}),
465
+ style: [inputRowStyle, inputStyle, IS_WEB && webNoOutline],
466
+ ...a11yProps,
467
+ ...(IS_WEB ? {
468
+ accessibilityRole: 'combobox',
469
+ 'aria-haspopup': 'listbox',
470
+ 'aria-expanded': isOpen
471
+ } : {}),
472
+ children: [renderValue ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
473
+ style: {
474
+ flex: 1,
475
+ justifyContent: 'center'
476
+ },
477
+ children: renderValue(selectedOption, hasValue)
478
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
479
+ style: [valueTextStyle, !hasValue && {
480
+ color: placeholderColor
481
+ }],
482
+ numberOfLines: 1,
483
+ children: displayLabel
484
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
485
+ accessibilityElementsHidden: true,
486
+ importantForAccessibility: "no",
487
+ pointerEvents: "none",
488
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, {
489
+ name: isOpen ? 'ic_chevron_up' : 'ic_chevron_down',
490
+ size: chevron.iconSize,
491
+ color: chevron.iconColor
492
+ })
493
+ })]
494
+ }), supportLabel != null && /*#__PURE__*/(0, _jsxRuntime.jsx)(_SupportText.default, {
495
+ label: supportLabel,
496
+ status: supportStatus,
497
+ modes: modes
498
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
499
+ visible: isOpen,
500
+ transparent: true,
501
+ animationType: "fade",
502
+ onRequestClose: closeMenu,
503
+ statusBarTranslucent: true,
504
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
505
+ style: _reactNative.StyleSheet.absoluteFill,
506
+ onPress: closeOnBackdropPress ? closeMenu : undefined,
507
+ accessibilityRole: "button",
508
+ accessibilityLabel: "Close options",
509
+ accessible: false,
510
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
511
+ style: _reactNative.StyleSheet.absoluteFill,
512
+ pointerEvents: "box-none",
513
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
514
+ style: popupStyle,
515
+ onLayout: handleMenuLayout,
516
+ pointerEvents: "auto",
517
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Dropdown.default, {
518
+ modes: modes,
519
+ maxHeight: menuMaxHeight,
520
+ style: menuStyle,
521
+ accessibilityLabel: `${resolvedA11yLabel} options`,
522
+ children: renderItems()
523
+ })
524
+ })
525
+ })
526
+ })
527
+ })]
528
+ });
529
+ }
530
+ const webNoOutline = {
531
+ outlineStyle: 'none',
532
+ outlineWidth: 0,
533
+ outlineColor: 'transparent',
534
+ cursor: 'pointer'
535
+ };
536
+ const styles = _reactNative.StyleSheet.create({
537
+ labelRow: {
538
+ flexDirection: 'row',
539
+ alignItems: 'baseline'
540
+ }
541
+ });
542
+ var _default = exports.default = DropdownInput;