@xsolla/xui-b2c-card-selector 0.217.0

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.
@@ -0,0 +1,773 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CardSelector: () => CardSelector,
24
+ CardSelectorGroup: () => CardSelectorGroup
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/CardSelector.tsx
29
+ var import_react2 = require("react");
30
+
31
+ // ../../foundation/primitives-native/src/Box.tsx
32
+ var import_react_native = require("react-native");
33
+ var import_jsx_runtime = require("react/jsx-runtime");
34
+ var roleToAccessibilityRole = {
35
+ button: "button",
36
+ link: "link",
37
+ img: "image",
38
+ image: "image",
39
+ summary: "summary",
40
+ switch: "switch",
41
+ checkbox: "checkbox",
42
+ radio: "radio",
43
+ heading: "header",
44
+ text: "text",
45
+ search: "search",
46
+ alert: "alert",
47
+ none: "none",
48
+ presentation: "none",
49
+ progressbar: "progressbar",
50
+ scrollbar: "scrollbar",
51
+ adjustable: "adjustable",
52
+ tab: "tab",
53
+ menu: "menu",
54
+ menuitem: "menuitem",
55
+ list: "list",
56
+ timer: "timer"
57
+ };
58
+ var toGapValue = (gap) => {
59
+ if (gap === void 0) return void 0;
60
+ if (typeof gap === "number") return gap;
61
+ const pxMatch = /^(\d+(?:\.\d+)?)px$/.exec(gap);
62
+ if (pxMatch) return Number(pxMatch[1]);
63
+ const asNumber = Number(gap);
64
+ return Number.isFinite(asNumber) ? asNumber : void 0;
65
+ };
66
+ var Box = ({
67
+ children,
68
+ onPress,
69
+ onLayout,
70
+ onMoveShouldSetResponder,
71
+ onResponderGrant,
72
+ onResponderMove,
73
+ onResponderRelease,
74
+ onResponderTerminate,
75
+ backgroundColor,
76
+ borderColor,
77
+ borderWidth,
78
+ borderBottomWidth,
79
+ borderBottomColor,
80
+ borderTopWidth,
81
+ borderTopColor,
82
+ borderLeftWidth,
83
+ borderLeftColor,
84
+ borderRightWidth,
85
+ borderRightColor,
86
+ borderRadius,
87
+ borderStyle,
88
+ height,
89
+ padding,
90
+ paddingHorizontal,
91
+ paddingVertical,
92
+ margin,
93
+ marginTop,
94
+ marginBottom,
95
+ marginLeft,
96
+ marginRight,
97
+ flexDirection,
98
+ flexWrap,
99
+ alignItems,
100
+ justifyContent,
101
+ alignSelf,
102
+ position,
103
+ top,
104
+ bottom,
105
+ left,
106
+ right,
107
+ width,
108
+ minWidth,
109
+ minHeight,
110
+ maxWidth,
111
+ maxHeight,
112
+ flex,
113
+ flexShrink,
114
+ gap,
115
+ overflow,
116
+ opacity,
117
+ zIndex,
118
+ hoverStyle,
119
+ pressStyle,
120
+ style,
121
+ disabled,
122
+ "data-testid": dataTestId,
123
+ testID,
124
+ as,
125
+ src,
126
+ alt,
127
+ ...rest
128
+ }) => {
129
+ const getContainerStyle = (pressed) => ({
130
+ backgroundColor: pressed && pressStyle?.backgroundColor ? pressStyle.backgroundColor : backgroundColor,
131
+ borderColor,
132
+ borderWidth,
133
+ borderBottomWidth,
134
+ borderBottomColor,
135
+ borderTopWidth,
136
+ borderTopColor,
137
+ borderLeftWidth,
138
+ borderLeftColor,
139
+ borderRightWidth,
140
+ borderRightColor,
141
+ borderRadius,
142
+ borderStyle,
143
+ overflow,
144
+ opacity: opacity ?? (disabled ? 0.5 : void 0),
145
+ zIndex,
146
+ height,
147
+ width,
148
+ minWidth,
149
+ minHeight,
150
+ maxWidth,
151
+ maxHeight,
152
+ padding,
153
+ paddingHorizontal,
154
+ paddingVertical,
155
+ margin,
156
+ marginTop,
157
+ marginBottom,
158
+ marginLeft,
159
+ marginRight,
160
+ flexDirection,
161
+ flexWrap,
162
+ alignItems,
163
+ justifyContent,
164
+ alignSelf,
165
+ position,
166
+ top,
167
+ bottom,
168
+ left,
169
+ right,
170
+ flex,
171
+ flexShrink,
172
+ gap: toGapValue(gap),
173
+ ...style
174
+ });
175
+ const finalTestID = dataTestId || testID;
176
+ const {
177
+ role,
178
+ tabIndex,
179
+ onKeyDown,
180
+ onKeyUp,
181
+ "aria-label": ariaLabel,
182
+ "aria-labelledby": _ariaLabelledBy,
183
+ "aria-current": _ariaCurrent,
184
+ "aria-disabled": ariaDisabled,
185
+ "aria-live": ariaLive,
186
+ "aria-busy": ariaBusy,
187
+ className,
188
+ "data-testid": _dataTestId,
189
+ cursor: _cursor,
190
+ type: _type,
191
+ ...nativeRest
192
+ } = rest;
193
+ const accessibilityRole = typeof role === "string" && roleToAccessibilityRole[role] || (as === "button" ? "button" : void 0);
194
+ const isDisabled = Boolean(disabled || ariaDisabled);
195
+ const accessibilityState = {
196
+ disabled: isDisabled || void 0,
197
+ busy: Boolean(ariaBusy) || void 0
198
+ };
199
+ const accessibilityLiveRegion = ariaLive === "polite" || ariaLive === "assertive" ? ariaLive : void 0;
200
+ if (as === "img" && src) {
201
+ const imageStyle = {
202
+ width,
203
+ height,
204
+ borderRadius,
205
+ position,
206
+ top,
207
+ bottom,
208
+ left,
209
+ right,
210
+ ...style
211
+ };
212
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
213
+ import_react_native.Image,
214
+ {
215
+ source: { uri: src },
216
+ style: imageStyle,
217
+ testID: finalTestID,
218
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : alt,
219
+ resizeMode: "cover",
220
+ ...nativeRest
221
+ }
222
+ );
223
+ }
224
+ if (onPress || as === "button") {
225
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
226
+ import_react_native.Pressable,
227
+ {
228
+ onPress: !isDisabled && onPress ? onPress : void 0,
229
+ disabled: isDisabled,
230
+ onLayout,
231
+ onMoveShouldSetResponder,
232
+ onResponderGrant,
233
+ onResponderMove,
234
+ onResponderRelease,
235
+ onResponderTerminate,
236
+ style: ({ pressed }) => getContainerStyle(pressed),
237
+ testID: finalTestID,
238
+ accessibilityRole,
239
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : void 0,
240
+ accessibilityState,
241
+ accessibilityLiveRegion,
242
+ ...nativeRest,
243
+ children
244
+ }
245
+ );
246
+ }
247
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
248
+ import_react_native.View,
249
+ {
250
+ style: getContainerStyle(),
251
+ testID: finalTestID,
252
+ onLayout,
253
+ onMoveShouldSetResponder,
254
+ onResponderGrant,
255
+ onResponderMove,
256
+ onResponderRelease,
257
+ onResponderTerminate,
258
+ accessibilityRole,
259
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : void 0,
260
+ accessibilityState,
261
+ accessibilityLiveRegion,
262
+ ...nativeRest,
263
+ children
264
+ }
265
+ );
266
+ };
267
+
268
+ // ../../foundation/primitives-native/src/Text.tsx
269
+ var import_react_native2 = require("react-native");
270
+ var import_jsx_runtime2 = require("react/jsx-runtime");
271
+ var roleMap = {
272
+ alert: "alert",
273
+ heading: "header",
274
+ button: "button",
275
+ link: "link",
276
+ text: "text"
277
+ };
278
+ var parseNumericValue = (value) => {
279
+ if (value === void 0) return void 0;
280
+ if (typeof value === "number") return value;
281
+ const parsed = parseFloat(value);
282
+ return isNaN(parsed) ? void 0 : parsed;
283
+ };
284
+ var Text = ({
285
+ children,
286
+ color,
287
+ fontSize,
288
+ fontWeight,
289
+ fontFamily,
290
+ textAlign,
291
+ lineHeight,
292
+ numberOfLines,
293
+ id,
294
+ role,
295
+ testID,
296
+ "data-testid": dataTestId,
297
+ style: styleProp,
298
+ ...props
299
+ }) => {
300
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
301
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
302
+ resolvedFontFamily = void 0;
303
+ }
304
+ const incomingStyle = import_react_native2.StyleSheet.flatten(styleProp);
305
+ const baseStyle = {
306
+ color: color ?? incomingStyle?.color,
307
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
308
+ fontWeight,
309
+ fontFamily: resolvedFontFamily,
310
+ textDecorationLine: props.textDecoration,
311
+ textAlign: textAlign ?? incomingStyle?.textAlign,
312
+ lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),
313
+ marginTop: parseNumericValue(
314
+ incomingStyle?.marginTop
315
+ ),
316
+ marginBottom: parseNumericValue(
317
+ incomingStyle?.marginBottom
318
+ )
319
+ };
320
+ const accessibilityRole = role ? roleMap[role] : void 0;
321
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
322
+ import_react_native2.Text,
323
+ {
324
+ style: baseStyle,
325
+ numberOfLines,
326
+ testID: dataTestId || testID || id,
327
+ accessibilityRole,
328
+ children
329
+ }
330
+ );
331
+ };
332
+
333
+ // src/CardSelector.tsx
334
+ var import_xui_core = require("@xsolla/xui-core");
335
+
336
+ // src/CardSelectorGroupContext.tsx
337
+ var import_react = require("react");
338
+ var CardSelectorGroupContext = (0, import_react.createContext)(null);
339
+ var useCardSelectorGroup = () => (0, import_react.useContext)(CardSelectorGroupContext);
340
+
341
+ // src/CardSelector.tsx
342
+ var import_jsx_runtime3 = require("react/jsx-runtime");
343
+ var platformMetrics = {
344
+ mobile: {
345
+ controlSize: "md",
346
+ controlBox: 18,
347
+ controlIcon: 12,
348
+ titleFontSize: 14,
349
+ titleLineHeight: 16,
350
+ descriptionFontSize: 12,
351
+ descriptionLineHeight: 16,
352
+ padding: 8,
353
+ gap: 8,
354
+ radius: 8
355
+ },
356
+ desktop: {
357
+ controlSize: "lg",
358
+ controlBox: 20,
359
+ controlIcon: 14,
360
+ titleFontSize: 16,
361
+ titleLineHeight: 20,
362
+ descriptionFontSize: 12,
363
+ descriptionLineHeight: 16,
364
+ padding: 10,
365
+ gap: 8,
366
+ radius: 8
367
+ }
368
+ };
369
+ var CheckGlyph = ({
370
+ size,
371
+ color
372
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
373
+ "svg",
374
+ {
375
+ width: size,
376
+ height: size,
377
+ viewBox: "0 0 12 12",
378
+ fill: "none",
379
+ xmlns: "http://www.w3.org/2000/svg",
380
+ "aria-hidden": "true",
381
+ focusable: "false",
382
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
383
+ "path",
384
+ {
385
+ d: "M10.6699 4.2959L6.00293 8.96387C5.72637 9.24043 5.27761 9.24031 5.00098 8.96387L2 5.96289L3.2959 4.66699L5.50098 6.87207L9.37402 3L10.6699 4.2959Z",
386
+ fill: color
387
+ }
388
+ )
389
+ }
390
+ );
391
+ var SelectionControl = ({ type, checked, disabled, metrics, theme }) => {
392
+ const check = theme.colors.control.check;
393
+ const faint = theme.colors.control.faint;
394
+ const backgroundColor = disabled ? check.bgDisable : checked ? check.bg : faint.bg;
395
+ const borderColor = disabled ? check.borderDisable : checked ? check.border : faint.border;
396
+ const iconColor = disabled ? theme.colors.control.text.disable : check.icon;
397
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
398
+ Box,
399
+ {
400
+ width: metrics.controlBox,
401
+ height: metrics.controlBox,
402
+ flexShrink: 0,
403
+ alignItems: "center",
404
+ justifyContent: "center",
405
+ borderWidth: 1,
406
+ borderStyle: "solid",
407
+ borderColor,
408
+ backgroundColor,
409
+ borderRadius: type === "radio" ? import_xui_core.radiusScale["999"] : theme.shape.checkbox[metrics.controlSize].borderRadius,
410
+ "aria-hidden": "true",
411
+ testID: "card-selector-control",
412
+ children: checked && (type === "radio" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
413
+ Box,
414
+ {
415
+ width: metrics.controlIcon,
416
+ height: metrics.controlIcon,
417
+ borderRadius: import_xui_core.radiusScale["999"],
418
+ backgroundColor: iconColor
419
+ }
420
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckGlyph, { size: metrics.controlIcon, color: iconColor }))
421
+ }
422
+ );
423
+ };
424
+ var asPlainText = (value) => typeof value === "string" ? value : void 0;
425
+ var CardSelector = ({
426
+ title,
427
+ description,
428
+ imageSrc,
429
+ imageAlt = "",
430
+ value,
431
+ type: typeProp,
432
+ control: controlProp,
433
+ platform: platformProp,
434
+ checked: checkedProp,
435
+ defaultChecked = false,
436
+ onChange,
437
+ state = "default",
438
+ disabled,
439
+ width = "100%",
440
+ "aria-label": ariaLabel,
441
+ testID,
442
+ themeMode,
443
+ themeProductContext = "b2c"
444
+ }) => {
445
+ const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
446
+ const group = useCardSelectorGroup();
447
+ const nodeRef = (0, import_react2.useRef)(null);
448
+ const type = typeProp ?? group?.type ?? "radio";
449
+ const control = controlProp ?? group?.control ?? "text";
450
+ const platform = platformProp ?? group?.platform ?? "mobile";
451
+ const metrics = platformMetrics[platform];
452
+ const isDisabled = Boolean(disabled) || state === "disabled" || Boolean(group?.disabled);
453
+ const [internalChecked, setInternalChecked] = (0, import_react2.useState)(defaultChecked);
454
+ const isGroupControlled = group !== null && type === "radio" && value !== void 0;
455
+ const isControlled = checkedProp !== void 0;
456
+ const checked = isControlled ? Boolean(checkedProp) : isGroupControlled ? group?.selectedValue === value : internalChecked;
457
+ const toggle = (0, import_react2.useCallback)(() => {
458
+ if (isDisabled) return;
459
+ if (type === "radio" && checked && isGroupControlled) return;
460
+ const next = type === "radio" ? true : !checked;
461
+ if (!isControlled && !isGroupControlled) {
462
+ setInternalChecked(next);
463
+ }
464
+ if (group && isGroupControlled && value !== void 0) {
465
+ group.select(value);
466
+ }
467
+ onChange?.(next, value);
468
+ }, [
469
+ checked,
470
+ group,
471
+ isControlled,
472
+ isDisabled,
473
+ isGroupControlled,
474
+ onChange,
475
+ type,
476
+ value
477
+ ]);
478
+ const focus = (0, import_react2.useCallback)(() => {
479
+ const node = nodeRef.current;
480
+ if (node && typeof node.focus === "function") node.focus();
481
+ }, []);
482
+ const itemRef = (0, import_react2.useRef)({
483
+ value: value ?? "",
484
+ disabled: Boolean(isDisabled),
485
+ focus
486
+ });
487
+ (0, import_react2.useEffect)(() => {
488
+ itemRef.current.value = value ?? "";
489
+ itemRef.current.disabled = isDisabled;
490
+ itemRef.current.focus = focus;
491
+ });
492
+ const registerItem = group?.register;
493
+ (0, import_react2.useEffect)(() => {
494
+ if (!registerItem) return void 0;
495
+ return registerItem(itemRef.current);
496
+ }, [registerItem]);
497
+ const handleKeyDown = (event) => {
498
+ if (isDisabled) return;
499
+ const key = event.key;
500
+ if (key === " " || key === "Spacebar" || key === "Space" || key === "Enter") {
501
+ event.preventDefault();
502
+ toggle();
503
+ return;
504
+ }
505
+ if (!group || type !== "radio") return;
506
+ const delta = key === "ArrowRight" || key === "ArrowDown" ? 1 : key === "ArrowLeft" || key === "ArrowUp" ? -1 : 0;
507
+ if (delta === 0) return;
508
+ event.preventDefault();
509
+ group.moveFocus(value, delta);
510
+ };
511
+ const showControl = control === "text" || checked;
512
+ const isOnImage = control === "onImage";
513
+ const surfaceBackground = checked ? theme.colors.overlay.brand : state === "press" ? theme.colors.overlay.brand : state === "hover" ? theme.colors.overlay.monoHover : theme.colors.overlay.mono;
514
+ const titleColor = isOnImage && checked ? theme.colors.control.check.bg : theme.colors.content.primary;
515
+ const derivedLabel = [asPlainText(title), asPlainText(description)].filter(Boolean).join(", ");
516
+ const media = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
517
+ Box,
518
+ {
519
+ position: "relative",
520
+ width: "100%",
521
+ overflow: "hidden",
522
+ borderRadius: isOnImage ? metrics.radius : 0,
523
+ style: { aspectRatio: 1 },
524
+ testID: "card-selector-media",
525
+ children: [
526
+ imageSrc && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
527
+ Box,
528
+ {
529
+ as: "img",
530
+ src: imageSrc,
531
+ alt: imageAlt,
532
+ position: "absolute",
533
+ top: 0,
534
+ left: 0,
535
+ width: "100%",
536
+ height: "100%",
537
+ style: { objectFit: "cover", pointerEvents: "none" },
538
+ testID: "card-selector-image"
539
+ }
540
+ ),
541
+ isOnImage && showControl && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
542
+ Box,
543
+ {
544
+ position: "absolute",
545
+ top: 0,
546
+ left: 0,
547
+ right: 0,
548
+ bottom: 0,
549
+ alignItems: "center",
550
+ justifyContent: "center",
551
+ zIndex: 1,
552
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
553
+ SelectionControl,
554
+ {
555
+ type,
556
+ checked,
557
+ disabled: isDisabled,
558
+ metrics,
559
+ theme
560
+ }
561
+ )
562
+ }
563
+ )
564
+ ]
565
+ }
566
+ );
567
+ const label = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box, { flexDirection: "column", gap: 2, flexShrink: 1, overflow: "hidden", children: [
568
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
569
+ Text,
570
+ {
571
+ color: titleColor,
572
+ fontFamily: theme.fonts.body,
573
+ fontSize: metrics.titleFontSize,
574
+ lineHeight: metrics.titleLineHeight,
575
+ fontWeight: "500",
576
+ numberOfLines: 1,
577
+ testID: "card-selector-title",
578
+ children: title
579
+ }
580
+ ),
581
+ description && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
582
+ Text,
583
+ {
584
+ color: theme.colors.content.tertiary,
585
+ fontFamily: theme.fonts.body,
586
+ fontSize: metrics.descriptionFontSize,
587
+ lineHeight: metrics.descriptionLineHeight,
588
+ fontWeight: "400",
589
+ numberOfLines: 1,
590
+ testID: "card-selector-description",
591
+ children: description
592
+ }
593
+ )
594
+ ] });
595
+ const hasGroupSelection = group?.selectedValue !== void 0;
596
+ const isRovingTarget = checked || !hasGroupSelection && Boolean(group?.isFirstEnabled(value));
597
+ const isRovingRadio = Boolean(group) && type === "radio";
598
+ const tabIndex = isDisabled || isRovingRadio && !isRovingTarget ? -1 : 0;
599
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
600
+ Box,
601
+ {
602
+ ref: nodeRef,
603
+ width,
604
+ flexDirection: "column",
605
+ gap: isOnImage ? metrics.gap : 0,
606
+ opacity: isDisabled ? 0.48 : 1,
607
+ cursor: isDisabled ? "not-allowed" : "pointer",
608
+ role: type === "radio" ? "radio" : "checkbox",
609
+ "aria-checked": checked,
610
+ "aria-disabled": isDisabled || void 0,
611
+ "aria-label": ariaLabel || derivedLabel || void 0,
612
+ tabIndex,
613
+ onPress: isDisabled ? void 0 : toggle,
614
+ onKeyDown: handleKeyDown,
615
+ focusStyle: {
616
+ outlineColor: theme.colors.border.brand,
617
+ outlineWidth: 2,
618
+ outlineOffset: 2,
619
+ outlineStyle: "solid"
620
+ },
621
+ testID: testID || "card-selector",
622
+ children: [
623
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
624
+ Box,
625
+ {
626
+ flexDirection: "column",
627
+ overflow: "hidden",
628
+ borderRadius: metrics.radius,
629
+ borderWidth: 2,
630
+ borderStyle: "solid",
631
+ borderColor: checked ? theme.colors.border.brand : "transparent",
632
+ backgroundColor: surfaceBackground,
633
+ hoverStyle: !isDisabled && !checked && !isOnImage ? { backgroundColor: theme.colors.overlay.monoHover } : void 0,
634
+ pressStyle: !isDisabled && !isOnImage ? { backgroundColor: theme.colors.overlay.brand } : void 0,
635
+ testID: "card-selector-surface",
636
+ children: [
637
+ media,
638
+ !isOnImage && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
639
+ Box,
640
+ {
641
+ flexDirection: "row",
642
+ alignItems: "center",
643
+ gap: metrics.gap,
644
+ padding: metrics.padding,
645
+ testID: "card-selector-bar",
646
+ children: [
647
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
648
+ SelectionControl,
649
+ {
650
+ type,
651
+ checked,
652
+ disabled: isDisabled,
653
+ metrics,
654
+ theme
655
+ }
656
+ ),
657
+ label
658
+ ]
659
+ }
660
+ )
661
+ ]
662
+ }
663
+ ),
664
+ isOnImage && label
665
+ ]
666
+ }
667
+ );
668
+ };
669
+
670
+ // src/CardSelectorGroup.tsx
671
+ var import_react3 = require("react");
672
+ var import_xui_core2 = require("@xsolla/xui-core");
673
+ var import_jsx_runtime4 = require("react/jsx-runtime");
674
+ var CardSelectorGroup = ({
675
+ children,
676
+ type = "radio",
677
+ value,
678
+ defaultValue,
679
+ onValueChange,
680
+ platform,
681
+ control,
682
+ disabled = false,
683
+ gap = 12,
684
+ "aria-label": ariaLabel,
685
+ "aria-labelledby": ariaLabelledBy,
686
+ testID,
687
+ themeMode,
688
+ themeProductContext = "b2c"
689
+ }) => {
690
+ (0, import_xui_core2.useResolvedTheme)({ themeMode, themeProductContext });
691
+ const [items, setItems] = (0, import_react3.useState)([]);
692
+ const [internalValue, setInternalValue] = (0, import_react3.useState)(defaultValue);
693
+ const isControlled = value !== void 0;
694
+ const selectedValue = isControlled ? value : internalValue;
695
+ const register = (0, import_react3.useCallback)((item) => {
696
+ setItems((prev) => [...prev, item]);
697
+ return () => setItems((prev) => prev.filter((entry) => entry !== item));
698
+ }, []);
699
+ const select = (0, import_react3.useCallback)(
700
+ (next) => {
701
+ if (!isControlled) setInternalValue(next);
702
+ onValueChange?.(next);
703
+ },
704
+ [isControlled, onValueChange]
705
+ );
706
+ const moveFocus = (0, import_react3.useCallback)(
707
+ (fromValue, delta) => {
708
+ const enabled = items.filter((item) => !item.disabled);
709
+ if (enabled.length === 0) return;
710
+ const currentIndex = enabled.findIndex(
711
+ (item) => item.value === fromValue
712
+ );
713
+ const nextIndex = currentIndex === -1 ? 0 : (currentIndex + delta + enabled.length) % enabled.length;
714
+ const next = enabled[nextIndex];
715
+ if (!next) return;
716
+ next.focus();
717
+ if (type === "radio" && next.value) select(next.value);
718
+ },
719
+ [items, select, type]
720
+ );
721
+ const isFirstEnabled = (0, import_react3.useCallback)(
722
+ (candidate) => {
723
+ const first = items.find((item) => !item.disabled);
724
+ return Boolean(first) && first?.value === candidate;
725
+ },
726
+ [items]
727
+ );
728
+ const contextValue = (0, import_react3.useMemo)(
729
+ () => ({
730
+ type,
731
+ platform,
732
+ control,
733
+ disabled,
734
+ selectedValue,
735
+ select,
736
+ register,
737
+ moveFocus,
738
+ isFirstEnabled
739
+ }),
740
+ [
741
+ control,
742
+ disabled,
743
+ isFirstEnabled,
744
+ moveFocus,
745
+ platform,
746
+ register,
747
+ select,
748
+ selectedValue,
749
+ type
750
+ ]
751
+ );
752
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardSelectorGroupContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
753
+ Box,
754
+ {
755
+ role: type === "radio" ? "radiogroup" : "group",
756
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
757
+ "aria-labelledby": ariaLabelledBy,
758
+ "aria-disabled": disabled || void 0,
759
+ flexDirection: "row",
760
+ flexWrap: "wrap",
761
+ alignItems: "flex-start",
762
+ gap,
763
+ testID: testID || "card-selector-group",
764
+ children
765
+ }
766
+ ) });
767
+ };
768
+ // Annotate the CommonJS export names for ESM import in node:
769
+ 0 && (module.exports = {
770
+ CardSelector,
771
+ CardSelectorGroup
772
+ });
773
+ //# sourceMappingURL=index.js.map