@xsolla/xui-radio-group 0.216.0 → 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.
package/README.md CHANGED
@@ -82,6 +82,70 @@ export default function RadioGroupWithDescriptions() {
82
82
  }
83
83
  ```
84
84
 
85
+ ### Card View
86
+
87
+ `RadioCard` renders each option as a selectable card with a title, a
88
+ description and a radio indicator. The **whole card** is the click target, and
89
+ the selected card is filled with the brand-tinted control surface.
90
+
91
+ ```tsx
92
+ import * as React from "react";
93
+ import { RadioGroup, RadioCard } from "@xsolla/xui-radio-group";
94
+
95
+ export default function DistributionModelPicker() {
96
+ const [model, setModel] = React.useState("p2p");
97
+
98
+ return (
99
+ <RadioGroup view="card" value={model} onChange={setModel}>
100
+ <RadioCard
101
+ value="f2p"
102
+ title="Free-to-play (F2P)"
103
+ description="Players have free access to the game after downloading the launcher."
104
+ />
105
+ <RadioCard
106
+ value="p2p"
107
+ title="Pay-to-play (P2P)"
108
+ description="Players must purchase the game to access it."
109
+ />
110
+ <RadioCard
111
+ value="s2p"
112
+ title="Subscribe-to-play (S2P)"
113
+ description="Players must buy a subscription to access the game."
114
+ />
115
+ </RadioGroup>
116
+ );
117
+ }
118
+ ```
119
+
120
+ ### Two-Column Card Layout
121
+
122
+ `columns` lays the cards out side by side. Children are distributed
123
+ round-robin, so the reading order stays left-to-right then top-to-bottom.
124
+
125
+ ```tsx
126
+ import * as React from "react";
127
+ import { RadioGroup, RadioCard } from "@xsolla/xui-radio-group";
128
+
129
+ export default function SalesChannelPicker() {
130
+ const [channel, setChannel] = React.useState("launcher");
131
+
132
+ return (
133
+ <RadioGroup view="card" columns={2} value={channel} onChange={setChannel}>
134
+ <RadioCard
135
+ value="launcher"
136
+ title="Sell in launcher"
137
+ description="Players buy the game inside the launcher."
138
+ />
139
+ <RadioCard
140
+ value="web"
141
+ title="Sell on the web"
142
+ description="Players buy the game on your web store."
143
+ />
144
+ </RadioGroup>
145
+ );
146
+ }
147
+ ```
148
+
85
149
  ### Disabled Radio Group
86
150
 
87
151
  ```tsx
@@ -234,7 +298,30 @@ Container component that provides selection context to Radio children.
234
298
  | size | `"sm" \| "md" \| "lg" \| "xl"` | `"md"` | Size applied to all child Radio components. |
235
299
  | disabled | `boolean` | `false` | Whether all radios are disabled. |
236
300
  | flexDirection | `"row" \| "column"` | `"column"` | Layout direction of the group. |
237
- | gap | `number` | `12` | Gap between radio items in pixels. |
301
+ | gap | `number` | `12` / `4` | Gap between items in pixels. Defaults to `4` when `view="card"`. |
302
+ | view | `"default" \| "card"` | `"default"`| Visual treatment of the options. `"card"` is for `RadioCard` children. |
303
+ | columns | `number` | `1` | Number of equal-width columns. Values above `1` take precedence over `flexDirection`. |
304
+
305
+ ### RadioCard
306
+
307
+ A selectable card option. Renders a title, an optional description and a radio
308
+ indicator, with the whole card as the click target. Must be used inside a
309
+ `RadioGroup`, which owns the selected value.
310
+
311
+ **RadioCard Props:**
312
+
313
+ | Prop | Type | Default | Description |
314
+ | :------------ | :----------------------------- | :------ | :------------------------------------------------------------------------------------------------ |
315
+ | `testID` | `string` | — | Test ID for testing frameworks. |
316
+ | value | `string` | - | **Required.** Value this card selects in the parent group. |
317
+ | title | `string` | - | **Required.** Primary line of the card. |
318
+ | description | `string` | - | Supporting line rendered under the title. |
319
+ | size | `"sm" \| "md" \| "lg" \| "xl"` | group | Size of the card and its indicator. Falls back to the group's `size`. |
320
+ | checked | `boolean` | group | Selected state. Normally derived from the parent group's `value`. |
321
+ | disabled | `boolean` | `false` | Disables this card only. The group's `disabled` disables every card. |
322
+ | padding | `number \| string` | by size | Overrides the size-derived internal padding (`8`/`12`/`16`/`16` vertical, `12`/`16`/`16`/`20` horizontal). |
323
+ | onPress | `() => void` | - | Called after the card is selected, in addition to the group's `onChange`. |
324
+ | themeMode | `ThemeMode` | context | Per-component theme mode override. |
238
325
 
