@xsolla/xui-toggle-button-group 0.216.1 → 0.218.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 +55 -1
- package/native/index.d.mts +30 -0
- package/native/index.d.ts +30 -0
- package/native/index.js +36 -25
- package/native/index.js.map +1 -1
- package/native/index.mjs +36 -25
- package/native/index.mjs.map +1 -1
- package/package.json +4 -4
- package/web/index.d.mts +30 -0
- package/web/index.d.ts +30 -0
- package/web/index.js +36 -25
- package/web/index.js.map +1 -1
- package/web/index.mjs +36 -25
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,8 @@ Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).
|
|
|
108
108
|
| `label` | `string` | Display text. Omit for icon-only items (set `aria-label` and `tooltip`). |
|
|
109
109
|
| `iconLeft` | `ReactNode` | Icon shown before the label. |
|
|
110
110
|
| `iconRight` | `ReactNode` | Icon shown after the label. |
|
|
111
|
-
| `disabled` | `boolean` | Disable the individual item. |
|
|
111
|
+
| `disabled` | `boolean` | Disable the individual item: inert, not focusable, painted with the disable palette. |
|
|
112
|
+
| `locked` | `boolean` | Mark the item as **selected and immutable**. Keeps the *active* palette (dimmed), never fires `onChange`, and stays keyboard-focusable so its `tooltip` is reachable. Takes precedence over `disabled`. See [Locked items](#locked-items). |
|
|
112
113
|
| `testID` | `string` | Applied as `data-testid` on the item's button root — one test id per option, for QA automation. |
|
|
113
114
|
| `aria-label` | `string` | Accessible label. Required when `label` is omitted. |
|
|
114
115
|
| `tooltip` | `Omit<TooltipProps, 'children'>` | `Tooltip` props for the button (web only). `placement` defaults to `"top"` (horizontal) or `"right"` (vertical); `size` defaults from `theme.sizing.toggleButtonGroup(size).tooltipSize`. |
|
|
@@ -143,6 +144,55 @@ const [values, setValues] = useState(['item1', 'item3']);
|
|
|
143
144
|
/>
|
|
144
145
|
```
|
|
145
146
|
|
|
147
|
+
### Locked items
|
|
148
|
+
|
|
149
|
+
An option that is switched on and can never be switched off — an irreversible
|
|
150
|
+
opt-in — is a `locked` item, not a `disabled` one. Typical cases: an operating
|
|
151
|
+
system a launcher already supports, a plan feature that cannot be dropped, a
|
|
152
|
+
mandatory permission scope, a region already launched.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
const [platforms, setPlatforms] = useState(['windows']);
|
|
156
|
+
|
|
157
|
+
<ToggleButtonGroup
|
|
158
|
+
multiple
|
|
159
|
+
items={[
|
|
160
|
+
{
|
|
161
|
+
id: 'windows',
|
|
162
|
+
label: 'Windows',
|
|
163
|
+
locked: true,
|
|
164
|
+
tooltip: { content: 'Windows is already live and cannot be removed' },
|
|
165
|
+
},
|
|
166
|
+
{ id: 'macos', label: 'MacOS' },
|
|
167
|
+
{ id: 'linux', label: 'Linux' },
|
|
168
|
+
]}
|
|
169
|
+
value={platforms}
|
|
170
|
+
onChange={(v) => setPlatforms(v as string[])}
|
|
171
|
+
aria-label="Operating systems"
|
|
172
|
+
/>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### `disabled` vs `locked`
|
|
176
|
+
|
|
177
|
+
| | `disabled` | `locked` |
|
|
178
|
+
| --- | --- | --- |
|
|
179
|
+
| Palette | disable tokens — a selected item loses its "on" signal | active tokens, dimmed — still reads as ON |
|
|
180
|
+
| Selected | Only if present in `value` | Always; the id need not be in `value` |
|
|
181
|
+
| Fires `onChange` | No | No |
|
|
182
|
+
| Native `disabled` attribute | Yes | No |
|
|
183
|
+
| Keyboard focusable | No (`tabIndex={-1}`) | Yes (`tabIndex={0}`) |
|
|
184
|
+
| `aria-disabled` | `true` | `true` |
|
|
185
|
+
| Tooltip reachable by keyboard | No | Yes |
|
|
186
|
+
|
|
187
|
+
Reach for `disabled` when the option is unavailable and the user has nothing to
|
|
188
|
+
learn from it; reach for `locked` when the option is on, will stay on, and the
|
|
189
|
+
user needs to understand why.
|
|
190
|
+
|
|
191
|
+
> **Design tokens.** The locked treatment is currently derived from the active
|
|
192
|
+
> palette plus an opacity step. When `bgActiveDisable`, `textActiveDisable` and
|
|
193
|
+
> `borderActiveDisable` are added to `theme.colors.control.toggleButton`, the
|
|
194
|
+
> component uses those values instead — no consumer change required.
|
|
195
|
+
|
|
146
196
|
### United appearance, full width
|
|
147
197
|
|
|
148
198
|
```tsx
|
|
@@ -199,3 +249,7 @@ import { BadgeDiscount, Crown } from '@xsolla/xui-icons-base';
|
|
|
199
249
|
- `role="radiogroup"` for single selection, `role="group"` for multiple selection.
|
|
200
250
|
- Arrow keys move between items; Enter/Space toggles selection.
|
|
201
251
|
- `aria-checked` and `aria-disabled` reflect item state.
|
|
252
|
+
- `locked` items follow the focusable-disabled pattern: `aria-checked="true"` +
|
|
253
|
+
`aria-disabled="true"` + `tabIndex={0}`, with no native `disabled` attribute.
|
|
254
|
+
They take part in arrow-key navigation and ignore Enter/Space, so a keyboard
|
|
255
|
+
user can focus the item and read its tooltip without being able to change it.
|
package/native/index.d.mts
CHANGED
|
@@ -18,6 +18,29 @@ interface ToggleButtonGroupItem {
|
|
|
18
18
|
iconRight?: React.ReactNode;
|
|
19
19
|
/** Whether the item is disabled */
|
|
20
20
|
disabled?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the item is **locked**: selected and immutable.
|
|
23
|
+
*
|
|
24
|
+
* Use for an irreversible opt-in — a capability that, once switched on, can
|
|
25
|
+
* never be switched off (an OS a launcher already supports, a plan feature
|
|
26
|
+
* that cannot be dropped, a mandatory permission scope, a region already
|
|
27
|
+
* launched).
|
|
28
|
+
*
|
|
29
|
+
* A locked item:
|
|
30
|
+
* - renders with the *active* palette, dimmed — so it still reads as ON,
|
|
31
|
+
* unlike `disabled`, which paints a selected item in the disable palette
|
|
32
|
+
* and loses the "on" signal entirely;
|
|
33
|
+
* - is implicitly active; it does not need to be present in `value`
|
|
34
|
+
* (although including it is harmless and recommended for clarity);
|
|
35
|
+
* - ignores clicks, Enter and Space, and never fires `onChange`;
|
|
36
|
+
* - stays keyboard-focusable (`aria-disabled="true"` + `tabIndex={0}`, no
|
|
37
|
+
* native `disabled` attribute), so a keyboard user can reach its
|
|
38
|
+
* `tooltip` and learn *why* it is locked. This is the focusable-disabled
|
|
39
|
+
* pattern, and is the main behavioural difference from `disabled`.
|
|
40
|
+
*
|
|
41
|
+
* Takes precedence over `disabled` when both are set.
|
|
42
|
+
*/
|
|
43
|
+
locked?: boolean;
|
|
21
44
|
/** Test ID applied as `data-testid` on the item's button root */
|
|
22
45
|
testID?: string;
|
|
23
46
|
/** Accessible label for screen readers */
|
|
@@ -62,6 +85,13 @@ interface ToggleButtonGroupProps extends ThemeOverrideProps {
|
|
|
62
85
|
* Used to pick one or several options from a linear set of closely related options.
|
|
63
86
|
* Can be used to filter or to sort elements.
|
|
64
87
|
*
|
|
88
|
+
* ## Item states
|
|
89
|
+
*
|
|
90
|
+
* - **Default / active** — selection driven by `value` / `defaultValue`.
|
|
91
|
+
* - **`disabled`** — inert and not focusable; painted with the disable palette.
|
|
92
|
+
* - **`locked`** — selected and immutable: painted with the *active* palette,
|
|
93
|
+
* dimmed, inert, but still keyboard-focusable so its `tooltip` is reachable.
|
|
94
|
+
*
|
|
65
95
|
* ## Accessibility Features
|
|
66
96
|
*
|
|
67
97
|
* - **Role**: Uses `role="radiogroup"` for single selection, `role="group"` for multiple
|
package/native/index.d.ts
CHANGED
|
@@ -18,6 +18,29 @@ interface ToggleButtonGroupItem {
|
|
|
18
18
|
iconRight?: React.ReactNode;
|
|
19
19
|
/** Whether the item is disabled */
|
|
20
20
|
disabled?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the item is **locked**: selected and immutable.
|
|
23
|
+
*
|
|
24
|
+
* Use for an irreversible opt-in — a capability that, once switched on, can
|
|
25
|
+
* never be switched off (an OS a launcher already supports, a plan feature
|
|
26
|
+
* that cannot be dropped, a mandatory permission scope, a region already
|
|
27
|
+
* launched).
|
|
28
|
+
*
|
|
29
|
+
* A locked item:
|
|
30
|
+
* - renders with the *active* palette, dimmed — so it still reads as ON,
|
|
31
|
+
* unlike `disabled`, which paints a selected item in the disable palette
|
|
32
|
+
* and loses the "on" signal entirely;
|
|
33
|
+
* - is implicitly active; it does not need to be present in `value`
|
|
34
|
+
* (although including it is harmless and recommended for clarity);
|
|
35
|
+
* - ignores clicks, Enter and Space, and never fires `onChange`;
|
|
36
|
+
* - stays keyboard-focusable (`aria-disabled="true"` + `tabIndex={0}`, no
|
|
37
|
+
* native `disabled` attribute), so a keyboard user can reach its
|
|
38
|
+
* `tooltip` and learn *why* it is locked. This is the focusable-disabled
|
|
39
|
+
* pattern, and is the main behavioural difference from `disabled`.
|
|
40
|
+
*
|
|
41
|
+
* Takes precedence over `disabled` when both are set.
|
|
42
|
+
*/
|
|
43
|
+
locked?: boolean;
|
|
21
44
|
/** Test ID applied as `data-testid` on the item's button root */
|
|
22
45
|
testID?: string;
|
|
23
46
|
/** Accessible label for screen readers */
|
|
@@ -62,6 +85,13 @@ interface ToggleButtonGroupProps extends ThemeOverrideProps {
|
|
|
62
85
|
* Used to pick one or several options from a linear set of closely related options.
|
|
63
86
|
* Can be used to filter or to sort elements.
|
|
64
87
|
*
|
|
88
|
+
* ## Item states
|
|
89
|
+
*
|
|
90
|
+
* - **Default / active** — selection driven by `value` / `defaultValue`.
|
|
91
|
+
* - **`disabled`** — inert and not focusable; painted with the disable palette.
|
|
92
|
+
* - **`locked`** — selected and immutable: painted with the *active* palette,
|
|
93
|
+
* dimmed, inert, but still keyboard-focusable so its `tooltip` is reachable.
|
|
94
|
+
*
|
|
65
95
|
* ## Accessibility Features
|
|
66
96
|
*
|
|
67
97
|
* - **Role**: Uses `role="radiogroup"` for single selection, `role="group"` for multiple
|
package/native/index.js
CHANGED
|
@@ -346,6 +346,11 @@ var isWeb = false;
|
|
|
346
346
|
var import_xui_core = require("@xsolla/xui-core");
|
|
347
347
|
var import_xui_tooltip = require("@xsolla/xui-tooltip");
|
|
348
348
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
349
|
+
var LOCKED_OPACITY = 0.6;
|
|
350
|
+
var isItemLocked = (item) => Boolean(item.locked);
|
|
351
|
+
var isItemDisabled = (item) => Boolean(item.disabled) && !isItemLocked(item);
|
|
352
|
+
var isItemInteractive = (item) => !isItemDisabled(item) && !isItemLocked(item);
|
|
353
|
+
var isItemFocusable = (item) => !isItemDisabled(item);
|
|
349
354
|
var cloneIconWithDefaults = (icon, defaultSize, defaultColor) => {
|
|
350
355
|
if (!import_react.default.isValidElement(icon)) return icon;
|
|
351
356
|
const iconElement = icon;
|
|
@@ -404,6 +409,11 @@ var ToggleButtonGroup = ({
|
|
|
404
409
|
const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
|
|
405
410
|
const sizeStyles = theme.sizing.toggleButtonGroup(size);
|
|
406
411
|
const itemRefs = (0, import_react.useRef)([]);
|
|
412
|
+
const toggleColors = theme.colors.control.toggleButton;
|
|
413
|
+
const lockedColors = toggleColors;
|
|
414
|
+
const hasLockedTokens = Boolean(
|
|
415
|
+
lockedColors.bgActiveDisable ?? lockedColors.textActiveDisable ?? lockedColors.borderActiveDisable
|
|
416
|
+
);
|
|
407
417
|
const [internalValue, setInternalValue] = (0, import_react.useState)(() => {
|
|
408
418
|
if (defaultValue !== void 0) {
|
|
409
419
|
return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
|
@@ -411,8 +421,8 @@ var ToggleButtonGroup = ({
|
|
|
411
421
|
return [];
|
|
412
422
|
});
|
|
413
423
|
const currentValue = value !== void 0 ? Array.isArray(value) ? value : [value] : internalValue;
|
|
414
|
-
const isItemActive = (
|
|
415
|
-
const
|
|
424
|
+
const isItemActive = (item) => currentValue.includes(item.id) || isItemLocked(item);
|
|
425
|
+
const focusableIndices = items.map((item, index) => isItemFocusable(item) ? index : -1).filter((i) => i !== -1);
|
|
416
426
|
const focusItem = (0, import_react.useCallback)((index) => {
|
|
417
427
|
const element = itemRefs.current[index];
|
|
418
428
|
if (element) {
|
|
@@ -446,28 +456,28 @@ var ToggleButtonGroup = ({
|
|
|
446
456
|
);
|
|
447
457
|
const handleKeyDown = (0, import_react.useCallback)(
|
|
448
458
|
(e, currentIndex) => {
|
|
449
|
-
const
|
|
459
|
+
const currentFocusableIndex = focusableIndices.indexOf(currentIndex);
|
|
450
460
|
switch (e.key) {
|
|
451
461
|
case "ArrowRight":
|
|
452
462
|
case "ArrowDown":
|
|
453
463
|
e.preventDefault();
|
|
454
464
|
{
|
|
455
|
-
const
|
|
456
|
-
focusItem(
|
|
465
|
+
const nextFocusableIndex = currentFocusableIndex < focusableIndices.length - 1 ? focusableIndices[currentFocusableIndex + 1] : focusableIndices[0];
|
|
466
|
+
focusItem(nextFocusableIndex);
|
|
457
467
|
}
|
|
458
468
|
break;
|
|
459
469
|
case "ArrowLeft":
|
|
460
470
|
case "ArrowUp":
|
|
461
471
|
e.preventDefault();
|
|
462
472
|
{
|
|
463
|
-
const
|
|
464
|
-
focusItem(
|
|
473
|
+
const prevFocusableIndex = currentFocusableIndex > 0 ? focusableIndices[currentFocusableIndex - 1] : focusableIndices[focusableIndices.length - 1];
|
|
474
|
+
focusItem(prevFocusableIndex);
|
|
465
475
|
}
|
|
466
476
|
break;
|
|
467
477
|
case "Enter":
|
|
468
478
|
case " ":
|
|
469
479
|
e.preventDefault();
|
|
470
|
-
if (
|
|
480
|
+
if (isItemInteractive(items[currentIndex])) {
|
|
471
481
|
handleSelect(items[currentIndex].id);
|
|
472
482
|
}
|
|
473
483
|
break;
|
|
@@ -475,15 +485,13 @@ var ToggleButtonGroup = ({
|
|
|
475
485
|
break;
|
|
476
486
|
}
|
|
477
487
|
},
|
|
478
|
-
[
|
|
488
|
+
[focusableIndices, focusItem, handleSelect, items]
|
|
479
489
|
);
|
|
480
490
|
const isSeparated = appearance === "separated";
|
|
481
491
|
const isVertical = view === "vertical";
|
|
482
492
|
const containerGap = isSeparated ? sizeStyles.itemGap : 0;
|
|
483
|
-
const firstActiveIndex = items.findIndex(
|
|
484
|
-
|
|
485
|
-
);
|
|
486
|
-
const firstEnabledIndex = enabledIndices[0] ?? 0;
|
|
493
|
+
const firstActiveIndex = items.findIndex((item) => isItemActive(item));
|
|
494
|
+
const firstFocusableIndex = focusableIndices[0] ?? 0;
|
|
487
495
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
488
496
|
Box,
|
|
489
497
|
{
|
|
@@ -502,26 +510,28 @@ var ToggleButtonGroup = ({
|
|
|
502
510
|
overflow: "hidden"
|
|
503
511
|
},
|
|
504
512
|
children: items.map((item, index) => {
|
|
505
|
-
const
|
|
506
|
-
const isDisabled = item
|
|
513
|
+
const isLocked = isItemLocked(item);
|
|
514
|
+
const isDisabled = isItemDisabled(item);
|
|
515
|
+
const isInteractive = isItemInteractive(item);
|
|
516
|
+
const isFocusable = isItemFocusable(item);
|
|
517
|
+
const isActive = isItemActive(item);
|
|
507
518
|
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
508
519
|
let tabIndex;
|
|
509
|
-
if (
|
|
520
|
+
if (!isFocusable) {
|
|
510
521
|
tabIndex = -1;
|
|
511
522
|
} else if (multiple) {
|
|
512
523
|
tabIndex = 0;
|
|
513
524
|
} else {
|
|
514
|
-
tabIndex = isActive || firstActiveIndex === -1 && index ===
|
|
525
|
+
tabIndex = isActive || firstActiveIndex === -1 && index === firstFocusableIndex ? 0 : -1;
|
|
515
526
|
}
|
|
516
527
|
const handlePress = () => {
|
|
517
|
-
if (
|
|
528
|
+
if (isInteractive) {
|
|
518
529
|
handleSelect(item.id);
|
|
519
530
|
}
|
|
520
531
|
};
|
|
521
|
-
const
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
const borderColor = isDisabled ? toggleColors.borderDisable : isActive ? toggleColors.borderActive : toggleColors.border;
|
|
532
|
+
const bgColor = isLocked ? lockedColors.bgActiveDisable ?? toggleColors.bgActive : isDisabled ? toggleColors.bgDisable : isActive ? toggleColors.bgActive : toggleColors.bg;
|
|
533
|
+
const textColor = isLocked ? lockedColors.textActiveDisable ?? toggleColors.textActive : isDisabled ? toggleColors.textDisable : isActive ? toggleColors.textActive : toggleColors.text;
|
|
534
|
+
const borderColor = isLocked ? lockedColors.borderActiveDisable ?? toggleColors.borderActive : isDisabled ? toggleColors.borderDisable : isActive ? toggleColors.borderActive : toggleColors.border;
|
|
525
535
|
const button = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
526
536
|
Box,
|
|
527
537
|
{
|
|
@@ -530,7 +540,7 @@ var ToggleButtonGroup = ({
|
|
|
530
540
|
id: itemId,
|
|
531
541
|
testID: item.testID,
|
|
532
542
|
"aria-checked": isActive,
|
|
533
|
-
"aria-disabled": isDisabled,
|
|
543
|
+
"aria-disabled": isDisabled || isLocked,
|
|
534
544
|
"aria-label": item["aria-label"] || item.label,
|
|
535
545
|
tabIndex,
|
|
536
546
|
disabled: isDisabled,
|
|
@@ -557,13 +567,14 @@ var ToggleButtonGroup = ({
|
|
|
557
567
|
borderColor,
|
|
558
568
|
borderStyle: "solid"
|
|
559
569
|
},
|
|
560
|
-
cursor:
|
|
561
|
-
hoverStyle:
|
|
570
|
+
cursor: isInteractive ? "pointer" : "not-allowed",
|
|
571
|
+
hoverStyle: isInteractive && !isActive ? {
|
|
562
572
|
backgroundColor: toggleColors.bgHover,
|
|
563
573
|
borderColor: toggleColors.borderHover
|
|
564
574
|
} : void 0,
|
|
565
575
|
style: {
|
|
566
576
|
flexShrink: 0,
|
|
577
|
+
...isLocked && !hasLockedTokens && { opacity: LOCKED_OPACITY },
|
|
567
578
|
...!isSeparated && getUnitedItemCornerStyle(
|
|
568
579
|
index,
|
|
569
580
|
items.length,
|
package/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.tsx","../../src/ToggleButtonGroup.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["export { ToggleButtonGroup } from \"./ToggleButtonGroup\";\nexport type {\n ToggleButtonGroupProps,\n ToggleButtonGroupItem,\n ToggleButtonGroupItemTooltip,\n ToggleButtonGroupSize,\n ToggleButtonGroupAppearance,\n ToggleButtonGroupView,\n} from \"./types\";\n","import React, { useCallback, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, isWeb } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport { Tooltip } from \"@xsolla/xui-tooltip\";\nimport type { ToggleButtonGroupProps } from \"./types\";\n\nconst cloneIconWithDefaults = (\n icon: React.ReactNode,\n defaultSize: number,\n defaultColor: string\n): React.ReactNode => {\n if (!React.isValidElement(icon)) return icon;\n\n const iconElement = icon as React.ReactElement<Record<string, unknown>>;\n const existingProps = iconElement.props || {};\n\n return React.cloneElement(iconElement, {\n ...existingProps,\n size: existingProps.size ?? defaultSize,\n color: existingProps.color ?? defaultColor,\n });\n};\n\nconst getUnitedItemCornerStyle = (\n index: number,\n total: number,\n borderRadius: number,\n isVertical: boolean\n): React.CSSProperties => {\n const isFirst = index === 0;\n const isLast = index === total - 1;\n if (!isFirst && !isLast) return {};\n\n const radius = `${borderRadius}px`;\n\n if (isVertical) {\n return {\n ...(isFirst && {\n borderTopLeftRadius: radius,\n borderTopRightRadius: radius,\n }),\n ...(isLast && {\n borderBottomLeftRadius: radius,\n borderBottomRightRadius: radius,\n }),\n };\n }\n\n return {\n ...(isFirst && {\n borderTopLeftRadius: radius,\n borderBottomLeftRadius: radius,\n }),\n ...(isLast && {\n borderTopRightRadius: radius,\n borderBottomRightRadius: radius,\n }),\n };\n};\n\n/**\n * ToggleButtonGroup - A control for picking one or several options from a set\n *\n * Used to pick one or several options from a linear set of closely related options.\n * Can be used to filter or to sort elements.\n *\n * ## Accessibility Features\n *\n * - **Role**: Uses `role=\"radiogroup\"` for single selection, `role=\"group\"` for multiple\n * - **Keyboard Navigation**: Arrow keys to navigate, Enter/Space to select\n * - **ARIA**: Proper aria-checked and aria-disabled states\n */\nexport const ToggleButtonGroup: React.FC<ToggleButtonGroupProps> = ({\n items,\n value,\n defaultValue,\n onChange,\n size = \"sm\",\n appearance = \"separated\",\n view = \"horizontal\",\n multiple = false,\n id,\n testID,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n fullWidth = false,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const sizeStyles = theme.sizing.toggleButtonGroup(size);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n\n const [internalValue, setInternalValue] = useState<string[]>(() => {\n if (defaultValue !== undefined) {\n return Array.isArray(defaultValue) ? defaultValue : [defaultValue];\n }\n return [];\n });\n\n const currentValue =\n value !== undefined\n ? Array.isArray(value)\n ? value\n : [value]\n : internalValue;\n\n const isItemActive = (itemId: string) => currentValue.includes(itemId);\n\n const enabledIndices = items\n .map((item, index) => (!item.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const focusItem = useCallback((index: number) => {\n const element = itemRefs.current[index];\n if (element) {\n element.focus();\n }\n }, []);\n\n const handleSelect = useCallback(\n (itemId: string) => {\n let newValue: string[];\n\n if (multiple) {\n if (currentValue.includes(itemId)) {\n newValue = currentValue.filter((v) => v !== itemId);\n } else {\n newValue = [...currentValue, itemId];\n }\n } else {\n newValue = [itemId];\n }\n\n if (value === undefined) {\n setInternalValue(newValue);\n }\n\n if (onChange) {\n if (multiple) {\n onChange(newValue);\n } else {\n onChange(newValue[0] || \"\");\n }\n }\n },\n [currentValue, multiple, value, onChange]\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent, currentIndex: number) => {\n const currentEnabledIndex = enabledIndices.indexOf(currentIndex);\n\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n e.preventDefault();\n {\n const nextEnabledIndex =\n currentEnabledIndex < enabledIndices.length - 1\n ? enabledIndices[currentEnabledIndex + 1]\n : enabledIndices[0];\n focusItem(nextEnabledIndex);\n }\n break;\n\n case \"ArrowLeft\":\n case \"ArrowUp\":\n e.preventDefault();\n {\n const prevEnabledIndex =\n currentEnabledIndex > 0\n ? enabledIndices[currentEnabledIndex - 1]\n : enabledIndices[enabledIndices.length - 1];\n focusItem(prevEnabledIndex);\n }\n break;\n\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (!items[currentIndex].disabled) {\n handleSelect(items[currentIndex].id);\n }\n break;\n\n default:\n break;\n }\n },\n [enabledIndices, focusItem, handleSelect, items]\n );\n\n const isSeparated = appearance === \"separated\";\n const isVertical = view === \"vertical\";\n const containerGap = isSeparated ? sizeStyles.itemGap : 0;\n\n // Determine first active item for tabindex in single selection mode\n const firstActiveIndex = items.findIndex((item) =>\n currentValue.includes(item.id)\n );\n const firstEnabledIndex = enabledIndices[0] ?? 0;\n\n return (\n <Box\n id={id}\n role={multiple ? \"group\" : \"radiogroup\"}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n testID={testID}\n flexDirection={isVertical ? \"column\" : \"row\"}\n alignItems=\"stretch\"\n flexWrap={isSeparated && !isVertical ? \"wrap\" : \"nowrap\"}\n gap={containerGap}\n width={fullWidth ? \"100%\" : \"fit-content\"}\n {...(!isSeparated && {\n borderRadius: sizeStyles.borderRadius,\n overflow: \"hidden\",\n })}\n >\n {items.map((item, index) => {\n const isActive = isItemActive(item.id);\n const isDisabled = item.disabled;\n const itemId = id ? `${id}-item-${item.id}` : undefined;\n\n let tabIndex: number;\n if (isDisabled) {\n tabIndex = -1;\n } else if (multiple) {\n // In multiple mode, all enabled items are tabbable\n tabIndex = 0;\n } else {\n // In single mode, only the active item (or first enabled if none active) is tabbable\n tabIndex =\n isActive || (firstActiveIndex === -1 && index === firstEnabledIndex)\n ? 0\n : -1;\n }\n\n const handlePress = () => {\n if (!isDisabled) {\n handleSelect(item.id);\n }\n };\n\n const toggleColors = theme.colors.control.toggleButton;\n\n const bgColor = isDisabled\n ? toggleColors.bgDisable\n : isActive\n ? toggleColors.bgActive\n : toggleColors.bg;\n\n const textColor = isDisabled\n ? toggleColors.textDisable\n : isActive\n ? toggleColors.textActive\n : toggleColors.text;\n\n const borderColor = isDisabled\n ? toggleColors.borderDisable\n : isActive\n ? toggleColors.borderActive\n : toggleColors.border;\n\n const button = (\n <Box\n as=\"button\"\n role={multiple ? \"checkbox\" : \"radio\"}\n id={itemId}\n testID={item.testID}\n aria-checked={isActive}\n aria-disabled={isDisabled}\n aria-label={item[\"aria-label\"] || item.label}\n tabIndex={tabIndex}\n disabled={isDisabled}\n ref={(el: HTMLElement | null) => {\n itemRefs.current[index] = el;\n }}\n onPress={handlePress}\n onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(e, index)}\n flex={fullWidth ? 1 : undefined}\n height={sizeStyles.height}\n paddingHorizontal={sizeStyles.paddingHorizontal}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={sizeStyles.gap}\n backgroundColor={bgColor}\n {...(isSeparated\n ? {\n borderWidth: theme.shape.toggleButtonGroup[size].borderWidth,\n borderColor: borderColor,\n borderStyle: \"solid\",\n borderRadius: sizeStyles.borderRadius,\n }\n : {\n borderWidth: theme.shape.toggleButtonGroup[size].borderWidth,\n borderColor: borderColor,\n borderStyle: \"solid\",\n })}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n hoverStyle={\n !isDisabled && !isActive\n ? {\n backgroundColor: toggleColors.bgHover,\n borderColor: toggleColors.borderHover,\n }\n : undefined\n }\n style={{\n flexShrink: 0,\n ...(!isSeparated &&\n getUnitedItemCornerStyle(\n index,\n items.length,\n sizeStyles.borderRadius,\n isVertical\n )),\n ...(!isSeparated &&\n !isVertical &&\n index < items.length - 1 && { marginRight: -1 }),\n ...(!isSeparated &&\n isVertical &&\n index < items.length - 1 && { marginBottom: -1 }),\n }}\n >\n {item.iconLeft &&\n cloneIconWithDefaults(\n item.iconLeft,\n sizeStyles.iconSize,\n textColor\n )}\n {item.label && (\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n fontWeight=\"400\"\n textAlign=\"center\"\n whiteSpace=\"nowrap\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n style={{\n lineHeight: `${sizeStyles.lineHeight}px`,\n }}\n >\n {item.label}\n </Text>\n )}\n {item.iconRight &&\n cloneIconWithDefaults(\n item.iconRight,\n sizeStyles.iconSize,\n textColor\n )}\n </Box>\n );\n\n if (!item.tooltip || !isWeb) {\n return <React.Fragment key={item.id}>{button}</React.Fragment>;\n }\n\n const defaultTooltipPlacement = isVertical ? \"right\" : \"top\";\n\n return (\n <Tooltip\n key={item.id}\n {...item.tooltip}\n placement={item.tooltip.placement ?? defaultTooltipPlacement}\n size={item.tooltip.size ?? sizeStyles.tooltipSize}\n style={\n fullWidth\n ? { flex: 1, ...item.tooltip.style }\n : item.tooltip.style\n }\n >\n {button}\n </Tooltip>\n );\n })}\n </Box>\n );\n};\n\nToggleButtonGroup.displayName = \"ToggleButtonGroup\";\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","export * from \"./Box\";\nexport * from \"./Text\";\nexport * from \"./Spinner\";\nexport * from \"./Icon\";\nexport * from \"./Divider\";\nexport * from \"./Input\";\nexport * from \"./TextArea\";\nexport * from \"./LinearGradient\";\n\nexport const isWeb = false;\nexport const isNative = true;\n"],"mappings":"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","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.tsx","../../src/ToggleButtonGroup.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["export { ToggleButtonGroup } from \"./ToggleButtonGroup\";\nexport type {\n ToggleButtonGroupProps,\n ToggleButtonGroupItem,\n ToggleButtonGroupItemTooltip,\n ToggleButtonGroupSize,\n ToggleButtonGroupAppearance,\n ToggleButtonGroupView,\n} from \"./types\";\n","import React, { useCallback, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, isWeb } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport { Tooltip } from \"@xsolla/xui-tooltip\";\nimport type {\n ToggleButtonGroupItem,\n ToggleButtonGroupProps,\n} from \"./types\";\n\n/**\n * Opacity applied to a `locked` item while the theme carries no dedicated\n * locked tokens.\n *\n * A locked item keeps the *active* fill, text and border — so it still reads as\n * ON — and is dimmed as a whole to signal that it is not interactive. Dimming\n * the element (rather than swapping in the disable palette) preserves the\n * text-on-fill contrast ratio; only the contrast against the page drops, which\n * is the conventional treatment for a non-interactive control.\n *\n * Once design lands `bgActiveDisable` / `textActiveDisable` /\n * `borderActiveDisable` in `theme.colors.control.toggleButton`, those values\n * are used verbatim and this opacity is not applied — see `lockedColors` below.\n */\nconst LOCKED_OPACITY = 0.6;\n\n/** A locked item is selected and immutable; `locked` wins over `disabled`. */\nconst isItemLocked = (item: ToggleButtonGroupItem) => Boolean(item.locked);\n\n/** Disabled in the native sense: inert *and* not keyboard-reachable. */\nconst isItemDisabled = (item: ToggleButtonGroupItem) =>\n Boolean(item.disabled) && !isItemLocked(item);\n\n/** Can the item's selection be changed by the user? */\nconst isItemInteractive = (item: ToggleButtonGroupItem) =>\n !isItemDisabled(item) && !isItemLocked(item);\n\n/**\n * Can the item receive keyboard focus? Locked items can — that is how a\n * keyboard user reaches the tooltip explaining why the item is locked.\n */\nconst isItemFocusable = (item: ToggleButtonGroupItem) => !isItemDisabled(item);\n\nconst cloneIconWithDefaults = (\n icon: React.ReactNode,\n defaultSize: number,\n defaultColor: string\n): React.ReactNode => {\n if (!React.isValidElement(icon)) return icon;\n\n const iconElement = icon as React.ReactElement<Record<string, unknown>>;\n const existingProps = iconElement.props || {};\n\n return React.cloneElement(iconElement, {\n ...existingProps,\n size: existingProps.size ?? defaultSize,\n color: existingProps.color ?? defaultColor,\n });\n};\n\nconst getUnitedItemCornerStyle = (\n index: number,\n total: number,\n borderRadius: number,\n isVertical: boolean\n): React.CSSProperties => {\n const isFirst = index === 0;\n const isLast = index === total - 1;\n if (!isFirst && !isLast) return {};\n\n const radius = `${borderRadius}px`;\n\n if (isVertical) {\n return {\n ...(isFirst && {\n borderTopLeftRadius: radius,\n borderTopRightRadius: radius,\n }),\n ...(isLast && {\n borderBottomLeftRadius: radius,\n borderBottomRightRadius: radius,\n }),\n };\n }\n\n return {\n ...(isFirst && {\n borderTopLeftRadius: radius,\n borderBottomLeftRadius: radius,\n }),\n ...(isLast && {\n borderTopRightRadius: radius,\n borderBottomRightRadius: radius,\n }),\n };\n};\n\n/**\n * ToggleButtonGroup - A control for picking one or several options from a set\n *\n * Used to pick one or several options from a linear set of closely related options.\n * Can be used to filter or to sort elements.\n *\n * ## Item states\n *\n * - **Default / active** — selection driven by `value` / `defaultValue`.\n * - **`disabled`** — inert and not focusable; painted with the disable palette.\n * - **`locked`** — selected and immutable: painted with the *active* palette,\n * dimmed, inert, but still keyboard-focusable so its `tooltip` is reachable.\n *\n * ## Accessibility Features\n *\n * - **Role**: Uses `role=\"radiogroup\"` for single selection, `role=\"group\"` for multiple\n * - **Keyboard Navigation**: Arrow keys to navigate, Enter/Space to select\n * - **ARIA**: Proper aria-checked and aria-disabled states\n */\nexport const ToggleButtonGroup: React.FC<ToggleButtonGroupProps> = ({\n items,\n value,\n defaultValue,\n onChange,\n size = \"sm\",\n appearance = \"separated\",\n view = \"horizontal\",\n multiple = false,\n id,\n testID,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n fullWidth = false,\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const sizeStyles = theme.sizing.toggleButtonGroup(size);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n\n const toggleColors = theme.colors.control.toggleButton;\n\n /**\n * Locked-state tokens are not in the Figma token export yet. Read them\n * optionally so the component picks them up automatically once design adds\n * them to `control.toggleButton`, with no further code change.\n */\n const lockedColors = toggleColors as typeof toggleColors & {\n bgActiveDisable?: string;\n textActiveDisable?: string;\n borderActiveDisable?: string;\n };\n\n const hasLockedTokens = Boolean(\n lockedColors.bgActiveDisable ??\n lockedColors.textActiveDisable ??\n lockedColors.borderActiveDisable\n );\n\n const [internalValue, setInternalValue] = useState<string[]>(() => {\n if (defaultValue !== undefined) {\n return Array.isArray(defaultValue) ? defaultValue : [defaultValue];\n }\n return [];\n });\n\n const currentValue =\n value !== undefined\n ? Array.isArray(value)\n ? value\n : [value]\n : internalValue;\n\n const isItemActive = (item: ToggleButtonGroupItem) =>\n currentValue.includes(item.id) || isItemLocked(item);\n\n const focusableIndices = items\n .map((item, index) => (isItemFocusable(item) ? index : -1))\n .filter((i) => i !== -1);\n\n const focusItem = useCallback((index: number) => {\n const element = itemRefs.current[index];\n if (element) {\n element.focus();\n }\n }, []);\n\n const handleSelect = useCallback(\n (itemId: string) => {\n let newValue: string[];\n\n if (multiple) {\n if (currentValue.includes(itemId)) {\n newValue = currentValue.filter((v) => v !== itemId);\n } else {\n newValue = [...currentValue, itemId];\n }\n } else {\n newValue = [itemId];\n }\n\n if (value === undefined) {\n setInternalValue(newValue);\n }\n\n if (onChange) {\n if (multiple) {\n onChange(newValue);\n } else {\n onChange(newValue[0] || \"\");\n }\n }\n },\n [currentValue, multiple, value, onChange]\n );\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent, currentIndex: number) => {\n const currentFocusableIndex = focusableIndices.indexOf(currentIndex);\n\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n e.preventDefault();\n {\n const nextFocusableIndex =\n currentFocusableIndex < focusableIndices.length - 1\n ? focusableIndices[currentFocusableIndex + 1]\n : focusableIndices[0];\n focusItem(nextFocusableIndex);\n }\n break;\n\n case \"ArrowLeft\":\n case \"ArrowUp\":\n e.preventDefault();\n {\n const prevFocusableIndex =\n currentFocusableIndex > 0\n ? focusableIndices[currentFocusableIndex - 1]\n : focusableIndices[focusableIndices.length - 1];\n focusItem(prevFocusableIndex);\n }\n break;\n\n case \"Enter\":\n case \" \":\n e.preventDefault();\n if (isItemInteractive(items[currentIndex])) {\n handleSelect(items[currentIndex].id);\n }\n break;\n\n default:\n break;\n }\n },\n [focusableIndices, focusItem, handleSelect, items]\n );\n\n const isSeparated = appearance === \"separated\";\n const isVertical = view === \"vertical\";\n const containerGap = isSeparated ? sizeStyles.itemGap : 0;\n\n // Determine first active item for tabindex in single selection mode\n const firstActiveIndex = items.findIndex((item) => isItemActive(item));\n const firstFocusableIndex = focusableIndices[0] ?? 0;\n\n return (\n <Box\n id={id}\n role={multiple ? \"group\" : \"radiogroup\"}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n testID={testID}\n flexDirection={isVertical ? \"column\" : \"row\"}\n alignItems=\"stretch\"\n flexWrap={isSeparated && !isVertical ? \"wrap\" : \"nowrap\"}\n gap={containerGap}\n width={fullWidth ? \"100%\" : \"fit-content\"}\n {...(!isSeparated && {\n borderRadius: sizeStyles.borderRadius,\n overflow: \"hidden\",\n })}\n >\n {items.map((item, index) => {\n const isLocked = isItemLocked(item);\n const isDisabled = isItemDisabled(item);\n const isInteractive = isItemInteractive(item);\n const isFocusable = isItemFocusable(item);\n const isActive = isItemActive(item);\n const itemId = id ? `${id}-item-${item.id}` : undefined;\n\n let tabIndex: number;\n if (!isFocusable) {\n tabIndex = -1;\n } else if (multiple) {\n // In multiple mode, all focusable items are tabbable\n tabIndex = 0;\n } else {\n // In single mode, only the active item (or first focusable if none active) is tabbable\n tabIndex =\n isActive ||\n (firstActiveIndex === -1 && index === firstFocusableIndex)\n ? 0\n : -1;\n }\n\n const handlePress = () => {\n if (isInteractive) {\n handleSelect(item.id);\n }\n };\n\n const bgColor = isLocked\n ? (lockedColors.bgActiveDisable ?? toggleColors.bgActive)\n : isDisabled\n ? toggleColors.bgDisable\n : isActive\n ? toggleColors.bgActive\n : toggleColors.bg;\n\n const textColor = isLocked\n ? (lockedColors.textActiveDisable ?? toggleColors.textActive)\n : isDisabled\n ? toggleColors.textDisable\n : isActive\n ? toggleColors.textActive\n : toggleColors.text;\n\n const borderColor = isLocked\n ? (lockedColors.borderActiveDisable ?? toggleColors.borderActive)\n : isDisabled\n ? toggleColors.borderDisable\n : isActive\n ? toggleColors.borderActive\n : toggleColors.border;\n\n const button = (\n <Box\n as=\"button\"\n role={multiple ? \"checkbox\" : \"radio\"}\n id={itemId}\n testID={item.testID}\n aria-checked={isActive}\n aria-disabled={isDisabled || isLocked}\n aria-label={item[\"aria-label\"] || item.label}\n tabIndex={tabIndex}\n // A locked item deliberately omits the native `disabled` attribute\n // so it stays focusable and its tooltip is reachable by keyboard.\n disabled={isDisabled}\n ref={(el: HTMLElement | null) => {\n itemRefs.current[index] = el;\n }}\n onPress={handlePress}\n onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(e, index)}\n flex={fullWidth ? 1 : undefined}\n height={sizeStyles.height}\n paddingHorizontal={sizeStyles.paddingHorizontal}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={sizeStyles.gap}\n backgroundColor={bgColor}\n {...(isSeparated\n ? {\n borderWidth: theme.shape.toggleButtonGroup[size].borderWidth,\n borderColor: borderColor,\n borderStyle: \"solid\",\n borderRadius: sizeStyles.borderRadius,\n }\n : {\n borderWidth: theme.shape.toggleButtonGroup[size].borderWidth,\n borderColor: borderColor,\n borderStyle: \"solid\",\n })}\n cursor={isInteractive ? \"pointer\" : \"not-allowed\"}\n hoverStyle={\n isInteractive && !isActive\n ? {\n backgroundColor: toggleColors.bgHover,\n borderColor: toggleColors.borderHover,\n }\n : undefined\n }\n style={{\n flexShrink: 0,\n ...(isLocked && !hasLockedTokens && { opacity: LOCKED_OPACITY }),\n ...(!isSeparated &&\n getUnitedItemCornerStyle(\n index,\n items.length,\n sizeStyles.borderRadius,\n isVertical\n )),\n ...(!isSeparated &&\n !isVertical &&\n index < items.length - 1 && { marginRight: -1 }),\n ...(!isSeparated &&\n isVertical &&\n index < items.length - 1 && { marginBottom: -1 }),\n }}\n >\n {item.iconLeft &&\n cloneIconWithDefaults(\n item.iconLeft,\n sizeStyles.iconSize,\n textColor\n )}\n {item.label && (\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n fontWeight=\"400\"\n textAlign=\"center\"\n whiteSpace=\"nowrap\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n style={{\n lineHeight: `${sizeStyles.lineHeight}px`,\n }}\n >\n {item.label}\n </Text>\n )}\n {item.iconRight &&\n cloneIconWithDefaults(\n item.iconRight,\n sizeStyles.iconSize,\n textColor\n )}\n </Box>\n );\n\n if (!item.tooltip || !isWeb) {\n return <React.Fragment key={item.id}>{button}</React.Fragment>;\n }\n\n const defaultTooltipPlacement = isVertical ? \"right\" : \"top\";\n\n return (\n <Tooltip\n key={item.id}\n {...item.tooltip}\n placement={item.tooltip.placement ?? defaultTooltipPlacement}\n size={item.tooltip.size ?? sizeStyles.tooltipSize}\n style={\n fullWidth\n ? { flex: 1, ...item.tooltip.style }\n : item.tooltip.style\n }\n >\n {button}\n </Tooltip>\n );\n })}\n </Box>\n );\n};\n\nToggleButtonGroup.displayName = \"ToggleButtonGroup\";\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","export * from \"./Box\";\nexport * from \"./Text\";\nexport * from \"./Spinner\";\nexport * from \"./Icon\";\nexport * from \"./Divider\";\nexport * from \"./Input\";\nexport * from \"./TextArea\";\nexport * from \"./LinearGradient\";\n\nexport const isWeb = false;\nexport const isNative = true;\n"],"mappings":"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","names":["import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","React"]}
|
package/native/index.mjs
CHANGED
|
@@ -317,6 +317,11 @@ var isWeb = false;
|
|
|
317
317
|
import { useResolvedTheme } from "@xsolla/xui-core";
|
|
318
318
|
import { Tooltip } from "@xsolla/xui-tooltip";
|
|
319
319
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
320
|
+
var LOCKED_OPACITY = 0.6;
|
|
321
|
+
var isItemLocked = (item) => Boolean(item.locked);
|
|
322
|
+
var isItemDisabled = (item) => Boolean(item.disabled) && !isItemLocked(item);
|
|
323
|
+
var isItemInteractive = (item) => !isItemDisabled(item) && !isItemLocked(item);
|
|
324
|
+
var isItemFocusable = (item) => !isItemDisabled(item);
|
|
320
325
|
var cloneIconWithDefaults = (icon, defaultSize, defaultColor) => {
|
|
321
326
|
if (!React.isValidElement(icon)) return icon;
|
|
322
327
|
const iconElement = icon;
|
|
@@ -375,6 +380,11 @@ var ToggleButtonGroup = ({
|
|
|
375
380
|
const { theme } = useResolvedTheme({ themeMode, themeProductContext });
|
|
376
381
|
const sizeStyles = theme.sizing.toggleButtonGroup(size);
|
|
377
382
|
const itemRefs = useRef([]);
|
|
383
|
+
const toggleColors = theme.colors.control.toggleButton;
|
|
384
|
+
const lockedColors = toggleColors;
|
|
385
|
+
const hasLockedTokens = Boolean(
|
|
386
|
+
lockedColors.bgActiveDisable ?? lockedColors.textActiveDisable ?? lockedColors.borderActiveDisable
|
|
387
|
+
);
|
|
378
388
|
const [internalValue, setInternalValue] = useState(() => {
|
|
379
389
|
if (defaultValue !== void 0) {
|
|
380
390
|
return Array.isArray(defaultValue) ? defaultValue : [defaultValue];
|
|
@@ -382,8 +392,8 @@ var ToggleButtonGroup = ({
|
|
|
382
392
|
return [];
|
|
383
393
|
});
|
|
384
394
|
const currentValue = value !== void 0 ? Array.isArray(value) ? value : [value] : internalValue;
|
|
385
|
-
const isItemActive = (
|
|
386
|
-
const
|
|
395
|
+
const isItemActive = (item) => currentValue.includes(item.id) || isItemLocked(item);
|
|
396
|
+
const focusableIndices = items.map((item, index) => isItemFocusable(item) ? index : -1).filter((i) => i !== -1);
|
|
387
397
|
const focusItem = useCallback((index) => {
|
|
388
398
|
const element = itemRefs.current[index];
|
|
389
399
|
if (element) {
|
|
@@ -417,28 +427,28 @@ var ToggleButtonGroup = ({
|
|
|
417
427
|
);
|
|
418
428
|
const handleKeyDown = useCallback(
|
|
419
429
|
(e, currentIndex) => {
|
|
420
|
-
const
|
|
430
|
+
const currentFocusableIndex = focusableIndices.indexOf(currentIndex);
|
|
421
431
|
switch (e.key) {
|
|
422
432
|
case "ArrowRight":
|
|
423
433
|
case "ArrowDown":
|
|
424
434
|
e.preventDefault();
|
|
425
435
|
{
|
|
426
|
-
const
|
|
427
|
-
focusItem(
|
|
436
|
+
const nextFocusableIndex = currentFocusableIndex < focusableIndices.length - 1 ? focusableIndices[currentFocusableIndex + 1] : focusableIndices[0];
|
|
437
|
+
focusItem(nextFocusableIndex);
|
|
428
438
|
}
|
|
429
439
|
break;
|
|
430
440
|
case "ArrowLeft":
|
|
431
441
|
case "ArrowUp":
|
|
432
442
|
e.preventDefault();
|
|
433
443
|
{
|
|
434
|
-
const
|
|
435
|
-
focusItem(
|
|
444
|
+
const prevFocusableIndex = currentFocusableIndex > 0 ? focusableIndices[currentFocusableIndex - 1] : focusableIndices[focusableIndices.length - 1];
|
|
445
|
+
focusItem(prevFocusableIndex);
|
|
436
446
|
}
|
|
437
447
|
break;
|
|
438
448
|
case "Enter":
|
|
439
449
|
case " ":
|
|
440
450
|
e.preventDefault();
|
|
441
|
-
if (
|
|
451
|
+
if (isItemInteractive(items[currentIndex])) {
|
|
442
452
|
handleSelect(items[currentIndex].id);
|
|
443
453
|
}
|
|
444
454
|
break;
|
|
@@ -446,15 +456,13 @@ var ToggleButtonGroup = ({
|
|
|
446
456
|
break;
|
|
447
457
|
}
|
|
448
458
|
},
|
|
449
|
-
[
|
|
459
|
+
[focusableIndices, focusItem, handleSelect, items]
|
|
450
460
|
);
|
|
451
461
|
const isSeparated = appearance === "separated";
|
|
452
462
|
const isVertical = view === "vertical";
|
|
453
463
|
const containerGap = isSeparated ? sizeStyles.itemGap : 0;
|
|
454
|
-
const firstActiveIndex = items.findIndex(
|
|
455
|
-
|
|
456
|
-
);
|
|
457
|
-
const firstEnabledIndex = enabledIndices[0] ?? 0;
|
|
464
|
+
const firstActiveIndex = items.findIndex((item) => isItemActive(item));
|
|
465
|
+
const firstFocusableIndex = focusableIndices[0] ?? 0;
|
|
458
466
|
return /* @__PURE__ */ jsx3(
|
|
459
467
|
Box,
|
|
460
468
|
{
|
|
@@ -473,26 +481,28 @@ var ToggleButtonGroup = ({
|
|
|
473
481
|
overflow: "hidden"
|
|
474
482
|
},
|
|
475
483
|
children: items.map((item, index) => {
|
|
476
|
-
const
|
|
477
|
-
const isDisabled = item
|
|
484
|
+
const isLocked = isItemLocked(item);
|
|
485
|
+
const isDisabled = isItemDisabled(item);
|
|
486
|
+
const isInteractive = isItemInteractive(item);
|
|
487
|
+
const isFocusable = isItemFocusable(item);
|
|
488
|
+
const isActive = isItemActive(item);
|
|
478
489
|
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
479
490
|
let tabIndex;
|
|
480
|
-
if (
|
|
491
|
+
if (!isFocusable) {
|
|
481
492
|
tabIndex = -1;
|
|
482
493
|
} else if (multiple) {
|
|
483
494
|
tabIndex = 0;
|
|
484
495
|
} else {
|
|
485
|
-
tabIndex = isActive || firstActiveIndex === -1 && index ===
|
|
496
|
+
tabIndex = isActive || firstActiveIndex === -1 && index === firstFocusableIndex ? 0 : -1;
|
|
486
497
|
}
|
|
487
498
|
const handlePress = () => {
|
|
488
|
-
if (
|
|
499
|
+
if (isInteractive) {
|
|
489
500
|
handleSelect(item.id);
|
|
490
501
|
}
|
|
491
502
|
};
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
const borderColor = isDisabled ? toggleColors.borderDisable : isActive ? toggleColors.borderActive : toggleColors.border;
|
|
503
|
+
const bgColor = isLocked ? lockedColors.bgActiveDisable ?? toggleColors.bgActive : isDisabled ? toggleColors.bgDisable : isActive ? toggleColors.bgActive : toggleColors.bg;
|
|
504
|
+
const textColor = isLocked ? lockedColors.textActiveDisable ?? toggleColors.textActive : isDisabled ? toggleColors.textDisable : isActive ? toggleColors.textActive : toggleColors.text;
|
|
505
|
+
const borderColor = isLocked ? lockedColors.borderActiveDisable ?? toggleColors.borderActive : isDisabled ? toggleColors.borderDisable : isActive ? toggleColors.borderActive : toggleColors.border;
|
|
496
506
|
const button = /* @__PURE__ */ jsxs(
|
|
497
507
|
Box,
|
|
498
508
|
{
|
|
@@ -501,7 +511,7 @@ var ToggleButtonGroup = ({
|
|
|
501
511
|
id: itemId,
|
|
502
512
|
testID: item.testID,
|
|
503
513
|
"aria-checked": isActive,
|
|
504
|
-
"aria-disabled": isDisabled,
|
|
514
|
+
"aria-disabled": isDisabled || isLocked,
|
|
505
515
|
"aria-label": item["aria-label"] || item.label,
|
|
506
516
|
tabIndex,
|
|
507
517
|
disabled: isDisabled,
|
|
@@ -528,13 +538,14 @@ var ToggleButtonGroup = ({
|
|
|
528
538
|
borderColor,
|
|
529
539
|
borderStyle: "solid"
|
|
530
540
|
},
|
|
531
|
-
cursor:
|
|
532
|
-
hoverStyle:
|
|
541
|
+
cursor: isInteractive ? "pointer" : "not-allowed",
|
|
542
|
+
hoverStyle: isInteractive && !isActive ? {
|
|
533
543
|
backgroundColor: toggleColors.bgHover,
|
|
534
544
|
borderColor: toggleColors.borderHover
|
|
535
545
|
} : void 0,
|
|
536
546
|
style: {
|
|
537
547
|
flexShrink: 0,
|
|
548
|
+
...isLocked && !hasLockedTokens && { opacity: LOCKED_OPACITY },
|
|
538
549
|
...!isSeparated && getUnitedItemCornerStyle(
|
|
539
550
|
index,
|
|
540
551
|
items.length,
|