@xsolla/xui-segmented 0.101.0 → 0.103.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 +2 -28
- package/native/index.d.mts +0 -2
- package/native/index.d.ts +0 -2
- package/native/index.js +111 -62
- package/native/index.js.flow +0 -5
- package/native/index.js.map +1 -1
- package/native/index.mjs +112 -63
- package/native/index.mjs.map +1 -1
- package/package.json +4 -4
- package/web/index.d.mts +0 -2
- package/web/index.d.ts +0 -2
- package/web/index.js +111 -62
- package/web/index.js.flow +0 -5
- package/web/index.js.map +1 -1
- package/web/index.mjs +112 -63
- package/web/index.mjs.map +1 -1
package/README.md
CHANGED
|
@@ -96,8 +96,7 @@ import { Segmented } from '@xsolla/xui-segmented';
|
|
|
96
96
|
]}
|
|
97
97
|
activeId="a" // Currently active item ID
|
|
98
98
|
onChange={handleChange} // Selection change handler
|
|
99
|
-
size="md"
|
|
100
|
-
stroke={false} // Show border around container
|
|
99
|
+
size="md" // Size variant
|
|
101
100
|
fullWidth={false} // Expand to container width
|
|
102
101
|
/>
|
|
103
102
|
```
|
|
@@ -128,30 +127,6 @@ export default function SegmentedSizes() {
|
|
|
128
127
|
}
|
|
129
128
|
```
|
|
130
129
|
|
|
131
|
-
### With Stroke Border
|
|
132
|
-
|
|
133
|
-
```tsx
|
|
134
|
-
import * as React from 'react';
|
|
135
|
-
import { Segmented } from '@xsolla/xui-segmented';
|
|
136
|
-
|
|
137
|
-
export default function StrokeSegmented() {
|
|
138
|
-
const [active, setActive] = React.useState('left');
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<Segmented
|
|
142
|
-
stroke={true}
|
|
143
|
-
items={[
|
|
144
|
-
{ id: 'left', label: 'Left' },
|
|
145
|
-
{ id: 'center', label: 'Center' },
|
|
146
|
-
{ id: 'right', label: 'Right' },
|
|
147
|
-
]}
|
|
148
|
-
activeId={active}
|
|
149
|
-
onChange={setActive}
|
|
150
|
-
/>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
130
|
### With Disabled Items
|
|
156
131
|
|
|
157
132
|
```tsx
|
|
@@ -187,7 +162,6 @@ export default function DisabledItemSegmented() {
|
|
|
187
162
|
| activeId | `string` | - | ID of the currently active item. |
|
|
188
163
|
| onChange | `(id: string) => void` | - | Selection change handler. |
|
|
189
164
|
| size | `"xl" \| "lg" \| "md" \| "sm"` | `"md"` | Size variant. |
|
|
190
|
-
| stroke | `boolean` | `false` | Show border around container. |
|
|
191
165
|
| fullWidth | `boolean` | `false` | Expand segments to fill container width. |
|
|
192
166
|
| id | `string` | - | HTML id attribute. |
|
|
193
167
|
| testID | `string` | - | Test identifier. |
|
|
@@ -214,7 +188,7 @@ interface SegmentedItemType {
|
|
|
214
188
|
|
|
215
189
|
## Behavior
|
|
216
190
|
|
|
217
|
-
- Active segment has elevated background
|
|
191
|
+
- Active segment has elevated background with smooth sliding animation
|
|
218
192
|
- Disabled segments show reduced opacity
|
|
219
193
|
- Keyboard navigation wraps around
|
|
220
194
|
- Hover effect on non-active segments
|
package/native/index.d.mts
CHANGED
|
@@ -21,8 +21,6 @@ interface SegmentedProps {
|
|
|
21
21
|
onChange?: (id: string) => void;
|
|
22
22
|
/** Size variant */
|
|
23
23
|
size?: "xl" | "lg" | "md" | "sm";
|
|
24
|
-
/** Whether to show a border around the container */
|
|
25
|
-
stroke?: boolean;
|
|
26
24
|
/** HTML id attribute */
|
|
27
25
|
id?: string;
|
|
28
26
|
/** Test ID for testing frameworks */
|
package/native/index.d.ts
CHANGED
|
@@ -21,8 +21,6 @@ interface SegmentedProps {
|
|
|
21
21
|
onChange?: (id: string) => void;
|
|
22
22
|
/** Size variant */
|
|
23
23
|
size?: "xl" | "lg" | "md" | "sm";
|
|
24
|
-
/** Whether to show a border around the container */
|
|
25
|
-
stroke?: boolean;
|
|
26
24
|
/** HTML id attribute */
|
|
27
25
|
id?: string;
|
|
28
26
|
/** Test ID for testing frameworks */
|
package/native/index.js
CHANGED
|
@@ -265,12 +265,12 @@ var Icon = ({ children, color, size }) => {
|
|
|
265
265
|
// src/Segmented.tsx
|
|
266
266
|
var import_xui_core = require("@xsolla/xui-core");
|
|
267
267
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
268
|
+
var isWeb = typeof document !== "undefined";
|
|
268
269
|
var Segmented = ({
|
|
269
270
|
items,
|
|
270
271
|
activeId,
|
|
271
272
|
onChange,
|
|
272
273
|
size = "md",
|
|
273
|
-
stroke = false,
|
|
274
274
|
id,
|
|
275
275
|
testID,
|
|
276
276
|
fullWidth = false
|
|
@@ -279,6 +279,23 @@ var Segmented = ({
|
|
|
279
279
|
const sizeStyles = theme.sizing.segmented(size);
|
|
280
280
|
const containerId = id ? `${id}-container` : void 0;
|
|
281
281
|
const itemRefs = (0, import_react2.useRef)([]);
|
|
282
|
+
const containerRef = (0, import_react2.useRef)(null);
|
|
283
|
+
const [indicatorStyle, setIndicatorStyle] = (0, import_react2.useState)({ left: 0, width: 0, initialized: false });
|
|
284
|
+
(0, import_react2.useEffect)(() => {
|
|
285
|
+
if (!isWeb) return;
|
|
286
|
+
const activeIndex = items.findIndex((item) => item.id === activeId);
|
|
287
|
+
const activeItemEl = itemRefs.current[activeIndex];
|
|
288
|
+
const containerEl = containerRef.current;
|
|
289
|
+
if (activeItemEl && containerEl) {
|
|
290
|
+
const containerRect = containerEl.getBoundingClientRect();
|
|
291
|
+
const itemRect = activeItemEl.getBoundingClientRect();
|
|
292
|
+
setIndicatorStyle({
|
|
293
|
+
left: itemRect.left - containerRect.left,
|
|
294
|
+
width: itemRect.width,
|
|
295
|
+
initialized: true
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}, [activeId, items]);
|
|
282
299
|
const enabledIndices = items.map((item, index) => !item.disabled ? index : -1).filter((i) => i !== -1);
|
|
283
300
|
const focusItem = (0, import_react2.useCallback)((index) => {
|
|
284
301
|
const element = itemRefs.current[index];
|
|
@@ -319,83 +336,115 @@ var Segmented = ({
|
|
|
319
336
|
},
|
|
320
337
|
[enabledIndices, focusItem, onChange, items]
|
|
321
338
|
);
|
|
322
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
339
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
323
340
|
Box,
|
|
324
341
|
{
|
|
325
342
|
id: containerId,
|
|
326
343
|
role: "radiogroup",
|
|
327
344
|
testID,
|
|
345
|
+
ref: (el) => {
|
|
346
|
+
containerRef.current = el;
|
|
347
|
+
},
|
|
328
348
|
flexDirection: "row",
|
|
329
349
|
alignItems: "center",
|
|
330
350
|
flexShrink: 0,
|
|
351
|
+
position: "relative",
|
|
331
352
|
width: fullWidth ? "100%" : "fit-content",
|
|
332
353
|
height: sizeStyles.height,
|
|
333
|
-
backgroundColor: theme.colors.
|
|
334
|
-
borderRadius:
|
|
335
|
-
padding:
|
|
336
|
-
borderWidth:
|
|
354
|
+
backgroundColor: theme.colors.control.segmented.bg,
|
|
355
|
+
borderRadius: sizeStyles.containerRadius,
|
|
356
|
+
padding: sizeStyles.containerPadding,
|
|
357
|
+
borderWidth: 1,
|
|
337
358
|
borderColor: theme.colors.border.secondary,
|
|
338
359
|
borderStyle: "solid",
|
|
339
360
|
overflow: "hidden",
|
|
340
|
-
children:
|
|
341
|
-
|
|
342
|
-
const isDisabled = item.disabled;
|
|
343
|
-
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
344
|
-
const handlePress = () => {
|
|
345
|
-
if (!isDisabled && onChange) {
|
|
346
|
-
onChange(item.id);
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
const textColor = isDisabled ? theme.colors.content.tertiary : isActive ? theme.colors.content.primary : theme.colors.content.secondary;
|
|
350
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
361
|
+
children: [
|
|
362
|
+
isWeb && indicatorStyle.initialized && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
351
363
|
Box,
|
|
352
364
|
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
position: "absolute",
|
|
366
|
+
zIndex: 0,
|
|
367
|
+
height: sizeStyles.itemHeight,
|
|
368
|
+
backgroundColor: theme.colors.control.segmented.bgActive,
|
|
369
|
+
borderRadius: sizeStyles.itemRadius,
|
|
370
|
+
style: {
|
|
371
|
+
left: indicatorStyle.left,
|
|
372
|
+
width: indicatorStyle.width,
|
|
373
|
+
transition: "left 200ms ease-out, width 200ms ease-out",
|
|
374
|
+
pointerEvents: "none"
|
|
375
|
+
},
|
|
376
|
+
"aria-hidden": true
|
|
377
|
+
}
|
|
378
|
+
),
|
|
379
|
+
items.map((item, index) => {
|
|
380
|
+
const isActive = item.id === activeId;
|
|
381
|
+
const isDisabled = item.disabled;
|
|
382
|
+
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
383
|
+
const handlePress = () => {
|
|
384
|
+
if (!isDisabled && onChange) {
|
|
385
|
+
onChange(item.id);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const textColor = isDisabled ? theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
|
|
389
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
390
|
+
Box,
|
|
391
|
+
{
|
|
392
|
+
as: "button",
|
|
393
|
+
role: "radio",
|
|
394
|
+
id: itemId,
|
|
395
|
+
"aria-checked": isActive,
|
|
396
|
+
"aria-disabled": isDisabled,
|
|
397
|
+
"aria-label": item["aria-label"] || item.label,
|
|
398
|
+
tabIndex: isActive ? 0 : -1,
|
|
399
|
+
disabled: isDisabled,
|
|
400
|
+
ref: (el) => {
|
|
401
|
+
itemRefs.current[index] = el;
|
|
402
|
+
},
|
|
403
|
+
onPress: handlePress,
|
|
404
|
+
onKeyDown: (e) => handleKeyDown(e, index),
|
|
405
|
+
flex: fullWidth ? 1 : void 0,
|
|
406
|
+
flexShrink: 0,
|
|
407
|
+
position: "relative",
|
|
408
|
+
zIndex: 1,
|
|
409
|
+
height: sizeStyles.itemHeight,
|
|
410
|
+
paddingHorizontal: sizeStyles.itemPaddingHorizontal,
|
|
411
|
+
paddingVertical: sizeStyles.itemPaddingVertical,
|
|
412
|
+
flexDirection: "row",
|
|
413
|
+
alignItems: "center",
|
|
414
|
+
justifyContent: "center",
|
|
415
|
+
gap: sizeStyles.gap,
|
|
416
|
+
backgroundColor: !isWeb && isActive ? theme.colors.control.segmented.bgActive : "transparent",
|
|
417
|
+
borderRadius: sizeStyles.itemRadius,
|
|
418
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
419
|
+
hoverStyle: !isDisabled && !isActive ? {
|
|
420
|
+
backgroundColor: theme.colors.control.segmented.bgHover
|
|
421
|
+
} : void 0,
|
|
422
|
+
focusStyle: {
|
|
423
|
+
outlineColor: theme.colors.border.brand,
|
|
424
|
+
outlineWidth: 2,
|
|
425
|
+
outlineOffset: -2
|
|
426
|
+
},
|
|
427
|
+
children: [
|
|
428
|
+
item.icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
430
|
+
Text,
|
|
431
|
+
{
|
|
432
|
+
color: textColor,
|
|
433
|
+
fontSize: sizeStyles.fontSize,
|
|
434
|
+
fontWeight: "500",
|
|
435
|
+
textAlign: "center",
|
|
436
|
+
whiteSpace: "nowrap",
|
|
437
|
+
overflow: "hidden",
|
|
438
|
+
textOverflow: "ellipsis",
|
|
439
|
+
children: item.label
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
]
|
|
363
443
|
},
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
height: "100%",
|
|
369
|
-
paddingHorizontal: sizeStyles.padding,
|
|
370
|
-
flexDirection: "row",
|
|
371
|
-
alignItems: "center",
|
|
372
|
-
justifyContent: "center",
|
|
373
|
-
gap: 8,
|
|
374
|
-
backgroundColor: isActive ? theme.colors.background.primary : "transparent",
|
|
375
|
-
borderRadius: isActive ? theme.radius.button - (stroke ? 1 : 0) : 0,
|
|
376
|
-
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
377
|
-
boxShadow: isActive ? "0px 1px 2px rgba(0, 0, 0, 0.1)" : "none",
|
|
378
|
-
hoverStyle: !isDisabled && !isActive ? {
|
|
379
|
-
backgroundColor: theme.colors.overlay.mono
|
|
380
|
-
} : void 0,
|
|
381
|
-
children: [
|
|
382
|
-
item.icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
|
|
383
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
384
|
-
Text,
|
|
385
|
-
{
|
|
386
|
-
color: textColor,
|
|
387
|
-
fontSize: sizeStyles.fontSize,
|
|
388
|
-
fontWeight: isActive ? "600" : "500",
|
|
389
|
-
textAlign: "center",
|
|
390
|
-
whiteSpace: "nowrap",
|
|
391
|
-
children: item.label
|
|
392
|
-
}
|
|
393
|
-
)
|
|
394
|
-
]
|
|
395
|
-
},
|
|
396
|
-
item.id
|
|
397
|
-
);
|
|
398
|
-
})
|
|
444
|
+
item.id
|
|
445
|
+
);
|
|
446
|
+
})
|
|
447
|
+
]
|
|
399
448
|
}
|
|
400
449
|
);
|
|
401
450
|
};
|
package/native/index.js.flow
CHANGED
package/native/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.tsx","../../src/Segmented.tsx","../../../primitives-native/src/Box.tsx","../../../primitives-native/src/Text.tsx","../../../primitives-native/src/Icon.tsx"],"sourcesContent":["export * from \"./Segmented\";\n","import React, { useCallback, useRef } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon } from \"@xsolla/xui-primitives\";\nimport { useDesignSystem } from \"@xsolla/xui-core\";\n\nexport interface SegmentedItemType {\n /** Unique identifier for the item */\n id: string;\n /** Display label for the item */\n label: string;\n /** Optional icon to display */\n icon?: React.ReactNode;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Accessible label for screen readers */\n \"aria-label\"?: string;\n}\n\nexport interface SegmentedProps {\n /** Array of items */\n items: SegmentedItemType[];\n /** ID of the currently active item */\n activeId?: string;\n /** Callback when an item is selected */\n onChange?: (id: string) => void;\n /** Size variant */\n size?: \"xl\" | \"lg\" | \"md\" | \"sm\";\n /** Whether to show a border around the container */\n stroke?: boolean;\n /** HTML id attribute */\n id?: string;\n /** Test ID for testing frameworks */\n testID?: string;\n /** Full width of the container */\n fullWidth?: boolean;\n}\n\n/**\n * Segmented - A control for switching between related views or filters\n */\nexport const Segmented: React.FC<SegmentedProps> = ({\n items,\n activeId,\n onChange,\n size = \"md\",\n stroke = false,\n id,\n testID,\n fullWidth = false,\n}) => {\n const { theme } = useDesignSystem();\n const sizeStyles = theme.sizing.segmented(size);\n\n const containerId = id ? `${id}-container` : undefined;\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\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 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 (onChange && !items[currentIndex].disabled) {\n onChange(items[currentIndex].id);\n }\n break;\n\n default:\n break;\n }\n },\n [enabledIndices, focusItem, onChange, items]\n );\n\n return (\n <Box\n id={containerId}\n role=\"radiogroup\"\n testID={testID}\n flexDirection=\"row\"\n alignItems=\"center\"\n flexShrink={0}\n width={fullWidth ? \"100%\" : \"fit-content\"}\n height={sizeStyles.height}\n backgroundColor={theme.colors.overlay.mono}\n borderRadius={theme.radius.button}\n padding={stroke ? 1 : 0}\n borderWidth={stroke ? 1 : 0}\n borderColor={theme.colors.border.secondary}\n borderStyle=\"solid\"\n overflow=\"hidden\"\n >\n {items.map((item, index) => {\n const isActive = item.id === activeId;\n const isDisabled = item.disabled;\n const itemId = id ? `${id}-item-${item.id}` : undefined;\n\n const handlePress = () => {\n if (!isDisabled && onChange) {\n onChange(item.id);\n }\n };\n\n const textColor = isDisabled\n ? theme.colors.content.tertiary\n : isActive\n ? theme.colors.content.primary\n : theme.colors.content.secondary;\n\n return (\n <Box\n key={item.id}\n as=\"button\"\n role=\"radio\"\n id={itemId}\n aria-checked={isActive}\n aria-disabled={isDisabled}\n aria-label={item[\"aria-label\"] || item.label}\n tabIndex={isActive ? 0 : -1}\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 flexShrink={0}\n height=\"100%\"\n paddingHorizontal={sizeStyles.padding}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={8}\n backgroundColor={\n isActive ? theme.colors.background.primary : \"transparent\"\n }\n borderRadius={isActive ? theme.radius.button - (stroke ? 1 : 0) : 0}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n boxShadow={isActive ? \"0px 1px 2px rgba(0, 0, 0, 0.1)\" : \"none\"}\n hoverStyle={\n !isDisabled && !isActive\n ? {\n backgroundColor: theme.colors.overlay.mono,\n }\n : undefined\n }\n >\n {item.icon && (\n <Icon size={sizeStyles.iconSize} color={textColor} aria-hidden>\n {item.icon}\n </Icon>\n )}\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n fontWeight={isActive ? \"600\" : \"500\"}\n textAlign=\"center\"\n whiteSpace=\"nowrap\"\n >\n {item.label}\n </Text>\n </Box>\n );\n })}\n </Box>\n );\n};\n\nSegmented.displayName = \"Segmented\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\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 alignItems,\n justifyContent,\n position,\n top,\n bottom,\n left,\n right,\n width,\n flex,\n overflow,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\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 zIndex,\n height: height as DimensionValue,\n width: width 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 alignItems,\n justifyContent,\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 ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Destructure and drop web-only props from rest before passing to RN components\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 className,\n \"data-testid\": _dataTestId,\n ...nativeRest\n } = rest as Record<string, unknown>;\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 resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n if (onPress) {\n return (\n <Pressable\n onPress={onPress}\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 {...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 {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport { Text as RNText, TextStyle, AccessibilityRole } from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\n// Map web roles to React Native accessibility roles\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n id,\n role,\n ...props\n}) => {\n // Extract the first font name from a comma-separated list (e.g. for web-style font stacks)\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n // On native, if we don't have the custom font loaded, it's better to use the system font\n // to avoid rendering issues or missing text.\n if (resolvedFontFamily === \"Pilat Wide Bold\") {\n resolvedFontFamily = undefined;\n }\n\n const style: TextStyle = {\n 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 };\n\n // Map role to React Native accessibilityRole\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText style={style} testID={id} accessibilityRole={accessibilityRole}>\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({ children, color, size }) => {\n const style: ViewStyle = {\n width: typeof size === \"number\" ? size : undefined,\n height: typeof size === \"number\" ? size : undefined,\n alignItems: \"center\",\n justifyContent: \"center\",\n };\n\n // On native, we try to pass the color down to children (like Text primitives)\n // to mimic the CSS inheritance behavior of the web version.\n const childrenWithProps = React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child, {\n color: child.props.color || color,\n // Also pass size if child seems to be an icon that needs it\n size: child.props.size || size,\n });\n }\n return child;\n });\n\n return <View style={style}>{childrenWithProps}</View>;\n};\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime","RNText","import_react_native","import_jsx_runtime","React","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.tsx","../../src/Segmented.tsx","../../../primitives-native/src/Box.tsx","../../../primitives-native/src/Text.tsx","../../../primitives-native/src/Icon.tsx"],"sourcesContent":["export * from \"./Segmented\";\n","import React, { useCallback, useRef, useState, useEffect } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon } from \"@xsolla/xui-primitives\";\nimport { useDesignSystem } from \"@xsolla/xui-core\";\n\n// Platform detection without importing react-native directly\nconst isWeb = typeof document !== \"undefined\";\n\nexport interface SegmentedItemType {\n /** Unique identifier for the item */\n id: string;\n /** Display label for the item */\n label: string;\n /** Optional icon to display */\n icon?: React.ReactNode;\n /** Whether the item is disabled */\n disabled?: boolean;\n /** Accessible label for screen readers */\n \"aria-label\"?: string;\n}\n\nexport interface SegmentedProps {\n /** Array of items */\n items: SegmentedItemType[];\n /** ID of the currently active item */\n activeId?: string;\n /** Callback when an item is selected */\n onChange?: (id: string) => void;\n /** Size variant */\n size?: \"xl\" | \"lg\" | \"md\" | \"sm\";\n /** HTML id attribute */\n id?: string;\n /** Test ID for testing frameworks */\n testID?: string;\n /** Full width of the container */\n fullWidth?: boolean;\n}\n\n/**\n * Segmented - A control for switching between related views or filters\n */\nexport const Segmented: React.FC<SegmentedProps> = ({\n items,\n activeId,\n onChange,\n size = \"md\",\n id,\n testID,\n fullWidth = false,\n}) => {\n const { theme } = useDesignSystem();\n const sizeStyles = theme.sizing.segmented(size);\n\n const containerId = id ? `${id}-container` : undefined;\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n const containerRef = useRef<HTMLElement | null>(null);\n\n // Indicator position for animation (web only)\n const [indicatorStyle, setIndicatorStyle] = useState<{\n left: number;\n width: number;\n initialized: boolean;\n }>({ left: 0, width: 0, initialized: false });\n\n // Update indicator position when active item changes (web only)\n useEffect(() => {\n if (!isWeb) return;\n\n const activeIndex = items.findIndex((item) => item.id === activeId);\n const activeItemEl = itemRefs.current[activeIndex];\n const containerEl = containerRef.current;\n\n if (activeItemEl && containerEl) {\n const containerRect = containerEl.getBoundingClientRect();\n const itemRect = activeItemEl.getBoundingClientRect();\n\n setIndicatorStyle({\n left: itemRect.left - containerRect.left,\n width: itemRect.width,\n initialized: true,\n });\n }\n }, [activeId, items]);\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 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 (onChange && !items[currentIndex].disabled) {\n onChange(items[currentIndex].id);\n }\n break;\n\n default:\n break;\n }\n },\n [enabledIndices, focusItem, onChange, items]\n );\n\n return (\n <Box\n id={containerId}\n role=\"radiogroup\"\n testID={testID}\n ref={(el: HTMLElement | null) => {\n containerRef.current = el;\n }}\n flexDirection=\"row\"\n alignItems=\"center\"\n flexShrink={0}\n position=\"relative\"\n width={fullWidth ? \"100%\" : \"fit-content\"}\n height={sizeStyles.height}\n backgroundColor={theme.colors.control.segmented.bg}\n borderRadius={sizeStyles.containerRadius}\n padding={sizeStyles.containerPadding}\n borderWidth={1}\n borderColor={theme.colors.border.secondary}\n borderStyle=\"solid\"\n overflow=\"hidden\"\n >\n {/* Sliding indicator (web only) */}\n {isWeb && indicatorStyle.initialized && (\n <Box\n position=\"absolute\"\n zIndex={0}\n height={sizeStyles.itemHeight}\n backgroundColor={theme.colors.control.segmented.bgActive}\n borderRadius={sizeStyles.itemRadius}\n style={{\n left: indicatorStyle.left,\n width: indicatorStyle.width,\n transition: \"left 200ms ease-out, width 200ms ease-out\",\n pointerEvents: \"none\",\n }}\n aria-hidden\n />\n )}\n {items.map((item, index) => {\n const isActive = item.id === activeId;\n const isDisabled = item.disabled;\n const itemId = id ? `${id}-item-${item.id}` : undefined;\n\n const handlePress = () => {\n if (!isDisabled && onChange) {\n onChange(item.id);\n }\n };\n\n const textColor = isDisabled\n ? theme.colors.control.segmented.textDisable\n : theme.colors.control.segmented.text;\n\n return (\n <Box\n key={item.id}\n as=\"button\"\n role=\"radio\"\n id={itemId}\n aria-checked={isActive}\n aria-disabled={isDisabled}\n aria-label={item[\"aria-label\"] || item.label}\n tabIndex={isActive ? 0 : -1}\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 flexShrink={0}\n position=\"relative\"\n zIndex={1}\n height={sizeStyles.itemHeight}\n paddingHorizontal={sizeStyles.itemPaddingHorizontal}\n paddingVertical={sizeStyles.itemPaddingVertical}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n gap={sizeStyles.gap}\n backgroundColor={\n !isWeb && isActive\n ? theme.colors.control.segmented.bgActive\n : \"transparent\"\n }\n borderRadius={sizeStyles.itemRadius}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n hoverStyle={\n !isDisabled && !isActive\n ? {\n backgroundColor: theme.colors.control.segmented.bgHover,\n }\n : undefined\n }\n focusStyle={{\n outlineColor: theme.colors.border.brand,\n outlineWidth: 2,\n outlineOffset: -2,\n }}\n >\n {item.icon && (\n <Icon size={sizeStyles.iconSize} color={textColor} aria-hidden>\n {item.icon}\n </Icon>\n )}\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n fontWeight=\"500\"\n textAlign=\"center\"\n whiteSpace=\"nowrap\"\n overflow=\"hidden\"\n textOverflow=\"ellipsis\"\n >\n {item.label}\n </Text>\n </Box>\n );\n })}\n </Box>\n );\n};\n\nSegmented.displayName = \"Segmented\";\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\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 alignItems,\n justifyContent,\n position,\n top,\n bottom,\n left,\n right,\n width,\n flex,\n overflow,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\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 zIndex,\n height: height as DimensionValue,\n width: width 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 alignItems,\n justifyContent,\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 ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Destructure and drop web-only props from rest before passing to RN components\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 className,\n \"data-testid\": _dataTestId,\n ...nativeRest\n } = rest as Record<string, unknown>;\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 resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n if (onPress) {\n return (\n <Pressable\n onPress={onPress}\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 {...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 {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport { Text as RNText, TextStyle, AccessibilityRole } from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\n// Map web roles to React Native accessibility roles\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n id,\n role,\n ...props\n}) => {\n // Extract the first font name from a comma-separated list (e.g. for web-style font stacks)\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n // On native, if we don't have the custom font loaded, it's better to use the system font\n // to avoid rendering issues or missing text.\n if (resolvedFontFamily === \"Pilat Wide Bold\") {\n resolvedFontFamily = undefined;\n }\n\n const style: TextStyle = {\n 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 };\n\n // Map role to React Native accessibilityRole\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText style={style} testID={id} accessibilityRole={accessibilityRole}>\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({ children, color, size }) => {\n const style: ViewStyle = {\n width: typeof size === \"number\" ? size : undefined,\n height: typeof size === \"number\" ? size : undefined,\n alignItems: \"center\",\n justifyContent: \"center\",\n };\n\n // On native, we try to pass the color down to children (like Text primitives)\n // to mimic the CSS inheritance behavior of the web version.\n const childrenWithProps = React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child, {\n color: child.props.color || color,\n // Also pass size if child seems to be an icon that needs it\n size: child.props.size || size,\n });\n }\n return child;\n });\n\n return <View style={style}>{childrenWithProps}</View>;\n};\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime","RNText","import_react_native","import_jsx_runtime","React","import_jsx_runtime"]}
|
package/native/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/Segmented.tsx
|
|
2
|
-
import { useCallback, useRef } from "react";
|
|
2
|
+
import { useCallback, useRef, useState, useEffect } from "react";
|
|
3
3
|
|
|
4
4
|
// ../primitives-native/src/Box.tsx
|
|
5
5
|
import {
|
|
@@ -233,12 +233,12 @@ var Icon = ({ children, color, size }) => {
|
|
|
233
233
|
// src/Segmented.tsx
|
|
234
234
|
import { useDesignSystem } from "@xsolla/xui-core";
|
|
235
235
|
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
236
|
+
var isWeb = typeof document !== "undefined";
|
|
236
237
|
var Segmented = ({
|
|
237
238
|
items,
|
|
238
239
|
activeId,
|
|
239
240
|
onChange,
|
|
240
241
|
size = "md",
|
|
241
|
-
stroke = false,
|
|
242
242
|
id,
|
|
243
243
|
testID,
|
|
244
244
|
fullWidth = false
|
|
@@ -247,6 +247,23 @@ var Segmented = ({
|
|
|
247
247
|
const sizeStyles = theme.sizing.segmented(size);
|
|
248
248
|
const containerId = id ? `${id}-container` : void 0;
|
|
249
249
|
const itemRefs = useRef([]);
|
|
250
|
+
const containerRef = useRef(null);
|
|
251
|
+
const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0, initialized: false });
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
if (!isWeb) return;
|
|
254
|
+
const activeIndex = items.findIndex((item) => item.id === activeId);
|
|
255
|
+
const activeItemEl = itemRefs.current[activeIndex];
|
|
256
|
+
const containerEl = containerRef.current;
|
|
257
|
+
if (activeItemEl && containerEl) {
|
|
258
|
+
const containerRect = containerEl.getBoundingClientRect();
|
|
259
|
+
const itemRect = activeItemEl.getBoundingClientRect();
|
|
260
|
+
setIndicatorStyle({
|
|
261
|
+
left: itemRect.left - containerRect.left,
|
|
262
|
+
width: itemRect.width,
|
|
263
|
+
initialized: true
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}, [activeId, items]);
|
|
250
267
|
const enabledIndices = items.map((item, index) => !item.disabled ? index : -1).filter((i) => i !== -1);
|
|
251
268
|
const focusItem = useCallback((index) => {
|
|
252
269
|
const element = itemRefs.current[index];
|
|
@@ -287,83 +304,115 @@ var Segmented = ({
|
|
|
287
304
|
},
|
|
288
305
|
[enabledIndices, focusItem, onChange, items]
|
|
289
306
|
);
|
|
290
|
-
return /* @__PURE__ */
|
|
307
|
+
return /* @__PURE__ */ jsxs(
|
|
291
308
|
Box,
|
|
292
309
|
{
|
|
293
310
|
id: containerId,
|
|
294
311
|
role: "radiogroup",
|
|
295
312
|
testID,
|
|
313
|
+
ref: (el) => {
|
|
314
|
+
containerRef.current = el;
|
|
315
|
+
},
|
|
296
316
|
flexDirection: "row",
|
|
297
317
|
alignItems: "center",
|
|
298
318
|
flexShrink: 0,
|
|
319
|
+
position: "relative",
|
|
299
320
|
width: fullWidth ? "100%" : "fit-content",
|
|
300
321
|
height: sizeStyles.height,
|
|
301
|
-
backgroundColor: theme.colors.
|
|
302
|
-
borderRadius:
|
|
303
|
-
padding:
|
|
304
|
-
borderWidth:
|
|
322
|
+
backgroundColor: theme.colors.control.segmented.bg,
|
|
323
|
+
borderRadius: sizeStyles.containerRadius,
|
|
324
|
+
padding: sizeStyles.containerPadding,
|
|
325
|
+
borderWidth: 1,
|
|
305
326
|
borderColor: theme.colors.border.secondary,
|
|
306
327
|
borderStyle: "solid",
|
|
307
328
|
overflow: "hidden",
|
|
308
|
-
children:
|
|
309
|
-
|
|
310
|
-
const isDisabled = item.disabled;
|
|
311
|
-
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
312
|
-
const handlePress = () => {
|
|
313
|
-
if (!isDisabled && onChange) {
|
|
314
|
-
onChange(item.id);
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
const textColor = isDisabled ? theme.colors.content.tertiary : isActive ? theme.colors.content.primary : theme.colors.content.secondary;
|
|
318
|
-
return /* @__PURE__ */ jsxs(
|
|
329
|
+
children: [
|
|
330
|
+
isWeb && indicatorStyle.initialized && /* @__PURE__ */ jsx4(
|
|
319
331
|
Box,
|
|
320
332
|
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
333
|
+
position: "absolute",
|
|
334
|
+
zIndex: 0,
|
|
335
|
+
height: sizeStyles.itemHeight,
|
|
336
|
+
backgroundColor: theme.colors.control.segmented.bgActive,
|
|
337
|
+
borderRadius: sizeStyles.itemRadius,
|
|
338
|
+
style: {
|
|
339
|
+
left: indicatorStyle.left,
|
|
340
|
+
width: indicatorStyle.width,
|
|
341
|
+
transition: "left 200ms ease-out, width 200ms ease-out",
|
|
342
|
+
pointerEvents: "none"
|
|
343
|
+
},
|
|
344
|
+
"aria-hidden": true
|
|
345
|
+
}
|
|
346
|
+
),
|
|
347
|
+
items.map((item, index) => {
|
|
348
|
+
const isActive = item.id === activeId;
|
|
349
|
+
const isDisabled = item.disabled;
|
|
350
|
+
const itemId = id ? `${id}-item-${item.id}` : void 0;
|
|
351
|
+
const handlePress = () => {
|
|
352
|
+
if (!isDisabled && onChange) {
|
|
353
|
+
onChange(item.id);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const textColor = isDisabled ? theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
|
|
357
|
+
return /* @__PURE__ */ jsxs(
|
|
358
|
+
Box,
|
|
359
|
+
{
|
|
360
|
+
as: "button",
|
|
361
|
+
role: "radio",
|
|
362
|
+
id: itemId,
|
|
363
|
+
"aria-checked": isActive,
|
|
364
|
+
"aria-disabled": isDisabled,
|
|
365
|
+
"aria-label": item["aria-label"] || item.label,
|
|
366
|
+
tabIndex: isActive ? 0 : -1,
|
|
367
|
+
disabled: isDisabled,
|
|
368
|
+
ref: (el) => {
|
|
369
|
+
itemRefs.current[index] = el;
|
|
370
|
+
},
|
|
371
|
+
onPress: handlePress,
|
|
372
|
+
onKeyDown: (e) => handleKeyDown(e, index),
|
|
373
|
+
flex: fullWidth ? 1 : void 0,
|
|
374
|
+
flexShrink: 0,
|
|
375
|
+
position: "relative",
|
|
376
|
+
zIndex: 1,
|
|
377
|
+
height: sizeStyles.itemHeight,
|
|
378
|
+
paddingHorizontal: sizeStyles.itemPaddingHorizontal,
|
|
379
|
+
paddingVertical: sizeStyles.itemPaddingVertical,
|
|
380
|
+
flexDirection: "row",
|
|
381
|
+
alignItems: "center",
|
|
382
|
+
justifyContent: "center",
|
|
383
|
+
gap: sizeStyles.gap,
|
|
384
|
+
backgroundColor: !isWeb && isActive ? theme.colors.control.segmented.bgActive : "transparent",
|
|
385
|
+
borderRadius: sizeStyles.itemRadius,
|
|
386
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
387
|
+
hoverStyle: !isDisabled && !isActive ? {
|
|
388
|
+
backgroundColor: theme.colors.control.segmented.bgHover
|
|
389
|
+
} : void 0,
|
|
390
|
+
focusStyle: {
|
|
391
|
+
outlineColor: theme.colors.border.brand,
|
|
392
|
+
outlineWidth: 2,
|
|
393
|
+
outlineOffset: -2
|
|
394
|
+
},
|
|
395
|
+
children: [
|
|
396
|
+
item.icon && /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
|
|
397
|
+
/* @__PURE__ */ jsx4(
|
|
398
|
+
Text,
|
|
399
|
+
{
|
|
400
|
+
color: textColor,
|
|
401
|
+
fontSize: sizeStyles.fontSize,
|
|
402
|
+
fontWeight: "500",
|
|
403
|
+
textAlign: "center",
|
|
404
|
+
whiteSpace: "nowrap",
|
|
405
|
+
overflow: "hidden",
|
|
406
|
+
textOverflow: "ellipsis",
|
|
407
|
+
children: item.label
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
]
|
|
331
411
|
},
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
height: "100%",
|
|
337
|
-
paddingHorizontal: sizeStyles.padding,
|
|
338
|
-
flexDirection: "row",
|
|
339
|
-
alignItems: "center",
|
|
340
|
-
justifyContent: "center",
|
|
341
|
-
gap: 8,
|
|
342
|
-
backgroundColor: isActive ? theme.colors.background.primary : "transparent",
|
|
343
|
-
borderRadius: isActive ? theme.radius.button - (stroke ? 1 : 0) : 0,
|
|
344
|
-
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
345
|
-
boxShadow: isActive ? "0px 1px 2px rgba(0, 0, 0, 0.1)" : "none",
|
|
346
|
-
hoverStyle: !isDisabled && !isActive ? {
|
|
347
|
-
backgroundColor: theme.colors.overlay.mono
|
|
348
|
-
} : void 0,
|
|
349
|
-
children: [
|
|
350
|
-
item.icon && /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
|
|
351
|
-
/* @__PURE__ */ jsx4(
|
|
352
|
-
Text,
|
|
353
|
-
{
|
|
354
|
-
color: textColor,
|
|
355
|
-
fontSize: sizeStyles.fontSize,
|
|
356
|
-
fontWeight: isActive ? "600" : "500",
|
|
357
|
-
textAlign: "center",
|
|
358
|
-
whiteSpace: "nowrap",
|
|
359
|
-
children: item.label
|
|
360
|
-
}
|
|
361
|
-
)
|
|
362
|
-
]
|
|
363
|
-
},
|
|
364
|
-
item.id
|
|
365
|
-
);
|
|
366
|
-
})
|
|
412
|
+
item.id
|
|
413
|
+
);
|
|
414
|
+
})
|
|
415
|
+
]
|
|
367
416
|
}
|
|
368
417
|
);
|
|
369
418
|
};
|