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.
Files changed (107) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/lib/commonjs/components/AmountInput/AmountInput.js +8 -5
  3. package/lib/commonjs/components/BenefitCard/BenefitCard.js +231 -0
  4. package/lib/commonjs/components/CcCard/CcCard.js +470 -0
  5. package/lib/commonjs/components/Checkbox/Checkbox.js +4 -3
  6. package/lib/commonjs/components/CheckboxItem/CheckboxItem.js +4 -3
  7. package/lib/commonjs/components/CompareTable/CompareTable.js +372 -0
  8. package/lib/commonjs/components/ComparisonBar/ComparisonBar.js +266 -0
  9. package/lib/commonjs/components/DropdownInput/DropdownInput.js +35 -3
  10. package/lib/commonjs/components/FormField/FormField.js +4 -3
  11. package/lib/commonjs/components/InputSearch/InputSearch.js +6 -4
  12. package/lib/commonjs/components/NoteInput/NoteInput.js +6 -5
  13. package/lib/commonjs/components/PdpCcCard/PdpCcCard.js +273 -0
  14. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.js +263 -0
  15. package/lib/commonjs/components/ProductMerchandisingCard/GlassFill.web.js +116 -0
  16. package/lib/commonjs/components/ProductMerchandisingCard/ProductMerchandisingCard.js +353 -0
  17. package/lib/commonjs/components/ProjectionMarker/ProjectionMarker.js +161 -0
  18. package/lib/commonjs/components/Radio/Radio.js +5 -5
  19. package/lib/commonjs/components/Slider/Slider.js +473 -0
  20. package/lib/commonjs/components/TextInput/TextInput.js +13 -8
  21. package/lib/commonjs/components/TextSegment/TextSegment.js +118 -0
  22. package/lib/commonjs/components/index.js +63 -0
  23. package/lib/commonjs/design-tokens/Coin Variables-variables-full.json +1 -1
  24. package/lib/commonjs/design-tokens/figma-modes.generated.js +38 -9
  25. package/lib/commonjs/icons/registry.js +1 -1
  26. package/lib/commonjs/utils/react-utils.js +22 -0
  27. package/lib/module/components/AmountInput/AmountInput.js +6 -4
  28. package/lib/module/components/BenefitCard/BenefitCard.js +225 -0
  29. package/lib/module/components/CcCard/CcCard.js +464 -0
  30. package/lib/module/components/Checkbox/Checkbox.js +5 -4
  31. package/lib/module/components/CheckboxItem/CheckboxItem.js +5 -4
  32. package/lib/module/components/CompareTable/CompareTable.js +367 -0
  33. package/lib/module/components/ComparisonBar/ComparisonBar.js +260 -0
  34. package/lib/module/components/DropdownInput/DropdownInput.js +36 -4
  35. package/lib/module/components/FormField/FormField.js +5 -4
  36. package/lib/module/components/InputSearch/InputSearch.js +6 -4
  37. package/lib/module/components/NoteInput/NoteInput.js +7 -6
  38. package/lib/module/components/PdpCcCard/PdpCcCard.js +267 -0
  39. package/lib/module/components/ProductMerchandisingCard/GlassFill.js +257 -0
  40. package/lib/module/components/ProductMerchandisingCard/GlassFill.web.js +111 -0
  41. package/lib/module/components/ProductMerchandisingCard/ProductMerchandisingCard.js +347 -0
  42. package/lib/module/components/ProjectionMarker/ProjectionMarker.js +156 -0
  43. package/lib/module/components/Radio/Radio.js +5 -4
  44. package/lib/module/components/Slider/Slider.js +468 -0
  45. package/lib/module/components/TextInput/TextInput.js +15 -10
  46. package/lib/module/components/TextSegment/TextSegment.js +113 -0
  47. package/lib/module/components/index.js +9 -0
  48. package/lib/module/design-tokens/Coin Variables-variables-full.json +1 -1
  49. package/lib/module/design-tokens/figma-modes.generated.js +38 -9
  50. package/lib/module/icons/registry.js +1 -1
  51. package/lib/module/utils/react-utils.js +21 -0
  52. package/lib/typescript/src/components/AmountInput/AmountInput.d.ts +3 -2
  53. package/lib/typescript/src/components/BenefitCard/BenefitCard.d.ts +93 -0
  54. package/lib/typescript/src/components/CcCard/CcCard.d.ts +137 -0
  55. package/lib/typescript/src/components/Checkbox/Checkbox.d.ts +3 -2
  56. package/lib/typescript/src/components/CheckboxItem/CheckboxItem.d.ts +2 -2
  57. package/lib/typescript/src/components/CompareTable/CompareTable.d.ts +88 -0
  58. package/lib/typescript/src/components/ComparisonBar/ComparisonBar.d.ts +118 -0
  59. package/lib/typescript/src/components/DropdownInput/DropdownInput.d.ts +20 -1
  60. package/lib/typescript/src/components/FormField/FormField.d.ts +2 -2
  61. package/lib/typescript/src/components/InputSearch/InputSearch.d.ts +23 -2
  62. package/lib/typescript/src/components/NoteInput/NoteInput.d.ts +19 -2
  63. package/lib/typescript/src/components/PdpCcCard/PdpCcCard.d.ts +84 -0
  64. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.d.ts +56 -0
  65. package/lib/typescript/src/components/ProductMerchandisingCard/GlassFill.web.d.ts +27 -0
  66. package/lib/typescript/src/components/ProductMerchandisingCard/ProductMerchandisingCard.d.ts +81 -0
  67. package/lib/typescript/src/components/ProjectionMarker/ProjectionMarker.d.ts +82 -0
  68. package/lib/typescript/src/components/Radio/Radio.d.ts +3 -2
  69. package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +2 -2
  70. package/lib/typescript/src/components/Slider/Slider.d.ts +99 -0
  71. package/lib/typescript/src/components/TextInput/TextInput.d.ts +9 -29
  72. package/lib/typescript/src/components/TextSegment/TextSegment.d.ts +100 -0
  73. package/lib/typescript/src/components/index.d.ts +10 -1
  74. package/lib/typescript/src/design-tokens/figma-modes.generated.d.ts +22 -2
  75. package/lib/typescript/src/icons/registry.d.ts +1 -1
  76. package/lib/typescript/src/utils/react-utils.d.ts +10 -0
  77. package/package.json +2 -1
  78. package/src/components/AmountInput/AmountInput.tsx +7 -5
  79. package/src/components/BenefitCard/BenefitCard.tsx +309 -0
  80. package/src/components/CcCard/CcCard.tsx +598 -0
  81. package/src/components/Checkbox/Checkbox.tsx +5 -4
  82. package/src/components/CheckboxItem/CheckboxItem.tsx +5 -4
  83. package/src/components/CompareTable/CompareTable.tsx +477 -0
  84. package/src/components/ComparisonBar/ComparisonBar.tsx +356 -0
  85. package/src/components/DropdownInput/DropdownInput.tsx +55 -3
  86. package/src/components/FormField/FormField.tsx +5 -4
  87. package/src/components/InputSearch/InputSearch.tsx +8 -5
  88. package/src/components/NoteInput/NoteInput.tsx +8 -6
  89. package/src/components/PdpCcCard/PdpCcCard.tsx +356 -0
  90. package/src/components/ProductMerchandisingCard/GlassFill.tsx +276 -0
  91. package/src/components/ProductMerchandisingCard/GlassFill.web.tsx +127 -0
  92. package/src/components/ProductMerchandisingCard/ProductMerchandisingCard.tsx +423 -0
  93. package/src/components/ProjectionMarker/ProjectionMarker.tsx +277 -0
  94. package/src/components/Radio/Radio.tsx +5 -4
  95. package/src/components/Slider/Slider.tsx +628 -0
  96. package/src/components/TextInput/TextInput.tsx +15 -11
  97. package/src/components/TextSegment/TextSegment.tsx +166 -0
  98. package/src/components/index.ts +10 -1
  99. package/src/design-tokens/Coin Variables-variables-full.json +1 -1
  100. package/src/design-tokens/figma-modes.generated.ts +38 -9
  101. package/src/icons/registry.ts +1 -1
  102. package/src/utils/react-utils.ts +23 -0
  103. package/lib/typescript/scripts/extract-component-tokens.d.ts +0 -9
  104. package/lib/typescript/scripts/generate-component-docs.d.ts +0 -9
  105. package/lib/typescript/scripts/generate-icon-registry.d.ts +0 -3
  106. package/lib/typescript/scripts/generate-mode-types.d.ts +0 -2
  107. package/lib/typescript/scripts/retype-modes.d.cts +0 -2
