@xsolla/xui-radio-group 0.216.1 → 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 +93 -2
- package/native/index.d.mts +68 -1
- package/native/index.d.ts +68 -1
- package/native/index.js +278 -4
- package/native/index.js.map +1 -1
- package/native/index.mjs +273 -4
- package/native/index.mjs.map +1 -1
- package/package.json +4 -4
- package/web/index.d.mts +68 -1
- package/web/index.d.ts +68 -1
- package/web/index.js +268 -4
- package/web/index.js.map +1 -1
- package/web/index.mjs +270 -4
- package/web/index.mjs.map +1 -1
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`
|
|
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
|
package/native/index.d.mts
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
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
357
|
+
gap,
|
|
358
|
+
view = "default",
|
|
359
|
+
columns = 1,
|
|
360
|
+
testID
|
|
280
361
|
}) => {
|
|
281
|
-
|
|
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
|
});
|