@xsolla/xui-radio-group 0.218.1 → 0.219.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 +52 -13
- package/native/index.d.mts +29 -1
- package/native/index.d.ts +29 -1
- package/native/index.js +27 -3
- package/native/index.js.map +1 -1
- package/native/index.mjs +26 -2
- package/native/index.mjs.map +1 -1
- package/package.json +4 -4
- package/web/index.d.mts +29 -1
- package/web/index.d.ts +29 -1
- package/web/index.js +30 -5
- package/web/index.js.map +1 -1
- package/web/index.mjs +29 -4
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -122,6 +122,10 @@ export default function DistributionModelPicker() {
|
|
|
122
122
|
`columns` lays the cards out side by side. Children are distributed
|
|
123
123
|
round-robin, so the reading order stays left-to-right then top-to-bottom.
|
|
124
124
|
|
|
125
|
+
Every card in a row is stretched to the height of the tallest one, so
|
|
126
|
+
descriptions that wrap to a different number of lines still line up. Use
|
|
127
|
+
`columns` — not `flexDirection="row"` — for a multi-column card layout.
|
|
128
|
+
|
|
125
129
|
```tsx
|
|
126
130
|
import * as React from "react";
|
|
127
131
|
import { RadioGroup, RadioCard } from "@xsolla/xui-radio-group";
|
|
@@ -146,6 +150,40 @@ export default function SalesChannelPicker() {
|
|
|
146
150
|
}
|
|
147
151
|
```
|
|
148
152
|
|
|
153
|
+
### Top-Aligned Card Content
|
|
154
|
+
|
|
155
|
+
In a stretched row, a card can end up taller than its own content. By default
|
|
156
|
+
its title and description are centred against the radio indicator. Pass
|
|
157
|
+
`contentAlign="top"` to pin the text to the top of the card instead — the
|
|
158
|
+
indicator stays vertically centred either way, which is the split a single
|
|
159
|
+
`align-items` on the card cannot express.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import * as React from "react";
|
|
163
|
+
import { RadioGroup, RadioCard } from "@xsolla/xui-radio-group";
|
|
164
|
+
|
|
165
|
+
export default function TopAlignedCards() {
|
|
166
|
+
const [channel, setChannel] = React.useState("launcher");
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<RadioGroup view="card" columns={2} value={channel} onChange={setChannel}>
|
|
170
|
+
<RadioCard
|
|
171
|
+
contentAlign="top"
|
|
172
|
+
value="launcher"
|
|
173
|
+
title="Sell in launcher"
|
|
174
|
+
description="After clicking the Buy button a payment widget opens, selling the game key under the SKU specified above."
|
|
175
|
+
/>
|
|
176
|
+
<RadioCard
|
|
177
|
+
contentAlign="top"
|
|
178
|
+
value="browser"
|
|
179
|
+
title="Sell in browser"
|
|
180
|
+
description="After clicking the Buy button the link opens in a browser."
|
|
181
|
+
/>
|
|
182
|
+
</RadioGroup>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
149
187
|
### Disabled Radio Group
|
|
150
188
|
|
|
151
189
|
```tsx
|
|
@@ -300,7 +338,7 @@ Container component that provides selection context to Radio children.
|
|
|
300
338
|
| flexDirection | `"row" \| "column"` | `"column"` | Layout direction of the group. |
|
|
301
339
|
| gap | `number` | `12` / `4` | Gap between items in pixels. Defaults to `4` when `view="card"`. |
|
|
302
340
|
| 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
|
|
341
|
+
| columns | `number` | `1` | Number of equal-width columns. Values above `1` take precedence over `flexDirection`, and stretch every option in a row to the height of the tallest one. |
|
|
304
342
|
|
|
305
343
|
### RadioCard
|
|
306
344
|
|
|
@@ -310,18 +348,19 @@ indicator, with the whole card as the click target. Must be used inside a
|
|
|
310
348
|
|
|
311
349
|
**RadioCard Props:**
|
|
312
350
|
|
|
313
|
-
| Prop | Type | Default
|
|
314
|
-
| :------------ | :----------------------------- |
|
|
315
|
-
| `testID` | `string` | —
|
|
316
|
-
| value | `string` | -
|
|
317
|
-
| title | `string` | -
|
|
318
|
-
| description | `string` | -
|
|
319
|
-
| size | `"sm" \| "md" \| "lg" \| "xl"` | group
|
|
320
|
-
| checked | `boolean` | group
|
|
321
|
-
| disabled | `boolean` | `false`
|
|
322
|
-
| padding | `number \| string` | by size
|
|
323
|
-
|
|
|
324
|
-
|
|
|
351
|
+
| Prop | Type | Default | Description |
|
|
352
|
+
| :------------ | :----------------------------- | :--------- | :------------------------------------------------------------------------------------------------ |
|
|
353
|
+
| `testID` | `string` | — | Test ID for testing frameworks. |
|
|
354
|
+
| value | `string` | - | **Required.** Value this card selects in the parent group. |
|
|
355
|
+
| title | `string` | - | **Required.** Primary line of the card. |
|
|
356
|
+
| description | `string` | - | Supporting line rendered under the title. |
|
|
357
|
+
| size | `"sm" \| "md" \| "lg" \| "xl"` | group | Size of the card and its indicator. Falls back to the group's `size`. |
|
|
358
|
+
| checked | `boolean` | group | Selected state. Normally derived from the parent group's `value`. |
|
|
359
|
+
| disabled | `boolean` | `false` | Disables this card only. The group's `disabled` disables every card. |
|
|
360
|
+
| padding | `number \| string` | by size | Overrides the size-derived internal padding (`8`/`12`/`16`/`16` vertical, `12`/`16`/`16`/`20` horizontal). |
|
|
361
|
+
| contentAlign | `"center" \| "top"` | `"center"` | Where the title/description block sits when the card is taller than its own content. The radio indicator stays vertically centred either way. |
|
|
362
|
+
| onPress | `() => void` | - | Called after the card is selected, in addition to the group's `onChange`. |
|
|
363
|
+
| themeMode | `ThemeMode` | context | Per-component theme mode override. |
|
|
325
364
|
|
|
326
365
|
### RadioGroup Context
|
|
327
366
|
|
package/native/index.d.mts
CHANGED
|
@@ -38,6 +38,11 @@ interface RadioGroupProps {
|
|
|
38
38
|
* short option list. Children are distributed round-robin so the visual
|
|
39
39
|
* reading order stays left-to-right, then top-to-bottom.
|
|
40
40
|
*
|
|
41
|
+
* Every option in the row is stretched to the height of the tallest one, so
|
|
42
|
+
* cards whose descriptions wrap to a different number of lines still line
|
|
43
|
+
* up. Pair it with `RadioCard`'s `contentAlign="top"` when the text should
|
|
44
|
+
* stay pinned to the top of the stretched card.
|
|
45
|
+
*
|
|
41
46
|
* Takes precedence over `flexDirection`.
|
|
42
47
|
*/
|
|
43
48
|
columns?: number;
|
|
@@ -47,6 +52,22 @@ interface RadioGroupProps {
|
|
|
47
52
|
declare const RadioGroup: React.FC<RadioGroupProps>;
|
|
48
53
|
|
|
49
54
|
type RadioCardSize = "sm" | "md" | "lg" | "xl";
|
|
55
|
+
/**
|
|
56
|
+
* Cross-axis placement of the card's text block.
|
|
57
|
+
*
|
|
58
|
+
* The radio indicator is *not* covered by this — it stays vertically centred
|
|
59
|
+
* in the card either way, which is the split a single `alignItems` on the
|
|
60
|
+
* card's row cannot express.
|
|
61
|
+
*
|
|
62
|
+
* - `center` — title and description centred against the indicator. The
|
|
63
|
+
* historical behaviour, and indistinguishable from `top` on a card that is
|
|
64
|
+
* only as tall as its own content.
|
|
65
|
+
* - `top` — title and description pinned to the top edge of the card's
|
|
66
|
+
* content box. Use this when the card is stretched taller than its own
|
|
67
|
+
* content, e.g. inside a `RadioGroup` with `columns > 1`, where every card
|
|
68
|
+
* in a row is equalised to the tallest one.
|
|
69
|
+
*/
|
|
70
|
+
type RadioCardContentAlign = "center" | "top";
|
|
50
71
|
interface RadioCardProps extends ThemeOverrideProps {
|
|
51
72
|
/** Value this card selects in the parent `RadioGroup`. */
|
|
52
73
|
value: string;
|
|
@@ -65,6 +86,13 @@ interface RadioCardProps extends ThemeOverrideProps {
|
|
|
65
86
|
* `padding` accepts.
|
|
66
87
|
*/
|
|
67
88
|
padding?: number | string;
|
|
89
|
+
/**
|
|
90
|
+
* Where the title/description block sits when the card is taller than its
|
|
91
|
+
* own content. The radio indicator stays vertically centred regardless.
|
|
92
|
+
*
|
|
93
|
+
* @default "center"
|
|
94
|
+
*/
|
|
95
|
+
contentAlign?: RadioCardContentAlign;
|
|
68
96
|
/** Called after the card is selected, in addition to the group's `onChange`. */
|
|
69
97
|
onPress?: () => void;
|
|
70
98
|
/** Accessible label, when `title` is not descriptive enough on its own. */
|
|
@@ -88,4 +116,4 @@ interface RadioCardProps extends ThemeOverrideProps {
|
|
|
88
116
|
*/
|
|
89
117
|
declare const RadioCard: React.FC<RadioCardProps>;
|
|
90
118
|
|
|
91
|
-
export { RadioCard, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
|
|
119
|
+
export { RadioCard, type RadioCardContentAlign, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
|
package/native/index.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ interface RadioGroupProps {
|
|
|
38
38
|
* short option list. Children are distributed round-robin so the visual
|
|
39
39
|
* reading order stays left-to-right, then top-to-bottom.
|
|
40
40
|
*
|
|
41
|
+
* Every option in the row is stretched to the height of the tallest one, so
|
|
42
|
+
* cards whose descriptions wrap to a different number of lines still line
|
|
43
|
+
* up. Pair it with `RadioCard`'s `contentAlign="top"` when the text should
|
|
44
|
+
* stay pinned to the top of the stretched card.
|
|
45
|
+
*
|
|
41
46
|
* Takes precedence over `flexDirection`.
|
|
42
47
|
*/
|
|
43
48
|
columns?: number;
|
|
@@ -47,6 +52,22 @@ interface RadioGroupProps {
|
|
|
47
52
|
declare const RadioGroup: React.FC<RadioGroupProps>;
|
|
48
53
|
|
|
49
54
|
type RadioCardSize = "sm" | "md" | "lg" | "xl";
|
|
55
|
+
/**
|
|
56
|
+
* Cross-axis placement of the card's text block.
|
|
57
|
+
*
|
|
58
|
+
* The radio indicator is *not* covered by this — it stays vertically centred
|
|
59
|
+
* in the card either way, which is the split a single `alignItems` on the
|
|
60
|
+
* card's row cannot express.
|
|
61
|
+
*
|
|
62
|
+
* - `center` — title and description centred against the indicator. The
|
|
63
|
+
* historical behaviour, and indistinguishable from `top` on a card that is
|
|
64
|
+
* only as tall as its own content.
|
|
65
|
+
* - `top` — title and description pinned to the top edge of the card's
|
|
66
|
+
* content box. Use this when the card is stretched taller than its own
|
|
67
|
+
* content, e.g. inside a `RadioGroup` with `columns > 1`, where every card
|
|
68
|
+
* in a row is equalised to the tallest one.
|
|
69
|
+
*/
|
|
70
|
+
type RadioCardContentAlign = "center" | "top";
|
|
50
71
|
interface RadioCardProps extends ThemeOverrideProps {
|
|
51
72
|
/** Value this card selects in the parent `RadioGroup`. */
|
|
52
73
|
value: string;
|
|
@@ -65,6 +86,13 @@ interface RadioCardProps extends ThemeOverrideProps {
|
|
|
65
86
|
* `padding` accepts.
|
|
66
87
|
*/
|
|
67
88
|
padding?: number | string;
|
|
89
|
+
/**
|
|
90
|
+
* Where the title/description block sits when the card is taller than its
|
|
91
|
+
* own content. The radio indicator stays vertically centred regardless.
|
|
92
|
+
*
|
|
93
|
+
* @default "center"
|
|
94
|
+
*/
|
|
95
|
+
contentAlign?: RadioCardContentAlign;
|
|
68
96
|
/** Called after the card is selected, in addition to the group's `onChange`. */
|
|
69
97
|
onPress?: () => void;
|
|
70
98
|
/** Accessible label, when `title` is not descriptive enough on its own. */
|
|
@@ -88,4 +116,4 @@ interface RadioCardProps extends ThemeOverrideProps {
|
|
|
88
116
|
*/
|
|
89
117
|
declare const RadioCard: React.FC<RadioCardProps>;
|
|
90
118
|
|
|
91
|
-
export { RadioCard, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
|
|
119
|
+
export { RadioCard, type RadioCardContentAlign, type RadioCardProps, RadioGroup, type RadioGroupContextProps, type RadioGroupProps, type RadioGroupView, useRadioGroup };
|
package/native/index.js
CHANGED
|
@@ -74,6 +74,7 @@ var toGapValue = (gap) => {
|
|
|
74
74
|
const asNumber = Number(gap);
|
|
75
75
|
return Number.isFinite(asNumber) ? asNumber : void 0;
|
|
76
76
|
};
|
|
77
|
+
var toNativeDisplay = (display) => display === "none" ? "none" : void 0;
|
|
77
78
|
var Box = ({
|
|
78
79
|
children,
|
|
79
80
|
onPress,
|
|
@@ -105,6 +106,7 @@ var Box = ({
|
|
|
105
106
|
marginBottom,
|
|
106
107
|
marginLeft,
|
|
107
108
|
marginRight,
|
|
109
|
+
display,
|
|
108
110
|
flexDirection,
|
|
109
111
|
flexWrap,
|
|
110
112
|
alignItems,
|
|
@@ -168,6 +170,7 @@ var Box = ({
|
|
|
168
170
|
marginBottom,
|
|
169
171
|
marginLeft,
|
|
170
172
|
marginRight,
|
|
173
|
+
display: toNativeDisplay(display),
|
|
171
174
|
flexDirection,
|
|
172
175
|
flexWrap,
|
|
173
176
|
alignItems,
|
|
@@ -342,7 +345,18 @@ var Text = ({
|
|
|
342
345
|
};
|
|
343
346
|
|
|
344
347
|
// src/RadioGroup.tsx
|
|
345
|
-
var import_jsx_runtime3 =
|
|
348
|
+
var import_jsx_runtime3 = (
|
|
349
|
+
// One slot per option. The column's main axis is vertical, so
|
|
350
|
+
// an option does not grow into the column's spare height by
|
|
351
|
+
// itself — the slot claims that space (`flexGrow`) and then
|
|
352
|
+
// hands it to the option, which stretches across the slot's
|
|
353
|
+
// cross axis because the slot is a row.
|
|
354
|
+
//
|
|
355
|
+
// `flexBasis: auto` rather than the `flex: 1` shorthand's `0`:
|
|
356
|
+
// a slot never starts smaller than its own content, so a tall
|
|
357
|
+
// option is never squashed to an equal share.
|
|
358
|
+
require("react/jsx-runtime")
|
|
359
|
+
);
|
|
346
360
|
var RadioGroupContext = (0, import_react.createContext)({});
|
|
347
361
|
var useRadioGroup = () => (0, import_react.useContext)(RadioGroupContext);
|
|
348
362
|
var DEFAULT_GAP = 12;
|
|
@@ -383,7 +397,7 @@ var RadioGroup = ({
|
|
|
383
397
|
role: "radiogroup",
|
|
384
398
|
"aria-disabled": disabled || void 0,
|
|
385
399
|
flexDirection: "row",
|
|
386
|
-
alignItems: "
|
|
400
|
+
alignItems: "stretch",
|
|
387
401
|
gap: resolvedGap,
|
|
388
402
|
children: columnBuckets.map((bucket, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
389
403
|
Box,
|
|
@@ -392,7 +406,15 @@ var RadioGroup = ({
|
|
|
392
406
|
gap: resolvedGap,
|
|
393
407
|
flex: 1,
|
|
394
408
|
style: { minWidth: 0 },
|
|
395
|
-
children: bucket
|
|
409
|
+
children: bucket.map((child, childIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
410
|
+
Box,
|
|
411
|
+
{
|
|
412
|
+
flexDirection: "row",
|
|
413
|
+
style: { flexGrow: 1, flexBasis: "auto", minWidth: 0 },
|
|
414
|
+
children: child
|
|
415
|
+
},
|
|
416
|
+
childIndex
|
|
417
|
+
))
|
|
396
418
|
},
|
|
397
419
|
index
|
|
398
420
|
))
|
|
@@ -430,6 +452,7 @@ var RadioCard = ({
|
|
|
430
452
|
checked: propChecked,
|
|
431
453
|
disabled = false,
|
|
432
454
|
padding,
|
|
455
|
+
contentAlign = "center",
|
|
433
456
|
onPress,
|
|
434
457
|
"aria-label": ariaLabel,
|
|
435
458
|
testID,
|
|
@@ -502,6 +525,7 @@ var RadioCard = ({
|
|
|
502
525
|
{
|
|
503
526
|
flexDirection: "column",
|
|
504
527
|
gap: sizing.textGap,
|
|
528
|
+
alignSelf: contentAlign === "top" ? "flex-start" : void 0,
|
|
505
529
|
style: { flex: 1, minWidth: 0 },
|
|
506
530
|
children: [
|
|
507
531
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
package/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.tsx","../../src/RadioGroup.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/RadioCard.tsx"],"sourcesContent":["export * from \"./RadioGroup\";\nexport * from \"./RadioCard\";\n","import React, { createContext, useContext, useMemo } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\n\n/**\n * Visual treatment of the group's options.\n *\n * - `default` — bare `Radio` rows, the historical behaviour.\n * - `card` — each option is a selectable card (`RadioCard`) with a title, a\n * description and a radio indicator, where the whole card is the click\n * target.\n */\nexport type RadioGroupView = \"default\" | \"card\";\n\nexport interface RadioGroupContextProps {\n value?: string;\n onChange?: (value: string) => void;\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n disabled?: boolean;\n view?: RadioGroupView;\n testID?: string;\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextProps>({});\n\nexport const useRadioGroup = () => useContext(RadioGroupContext);\n\nexport interface RadioGroupProps {\n value?: string;\n onChange?: (value: string) => void;\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n disabled?: boolean;\n children: React.ReactNode;\n flexDirection?: \"row\" | \"column\";\n gap?: number;\n /**\n * Visual treatment of the options. `card` renders `RadioCard` children as\n * selectable cards; it does not change the selection semantics.\n */\n view?: RadioGroupView;\n /**\n * Number of side-by-side columns. Values above `1` lay the options out as a\n * row of equal-width columns, which is how the Pentagram screens present a\n * short option list. Children are distributed round-robin so the visual\n * reading order stays left-to-right, then top-to-bottom.\n *\n * Takes precedence over `flexDirection`.\n */\n columns?: number;\n /** Test ID for testing frameworks */\n testID?: string;\n}\n\n/** Gap between bare `Radio` rows. */\nconst DEFAULT_GAP = 12;\n/**\n * Gap between cards. Matches the `itemSpacing` bound on the Figma \"Row\" /\n * \"Column\" frames of the distribution-model picker (node 5725:44606).\n */\nconst CARD_GAP = 4;\n\nexport const RadioGroup: React.FC<RadioGroupProps> = ({\n value,\n onChange,\n size = \"md\",\n disabled = false,\n children,\n flexDirection = \"column\",\n gap,\n view = \"default\",\n columns = 1,\n testID,\n}) => {\n const resolvedGap = gap ?? (view === \"card\" ? CARD_GAP : DEFAULT_GAP);\n\n // Distribute the children across `columns` equal-width columns. Round-robin\n // (rather than chunking) keeps the reading order row-major, so option 2 sits\n // to the right of option 1 instead of below it.\n const columnBuckets = useMemo(() => {\n if (columns <= 1) return null;\n const items = React.Children.toArray(children);\n const buckets: React.ReactNode[][] = Array.from(\n { length: columns },\n () => []\n );\n items.forEach((child, index) => {\n buckets[index % columns].push(child);\n });\n return buckets;\n }, [children, columns]);\n\n return (\n <RadioGroupContext.Provider\n value={{ value, onChange, size, disabled, view }}\n >\n {columnBuckets ? (\n <Box\n testID={testID}\n role=\"radiogroup\"\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n alignItems=\"flex-start\"\n gap={resolvedGap}\n >\n {columnBuckets.map((bucket, index) => (\n <Box\n key={index}\n flexDirection=\"column\"\n gap={resolvedGap}\n flex={1}\n style={{ minWidth: 0 }}\n >\n {bucket}\n </Box>\n ))}\n </Box>\n ) : (\n <Box\n testID={testID}\n role=\"radiogroup\"\n aria-disabled={disabled || undefined}\n flexDirection={flexDirection}\n gap={resolvedGap}\n >\n {children}\n </Box>\n )}\n </RadioGroupContext.Provider>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport {\n useResolvedTheme,\n radiusScale,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { useRadioGroup } from \"./RadioGroup\";\n\ntype RadioCardSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n/**\n * Comfortable internal padding per size, so a card never renders its content\n * edge-to-edge out of the box. Derived from the Pentagram distribution-model\n * picker (Figma node 5725:44606); override with the `padding` prop.\n */\nconst CARD_PADDING: Record<\n RadioCardSize,\n { vertical: number; horizontal: number }\n> = {\n sm: { vertical: 8, horizontal: 12 },\n md: { vertical: 12, horizontal: 16 },\n lg: { vertical: 16, horizontal: 16 },\n xl: { vertical: 16, horizontal: 20 },\n};\n\n/** Gap between the text block and the radio indicator. */\nconst CONTENT_GAP = 12;\n\nexport interface RadioCardProps extends ThemeOverrideProps {\n /** Value this card selects in the parent `RadioGroup`. */\n value: string;\n /** Primary line of the card. */\n title: string;\n /** Supporting line rendered under the title. */\n description?: string;\n /** Size of the card and its radio indicator. Falls back to the group's size. */\n size?: RadioCardSize;\n /** Selected state. Normally derived from the parent `RadioGroup`. */\n checked?: boolean;\n /** Disables this card only. The group's `disabled` disables every card. */\n disabled?: boolean;\n /**\n * Overrides the size-derived internal padding. Accepts anything `Box`'s\n * `padding` accepts.\n */\n padding?: number | string;\n /** Called after the card is selected, in addition to the group's `onChange`. */\n onPress?: () => void;\n /** Accessible label, when `title` is not descriptive enough on its own. */\n \"aria-label\"?: string;\n /** Test ID for testing frameworks */\n testID?: string;\n}\n\n/**\n * A selectable card option for `RadioGroup`.\n *\n * The whole card is the click target — not just the radio dot — and the\n * selected card is filled with the brand-tinted control surface. Compose them\n * inside a `RadioGroup` exactly like `Radio`:\n *\n * ```tsx\n * <RadioGroup view=\"card\" value={value} onChange={setValue}>\n * <RadioCard value=\"f2p\" title=\"Free-to-play (F2P)\" description=\"...\" />\n * <RadioCard value=\"p2p\" title=\"Pay-to-play (P2P)\" description=\"...\" />\n * </RadioGroup>\n * ```\n */\nexport const RadioCard: React.FC<RadioCardProps> = ({\n value,\n title,\n description,\n size: propSize,\n checked: propChecked,\n disabled = false,\n padding,\n onPress,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const radioGroup = useRadioGroup();\n\n const size: RadioCardSize = propSize || radioGroup.size || \"md\";\n const checked =\n propChecked !== undefined ? propChecked : radioGroup.value === value;\n const isDisabled = disabled || Boolean(radioGroup.disabled);\n\n const sizing = theme.sizing.radio(size);\n const checkColors = theme.colors.control.check;\n const faintColors = theme.colors.control.faint;\n const textColors = theme.colors.control.text;\n const brandSurface = theme.colors.control.brand.secondary;\n const spacing = CARD_PADDING[size];\n\n const surfaceColor = checked\n ? brandSurface.bg\n : theme.colors.background.secondary;\n\n // Hover is an overlay laid over the resting fill, not a swap to a different\n // surface colour — a plain `backgroundColor` would replace the brand tint on\n // the selected card. Compose both layers the way Cell's surface view does.\n const overlay = theme.colors.overlay.mono;\n const hoverStyle = isDisabled\n ? undefined\n : {\n background: `linear-gradient(0deg, ${overlay} 0%, ${overlay} 100%), ${surfaceColor}`,\n };\n\n const handlePress = () => {\n if (isDisabled) return;\n if (radioGroup.onChange) {\n radioGroup.onChange(value);\n }\n if (onPress) {\n onPress();\n }\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault(); // Space would otherwise scroll the page\n handlePress();\n }\n };\n\n const indicatorBg = isDisabled\n ? checkColors.bgDisable\n : checked\n ? checkColors.bg\n : faintColors.bg;\n\n const indicatorBorder = isDisabled\n ? checkColors.borderDisable\n : checked\n ? checkColors.border\n : faintColors.border;\n\n const dotColor = isDisabled ? textColors.disable : checkColors.icon;\n const titleColor = isDisabled ? textColors.disable : textColors.primary;\n const descriptionColor = isDisabled\n ? textColors.disable\n : theme.colors.content.tertiary;\n\n return (\n <Box\n testID={testID}\n role=\"radio\"\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel}\n tabIndex={isDisabled ? -1 : 0}\n onPress={handlePress}\n onKeyDown={handleKeyDown}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"space-between\"\n gap={CONTENT_GAP}\n width=\"100%\"\n backgroundColor={surfaceColor}\n borderRadius={theme.shape.cell.borderRadius}\n padding={padding}\n paddingVertical={padding === undefined ? spacing.vertical : undefined}\n paddingHorizontal={padding === undefined ? spacing.horizontal : undefined}\n hoverStyle={hoverStyle}\n >\n <Box\n flexDirection=\"column\"\n gap={sizing.textGap}\n style={{ flex: 1, minWidth: 0 }}\n >\n <Text\n color={titleColor}\n fontSize={sizing.fontSize}\n lineHeight={sizing.lineHeight}\n fontWeight=\"400\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={descriptionColor}\n fontSize={sizing.descriptionFontSize}\n lineHeight={sizing.descriptionLineHeight}\n >\n {description}\n </Text>\n )}\n </Box>\n\n {/*\n The indicator is presentational: the card itself carries `role=\"radio\"`\n and the press handler, so nesting an interactive `Radio` here would\n duplicate the radio role and its tab stop.\n */}\n <Box\n width={sizing.size}\n height={sizing.size}\n backgroundColor={indicatorBg}\n borderColor={indicatorBorder}\n borderWidth={sizing.borderWidth}\n borderRadius={radiusScale[\"999\"]}\n alignItems=\"center\"\n justifyContent=\"center\"\n style={{ flexShrink: 0 }}\n >\n {checked && (\n <Box\n width={sizing.iconSize}\n height={sizing.iconSize}\n backgroundColor={dotColor}\n borderRadius={radiusScale[\"999\"]}\n />\n )}\n </Box>\n </Box>\n );\n};\n"],"mappings":"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","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.tsx","../../src/RadioGroup.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/RadioCard.tsx"],"sourcesContent":["export * from \"./RadioGroup\";\nexport * from \"./RadioCard\";\n","import React, { createContext, useContext, useMemo } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\n\n/**\n * Visual treatment of the group's options.\n *\n * - `default` — bare `Radio` rows, the historical behaviour.\n * - `card` — each option is a selectable card (`RadioCard`) with a title, a\n * description and a radio indicator, where the whole card is the click\n * target.\n */\nexport type RadioGroupView = \"default\" | \"card\";\n\nexport interface RadioGroupContextProps {\n value?: string;\n onChange?: (value: string) => void;\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n disabled?: boolean;\n view?: RadioGroupView;\n testID?: string;\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextProps>({});\n\nexport const useRadioGroup = () => useContext(RadioGroupContext);\n\nexport interface RadioGroupProps {\n value?: string;\n onChange?: (value: string) => void;\n size?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n disabled?: boolean;\n children: React.ReactNode;\n flexDirection?: \"row\" | \"column\";\n gap?: number;\n /**\n * Visual treatment of the options. `card` renders `RadioCard` children as\n * selectable cards; it does not change the selection semantics.\n */\n view?: RadioGroupView;\n /**\n * Number of side-by-side columns. Values above `1` lay the options out as a\n * row of equal-width columns, which is how the Pentagram screens present a\n * short option list. Children are distributed round-robin so the visual\n * reading order stays left-to-right, then top-to-bottom.\n *\n * Every option in the row is stretched to the height of the tallest one, so\n * cards whose descriptions wrap to a different number of lines still line\n * up. Pair it with `RadioCard`'s `contentAlign=\"top\"` when the text should\n * stay pinned to the top of the stretched card.\n *\n * Takes precedence over `flexDirection`.\n */\n columns?: number;\n /** Test ID for testing frameworks */\n testID?: string;\n}\n\n/** Gap between bare `Radio` rows. */\nconst DEFAULT_GAP = 12;\n/**\n * Gap between cards. Matches the `itemSpacing` bound on the Figma \"Row\" /\n * \"Column\" frames of the distribution-model picker (node 5725:44606).\n */\nconst CARD_GAP = 4;\n\nexport const RadioGroup: React.FC<RadioGroupProps> = ({\n value,\n onChange,\n size = \"md\",\n disabled = false,\n children,\n flexDirection = \"column\",\n gap,\n view = \"default\",\n columns = 1,\n testID,\n}) => {\n const resolvedGap = gap ?? (view === \"card\" ? CARD_GAP : DEFAULT_GAP);\n\n // Distribute the children across `columns` equal-width columns. Round-robin\n // (rather than chunking) keeps the reading order row-major, so option 2 sits\n // to the right of option 1 instead of below it.\n const columnBuckets = useMemo(() => {\n if (columns <= 1) return null;\n const items = React.Children.toArray(children);\n const buckets: React.ReactNode[][] = Array.from(\n { length: columns },\n () => []\n );\n items.forEach((child, index) => {\n buckets[index % columns].push(child);\n });\n return buckets;\n }, [children, columns]);\n\n return (\n <RadioGroupContext.Provider\n value={{ value, onChange, size, disabled, view }}\n >\n {columnBuckets ? (\n <Box\n testID={testID}\n role=\"radiogroup\"\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n // `stretch` (not `flex-start`) is what makes every column as tall as\n // the tallest one. On its own that only stretches the column — the\n // option inside it is stretched by its slot wrapper below.\n alignItems=\"stretch\"\n gap={resolvedGap}\n >\n {columnBuckets.map((bucket, index) => (\n <Box\n key={index}\n flexDirection=\"column\"\n gap={resolvedGap}\n flex={1}\n style={{ minWidth: 0 }}\n >\n {bucket.map((child, childIndex) => (\n // One slot per option. The column's main axis is vertical, so\n // an option does not grow into the column's spare height by\n // itself — the slot claims that space (`flexGrow`) and then\n // hands it to the option, which stretches across the slot's\n // cross axis because the slot is a row.\n //\n // `flexBasis: auto` rather than the `flex: 1` shorthand's `0`:\n // a slot never starts smaller than its own content, so a tall\n // option is never squashed to an equal share.\n <Box\n key={childIndex}\n flexDirection=\"row\"\n style={{ flexGrow: 1, flexBasis: \"auto\", minWidth: 0 }}\n >\n {child}\n </Box>\n ))}\n </Box>\n ))}\n </Box>\n ) : (\n <Box\n testID={testID}\n role=\"radiogroup\"\n aria-disabled={disabled || undefined}\n flexDirection={flexDirection}\n gap={resolvedGap}\n >\n {children}\n </Box>\n )}\n </RadioGroupContext.Provider>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport {\n useResolvedTheme,\n radiusScale,\n type ThemeOverrideProps,\n} from \"@xsolla/xui-core\";\nimport { useRadioGroup } from \"./RadioGroup\";\n\ntype RadioCardSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n/**\n * Cross-axis placement of the card's text block.\n *\n * The radio indicator is *not* covered by this — it stays vertically centred\n * in the card either way, which is the split a single `alignItems` on the\n * card's row cannot express.\n *\n * - `center` — title and description centred against the indicator. The\n * historical behaviour, and indistinguishable from `top` on a card that is\n * only as tall as its own content.\n * - `top` — title and description pinned to the top edge of the card's\n * content box. Use this when the card is stretched taller than its own\n * content, e.g. inside a `RadioGroup` with `columns > 1`, where every card\n * in a row is equalised to the tallest one.\n */\nexport type RadioCardContentAlign = \"center\" | \"top\";\n\n/**\n * Comfortable internal padding per size, so a card never renders its content\n * edge-to-edge out of the box. Derived from the Pentagram distribution-model\n * picker (Figma node 5725:44606); override with the `padding` prop.\n */\nconst CARD_PADDING: Record<\n RadioCardSize,\n { vertical: number; horizontal: number }\n> = {\n sm: { vertical: 8, horizontal: 12 },\n md: { vertical: 12, horizontal: 16 },\n lg: { vertical: 16, horizontal: 16 },\n xl: { vertical: 16, horizontal: 20 },\n};\n\n/** Gap between the text block and the radio indicator. */\nconst CONTENT_GAP = 12;\n\nexport interface RadioCardProps extends ThemeOverrideProps {\n /** Value this card selects in the parent `RadioGroup`. */\n value: string;\n /** Primary line of the card. */\n title: string;\n /** Supporting line rendered under the title. */\n description?: string;\n /** Size of the card and its radio indicator. Falls back to the group's size. */\n size?: RadioCardSize;\n /** Selected state. Normally derived from the parent `RadioGroup`. */\n checked?: boolean;\n /** Disables this card only. The group's `disabled` disables every card. */\n disabled?: boolean;\n /**\n * Overrides the size-derived internal padding. Accepts anything `Box`'s\n * `padding` accepts.\n */\n padding?: number | string;\n /**\n * Where the title/description block sits when the card is taller than its\n * own content. The radio indicator stays vertically centred regardless.\n *\n * @default \"center\"\n */\n contentAlign?: RadioCardContentAlign;\n /** Called after the card is selected, in addition to the group's `onChange`. */\n onPress?: () => void;\n /** Accessible label, when `title` is not descriptive enough on its own. */\n \"aria-label\"?: string;\n /** Test ID for testing frameworks */\n testID?: string;\n}\n\n/**\n * A selectable card option for `RadioGroup`.\n *\n * The whole card is the click target — not just the radio dot — and the\n * selected card is filled with the brand-tinted control surface. Compose them\n * inside a `RadioGroup` exactly like `Radio`:\n *\n * ```tsx\n * <RadioGroup view=\"card\" value={value} onChange={setValue}>\n * <RadioCard value=\"f2p\" title=\"Free-to-play (F2P)\" description=\"...\" />\n * <RadioCard value=\"p2p\" title=\"Pay-to-play (P2P)\" description=\"...\" />\n * </RadioGroup>\n * ```\n */\nexport const RadioCard: React.FC<RadioCardProps> = ({\n value,\n title,\n description,\n size: propSize,\n checked: propChecked,\n disabled = false,\n padding,\n contentAlign = \"center\",\n onPress,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const radioGroup = useRadioGroup();\n\n const size: RadioCardSize = propSize || radioGroup.size || \"md\";\n const checked =\n propChecked !== undefined ? propChecked : radioGroup.value === value;\n const isDisabled = disabled || Boolean(radioGroup.disabled);\n\n const sizing = theme.sizing.radio(size);\n const checkColors = theme.colors.control.check;\n const faintColors = theme.colors.control.faint;\n const textColors = theme.colors.control.text;\n const brandSurface = theme.colors.control.brand.secondary;\n const spacing = CARD_PADDING[size];\n\n const surfaceColor = checked\n ? brandSurface.bg\n : theme.colors.background.secondary;\n\n // Hover is an overlay laid over the resting fill, not a swap to a different\n // surface colour — a plain `backgroundColor` would replace the brand tint on\n // the selected card. Compose both layers the way Cell's surface view does.\n const overlay = theme.colors.overlay.mono;\n const hoverStyle = isDisabled\n ? undefined\n : {\n background: `linear-gradient(0deg, ${overlay} 0%, ${overlay} 100%), ${surfaceColor}`,\n };\n\n const handlePress = () => {\n if (isDisabled) return;\n if (radioGroup.onChange) {\n radioGroup.onChange(value);\n }\n if (onPress) {\n onPress();\n }\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault(); // Space would otherwise scroll the page\n handlePress();\n }\n };\n\n const indicatorBg = isDisabled\n ? checkColors.bgDisable\n : checked\n ? checkColors.bg\n : faintColors.bg;\n\n const indicatorBorder = isDisabled\n ? checkColors.borderDisable\n : checked\n ? checkColors.border\n : faintColors.border;\n\n const dotColor = isDisabled ? textColors.disable : checkColors.icon;\n const titleColor = isDisabled ? textColors.disable : textColors.primary;\n const descriptionColor = isDisabled\n ? textColors.disable\n : theme.colors.content.tertiary;\n\n return (\n <Box\n testID={testID}\n role=\"radio\"\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel}\n tabIndex={isDisabled ? -1 : 0}\n onPress={handlePress}\n onKeyDown={handleKeyDown}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n flexDirection=\"row\"\n // Keeps the indicator centred. The text block opts out of this via its\n // own `alignSelf` below, which is what lets the two be aligned\n // independently.\n alignItems=\"center\"\n justifyContent=\"space-between\"\n gap={CONTENT_GAP}\n width=\"100%\"\n backgroundColor={surfaceColor}\n borderRadius={theme.shape.cell.borderRadius}\n padding={padding}\n paddingVertical={padding === undefined ? spacing.vertical : undefined}\n paddingHorizontal={padding === undefined ? spacing.horizontal : undefined}\n hoverStyle={hoverStyle}\n >\n <Box\n flexDirection=\"column\"\n gap={sizing.textGap}\n alignSelf={contentAlign === \"top\" ? \"flex-start\" : undefined}\n style={{ flex: 1, minWidth: 0 }}\n >\n <Text\n color={titleColor}\n fontSize={sizing.fontSize}\n lineHeight={sizing.lineHeight}\n fontWeight=\"400\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={descriptionColor}\n fontSize={sizing.descriptionFontSize}\n lineHeight={sizing.descriptionLineHeight}\n >\n {description}\n </Text>\n )}\n </Box>\n\n {/*\n The indicator is presentational: the card itself carries `role=\"radio\"`\n and the press handler, so nesting an interactive `Radio` here would\n duplicate the radio role and its tab stop.\n */}\n <Box\n width={sizing.size}\n height={sizing.size}\n backgroundColor={indicatorBg}\n borderColor={indicatorBorder}\n borderWidth={sizing.borderWidth}\n borderRadius={radiusScale[\"999\"]}\n alignItems=\"center\"\n justifyContent=\"center\"\n style={{ flexShrink: 0 }}\n >\n {checked && (\n <Box\n width={sizing.iconSize}\n height={sizing.iconSize}\n backgroundColor={dotColor}\n borderRadius={radiusScale[\"999\"]}\n />\n )}\n </Box>\n </Box>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA0D;;;ACC1D,0BASO;AAqND;AAlNN,IAAM,0BAA6D;AAAA,EACjE,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,aAAa,CAAC,QAAyD;AAC3E,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,sBAAsB,KAAK,GAAG;AAC9C,MAAI,QAAS,QAAO,OAAO,QAAQ,CAAC,CAAC;AACrC,QAAM,WAAW,OAAO,GAAG;AAC3B,SAAO,OAAO,SAAS,QAAQ,IAAI,WAAW;AAChD;AASA,IAAM,kBAAkB,CACtB,YAC0B,YAAY,SAAS,SAAS;AAEnD,IAAM,MAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,oBAAoB,CAAC,aAAkC;AAAA,IAC3D,iBACE,WAAW,YAAY,kBACnB,WAAW,kBACX;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,YAAY,WAAW,MAAM;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,OAAO;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,WAAW,GAAG;AAAA,IACnB,GAAI;AAAA,EACN;AAEA,QAAM,cAAc,cAAc;AAKlC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,aAAa;AAAA,IACb;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,oBACH,OAAO,SAAS,YAAY,wBAAwB,IAAI,MACxD,OAAO,WAAW,WAAW;AAChC,QAAM,aAAa,QAAQ,YAAY,YAAY;AACnD,QAAM,qBAAqB;AAAA,IACzB,UAAU,cAAc;AAAA,IACxB,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,0BACJ,aAAa,YAAY,aAAa,cAAc,WAAW;AAGjE,MAAI,OAAO,SAAS,KAAK;AACvB,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI;AAAA,IACN;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,QAChE,YAAW;AAAA,QACV,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AAIA,MAAI,WAAW,OAAO,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,CAAC,cAAc,UAAU,UAAU;AAAA,QAC5C,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,CAAC,EAAE,QAAQ,MAAM,kBAAkB,OAAO;AAAA,QACjD,QAAQ;AAAA,QACR;AAAA,QACA,oBACE,OAAO,cAAc,WAAW,YAAY;AAAA,QAE9C;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,kBAAkB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,MAChE;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;;;ACxRA,IAAAA,uBAKO;AAqEH,IAAAC,sBAAA;AAlEJ,IAAM,UAA6C;AAAA,EACjD,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AACR;AAEA,IAAM,oBAAoB,CACxB,UACuB;AACvB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,WAAW,KAAK;AAC/B,SAAO,MAAM,MAAM,IAAI,SAAY;AACrC;AAEO,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,GAAG;AACL,MAAM;AACJ,MAAI,qBAAqB,aACrB,WAAW,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,SAAS,EAAE,EAAE,KAAK,IACnD;AAEJ,MACE,uBAAuB,gBACvB,uBAAuB,qBACvB,uBAAuB,iBACvB;AACA,yBAAqB;AAAA,EACvB;AAEA,QAAM,gBAAgB,gCAAW,QAAQ,SAAS;AAElD,QAAM,YAAuB;AAAA,IAC3B,OAAO,SAAS,eAAe;AAAA,IAC/B,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,IACpD;AAAA,IACA,YAAY;AAAA,IACZ,oBAAoB,MAAM;AAAA,IAC1B,WAAW,aAAa,eAAe;AAAA,IACvC,YAAY,kBAAkB,cAAc,eAAe,UAAU;AAAA,IACrE,WAAW;AAAA,MACT,eAAe;AAAA,IACjB;AAAA,IACA,cAAc;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,oBAAoB,OAAO,QAAQ,IAAI,IAAI;AAEjD,SACE;AAAA,IAAC,qBAAAC;AAAA,IAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA,QAAQ,cAAc,UAAU;AAAA,MAChC;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;AF8CgB,IAAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA;AAAA;AA3GhB,IAAM,wBAAoB,4BAAsC,CAAC,CAAC;AAE3D,IAAM,gBAAgB,UAAM,yBAAW,iBAAiB;AAkC/D,IAAM,cAAc;AAKpB,IAAM,WAAW;AAEV,IAAM,aAAwC,CAAC;AAAA,EACpD;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,WAAW;AAAA,EACX;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AACF,MAAM;AACJ,QAAM,cAAc,QAAQ,SAAS,SAAS,WAAW;AAKzD,QAAM,oBAAgB,sBAAQ,MAAM;AAClC,QAAI,WAAW,EAAG,QAAO;AACzB,UAAM,QAAQ,aAAAC,QAAM,SAAS,QAAQ,QAAQ;AAC7C,UAAM,UAA+B,MAAM;AAAA,MACzC,EAAE,QAAQ,QAAQ;AAAA,MAClB,MAAM,CAAC;AAAA,IACT;AACA,UAAM,QAAQ,CAAC,OAAO,UAAU;AAC9B,cAAQ,QAAQ,OAAO,EAAE,KAAK,KAAK;AAAA,IACrC,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,SACE;AAAA,IAAC,kBAAkB;AAAA,IAAlB;AAAA,MACC,OAAO,EAAE,OAAO,UAAU,MAAM,UAAU,KAAK;AAAA,MAE9C,0BACC;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,MAAK;AAAA,UACL,iBAAe,YAAY;AAAA,UAC3B,eAAc;AAAA,UAId,YAAW;AAAA,UACX,KAAK;AAAA,UAEJ,wBAAc,IAAI,CAAC,QAAQ,UAC1B;AAAA,YAAC;AAAA;AAAA,cAEC,eAAc;AAAA,cACd,KAAK;AAAA,cACL,MAAM;AAAA,cACN,OAAO,EAAE,UAAU,EAAE;AAAA,cAEpB,iBAAO,IAAI,CAAC,OAAO,eAUlB;AAAA,gBAAC;AAAA;AAAA,kBAEC,eAAc;AAAA,kBACd,OAAO,EAAE,UAAU,GAAG,WAAW,QAAQ,UAAU,EAAE;AAAA,kBAEpD;AAAA;AAAA,gBAJI;AAAA,cAKP,CACD;AAAA;AAAA,YAvBI;AAAA,UAwBP,CACD;AAAA;AAAA,MACH,IAEA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,MAAK;AAAA,UACL,iBAAe,YAAY;AAAA,UAC3B;AAAA,UACA,KAAK;AAAA,UAEJ;AAAA;AAAA,MACH;AAAA;AAAA,EAEJ;AAEJ;;;AGvJA,sBAIO;AAiMD,IAAAC,sBAAA;AAtKN,IAAM,eAGF;AAAA,EACF,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG;AAAA,EAClC,IAAI,EAAE,UAAU,IAAI,YAAY,GAAG;AAAA,EACnC,IAAI,EAAE,UAAU,IAAI,YAAY,GAAG;AAAA,EACnC,IAAI,EAAE,UAAU,IAAI,YAAY,GAAG;AACrC;AAGA,IAAM,cAAc;AAiDb,IAAM,YAAsC,CAAC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,QAAI,kCAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,aAAa,cAAc;AAEjC,QAAM,OAAsB,YAAY,WAAW,QAAQ;AAC3D,QAAM,UACJ,gBAAgB,SAAY,cAAc,WAAW,UAAU;AACjE,QAAM,aAAa,YAAY,QAAQ,WAAW,QAAQ;AAE1D,QAAM,SAAS,MAAM,OAAO,MAAM,IAAI;AACtC,QAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,QAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,QAAM,aAAa,MAAM,OAAO,QAAQ;AACxC,QAAM,eAAe,MAAM,OAAO,QAAQ,MAAM;AAChD,QAAM,UAAU,aAAa,IAAI;AAEjC,QAAM,eAAe,UACjB,aAAa,KACb,MAAM,OAAO,WAAW;AAK5B,QAAM,UAAU,MAAM,OAAO,QAAQ;AACrC,QAAM,aAAa,aACf,SACA;AAAA,IACE,YAAY,yBAAyB,OAAO,QAAQ,OAAO,WAAW,YAAY;AAAA,EACpF;AAEJ,QAAM,cAAc,MAAM;AACxB,QAAI,WAAY;AAChB,QAAI,WAAW,UAAU;AACvB,iBAAW,SAAS,KAAK;AAAA,IAC3B;AACA,QAAI,SAAS;AACX,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,UAA+B;AACpD,QAAI,WAAY;AAChB,QAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,SAAS;AAC9C,YAAM,eAAe;AACrB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,cAAc,aAChB,YAAY,YACZ,UACE,YAAY,KACZ,YAAY;AAElB,QAAM,kBAAkB,aACpB,YAAY,gBACZ,UACE,YAAY,SACZ,YAAY;AAElB,QAAM,WAAW,aAAa,WAAW,UAAU,YAAY;AAC/D,QAAM,aAAa,aAAa,WAAW,UAAU,WAAW;AAChE,QAAM,mBAAmB,aACrB,WAAW,UACX,MAAM,OAAO,QAAQ;AAEzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,gBAAc;AAAA,MACd,iBAAe,cAAc;AAAA,MAC7B,cAAY;AAAA,MACZ,UAAU,aAAa,KAAK;AAAA,MAC5B,SAAS;AAAA,MACT,WAAW;AAAA,MACX,QAAQ,aAAa,gBAAgB;AAAA,MACrC,eAAc;AAAA,MAId,YAAW;AAAA,MACX,gBAAe;AAAA,MACf,KAAK;AAAA,MACL,OAAM;AAAA,MACN,iBAAiB;AAAA,MACjB,cAAc,MAAM,MAAM,KAAK;AAAA,MAC/B;AAAA,MACA,iBAAiB,YAAY,SAAY,QAAQ,WAAW;AAAA,MAC5D,mBAAmB,YAAY,SAAY,QAAQ,aAAa;AAAA,MAChE;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,eAAc;AAAA,YACd,KAAK,OAAO;AAAA,YACZ,WAAW,iBAAiB,QAAQ,eAAe;AAAA,YACnD,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;AAAA,YAE9B;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,OAAO;AAAA,kBACjB,YAAY,OAAO;AAAA,kBACnB,YAAW;AAAA,kBAEV;AAAA;AAAA,cACH;AAAA,cACC,eACC;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,OAAO;AAAA,kBACjB,YAAY,OAAO;AAAA,kBAElB;AAAA;AAAA,cACH;AAAA;AAAA;AAAA,QAEJ;AAAA,QAOA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,OAAO;AAAA,YACd,QAAQ,OAAO;AAAA,YACf,iBAAiB;AAAA,YACjB,aAAa;AAAA,YACb,aAAa,OAAO;AAAA,YACpB,cAAc,4BAAY,KAAK;AAAA,YAC/B,YAAW;AAAA,YACX,gBAAe;AAAA,YACf,OAAO,EAAE,YAAY,EAAE;AAAA,YAEtB,qBACC;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO,OAAO;AAAA,gBACd,QAAQ,OAAO;AAAA,gBACf,iBAAiB;AAAA,gBACjB,cAAc,4BAAY,KAAK;AAAA;AAAA,YACjC;AAAA;AAAA,QAEJ;AAAA;AAAA;AAAA,EACF;AAEJ;","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React","import_jsx_runtime"]}
|
package/native/index.mjs
CHANGED
|
@@ -40,6 +40,7 @@ var toGapValue = (gap) => {
|
|
|
40
40
|
const asNumber = Number(gap);
|
|
41
41
|
return Number.isFinite(asNumber) ? asNumber : void 0;
|
|
42
42
|
};
|
|
43
|
+
var toNativeDisplay = (display) => display === "none" ? "none" : void 0;
|
|
43
44
|
var Box = ({
|
|
44
45
|
children,
|
|
45
46
|
onPress,
|
|
@@ -71,6 +72,7 @@ var Box = ({
|
|
|
71
72
|
marginBottom,
|
|
72
73
|
marginLeft,
|
|
73
74
|
marginRight,
|
|
75
|
+
display,
|
|
74
76
|
flexDirection,
|
|
75
77
|
flexWrap,
|
|
76
78
|
alignItems,
|
|
@@ -134,6 +136,7 @@ var Box = ({
|
|
|
134
136
|
marginBottom,
|
|
135
137
|
marginLeft,
|
|
136
138
|
marginRight,
|
|
139
|
+
display: toNativeDisplay(display),
|
|
137
140
|
flexDirection,
|
|
138
141
|
flexWrap,
|
|
139
142
|
alignItems,
|
|
@@ -352,7 +355,7 @@ var RadioGroup = ({
|
|
|
352
355
|
role: "radiogroup",
|
|
353
356
|
"aria-disabled": disabled || void 0,
|
|
354
357
|
flexDirection: "row",
|
|
355
|
-
alignItems: "
|
|
358
|
+
alignItems: "stretch",
|
|
356
359
|
gap: resolvedGap,
|
|
357
360
|
children: columnBuckets.map((bucket, index) => /* @__PURE__ */ jsx3(
|
|
358
361
|
Box,
|
|
@@ -361,7 +364,26 @@ var RadioGroup = ({
|
|
|
361
364
|
gap: resolvedGap,
|
|
362
365
|
flex: 1,
|
|
363
366
|
style: { minWidth: 0 },
|
|
364
|
-
children: bucket
|
|
367
|
+
children: bucket.map((child, childIndex) => (
|
|
368
|
+
// One slot per option. The column's main axis is vertical, so
|
|
369
|
+
// an option does not grow into the column's spare height by
|
|
370
|
+
// itself — the slot claims that space (`flexGrow`) and then
|
|
371
|
+
// hands it to the option, which stretches across the slot's
|
|
372
|
+
// cross axis because the slot is a row.
|
|
373
|
+
//
|
|
374
|
+
// `flexBasis: auto` rather than the `flex: 1` shorthand's `0`:
|
|
375
|
+
// a slot never starts smaller than its own content, so a tall
|
|
376
|
+
// option is never squashed to an equal share.
|
|
377
|
+
/* @__PURE__ */ jsx3(
|
|
378
|
+
Box,
|
|
379
|
+
{
|
|
380
|
+
flexDirection: "row",
|
|
381
|
+
style: { flexGrow: 1, flexBasis: "auto", minWidth: 0 },
|
|
382
|
+
children: child
|
|
383
|
+
},
|
|
384
|
+
childIndex
|
|
385
|
+
)
|
|
386
|
+
))
|
|
365
387
|
},
|
|
366
388
|
index
|
|
367
389
|
))
|
|
@@ -402,6 +424,7 @@ var RadioCard = ({
|
|
|
402
424
|
checked: propChecked,
|
|
403
425
|
disabled = false,
|
|
404
426
|
padding,
|
|
427
|
+
contentAlign = "center",
|
|
405
428
|
onPress,
|
|
406
429
|
"aria-label": ariaLabel,
|
|
407
430
|
testID,
|
|
@@ -474,6 +497,7 @@ var RadioCard = ({
|
|
|
474
497
|
{
|
|
475
498
|
flexDirection: "column",
|
|
476
499
|
gap: sizing.textGap,
|
|
500
|
+
alignSelf: contentAlign === "top" ? "flex-start" : void 0,
|
|
477
501
|
style: { flex: 1, minWidth: 0 },
|
|
478
502
|
children: [
|
|
479
503
|
/* @__PURE__ */ jsx4(
|