@@ -0,0 +1,473 @@
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 _reactNativeSvg = _interopRequireWildcard(require("react-native-svg"));
10
+ var _figmaVariablesResolver = require("../../design-tokens/figma-variables-resolver");
11
+ var _reactUtils = require("../../utils/react-utils");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ /**
15
+ * Imperative handle exposed via `ref`. The primary way to read a slider's value
16
+ * is still the controlled `value` + `onChange` pair (each `<Slider />` reports
17
+ * its own value, so multiple sliders are disambiguated by their own handlers).
18
+ * The ref is a convenience for reading the latest value on demand (e.g. on a
19
+ * button press) or imperatively nudging an uncontrolled slider.
20
+ *
21
+ * @example
22
+ * const slider = useRef<SliderHandle>(null)
23
+ * slider.current?.setValue(50)
24
+ * const v = slider.current?.getValue()
25
+ */
26
+
27
+ const clamp = (n, min, max) => Math.min(Math.max(n, min), max);
28
+ const asNum = (raw, fallback) => {
29
+ const n = typeof raw === 'number' ? raw : parseFloat(raw);
30
+ return Number.isFinite(n) ? n : fallback;
31
+ };
32
+ const asStr = (raw, fallback) => raw != null ? String(raw) : fallback;
33
+
34
+ // The unfilled track is rendered at a lighter emphasis in the design.
35
+ const TRACK_EMPHASIS = {
36
+ Emphasis: 'Low'
37
+ };
38
+
39
+ /**
40
+ * Slider — Figma node 5373:446 ("Slider").
41
+ *
42
+ * A horizontal, single-thumb slider driven entirely by design tokens. It maps a
43
+ * numeric range (`minValue`–`maxValue`) onto a track with a filled indicator, a
44
+ * draggable handle, a value bubble pinned above the handle, and optional min/max
45
+ * labels beneath the track. Built on `PanResponder` so the one code path works
46
+ * on iOS, Android and the web.
47
+ *
48
+ * The design only labels the range bounds, but a slider's purpose is to bind a
49
+ * value — so the live value is surfaced through the bubble (formatted via
50
+ * `formatOptions` / `formatValue`) and reported through `onChange` /
51
+ * `onChangeEnd`. Supports controlled and uncontrolled usage, web keyboard
52
+ * control, and an imperative {@link SliderHandle} `ref` for on-demand reads.
53
+ */
54
+ const Slider = /*#__PURE__*/(0, _react.forwardRef)(function Slider({
55
+ value: controlledValue,
56
+ defaultValue,
57
+ onChange,
58
+ onChangeEnd,
59
+ minValue = 0,
60
+ maxValue = 100,
61
+ step = 1,
62
+ isDisabled = false,
63
+ formatOptions,
64
+ locale,
65
+ formatValue,
66
+ renderTooltip,
67
+ alwaysShowTooltip = true,
68
+ showLabels = true,
69
+ minLabel,
70
+ maxLabel,
71
+ width = '100%',
72
+ modes = _reactUtils.EMPTY_MODES,
73
+ style,
74
+ accessibilityLabel
75
+ }, ref) {
76
+ const tokens = (0, _react.useMemo)(() => resolveTokens(modes), [modes]);
77
+ const isControlled = controlledValue !== undefined;
78
+ const [internalValue, setInternalValue] = (0, _react.useState)(() => clamp(defaultValue ?? minValue, minValue, maxValue));
79
+ const rawValue = isControlled ? controlledValue : internalValue;
80
+ const currentValue = clamp(rawValue, minValue, maxValue);
81
+ const [trackWidth, setTrackWidth] = (0, _react.useState)(0);
82
+ const [tooltipWidth, setTooltipWidth] = (0, _react.useState)(0);
83
+ const [isDragging, setIsDragging] = (0, _react.useState)(false);
84
+ const [isHovered, setIsHovered] = (0, _react.useState)(false);
85
+ const quantize = (0, _react.useCallback)(v => {
86
+ if (!step || step <= 0) return clamp(v, minValue, maxValue);
87
+ const stepped = Math.round((v - minValue) / step) * step + minValue;
88
+ // Round away float dust introduced by the division (e.g. 0.30000000000004).
89
+ const decimals = (String(step).split('.')[1] || '').length;
90
+ const fixed = decimals > 0 ? Number(stepped.toFixed(decimals)) : stepped;
91
+ return clamp(fixed, minValue, maxValue);
92
+ }, [minValue, maxValue, step]);
93
+
94
+ // Keep the latest interactive config in a ref so the PanResponder (created
95
+ // once) always reads fresh values without being recreated mid-gesture.
96
+ const interaction = (0, _react.useRef)({
97
+ trackWidth,
98
+ minValue,
99
+ maxValue,
100
+ isDisabled,
101
+ isControlled,
102
+ quantize,
103
+ onChange,
104
+ onChangeEnd
105
+ });
106
+ interaction.current = {
107
+ trackWidth,
108
+ minValue,
109
+ maxValue,
110
+ isDisabled,
111
+ isControlled,
112
+ quantize,
113
+ onChange,
114
+ onChangeEnd
115
+ };
116
+ const lastValueRef = (0, _react.useRef)(currentValue);
117
+ lastValueRef.current = currentValue;
118
+ const commit = (0, _react.useCallback)((next, isFinal) => {
119
+ const {
120
+ isControlled: ctrl,
121
+ onChange: change,
122
+ onChangeEnd: changeEnd
123
+ } = interaction.current;
124
+ const changed = next !== lastValueRef.current;
125
+ lastValueRef.current = next;
126
+ if (changed) {
127
+ if (!ctrl) setInternalValue(next);
128
+ change?.(next);
129
+ }
130
+ if (isFinal) changeEnd?.(next);
131
+ }, []);
132
+ const valueFromX = (0, _react.useCallback)(x => {
133
+ const {
134
+ trackWidth: w,
135
+ minValue: min,
136
+ maxValue: max,
137
+ quantize: q
138
+ } = interaction.current;
139
+ if (w <= 0) return lastValueRef.current;
140
+ const ratio = clamp(x / w, 0, 1);
141
+ return q(min + ratio * (max - min));
142
+ }, []);
143
+ const panResponder = (0, _react.useRef)(_reactNative.PanResponder.create({
144
+ onStartShouldSetPanResponder: () => !interaction.current.isDisabled,
145
+ onMoveShouldSetPanResponder: () => !interaction.current.isDisabled,
146
+ onPanResponderGrant: e => {
147
+ if (interaction.current.isDisabled) return;
148
+ setIsDragging(true);
149
+ commit(valueFromX(e.nativeEvent.locationX), false);
150
+ },
151
+ onPanResponderMove: e => {
152
+ if (interaction.current.isDisabled) return;
153
+ commit(valueFromX(e.nativeEvent.locationX), false);
154
+ },
155
+ onPanResponderRelease: e => {
156
+ setIsDragging(false);
157
+ if (interaction.current.isDisabled) return;
158
+ commit(valueFromX(e.nativeEvent.locationX), true);
159
+ },
160
+ onPanResponderTerminate: () => {
161
+ setIsDragging(false);
162
+ if (interaction.current.isDisabled) return;
163
+ commit(lastValueRef.current, true);
164
+ }
165
+ })).current;
166
+
167
+ // Keyboard support (web). Arrow/Page keys nudge, Home/End jump to bounds.
168
+ const handleKeyDown = (0, _react.useCallback)(e => {
169
+ if (isDisabled) return;
170
+ const big = Math.max(step, (maxValue - minValue) / 10);
171
+ let next = null;
172
+ switch (e.key) {
173
+ case 'ArrowRight':
174
+ case 'ArrowUp':
175
+ next = quantize(currentValue + step);
176
+ break;
177
+ case 'ArrowLeft':
178
+ case 'ArrowDown':
179
+ next = quantize(currentValue - step);
180
+ break;
181
+ case 'PageUp':
182
+ next = quantize(currentValue + big);
183
+ break;
184
+ case 'PageDown':
185
+ next = quantize(currentValue - big);
186
+ break;
187
+ case 'Home':
188
+ next = minValue;
189
+ break;
190
+ case 'End':
191
+ next = maxValue;
192
+ break;
193
+ default:
194
+ return;
195
+ }
196
+ e.preventDefault?.();
197
+ commit(next, true);
198
+ }, [isDisabled, step, minValue, maxValue, currentValue, quantize, commit]);
199
+ const handleAccessibilityAction = (0, _react.useCallback)(event => {
200
+ if (isDisabled) return;
201
+ const name = event.nativeEvent.actionName;
202
+ if (name === 'increment') commit(quantize(currentValue + step), true);else if (name === 'decrement') commit(quantize(currentValue - step), true);
203
+ }, [isDisabled, step, currentValue, quantize, commit]);
204
+ (0, _react.useImperativeHandle)(ref, () => ({
205
+ getValue: () => lastValueRef.current,
206
+ setValue: v => commit(quantize(v), true),
207
+ increment: by => commit(quantize(lastValueRef.current + (by ?? step)), true),
208
+ decrement: by => commit(quantize(lastValueRef.current - (by ?? step)), true)
209
+ }), [quantize, commit, step]);
210
+ const defaultFormat = (0, _react.useCallback)(v => {
211
+ try {
212
+ return new Intl.NumberFormat(locale, formatOptions).format(v);
213
+ } catch {
214
+ return String(v);
215
+ }
216
+ }, [locale, formatOptions]);
217
+ const format = formatValue ?? defaultFormat;
218
+ const ratio = maxValue > minValue ? (currentValue - minValue) / (maxValue - minValue) : 0;
219
+ const percent = clamp(ratio, 0, 1) * 100;
220
+ const handleSize = tokens.handleSize;
221
+ // The bubble floats (absolutely positioned, no reserved layout space). When
222
+ // `alwaysShowTooltip` is false it only appears while interacting: dragging on
223
+ // any platform, or hovering on web. Lifting the finger clears `isDragging`,
224
+ // so the bubble disappears immediately on touch release.
225
+ const showTooltip = alwaysShowTooltip || isDragging || isHovered;
226
+ const onTrackLayout = (0, _react.useCallback)(e => {
227
+ setTrackWidth(e.nativeEvent.layout.width);
228
+ }, []);
229
+ const onTooltipLayout = (0, _react.useCallback)(e => {
230
+ setTooltipWidth(e.nativeEvent.layout.width);
231
+ }, []);
232
+ const valueText = format(currentValue);
233
+ const tooltipNode = showTooltip ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
234
+ onLayout: onTooltipLayout,
235
+ style: [styles.tooltip, {
236
+ backgroundColor: tokens.tooltipBackground,
237
+ paddingHorizontal: tokens.tooltipPaddingH,
238
+ paddingVertical: tokens.tooltipPaddingV,
239
+ borderRadius: tokens.tooltipRadius,
240
+ maxWidth: tokens.tooltipMaxWidth,
241
+ bottom: handleSize + tokens.tooltipWrapGap,
242
+ // Pinned to the value point on the full-width track area (not
243
+ // the tiny handle-width thumb). An absolute child with only
244
+ // `left` set is clamped by Yoga to `parentWidth - left`, so
245
+ // parenting it to the 20px thumb collapsed the text to ~0px
246
+ // and rendered an empty bubble on device. Centre via translate.
247
+ left: `${percent}%`,
248
+ transform: [{
249
+ translateX: -tooltipWidth / 2
250
+ }]
251
+ }],
252
+ pointerEvents: "none",
253
+ children: [renderTooltip ? renderTooltip(currentValue) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
254
+ style: tokens.tooltipLabel,
255
+ numberOfLines: 1,
256
+ children: valueText
257
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
258
+ style: [styles.tip, {
259
+ bottom: -tokens.tipHeight
260
+ }],
261
+ pointerEvents: "none",
262
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.default, {
263
+ width: tokens.tipWidth,
264
+ height: tokens.tipHeight,
265
+ viewBox: `0 0 ${tokens.tipWidth} ${tokens.tipHeight}`,
266
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSvg.Path, {
267
+ d: `M0 0 L${tokens.tipWidth / 2} ${tokens.tipHeight} L${tokens.tipWidth} 0 Z`,
268
+ fill: tokens.tooltipBackground
269
+ })
270
+ })
271
+ })]
272
+ }) : null;
273
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
274
+ style: [{
275
+ width,
276
+ gap: tokens.gap,
277
+ paddingTop: tokens.paddingTop,
278
+ paddingBottom: tokens.paddingBottom,
279
+ paddingLeft: tokens.paddingLeft,
280
+ paddingRight: tokens.paddingRight,
281
+ opacity: isDisabled ? 0.5 : 1
282
+ }, style],
283
+ accessibilityLabel: accessibilityLabel,
284
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
285
+ ...panResponder.panHandlers,
286
+ onLayout: onTrackLayout,
287
+ style: [styles.trackArea, {
288
+ height: handleSize
289
+ }],
290
+ accessible: true,
291
+ accessibilityRole: "adjustable",
292
+ accessibilityLabel: accessibilityLabel,
293
+ accessibilityValue: {
294
+ min: minValue,
295
+ max: maxValue,
296
+ now: currentValue,
297
+ text: valueText
298
+ },
299
+ accessibilityState: {
300
+ disabled: isDisabled
301
+ },
302
+ accessibilityActions: [{
303
+ name: 'increment'
304
+ }, {
305
+ name: 'decrement'
306
+ }],
307
+ onAccessibilityAction: handleAccessibilityAction,
308
+ focusable: !isDisabled,
309
+ ...(_reactNative.Platform.OS === 'web' ? {
310
+ onKeyDown: handleKeyDown,
311
+ tabIndex: isDisabled ? -1 : 0,
312
+ onMouseEnter: () => !isDisabled && setIsHovered(true),
313
+ onMouseLeave: () => setIsHovered(false)
314
+ } : {}),
315
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
316
+ style: {
317
+ height: tokens.trackHeight,
318
+ borderRadius: tokens.trackRadius,
319
+ backgroundColor: tokens.trackBackground
320
+ }
321
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
322
+ pointerEvents: "none",
323
+ style: {
324
+ position: 'absolute',
325
+ left: 0,
326
+ top: (handleSize - tokens.indicatorHeight) / 2,
327
+ height: tokens.indicatorHeight,
328
+ width: `${percent}%`,
329
+ borderRadius: tokens.indicatorRadius,
330
+ backgroundColor: tokens.indicatorBackground
331
+ }
332
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
333
+ pointerEvents: "none",
334
+ style: {
335
+ position: 'absolute',
336
+ top: 0,
337
+ left: `${percent}%`,
338
+ width: handleSize,
339
+ height: handleSize,
340
+ transform: [{
341
+ translateX: -handleSize / 2
342
+ }]
343
+ },
344
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
345
+ style: {
346
+ width: handleSize,
347
+ height: handleSize,
348
+ borderRadius: tokens.handleRadius,
349
+ backgroundColor: tokens.handleBackground
350
+ }
351
+ })
352
+ }), tooltipNode]
353
+ }), showLabels ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
354
+ style: styles.labelWrap,
355
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
356
+ style: tokens.label,
357
+ children: minLabel ?? format(minValue)
358
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
359
+ style: tokens.label,
360
+ children: maxLabel ?? format(maxValue)
361
+ })]
362
+ }) : null]
363
+ });
364
+ });
365
+ function resolveTokens(modes) {
366
+ // NOTE: token names are passed as string literals DIRECTLY to
367
+ // getVariableByName so the `extract-component-tokens` script can statically
368
+ // collect them for the generated docs. Do not refactor into a helper that
369
+ // receives the name as a variable.
370
+ const gap = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/gap', modes), 16);
371
+ const paddingTop = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/padding/top', modes), 8);
372
+ const paddingBottom = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/padding/bottom', modes), 0);
373
+ const paddingLeft = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/padding/left', modes), 0);
374
+ const paddingRight = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/padding/right', modes), 0);
375
+ const trackHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/track/height', modes), 4);
376
+ const trackRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/track/radius', modes), 999);
377
+ // The unfilled track uses a lighter emphasis in the design; consumer modes
378
+ // still win over the baked-in `Emphasis: Low`.
379
+ const trackBackground = asStr((0, _figmaVariablesResolver.getVariableByName)('slider/track/background', {
380
+ ...TRACK_EMPHASIS,
381
+ ...modes
382
+ }), '#fde8c9');
383
+ const indicatorHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/indicator/height', modes), 4);
384
+ const indicatorRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/indicator/radius', modes), 999);
385
+ const indicatorBackground = asStr((0, _figmaVariablesResolver.getVariableByName)('slider/indicator/background', modes), '#f7ab21');
386
+ const handleSize = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/handle/size', modes), 20);
387
+ const handleRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/handle/radius', modes), 999999);
388
+ const handleBackground = asStr((0, _figmaVariablesResolver.getVariableByName)('slider/handle/background', modes), '#f7ab21');
389
+ const tooltipWrapGap = asNum((0, _figmaVariablesResolver.getVariableByName)('slider/tooltipWrap/gap', modes), 12);
390
+ const tooltipBackground = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/background', modes), '#0f0d0a');
391
+ const tooltipPaddingH = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/padding/horizontal', modes), 12);
392
+ const tooltipPaddingV = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/padding/vertical', modes), 8);
393
+ const tooltipRadius = asNum((0, _figmaVariablesResolver.getVariableByName)('radius', modes), 8);
394
+ const tooltipMaxWidth = asNum((0, _figmaVariablesResolver.getVariableByName)('maxWidth', modes), 280);
395
+ const tipWidth = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/tipItem/width', modes), 16);
396
+ const tipHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/tipItem/height', modes), 8);
397
+ const tooltipLabelColor = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/label/color', modes), '#ffffff');
398
+ const tooltipLabelSize = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/label/fontSize', modes), 14);
399
+ const tooltipLabelFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/lable/fontFamily', modes), 'JioType Var');
400
+ const tooltipLabelLineHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('tooltip/label/lineHeight', modes), 18);
401
+ const tooltipLabelWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('tooltip/label/fontWeight', modes), '500');
402
+ const labelColor = asStr((0, _figmaVariablesResolver.getVariableByName)('text/foreground', modes), '#000000');
403
+ const labelSize = asNum((0, _figmaVariablesResolver.getVariableByName)('text/fontSize', modes), 14);
404
+ const labelFamily = asStr((0, _figmaVariablesResolver.getVariableByName)('text/fontFamily', modes), 'JioType Var');
405
+ const labelLineHeight = asNum((0, _figmaVariablesResolver.getVariableByName)('text/lineHeight', modes), 20);
406
+ const labelWeight = asStr((0, _figmaVariablesResolver.getVariableByName)('text/fontWeight', modes), '500');
407
+ const labelLetterSpacing = asNum((0, _figmaVariablesResolver.getVariableByName)('text/letterSpacing', modes), -0.5);
408
+ return {
409
+ gap,
410
+ paddingTop,
411
+ paddingBottom,
412
+ paddingLeft,
413
+ paddingRight,
414
+ trackHeight,
415
+ trackRadius,
416
+ trackBackground,
417
+ indicatorHeight,
418
+ indicatorRadius,
419
+ indicatorBackground,
420
+ handleSize,
421
+ handleRadius,
422
+ handleBackground,
423
+ tooltipWrapGap,
424
+ tooltipBackground,
425
+ tooltipPaddingH,
426
+ tooltipPaddingV,
427
+ tooltipRadius,
428
+ tooltipMaxWidth,
429
+ tooltipLineHeight: tooltipLabelLineHeight,
430
+ tipWidth,
431
+ tipHeight,
432
+ tooltipLabel: {
433
+ color: tooltipLabelColor,
434
+ fontSize: tooltipLabelSize,
435
+ fontFamily: tooltipLabelFamily,
436
+ lineHeight: tooltipLabelLineHeight,
437
+ fontWeight: tooltipLabelWeight,
438
+ includeFontPadding: false
439
+ },
440
+ label: {
441
+ color: labelColor,
442
+ fontSize: labelSize,
443
+ fontFamily: labelFamily,
444
+ lineHeight: labelLineHeight,
445
+ fontWeight: labelWeight,
446
+ letterSpacing: labelLetterSpacing,
447
+ includeFontPadding: false
448
+ }
449
+ };
450
+ }
451
+ const styles = {
452
+ trackArea: {
453
+ width: '100%',
454
+ justifyContent: 'center',
455
+ position: 'relative'
456
+ },
457
+ labelWrap: {
458
+ flexDirection: 'row',
459
+ justifyContent: 'space-between',
460
+ alignItems: 'flex-start',
461
+ width: '100%'
462
+ },
463
+ tooltip: {
464
+ position: 'absolute',
465
+ alignItems: 'center',
466
+ justifyContent: 'center'
467
+ },
468
+ tip: {
469
+ position: 'absolute',
470
+ alignSelf: 'center'
471
+ }
472
+ };
473
+ var _default = exports.default = Slider;
@@ -101,7 +101,7 @@ function makePlaceholderColor(color, opacity = 0.5) {
101
101
  // Fallback: return original color
102
102
  return color;
103
103
  }