239
326
  ### RadioGroup Context
240
327
 
@@ -246,6 +333,7 @@ interface RadioGroupContextProps {
246
333
  onChange?: (value: string) => void; // Selection handler
247
334
  size?: "sm" | "md" | "lg" | "xl"; // Size for radios
248
335
  disabled?: boolean; // Disabled state
336
+ view?: "default" | "card"; // Visual treatment
249
337
  }
250
338
  ```
251
339
 
@@ -257,8 +345,11 @@ Child Radio components automatically:
257
345
 
258
346
  ## Accessibility
259
347
 
260
- - RadioGroup provides proper grouping semantics
348
+ - RadioGroup renders `role="radiogroup"` and provides proper grouping semantics
261
349
  - Only one radio can be selected at a time
350
+ - `RadioCard` carries the `role="radio"` semantics and the tab stop itself, so
351
+ its indicator is presentational and never becomes a second tab stop
352
+ - `RadioCard` responds to both <kbd>Space</kbd> and <kbd>Enter</kbd>
262
353
  - Arrow keys navigate between options when focused
263
354
  - Tab key moves focus to/from the group
264
355
  - Disabled state properly communicated to all children
@@ -1,10 +1,21 @@
1
1
  import React from 'react';
2
+ import { ThemeOverrideProps } from '@xsolla/xui-core';
2
3
 
4
+ /**
5
+ * Visual treatment of the group's options.
6
+ *
7
+ * - `default` — bare `Radio` rows, the historical behaviour.
8
+ * - `card` — each option is a selectable card (`RadioCard`) with a title, a
9
+ * description and a radio indicator, where the whole card is the click
10
+ * target.
11
+ */
12
+ type RadioGroupView = "default" | "card";
3
13
  interface RadioGroupContextProps {
4
14
  value?: string;
5
15
  onChange?: (value: string) => void;
6
16
  size?: "sm" | "md" | "lg" | "xl";
7
17
  disabled?: boolean;
18
+ view?: RadioGroupView;
8
19
  testID?: string;
9
20
  }
10
21
  declare const useRadioGroup: () => RadioGroupContextProps;
@@ -16,9 +27,65 @@ interface RadioGroupProps {
16
27
  children: React.ReactNode;
17
28
  flexDirection?: "row" | "column";
18
29
  gap?: number;
30
+ /**
31
+ * Visual treatment of the options. `card` renders `RadioCard` children as
32
+ * selectable cards; it does not change the selection semantics.
33
+ */
34
+ view?: RadioGroupView;
35
+ /**
36
+ * Number of side-by-side columns. Values above `1` lay the options out as a
37
+ * row of equal-width columns, which is how the Pentagram screens present a
38
+ * short option list. Children are distributed round-robin so the visual
39
+ * reading order stays left-to-right, then top-to-bottom.
40
+ *
41
+ * Takes precedence over `flexDirection`.
42
+ */
43
+ columns?: number;
19
44
  /** Test ID for testing frameworks */
20
45
  testID?: string;
21
46
  }
22
47
  declare const RadioGroup: React.FC<RadioGroupProps>;
23
48
 
24
- export { RadioGroup, type RadioGroupContextProps, type RadioGroupProps, useRadioGroup };
49
+ type RadioCardSize = "sm" | "md" | "lg" | "xl";
50
+ interface RadioCardProps extends ThemeOverrideProps {
51
+ /** Value this card selects in the parent `RadioGroup`. */
52
+ value: string;
53
+ /** Primary line of the card. */
54
+ title: string;
55
+ /** Supporting line rendered under the title. */
56
+ description?: string;
57
+ /** Size of the card and its radio indicator. Falls back to the group's size. */
58
+ size?: RadioCardSize;
59
+ /** Selected state. Normally derived from the parent `RadioGroup`. */
60
+ checked?: boolean;
61
+ /** Disables this card only. The group's `disabled` disables every card. */
62
+ disabled?: boolean;
63
+ /**
64
+ * Overrides the size-derived internal padding. Accepts anything `Box`'s
65
+ * `padding` accepts.
66
+ */
67
+ padding?: number | string;
68
+ /** Called after the card is selected, in addition to the group's `onChange`. */
69
+ onPress?: () => void;
70
+ /** Accessible label, when `title` is not descriptive enough on its own. */
71
+ "aria-label"?: string;
72
+ /** Test ID for testing frameworks */
73
+ testID?: string;
74
+ }
75
+ /**
76
+ * A selectable card option for `RadioGroup`.
77
+ *
78
+ * The whole card is the click target — not just the radio dot — and the
79
+ * selected card is filled with the brand-tinted control surface. Compose them
80
+ * inside a `RadioGroup` exactly like `Radio`:
81
+ *
82
+ * ```tsx
83
+ * <RadioGroup view="card" value={value} onChange={setValue}>
84
+ * <RadioCard value="f2p" title="Free-to-play (F2P)" description="..." />
85
+ * <RadioCard value="p2p" title="Pay-to-play (P2P)" description="..." />
86
+ * </RadioGroup>
87
+ * ```
88
+ */
89
+ declare const RadioCard: React.FC<RadioCardProps>;
90
+
91
+ export { RadioCard, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
package/native/index.d.ts CHANGED
@@ -1,10 +1,21 @@
1
1
  import React from 'react';
2
+ import { ThemeOverrideProps } from '@xsolla/xui-core';
2
3
 
4
+ /**
5
+ * Visual treatment of the group's options.
6
+ *
7
+ * - `default` — bare `Radio` rows, the historical behaviour.
8
+ * - `card` — each option is a selectable card (`RadioCard`) with a title, a
9
+ * description and a radio indicator, where the whole card is the click
10
+ * target.
11
+ */
12
+ type RadioGroupView = "default" | "card";
3
13
  interface RadioGroupContextProps {
4
14
  value?: string;
5
15
  onChange?: (value: string) => void;
6
16
  size?: "sm" | "md" | "lg" | "xl";
7
17
  disabled?: boolean;
18
+ view?: RadioGroupView;
8
19
  testID?: string;
9
20
  }
10
21
  declare const useRadioGroup: () => RadioGroupContextProps;
@@ -16,9 +27,65 @@ interface RadioGroupProps {
16
27
  children: React.ReactNode;
17
28
  flexDirection?: "row" | "column";
18
29
  gap?: number;
30
+ /**
31
+ * Visual treatment of the options. `card` renders `RadioCard` children as
32
+ * selectable cards; it does not change the selection semantics.
33
+ */
34
+ view?: RadioGroupView;
35
+ /**
36
+ * Number of side-by-side columns. Values above `1` lay the options out as a
37
+ * row of equal-width columns, which is how the Pentagram screens present a
38
+ * short option list. Children are distributed round-robin so the visual
39
+ * reading order stays left-to-right, then top-to-bottom.
40
+ *
41
+ * Takes precedence over `flexDirection`.
42
+ */
43
+ columns?: number;
19
44
  /** Test ID for testing frameworks */
20
45
  testID?: string;
21
46
  }
22
47
  declare const RadioGroup: React.FC<RadioGroupProps>;
23
48
 
24
- export { RadioGroup, type RadioGroupContextProps, type RadioGroupProps, useRadioGroup };
49
+ type RadioCardSize = "sm" | "md" | "lg" | "xl";
50
+ interface RadioCardProps extends ThemeOverrideProps {
51
+ /** Value this card selects in the parent `RadioGroup`. */
52
+ value: string;
53
+ /** Primary line of the card. */
54
+ title: string;
55
+ /** Supporting line rendered under the title. */
56
+ description?: string;
57
+ /** Size of the card and its radio indicator. Falls back to the group's size. */
58
+ size?: RadioCardSize;
59
+ /** Selected state. Normally derived from the parent `RadioGroup`. */
60
+ checked?: boolean;
61
+ /** Disables this card only. The group's `disabled` disables every card. */
62
+ disabled?: boolean;
63
+ /**
64
+ * Overrides the size-derived internal padding. Accepts anything `Box`'s
65
+ * `padding` accepts.
66
+ */
67
+ padding?: number | string;
68
+ /** Called after the card is selected, in addition to the group's `onChange`. */
69
+ onPress?: () => void;
70
+ /** Accessible label, when `title` is not descriptive enough on its own. */
71
+ "aria-label"?: string;
72
+ /** Test ID for testing frameworks */
73
+ testID?: string;
74
+ }
75
+ /**
76
+ * A selectable card option for `RadioGroup`.
77
+ *
78
+ * The whole card is the click target — not just the radio dot — and the
79
+ * selected card is filled with the brand-tinted control surface. Compose them
80
+ * inside a `RadioGroup` exactly like `Radio`:
81
+ *
82
+ * ```tsx
83
+ * <RadioGroup view="card" value={value} onChange={setValue}>
84
+ * <RadioCard value="f2p" title="Free-to-play (F2P)" description="..." />
85
+ * <RadioCard value="p2p" title="Pay-to-play (P2P)" description="..." />
86
+ * </RadioGroup>
87
+ * ```
88
+ */
89
+ declare const RadioCard: React.FC<RadioCardProps>;
90
+
91
+ export { RadioCard, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
package/native/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,18 +17,27 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.tsx
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ RadioCard: () => RadioCard,
23
34
  RadioGroup: () => RadioGroup,
24
35
  useRadioGroup: () => useRadioGroup
25
36
  });
26
37
  module.exports = __toCommonJS(index_exports);
27
38
 
28
39
  // src/RadioGroup.tsx
29
- var import_react = require("react");
40
+ var import_react = __toESM(require("react"));
30
41
 
31
42
  // ../../foundation/primitives-native/src/Box.tsx
32
43
  var import_react_native = require("react-native");
@@ -265,10 +276,77 @@ var Box = ({
265
276
  );
266
277
  };
267
278
 
268
- // src/RadioGroup.tsx
279
+ // ../../foundation/primitives-native/src/Text.tsx
280
+ var import_react_native2 = require("react-native");
269
281
  var import_jsx_runtime2 = require("react/jsx-runtime");
282
+ var roleMap = {
283
+ alert: "alert",
284
+ heading: "header",
285
+ button: "button",
286
+ link: "link",
287
+ text: "text"
288
+ };
289
+ var parseNumericValue = (value) => {
290
+ if (value === void 0) return void 0;
291
+ if (typeof value === "number") return value;
292
+ const parsed = parseFloat(value);
293
+ return isNaN(parsed) ? void 0 : parsed;
294
+ };
295
+ var Text = ({
296
+ children,
297
+ color,
298
+ fontSize,
299
+ fontWeight,
300
+ fontFamily,
301
+ textAlign,
302
+ lineHeight,
303
+ numberOfLines,
304
+ id,
305
+ role,
306
+ testID,
307
+ "data-testid": dataTestId,
308
+ style: styleProp,
309
+ ...props
310
+ }) => {
311
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
312
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
313
+ resolvedFontFamily = void 0;
314
+ }
315
+ const incomingStyle = import_react_native2.StyleSheet.flatten(styleProp);
316
+ const baseStyle = {
317
+ color: color ?? incomingStyle?.color,
318
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
319
+ fontWeight,
320
+ fontFamily: resolvedFontFamily,
321
+ textDecorationLine: props.textDecoration,
322
+ textAlign: textAlign ?? incomingStyle?.textAlign,
323
+ lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),
324
+ marginTop: parseNumericValue(
325
+ incomingStyle?.marginTop
326
+ ),
327
+ marginBottom: parseNumericValue(
328
+ incomingStyle?.marginBottom
329
+ )
330
+ };
331
+ const accessibilityRole = role ? roleMap[role] : void 0;
332
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
333
+ import_react_native2.Text,
334
+ {
335
+ style: baseStyle,
336
+ numberOfLines,
337
+ testID: dataTestId || testID || id,
338
+ accessibilityRole,
339
+ children
340
+ }
341
+ );
342
+ };
343
+
344
+ // src/RadioGroup.tsx
345
+ var import_jsx_runtime3 = require("react/jsx-runtime");
270
346
  var RadioGroupContext = (0, import_react.createContext)({});
271
347
  var useRadioGroup = () => (0, import_react.useContext)(RadioGroupContext);
348
+ var DEFAULT_GAP = 12;
349
+ var CARD_GAP = 4;
272
350
  var RadioGroup = ({
273
351
  value,
274
352
  onChange,
@@ -276,12 +354,208 @@ var RadioGroup = ({
276
354
  disabled = false,
277
355
  children,
278
356
  flexDirection = "column",
279
- gap = 12
357
+ gap,
358
+ view = "default",
359
+ columns = 1,
360
+ testID
280
361
  }) => {
281
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RadioGroupContext.Provider, { value: { value, onChange, size, disabled }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box, { flexDirection, gap, children }) });
362
+ const resolvedGap = gap ?? (view === "card" ? CARD_GAP : DEFAULT_GAP);
363
+ const columnBuckets = (0, import_react.useMemo)(() => {
364
+ if (columns <= 1) return null;
365
+ const items = import_react.default.Children.toArray(children);
366
+ const buckets = Array.from(
367
+ { length: columns },
368
+ () => []
369
+ );
370
+ items.forEach((child, index) => {
371
+ buckets[index % columns].push(child);
372
+ });
373
+ return buckets;
374
+ }, [children, columns]);
375
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
376
+ RadioGroupContext.Provider,
377
+ {
378
+ value: { value, onChange, size, disabled, view },
379
+ children: columnBuckets ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
380
+ Box,
381
+ {
382
+ testID,
383
+ role: "radiogroup",
384
+ "aria-disabled": disabled || void 0,
385
+ flexDirection: "row",
386
+ alignItems: "flex-start",
387
+ gap: resolvedGap,
388
+ children: columnBuckets.map((bucket, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
389
+ Box,
390
+ {
391
+ flexDirection: "column",
392
+ gap: resolvedGap,
393
+ flex: 1,
394
+ style: { minWidth: 0 },
395
+ children: bucket
396
+ },
397
+ index
398
+ ))
399
+ }
400
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
401
+ Box,
402
+ {
403
+ testID,
404
+ role: "radiogroup",
405
+ "aria-disabled": disabled || void 0,
406
+ flexDirection,
407
+ gap: resolvedGap,
408
+ children
409
+ }
410
+ )
411
+ }
412
+ );
413
+ };
414
+
415
+ // src/RadioCard.tsx
416
+ var import_xui_core = require("@xsolla/xui-core");
417
+ var import_jsx_runtime4 = require("react/jsx-runtime");
418
+ var CARD_PADDING = {
419
+ sm: { vertical: 8, horizontal: 12 },
420
+ md: { vertical: 12, horizontal: 16 },
421
+ lg: { vertical: 16, horizontal: 16 },
422
+ xl: { vertical: 16, horizontal: 20 }
423
+ };
424
+ var CONTENT_GAP = 12;
425
+ var RadioCard = ({
426
+ value,
427
+ title,
428
+ description,
429
+ size: propSize,
430
+ checked: propChecked,
431
+ disabled = false,
432
+ padding,
433
+ onPress,
434
+ "aria-label": ariaLabel,
435
+ testID,
436
+ themeMode,
437
+ themeProductContext
438
+ }) => {
439
+ const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
440
+ const radioGroup = useRadioGroup();
441
+ const size = propSize || radioGroup.size || "md";
442
+ const checked = propChecked !== void 0 ? propChecked : radioGroup.value === value;
443
+ const isDisabled = disabled || Boolean(radioGroup.disabled);
444
+ const sizing = theme.sizing.radio(size);
445
+ const checkColors = theme.colors.control.check;
446
+ const faintColors = theme.colors.control.faint;
447
+ const textColors = theme.colors.control.text;
448
+ const brandSurface = theme.colors.control.brand.secondary;
449
+ const spacing = CARD_PADDING[size];
450
+ const surfaceColor = checked ? brandSurface.bg : theme.colors.background.secondary;
451
+ const overlay = theme.colors.overlay.mono;
452
+ const hoverStyle = isDisabled ? void 0 : {
453
+ background: `linear-gradient(0deg, ${overlay} 0%, ${overlay} 100%), ${surfaceColor}`
454
+ };
455
+ const handlePress = () => {
456
+ if (isDisabled) return;
457
+ if (radioGroup.onChange) {
458
+ radioGroup.onChange(value);
459
+ }
460
+ if (onPress) {
461
+ onPress();
462
+ }
463
+ };
464
+ const handleKeyDown = (event) => {
465
+ if (isDisabled) return;
466
+ if (event.key === " " || event.key === "Enter") {
467
+ event.preventDefault();
468
+ handlePress();
469
+ }
470
+ };
471
+ const indicatorBg = isDisabled ? checkColors.bgDisable : checked ? checkColors.bg : faintColors.bg;
472
+ const indicatorBorder = isDisabled ? checkColors.borderDisable : checked ? checkColors.border : faintColors.border;
473
+ const dotColor = isDisabled ? textColors.disable : checkColors.icon;
474
+ const titleColor = isDisabled ? textColors.disable : textColors.primary;
475
+ const descriptionColor = isDisabled ? textColors.disable : theme.colors.content.tertiary;
476
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
477
+ Box,
478
+ {
479
+ testID,
480
+ role: "radio",
481
+ "aria-checked": checked,
482
+ "aria-disabled": isDisabled || void 0,
483
+ "aria-label": ariaLabel,
484
+ tabIndex: isDisabled ? -1 : 0,
485
+ onPress: handlePress,
486
+ onKeyDown: handleKeyDown,
487
+ cursor: isDisabled ? "not-allowed" : "pointer",
488
+ flexDirection: "row",
489
+ alignItems: "center",
490
+ justifyContent: "space-between",
491
+ gap: CONTENT_GAP,
492
+ width: "100%",
493
+ backgroundColor: surfaceColor,
494
+ borderRadius: theme.shape.cell.borderRadius,
495
+ padding,
496
+ paddingVertical: padding === void 0 ? spacing.vertical : void 0,
497
+ paddingHorizontal: padding === void 0 ? spacing.horizontal : void 0,
498
+ hoverStyle,
499
+ children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
501
+ Box,
502
+ {
503
+ flexDirection: "column",
504
+ gap: sizing.textGap,
505
+ style: { flex: 1, minWidth: 0 },
506
+ children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
508
+ Text,
509
+ {
510
+ color: titleColor,
511
+ fontSize: sizing.fontSize,
512
+ lineHeight: sizing.lineHeight,
513
+ fontWeight: "400",
514
+ children: title
515
+ }
516
+ ),
517
+ description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
518
+ Text,
519
+ {
520
+ color: descriptionColor,
521
+ fontSize: sizing.descriptionFontSize,
522
+ lineHeight: sizing.descriptionLineHeight,
523
+ children: description
524
+ }
525
+ )
526
+ ]
527
+ }
528
+ ),
529
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
530
+ Box,
531
+ {
532
+ width: sizing.size,
533
+ height: sizing.size,
534
+ backgroundColor: indicatorBg,
535
+ borderColor: indicatorBorder,
536
+ borderWidth: sizing.borderWidth,
537
+ borderRadius: import_xui_core.radiusScale["999"],
538
+ alignItems: "center",
539
+ justifyContent: "center",
540
+ style: { flexShrink: 0 },
541
+ children: checked && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
542
+ Box,
543
+ {
544
+ width: sizing.iconSize,
545
+ height: sizing.iconSize,
546
+ backgroundColor: dotColor,
547
+ borderRadius: import_xui_core.radiusScale["999"]
548
+ }
549
+ )
550
+ }
551
+ )
552
+ ]
553
+ }
554
+ );
282
555
  };
283
556
  // Annotate the CommonJS export names for ESM import in node:
284
557
  0 && (module.exports = {
558
+ RadioCard,
285
559
  RadioGroup,
286
560
  useRadioGroup
287
561
  });