104
- function TextInput({
104
+ const TextInputBase = /*#__PURE__*/(0, _react.forwardRef)(function TextInput({
105
105
  placeholder = '',
106
106
  value = '',
107
107
  onChangeText,
@@ -116,7 +116,7 @@ function TextInput({
116
116
  accessibilityLabel,
117
117
  accessibilityHint,
118
118
  ...rest
119
- }) {
119
+ }, ref) {
120
120
  // Track focus state to hide placeholder when focused
121
121
  const [isFocused, setIsFocused] = (0, _react.useState)(false);
122
122
  const [isHovered, setIsHovered] = (0, _react.useState)(false);
@@ -231,7 +231,7 @@ function TextInput({
231
231
  importantForAccessibility: "no",
232
232
  children: processedLeading
233
233
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
234
- ref: inputRef,
234
+ ref: (0, _reactUtils.mergeRefs)(inputRef, ref),
235
235
  accessibilityLabel: undefined,
236
236
  accessibilityHint: accessibilityHint,
237
237
  placeholder: displayPlaceholder,
@@ -264,7 +264,7 @@ function TextInput({
264
264
  style: containerStyleArray,
265
265
  children: inner
266
266
  });
267
- }
267
+ });
268
268
 
269
269
  /**
270
270
  * TextInput.Search component that mirrors the Figma "textInput.search" component.
@@ -286,7 +286,7 @@ function TextInput({
286
286
  * @param {Object} [props.rest] - Additional props passed to the underlying TextInput
287
287
  */
288
288
 
289
- function TextInputSearch({
289
+ const TextInputSearch = /*#__PURE__*/(0, _react.forwardRef)(function TextInputSearch({
290
290
  placeholder = 'Search',
291
291
  value = '',
292
292
  onChangeText,
@@ -299,7 +299,7 @@ function TextInputSearch({
299
299
  onFocus,
300
300
  onBlur,
301
301
  ...rest
302
- }) {
302
+ }, ref) {
303
303
  // Resolve icon tokens for default search icon
304
304
  const iconColor = (0, _figmaVariablesResolver.getVariableByName)('textInput/icon/color', modes) || '#24262b';
305
305
  const iconSize = (0, _figmaVariablesResolver.getVariableByName)('textInput/icon/size', modes) || 18;
@@ -336,10 +336,15 @@ function TextInputSearch({
336
336
  textInputProps.accessibilityHint = accessibilityHint;
337
337
  }
338
338
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(TextInput, {
339
+ ref: ref,
339
340
  ...textInputProps
340
341
  });
341
- }
342
+ });
343
+
344
+ // Attach Search as a property of TextInput using namespace pattern. The base
345
+ // component is created via forwardRef, so we compose the static onto a typed
346
+ // alias to keep `TextInput.Search` strongly typed.
342
347
 
343
- // Attach Search as a property of TextInput using namespace pattern
348
+ const TextInput = TextInputBase;
344
349
  TextInput.Search = TextInputSearch;
345
350
  var _default = exports.default = TextInput;
@@ -0,0 +1,118 @@
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 _reactUtils = require("../../utils/react-utils");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ 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); }
13
+ /**
14
+ * A single coloured run inside a {@link TextSegment}. Each run can carry its own
15
+ * `modes`, so individual fragments of the same paragraph can resolve different
16
+ * design tokens (most commonly a different `text/foreground` colour) while still
17
+ * flowing — and wrapping — inline as one continuous line of text.
18
+ */
19
+
20
+ const TEXT_ALIGN_MAP = {
21
+ Left: 'left',
22
+ Center: 'center'
23
+ };
24
+
25
+ /**
26
+ * Resolves the `text/*` design tokens for a given modes object into an RN
27
+ * `TextStyle`. Shared by the outer paragraph and every inline run so a run that
28
+ * only overrides colour still inherits the family/size/weight/spacing.
29
+ */
30
+ function resolveTextStyle(modes) {
31
+ return {
32
+ color: (0, _figmaVariablesResolver.getVariableByName)('text/foreground', modes) ?? '#000000',
33
+ fontFamily: (0, _figmaVariablesResolver.getVariableByName)('text/fontFamily', modes) ?? 'JioType',
34
+ fontSize: (0, _figmaVariablesResolver.getVariableByName)('text/fontSize', modes) ?? 14,
35
+ fontWeight: String((0, _figmaVariablesResolver.getVariableByName)('text/fontWeight', modes) ?? '500'),
36
+ lineHeight: (0, _figmaVariablesResolver.getVariableByName)('text/lineHeight', modes) ?? 20,
37
+ letterSpacing: (0, _figmaVariablesResolver.getVariableByName)('text/letterSpacing', modes) ?? -0.5
38
+ };
39
+ }
40
+
41
+ /**
42
+ * TextSegment — composes several differently-styled text runs into a single
43
+ * paragraph that wraps as one continuous line.
44
+ *
45
+ * On the web you would reach for `<span>`s inside a `<p>`; the React Native
46
+ * equivalent is nesting `<Text>` inside a parent `<Text>`. That nesting is the
47
+ * whole trick: sibling `<View>`s would lay out as flex blocks and break onto
48
+ * rigid rows, but nested `<Text>` nodes flow inline and wrap naturally at word
49
+ * boundaries — exactly like a real paragraph — while each nested run keeps its
50
+ * own colour/weight resolved from its own `modes`.
51
+ *
52
+ * Two equivalent ways to author content:
53
+ * - **`segments` prop** — declarative array of `{ text, modes }` runs.
54
+ * - **`children`** — pass library `Text` elements (or strings); the parent
55
+ * `modes` cascade down and each child may override its own.
56
+ *
57
+ * @example Data-driven
58
+ * ```tsx
59
+ * <TextSegment
60
+ * segments={[
61
+ * { text: 'Upsell message ' },
62
+ * { text: 'JioFinance+', modes: { 'Text Appearance': 'Primary' } },
63
+ * ]}
64
+ * />
65
+ * ```
66
+ *
67
+ * @example Compositional
68
+ * ```tsx
69
+ * <TextSegment numberOfLines={2}>
70
+ * <Text>Pay with </Text>
71
+ * <Text modes={{ 'Text Appearance': 'Primary' }}>JioFinance+</Text>
72
+ * <Text> and earn rewards on every transaction.</Text>
73
+ * </TextSegment>
74
+ * ```
75
+ */
76
+ function TextSegment({
77
+ segments,
78
+ children,
79
+ textAlign = 'Left',
80
+ numberOfLines,
81
+ modes = _reactUtils.EMPTY_MODES,
82
+ style
83
+ }) {
84
+ const baseStyle = (0, _react.useMemo)(() => ({
85
+ ...resolveTextStyle(modes),
86
+ textAlign: TEXT_ALIGN_MAP[textAlign]
87
+ }), [modes, textAlign]);
88
+
89
+ // children win over `segments` so the compositional API can always override.
90
+ const hasChildren = children !== undefined && children !== null && children !== false;
91
+ const renderedSegments = (0, _react.useMemo)(() => {
92
+ if (hasChildren || !segments?.length) return null;
93
+ return segments.map((run, index) => {
94
+ // Parent modes first, then the run's own modes override them — mirrors the
95
+ // merge order used by `cloneChildrenWithModes` for the children API.
96
+ const runModes = run.modes ? {
97
+ ...modes,
98
+ ...run.modes
99
+ } : modes;
100
+ const runStyle = run.modes ? resolveTextStyle(runModes) : undefined;
101
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
102
+ style: [runStyle, run.style],
103
+ children: run.text
104
+ }, index);
105
+ });
106
+ }, [segments, hasChildren, modes]);
107
+
108
+ // The cascade: every child (and its descendants) receives the parent modes so
109
+ // nested library `Text` runs resolve their tokens against the same baseline,
110
+ // while still honouring any per-child `modes` override.
111
+ const content = hasChildren ? (0, _reactUtils.cloneChildrenWithModes)(children, modes) : renderedSegments;
112
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
113
+ style: [baseStyle, style],
114
+ numberOfLines: numberOfLines,
115
+ children: content
116
+ });
117
+ }
118
+ var _default = exports.default = /*#__PURE__*/_react.default.memo(TextSegment);