@xsolla/xui-segmented 0.220.0 → 0.221.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/native/index.mjs CHANGED
@@ -357,30 +357,64 @@ var Segmented = ({
357
357
  id,
358
358
  testID,
359
359
  fullWidth = false,
360
+ overflow = "ellipsis",
360
361
  themeMode,
361
362
  themeProductContext
362
363
  }) => {
363
364
  const { theme } = useResolvedTheme({ themeMode, themeProductContext });
364
365
  const sizeStyles = theme.sizing.segmented(size);
366
+ const isWrap = overflow === "wrap";
365
367
  const containerId = id ? `${id}-container` : void 0;
366
368
  const itemRefs = useRef([]);
367
369
  const containerRef = useRef(null);
368
- const [indicatorStyle, setIndicatorStyle] = useState({ left: 0, width: 0, initialized: false });
369
- useEffect(() => {
370
+ const [indicatorStyle, setIndicatorStyle] = useState({
371
+ left: 0,
372
+ top: 0,
373
+ width: 0,
374
+ height: 0,
375
+ initialized: false
376
+ });
377
+ const activeIndex = items.findIndex((item) => item.id === activeId);
378
+ const updateIndicator = useCallback(() => {
370
379
  if (!isWeb) return;
371
- const activeIndex = items.findIndex((item) => item.id === activeId);
372
380
  const activeItemEl = itemRefs.current[activeIndex];
373
381
  const containerEl = containerRef.current;
374
- if (activeItemEl && containerEl) {
375
- const containerRect = containerEl.getBoundingClientRect();
376
- const itemRect = activeItemEl.getBoundingClientRect();
377
- setIndicatorStyle({
378
- left: itemRect.left - containerRect.left,
379
- width: itemRect.width,
380
- initialized: true
381
- });
382
- }
383
- }, [activeId, items]);
382
+ if (!activeItemEl || !containerEl) return;
383
+ const containerRect = containerEl.getBoundingClientRect();
384
+ const itemRect = activeItemEl.getBoundingClientRect();
385
+ const next = {
386
+ left: itemRect.left - containerRect.left - containerEl.clientLeft,
387
+ top: itemRect.top - containerRect.top - containerEl.clientTop,
388
+ width: itemRect.width,
389
+ height: itemRect.height
390
+ };
391
+ setIndicatorStyle(
392
+ (prev) => prev.initialized && prev.left === next.left && prev.top === next.top && prev.width === next.width && prev.height === next.height ? prev : { ...next, initialized: true }
393
+ );
394
+ }, [activeIndex]);
395
+ useEffect(() => {
396
+ if (!isWeb) return;
397
+ updateIndicator();
398
+ if (typeof ResizeObserver === "undefined") return;
399
+ const observer = new ResizeObserver(() => updateIndicator());
400
+ if (containerRef.current) observer.observe(containerRef.current);
401
+ itemRefs.current.slice(0, items.length).forEach((el) => {
402
+ if (el) observer.observe(el);
403
+ });
404
+ return () => observer.disconnect();
405
+ }, [updateIndicator, items.length, overflow, size, fullWidth]);
406
+ useEffect(() => {
407
+ if (!isWeb || typeof document === "undefined") return;
408
+ const fonts = document.fonts;
409
+ if (!fonts) return;
410
+ let cancelled = false;
411
+ fonts.ready.then(() => {
412
+ if (!cancelled) updateIndicator();
413
+ });
414
+ return () => {
415
+ cancelled = true;
416
+ };
417
+ }, [updateIndicator]);
384
418
  const enabledIndices = items.map((item, index) => !item.disabled ? index : -1).filter((i) => i !== -1);
385
419
  const focusItem = useCallback((index) => {
386
420
  const element = itemRefs.current[index];
@@ -421,6 +455,9 @@ var Segmented = ({
421
455
  },
422
456
  [enabledIndices, focusItem, onChange, items]
423
457
  );
458
+ const disableColors = theme.colors.control.disable;
459
+ const isActiveItemDisabled = items.find((item) => item.id === activeId)?.disabled === true;
460
+ const indicatorBg = isActiveItemDisabled ? disableColors.bgActive : theme.colors.control.segmented.bgActive;
424
461
  return /* @__PURE__ */ jsxs(
425
462
  Box,
426
463
  {
@@ -431,11 +468,12 @@ var Segmented = ({
431
468
  containerRef.current = el;
432
469
  },
433
470
  flexDirection: "row",
434
- alignItems: "center",
471
+ alignItems: "stretch",
435
472
  flexShrink: 0,
436
473
  position: "relative",
437
474
  width: fullWidth ? "100%" : "fit-content",
438
- height: sizeStyles.height,
475
+ maxWidth: "100%",
476
+ minHeight: sizeStyles.height,
439
477
  backgroundColor: theme.colors.control.segmented.bg,
440
478
  borderRadius: sizeStyles.containerRadius,
441
479
  padding: sizeStyles.containerPadding,
@@ -449,13 +487,14 @@ var Segmented = ({
449
487
  {
450
488
  position: "absolute",
451
489
  zIndex: 0,
452
- height: sizeStyles.itemHeight,
453
- backgroundColor: theme.colors.control.segmented.bgActive,
490
+ backgroundColor: indicatorBg,
454
491
  borderRadius: sizeStyles.itemRadius,
455
492
  style: {
456
493
  left: indicatorStyle.left,
494
+ top: indicatorStyle.top,
457
495
  width: indicatorStyle.width,
458
- transition: "left 200ms ease-out, width 200ms ease-out",
496
+ height: indicatorStyle.height,
497
+ transition: "left 200ms ease-out, top 200ms ease-out, width 200ms ease-out, height 200ms ease-out",
459
498
  pointerEvents: "none"
460
499
  },
461
500
  "aria-hidden": true
@@ -470,7 +509,7 @@ var Segmented = ({
470
509
  onChange(item.id);
471
510
  }
472
511
  };
473
- const textColor = isDisabled ? theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
512
+ const textColor = isDisabled ? isActive ? disableColors.contentActive : theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
474
513
  return /* @__PURE__ */ jsxs(
475
514
  Box,
476
515
  {
@@ -488,17 +527,18 @@ var Segmented = ({
488
527
  onPress: handlePress,
489
528
  onKeyDown: (e) => handleKeyDown(e, index),
490
529
  flex: fullWidth ? 1 : void 0,
491
- flexShrink: 0,
530
+ flexShrink: 1,
531
+ minWidth: 0,
492
532
  position: "relative",
493
533
  zIndex: 1,
494
- height: sizeStyles.itemHeight,
534
+ minHeight: sizeStyles.itemHeight,
495
535
  paddingHorizontal: sizeStyles.itemPaddingHorizontal,
496
536
  paddingVertical: sizeStyles.itemPaddingVertical,
497
537
  flexDirection: "row",
498
538
  alignItems: "center",
499
539
  justifyContent: "center",
500
540
  gap: sizeStyles.gap,
501
- backgroundColor: !isWeb && isActive ? theme.colors.control.segmented.bgActive : "transparent",
541
+ backgroundColor: !isWeb && isActive ? indicatorBg : "transparent",
502
542
  borderRadius: sizeStyles.itemRadius,
503
543
  cursor: isDisabled ? "not-allowed" : "pointer",
504
544
  hoverStyle: !isDisabled && !isActive ? {
@@ -510,17 +550,32 @@ var Segmented = ({
510
550
  outlineOffset: -2
511
551
  },
512
552
  children: [
513
- item.icon && /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
514
- /* @__PURE__ */ jsx4(
553
+ item.icon && /* @__PURE__ */ jsx4(Box, { flexShrink: 0, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsx4(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }) }),
554
+ isWrap ? /* @__PURE__ */ jsx4(
555
+ Text,
556
+ {
557
+ color: textColor,
558
+ fontSize: sizeStyles.fontSize,
559
+ lineHeight: sizeStyles.lineHeight,
560
+ fontWeight: "500",
561
+ textAlign: "center",
562
+ whiteSpace: "normal",
563
+ style: isWeb ? {
564
+ minWidth: 0,
565
+ overflowWrap: "anywhere",
566
+ textWrap: "balance"
567
+ } : void 0,
568
+ children: item.label
569
+ }
570
+ ) : /* @__PURE__ */ jsx4(
515
571
  Text,
516
572
  {
517
573
  color: textColor,
518
574
  fontSize: sizeStyles.fontSize,
519
575
  fontWeight: "500",
520
576
  textAlign: "center",
521
- whiteSpace: "nowrap",
522
- overflow: "hidden",
523
- textOverflow: "ellipsis",
577
+ numberOfLines: 1,
578
+ title: item.label,
524
579
  children: item.label
525
580
  }
526
581
  )
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Segmented.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/Icon.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["import React, { useCallback, useRef, useState, useEffect } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon, isWeb } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, type ThemeOverrideProps } 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 extends ThemeOverrideProps {\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 id?: string;\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 themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\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 AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({\n children,\n color,\n size,\n testID,\n \"data-testid\": dataTestId,\n}) => {\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 (\n <View style={style} testID={dataTestId || testID}>\n {childrenWithProps}\n </View>\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":["jsx","View","jsx","jsx"]}
1
+ {"version":3,"sources":["../../src/Segmented.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../../../foundation/primitives-native/src/Icon.tsx","../../../../foundation/primitives-native/src/index.tsx"],"sourcesContent":["import React, { useCallback, useRef, useState, useEffect } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text, Icon, isWeb } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, type ThemeOverrideProps } 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\n/**\n * How a label that does not fit its segment is handled.\n * - `\"ellipsis\"`: stays on one line and is truncated with `…`. The full label\n * is shown as a native tooltip on web. Control height never changes.\n * - `\"wrap\"`: wraps onto as many lines as it needs (balanced on web). The\n * control grows in height only when a label actually wraps.\n */\nexport type SegmentedOverflow = \"ellipsis\" | \"wrap\";\n\nexport interface SegmentedProps extends ThemeOverrideProps {\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 id?: string;\n testID?: string;\n /** Full width of the container */\n fullWidth?: boolean;\n /**\n * How labels that don't fit are handled: `\"ellipsis\"` truncates on one line\n * (full text in a tooltip on web), `\"wrap\"` wraps the label and lets the\n * control grow in height. Short labels render identically in both modes.\n * @default \"ellipsis\"\n */\n overflow?: SegmentedOverflow;\n}\n\ninterface IndicatorStyle {\n left: number;\n top: number;\n width: number;\n height: number;\n initialized: 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 overflow = \"ellipsis\",\n themeMode,\n themeProductContext,\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const sizeStyles = theme.sizing.segmented(size);\n const isWrap = overflow === \"wrap\";\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<IndicatorStyle>({\n left: 0,\n top: 0,\n width: 0,\n height: 0,\n initialized: false,\n });\n\n const activeIndex = items.findIndex((item) => item.id === activeId);\n\n // Measure the active item and move the indicator onto it (web only).\n // Positions are relative to the container's padding box (what\n // `position: absolute` resolves against), so the container border is\n // subtracted via clientLeft/clientTop.\n const updateIndicator = useCallback(() => {\n if (!isWeb) return;\n\n const activeItemEl = itemRefs.current[activeIndex];\n const containerEl = containerRef.current;\n if (!activeItemEl || !containerEl) return;\n\n const containerRect = containerEl.getBoundingClientRect();\n const itemRect = activeItemEl.getBoundingClientRect();\n\n const next = {\n left: itemRect.left - containerRect.left - containerEl.clientLeft,\n top: itemRect.top - containerRect.top - containerEl.clientTop,\n width: itemRect.width,\n height: itemRect.height,\n };\n\n setIndicatorStyle((prev) =>\n prev.initialized &&\n prev.left === next.left &&\n prev.top === next.top &&\n prev.width === next.width &&\n prev.height === next.height\n ? prev\n : { ...next, initialized: true }\n );\n }, [activeIndex]);\n\n // Re-measure when the active item changes, and whenever the container or\n // any item resizes afterwards (web font swap, viewport resize, label wrap).\n useEffect(() => {\n if (!isWeb) return;\n\n updateIndicator();\n\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(() => updateIndicator());\n if (containerRef.current) observer.observe(containerRef.current);\n itemRefs.current.slice(0, items.length).forEach((el) => {\n if (el) observer.observe(el);\n });\n\n return () => observer.disconnect();\n }, [updateIndicator, items.length, overflow, size, fullWidth]);\n\n // Web fonts can finish loading after the first measurement.\n useEffect(() => {\n if (!isWeb || typeof document === \"undefined\") return;\n const fonts = (document as Document & { fonts?: FontFaceSet }).fonts;\n if (!fonts) return;\n\n let cancelled = false;\n fonts.ready.then(() => {\n if (!cancelled) updateIndicator();\n });\n return () => {\n cancelled = true;\n };\n }, [updateIndicator]);\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 // XUITSD-68: a disabled item that is also the active one has no\n // segmented-specific tokens in Figma, so the indicator and its label use the\n // shared control.disable active pair. Disabled inactive items keep\n // control.segmented.textDisable.\n const disableColors = theme.colors.control.disable;\n const isActiveItemDisabled =\n items.find((item) => item.id === activeId)?.disabled === true;\n const indicatorBg = isActiveItemDisabled\n ? disableColors.bgActive\n : theme.colors.control.segmented.bgActive;\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=\"stretch\"\n flexShrink={0}\n position=\"relative\"\n width={fullWidth ? \"100%\" : \"fit-content\"}\n maxWidth=\"100%\"\n minHeight={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 backgroundColor={indicatorBg}\n borderRadius={sizeStyles.itemRadius}\n style={{\n left: indicatorStyle.left,\n top: indicatorStyle.top,\n width: indicatorStyle.width,\n height: indicatorStyle.height,\n transition:\n \"left 200ms ease-out, top 200ms ease-out, width 200ms ease-out, height 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 ? isActive\n ? disableColors.contentActive\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={1}\n minWidth={0}\n position=\"relative\"\n zIndex={1}\n minHeight={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={!isWeb && isActive ? indicatorBg : \"transparent\"}\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 <Box flexShrink={0} alignItems=\"center\" justifyContent=\"center\">\n <Icon size={sizeStyles.iconSize} color={textColor} aria-hidden>\n {item.icon}\n </Icon>\n </Box>\n )}\n {isWrap ? (\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n lineHeight={sizeStyles.lineHeight}\n fontWeight=\"500\"\n textAlign=\"center\"\n whiteSpace=\"normal\"\n style={\n isWeb\n ? ({\n minWidth: 0,\n overflowWrap: \"anywhere\",\n textWrap: \"balance\",\n } as React.CSSProperties)\n : undefined\n }\n >\n {item.label}\n </Text>\n ) : (\n <Text\n color={textColor}\n fontSize={sizeStyles.fontSize}\n fontWeight=\"500\"\n textAlign=\"center\"\n numberOfLines={1}\n title={item.label}\n >\n {item.label}\n </Text>\n )}\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 AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import React from \"react\";\nimport { View, ViewStyle } from \"react-native\";\nimport { IconProps } from \"@xsolla/xui-primitives-core\";\n\nexport const Icon: React.FC<IconProps> = ({\n children,\n color,\n size,\n testID,\n \"data-testid\": dataTestId,\n}) => {\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 (\n <View style={style} testID={dataTestId || testID}>\n {childrenWithProps}\n </View>\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":";AAAA,SAAgB,aAAa,QAAQ,UAAU,iBAAiB;;;ACChE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAqND;AAlNN,IAAM,0BAA6D;AAAA,EACjE,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,aAAa,CAAC,QAAyD;AAC3E,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,sBAAsB,KAAK,GAAG;AAC9C,MAAI,QAAS,QAAO,OAAO,QAAQ,CAAC,CAAC;AACrC,QAAM,WAAW,OAAO,GAAG;AAC3B,SAAO,OAAO,SAAS,QAAQ,IAAI,WAAW;AAChD;AASA,IAAM,kBAAkB,CACtB,YAC0B,YAAY,SAAS,SAAS;AAEnD,IAAM,MAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,oBAAoB,CAAC,aAAkC;AAAA,IAC3D,iBACE,WAAW,YAAY,kBACnB,WAAW,kBACX;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,YAAY,WAAW,MAAM;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gBAAgB,OAAO;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,WAAW,GAAG;AAAA,IACnB,GAAI;AAAA,EACN;AAEA,QAAM,cAAc,cAAc;AAKlC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,aAAa;AAAA,IACb;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,oBACH,OAAO,SAAS,YAAY,wBAAwB,IAAI,MACxD,OAAO,WAAW,WAAW;AAChC,QAAM,aAAa,QAAQ,YAAY,YAAY;AACnD,QAAM,qBAAqB;AAAA,IACzB,UAAU,cAAc;AAAA,IACxB,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,0BACJ,aAAa,YAAY,aAAa,cAAc,WAAW;AAGjE,MAAI,OAAO,SAAS,KAAK;AACvB,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI;AAAA,IACN;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,QAChE,YAAW;AAAA,QACV,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AAIA,MAAI,WAAW,OAAO,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,CAAC,cAAc,UAAU,UAAU;AAAA,QAC5C,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,CAAC,EAAE,QAAQ,MAAM,kBAAkB,OAAO;AAAA,QACjD,QAAQ;AAAA,QACR;AAAA,QACA,oBACE,OAAO,cAAc,WAAW,YAAY;AAAA,QAE9C;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,kBAAkB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,MAChE;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;;;ACxRA;AAAA,EACE,QAAQ;AAAA,EAGR;AAAA,OACK;AAqEH,gBAAAA,YAAA;AAlEJ,IAAM,UAA6C;AAAA,EACjD,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AACR;AAEA,IAAM,oBAAoB,CACxB,UACuB;AACvB,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,SAAS,WAAW,KAAK;AAC/B,SAAO,MAAM,MAAM,IAAI,SAAY;AACrC;AAEO,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,OAAO;AAAA,EACP,GAAG;AACL,MAAM;AACJ,MAAI,qBAAqB,aACrB,WAAW,MAAM,GAAG,EAAE,CAAC,EAAE,QAAQ,SAAS,EAAE,EAAE,KAAK,IACnD;AAEJ,MACE,uBAAuB,gBACvB,uBAAuB,qBACvB,uBAAuB,iBACvB;AACA,yBAAqB;AAAA,EACvB;AAEA,QAAM,gBAAgB,WAAW,QAAQ,SAAS;AAElD,QAAM,YAAuB;AAAA,IAC3B,OAAO,SAAS,eAAe;AAAA,IAC/B,UAAU,OAAO,aAAa,WAAW,WAAW;AAAA,IACpD;AAAA,IACA,YAAY;AAAA,IACZ,oBAAoB,MAAM;AAAA,IAC1B,WAAW,aAAa,eAAe;AAAA,IACvC,YAAY,kBAAkB,cAAc,eAAe,UAAU;AAAA,IACrE,WAAW;AAAA,MACT,eAAe;AAAA,IACjB;AAAA,IACA,cAAc;AAAA,MACZ,eAAe;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,oBAAoB,OAAO,QAAQ,IAAI,IAAI;AAEjD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA,QAAQ,cAAc,UAAU;AAAA,MAChC;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;;;ACpFA,OAAO,WAAW;AAClB,SAAS,QAAAC,aAAuB;AA+B5B,gBAAAC,YAAA;AA5BG,IAAM,OAA4B,CAAC;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,MAAM;AACJ,QAAM,QAAmB;AAAA,IACvB,OAAO,OAAO,SAAS,WAAW,OAAO;AAAA,IACzC,QAAQ,OAAO,SAAS,WAAW,OAAO;AAAA,IAC1C,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB;AAIA,QAAM,oBAAoB,MAAM,SAAS,IAAI,UAAU,CAAC,UAAU;AAChE,QAAI,MAAM,eAAe,KAAK,GAAG;AAC/B,aAAO,MAAM,aAAa,OAAO;AAAA,QAC/B,OAAO,MAAM,MAAM,SAAS;AAAA;AAAA,QAE5B,MAAM,MAAM,MAAM,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,CAAC;AAED,SACE,gBAAAA,KAACD,OAAA,EAAK,OAAc,QAAQ,cAAc,QACvC,6BACH;AAEJ;;;AC3BO,IAAM,QAAQ;;;AJNrB,SAAS,wBAAiD;AAoPlD,gBAAAE,MAmCE,YAnCF;AA3LD,IAAM,YAAsC,CAAC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,WAAW;AAAA,EACX;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,IAAI,iBAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,QAAM,aAAa,MAAM,OAAO,UAAU,IAAI;AAC9C,QAAM,SAAS,aAAa;AAE5B,QAAM,cAAc,KAAK,GAAG,EAAE,eAAe;AAC7C,QAAM,WAAW,OAA+B,CAAC,CAAC;AAClD,QAAM,eAAe,OAA2B,IAAI;AAGpD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAyB;AAAA,IACnE,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,aAAa;AAAA,EACf,CAAC;AAED,QAAM,cAAc,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,QAAQ;AAMlE,QAAM,kBAAkB,YAAY,MAAM;AACxC,QAAI,CAAC,MAAO;AAEZ,UAAM,eAAe,SAAS,QAAQ,WAAW;AACjD,UAAM,cAAc,aAAa;AACjC,QAAI,CAAC,gBAAgB,CAAC,YAAa;AAEnC,UAAM,gBAAgB,YAAY,sBAAsB;AACxD,UAAM,WAAW,aAAa,sBAAsB;AAEpD,UAAM,OAAO;AAAA,MACX,MAAM,SAAS,OAAO,cAAc,OAAO,YAAY;AAAA,MACvD,KAAK,SAAS,MAAM,cAAc,MAAM,YAAY;AAAA,MACpD,OAAO,SAAS;AAAA,MAChB,QAAQ,SAAS;AAAA,IACnB;AAEA;AAAA,MAAkB,CAAC,SACjB,KAAK,eACL,KAAK,SAAS,KAAK,QACnB,KAAK,QAAQ,KAAK,OAClB,KAAK,UAAU,KAAK,SACpB,KAAK,WAAW,KAAK,SACjB,OACA,EAAE,GAAG,MAAM,aAAa,KAAK;AAAA,IACnC;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAIhB,YAAU,MAAM;AACd,QAAI,CAAC,MAAO;AAEZ,oBAAgB;AAEhB,QAAI,OAAO,mBAAmB,YAAa;AAE3C,UAAM,WAAW,IAAI,eAAe,MAAM,gBAAgB,CAAC;AAC3D,QAAI,aAAa,QAAS,UAAS,QAAQ,aAAa,OAAO;AAC/D,aAAS,QAAQ,MAAM,GAAG,MAAM,MAAM,EAAE,QAAQ,CAAC,OAAO;AACtD,UAAI,GAAI,UAAS,QAAQ,EAAE;AAAA,IAC7B,CAAC;AAED,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,iBAAiB,MAAM,QAAQ,UAAU,MAAM,SAAS,CAAC;AAG7D,YAAU,MAAM;AACd,QAAI,CAAC,SAAS,OAAO,aAAa,YAAa;AAC/C,UAAM,QAAS,SAAgD;AAC/D,QAAI,CAAC,MAAO;AAEZ,QAAI,YAAY;AAChB,UAAM,MAAM,KAAK,MAAM;AACrB,UAAI,CAAC,UAAW,iBAAgB;AAAA,IAClC,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,iBAAiB,MACpB,IAAI,CAAC,MAAM,UAAW,CAAC,KAAK,WAAW,QAAQ,EAAG,EAClD,OAAO,CAAC,MAAM,MAAM,EAAE;AAEzB,QAAM,YAAY,YAAY,CAAC,UAAkB;AAC/C,UAAM,UAAU,SAAS,QAAQ,KAAK;AACtC,QAAI,SAAS;AACX,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgB;AAAA,IACpB,CAAC,GAAwB,iBAAyB;AAChD,YAAM,sBAAsB,eAAe,QAAQ,YAAY;AAE/D,cAAQ,EAAE,KAAK;AAAA,QACb,KAAK;AAAA,QACL,KAAK;AACH,YAAE,eAAe;AACjB;AACE,kBAAM,mBACJ,sBAAsB,eAAe,SAAS,IAC1C,eAAe,sBAAsB,CAAC,IACtC,eAAe,CAAC;AACtB,sBAAU,gBAAgB;AAAA,UAC5B;AACA;AAAA,QAEF,KAAK;AAAA,QACL,KAAK;AACH,YAAE,eAAe;AACjB;AACE,kBAAM,mBACJ,sBAAsB,IAClB,eAAe,sBAAsB,CAAC,IACtC,eAAe,eAAe,SAAS,CAAC;AAC9C,sBAAU,gBAAgB;AAAA,UAC5B;AACA;AAAA,QAEF,KAAK;AAAA,QACL,KAAK;AACH,YAAE,eAAe;AACjB,cAAI,YAAY,CAAC,MAAM,YAAY,EAAE,UAAU;AAC7C,qBAAS,MAAM,YAAY,EAAE,EAAE;AAAA,UACjC;AACA;AAAA,QAEF;AACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,gBAAgB,WAAW,UAAU,KAAK;AAAA,EAC7C;AAMA,QAAM,gBAAgB,MAAM,OAAO,QAAQ;AAC3C,QAAM,uBACJ,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,QAAQ,GAAG,aAAa;AAC3D,QAAM,cAAc,uBAChB,cAAc,WACd,MAAM,OAAO,QAAQ,UAAU;AAEnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAI;AAAA,MACJ,MAAK;AAAA,MACL;AAAA,MACA,KAAK,CAAC,OAA2B;AAC/B,qBAAa,UAAU;AAAA,MACzB;AAAA,MACA,eAAc;AAAA,MACd,YAAW;AAAA,MACX,YAAY;AAAA,MACZ,UAAS;AAAA,MACT,OAAO,YAAY,SAAS;AAAA,MAC5B,UAAS;AAAA,MACT,WAAW,WAAW;AAAA,MACtB,iBAAiB,MAAM,OAAO,QAAQ,UAAU;AAAA,MAChD,cAAc,WAAW;AAAA,MACzB,SAAS,WAAW;AAAA,MACpB,aAAa;AAAA,MACb,aAAa,MAAM,OAAO,OAAO;AAAA,MACjC,aAAY;AAAA,MACZ,UAAS;AAAA,MAGR;AAAA,iBAAS,eAAe,eACvB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,UAAS;AAAA,YACT,QAAQ;AAAA,YACR,iBAAiB;AAAA,YACjB,cAAc,WAAW;AAAA,YACzB,OAAO;AAAA,cACL,MAAM,eAAe;AAAA,cACrB,KAAK,eAAe;AAAA,cACpB,OAAO,eAAe;AAAA,cACtB,QAAQ,eAAe;AAAA,cACvB,YACE;AAAA,cACF,eAAe;AAAA,YACjB;AAAA,YACA,eAAW;AAAA;AAAA,QACb;AAAA,QAED,MAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,gBAAM,WAAW,KAAK,OAAO;AAC7B,gBAAM,aAAa,KAAK;AACxB,gBAAM,SAAS,KAAK,GAAG,EAAE,SAAS,KAAK,EAAE,KAAK;AAE9C,gBAAM,cAAc,MAAM;AACxB,gBAAI,CAAC,cAAc,UAAU;AAC3B,uBAAS,KAAK,EAAE;AAAA,YAClB;AAAA,UACF;AAEA,gBAAM,YAAY,aACd,WACE,cAAc,gBACd,MAAM,OAAO,QAAQ,UAAU,cACjC,MAAM,OAAO,QAAQ,UAAU;AAEnC,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,IAAG;AAAA,cACH,MAAK;AAAA,cACL,IAAI;AAAA,cACJ,gBAAc;AAAA,cACd,iBAAe;AAAA,cACf,cAAY,KAAK,YAAY,KAAK,KAAK;AAAA,cACvC,UAAU,WAAW,IAAI;AAAA,cACzB,UAAU;AAAA,cACV,KAAK,CAAC,OAA2B;AAC/B,yBAAS,QAAQ,KAAK,IAAI;AAAA,cAC5B;AAAA,cACA,SAAS;AAAA,cACT,WAAW,CAAC,MAA2B,cAAc,GAAG,KAAK;AAAA,cAC7D,MAAM,YAAY,IAAI;AAAA,cACtB,YAAY;AAAA,cACZ,UAAU;AAAA,cACV,UAAS;AAAA,cACT,QAAQ;AAAA,cACR,WAAW,WAAW;AAAA,cACtB,mBAAmB,WAAW;AAAA,cAC9B,iBAAiB,WAAW;AAAA,cAC5B,eAAc;AAAA,cACd,YAAW;AAAA,cACX,gBAAe;AAAA,cACf,KAAK,WAAW;AAAA,cAChB,iBAAiB,CAAC,SAAS,WAAW,cAAc;AAAA,cACpD,cAAc,WAAW;AAAA,cACzB,QAAQ,aAAa,gBAAgB;AAAA,cACrC,YACE,CAAC,cAAc,CAAC,WACZ;AAAA,gBACE,iBAAiB,MAAM,OAAO,QAAQ,UAAU;AAAA,cAClD,IACA;AAAA,cAEN,YAAY;AAAA,gBACV,cAAc,MAAM,OAAO,OAAO;AAAA,gBAClC,cAAc;AAAA,gBACd,eAAe;AAAA,cACjB;AAAA,cAEC;AAAA,qBAAK,QACJ,gBAAAA,KAAC,OAAI,YAAY,GAAG,YAAW,UAAS,gBAAe,UACrD,0BAAAA,KAAC,QAAK,MAAM,WAAW,UAAU,OAAO,WAAW,eAAW,MAC3D,eAAK,MACR,GACF;AAAA,gBAED,SACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,WAAW;AAAA,oBACrB,YAAY,WAAW;AAAA,oBACvB,YAAW;AAAA,oBACX,WAAU;AAAA,oBACV,YAAW;AAAA,oBACX,OACE,QACK;AAAA,sBACC,UAAU;AAAA,sBACV,cAAc;AAAA,sBACd,UAAU;AAAA,oBACZ,IACA;AAAA,oBAGL,eAAK;AAAA;AAAA,gBACR,IAEA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,UAAU,WAAW;AAAA,oBACrB,YAAW;AAAA,oBACX,WAAU;AAAA,oBACV,eAAe;AAAA,oBACf,OAAO,KAAK;AAAA,oBAEX,eAAK;AAAA;AAAA,gBACR;AAAA;AAAA;AAAA,YA/EG,KAAK;AAAA,UAiFZ;AAAA,QAEJ,CAAC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,UAAU,cAAc;","names":["jsx","View","jsx","jsx"]}
package/package.json CHANGED
@@ -1,25 +1,31 @@
1
1
  {
2
2
  "name": "@xsolla/xui-segmented",
3
- "version": "0.220.0",
3
+ "version": "0.221.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "yarn build:web && yarn build:native",
9
9
  "build:web": "PLATFORM=web tsup",
10
- "build:native": "PLATFORM=native tsup"
10
+ "build:native": "PLATFORM=native tsup",
11
+ "test": "vitest"
11
12
  },
12
13
  "dependencies": {
13
- "@xsolla/xui-badge": "0.220.0",
14
- "@xsolla/xui-core": "0.220.0",
15
- "@xsolla/xui-primitives-core": "0.220.0"
14
+ "@xsolla/xui-badge": "0.221.0",
15
+ "@xsolla/xui-core": "0.221.0",
16
+ "@xsolla/xui-primitives-core": "0.221.0"
16
17
  },
17
18
  "peerDependencies": {
18
19
  "react": ">=16.8.0",
19
20
  "styled-components": ">=4"
20
21
  },
21
22
  "devDependencies": {
23
+ "@testing-library/jest-dom": "^6.9.1",
24
+ "@testing-library/react": "^14.1.2",
22
25
  "@vitest/coverage-v8": "^4.0.18",
26
+ "jsdom": "^24.0.0",
27
+ "react": "^18.0.0",
28
+ "react-dom": "^18.0.0",
23
29
  "tsup": "^8.0.0",
24
30
  "vitest": "^4.0.18"
25
31
  },
package/web/index.d.mts CHANGED
@@ -13,6 +13,14 @@ interface SegmentedItemType {
13
13
  /** Accessible label for screen readers */
14
14
  "aria-label"?: string;
15
15
  }
16
+ /**
17
+ * How a label that does not fit its segment is handled.
18
+ * - `"ellipsis"`: stays on one line and is truncated with `…`. The full label
19
+ * is shown as a native tooltip on web. Control height never changes.
20
+ * - `"wrap"`: wraps onto as many lines as it needs (balanced on web). The
21
+ * control grows in height only when a label actually wraps.
22
+ */
23
+ type SegmentedOverflow = "ellipsis" | "wrap";
16
24
  interface SegmentedProps extends ThemeOverrideProps {
17
25
  /** Array of items */
18
26
  items: SegmentedItemType[];
@@ -26,10 +34,17 @@ interface SegmentedProps extends ThemeOverrideProps {
26
34
  testID?: string;
27
35
  /** Full width of the container */
28
36
  fullWidth?: boolean;
37
+ /**
38
+ * How labels that don't fit are handled: `"ellipsis"` truncates on one line
39
+ * (full text in a tooltip on web), `"wrap"` wraps the label and lets the
40
+ * control grow in height. Short labels render identically in both modes.
41
+ * @default "ellipsis"
42
+ */
43
+ overflow?: SegmentedOverflow;
29
44
  }
30
45
  /**
31
46
  * Segmented - A control for switching between related views or filters
32
47
  */
33
48
  declare const Segmented: React.FC<SegmentedProps>;
34
49
 
35
- export { Segmented, type SegmentedItemType, type SegmentedProps };
50
+ export { Segmented, type SegmentedItemType, type SegmentedOverflow, type SegmentedProps };
package/web/index.d.ts CHANGED
@@ -13,6 +13,14 @@ interface SegmentedItemType {
13
13
  /** Accessible label for screen readers */
14
14
  "aria-label"?: string;
15
15
  }
16
+ /**
17
+ * How a label that does not fit its segment is handled.
18
+ * - `"ellipsis"`: stays on one line and is truncated with `…`. The full label
19
+ * is shown as a native tooltip on web. Control height never changes.
20
+ * - `"wrap"`: wraps onto as many lines as it needs (balanced on web). The
21
+ * control grows in height only when a label actually wraps.
22
+ */
23
+ type SegmentedOverflow = "ellipsis" | "wrap";
16
24
  interface SegmentedProps extends ThemeOverrideProps {
17
25
  /** Array of items */
18
26
  items: SegmentedItemType[];
@@ -26,10 +34,17 @@ interface SegmentedProps extends ThemeOverrideProps {
26
34
  testID?: string;
27
35
  /** Full width of the container */
28
36
  fullWidth?: boolean;
37
+ /**
38
+ * How labels that don't fit are handled: `"ellipsis"` truncates on one line
39
+ * (full text in a tooltip on web), `"wrap"` wraps the label and lets the
40
+ * control grow in height. Short labels render identically in both modes.
41
+ * @default "ellipsis"
42
+ */
43
+ overflow?: SegmentedOverflow;
29
44
  }
30
45
  /**
31
46
  * Segmented - A control for switching between related views or filters
32
47
  */
33
48
  declare const Segmented: React.FC<SegmentedProps>;
34
49
 
35
- export { Segmented, type SegmentedItemType, type SegmentedProps };
50
+ export { Segmented, type SegmentedItemType, type SegmentedOverflow, type SegmentedProps };
package/web/index.js CHANGED
@@ -416,30 +416,64 @@ var Segmented = ({
416
416
  id,
417
417
  testID,
418
418
  fullWidth = false,
419
+ overflow = "ellipsis",
419
420
  themeMode,
420
421
  themeProductContext
421
422
  }) => {
422
423
  const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
423
424
  const sizeStyles = theme.sizing.segmented(size);
425
+ const isWrap = overflow === "wrap";
424
426
  const containerId = id ? `${id}-container` : void 0;
425
427
  const itemRefs = (0, import_react3.useRef)([]);
426
428
  const containerRef = (0, import_react3.useRef)(null);
427
- const [indicatorStyle, setIndicatorStyle] = (0, import_react3.useState)({ left: 0, width: 0, initialized: false });
428
- (0, import_react3.useEffect)(() => {
429
+ const [indicatorStyle, setIndicatorStyle] = (0, import_react3.useState)({
430
+ left: 0,
431
+ top: 0,
432
+ width: 0,
433
+ height: 0,
434
+ initialized: false
435
+ });
436
+ const activeIndex = items.findIndex((item) => item.id === activeId);
437
+ const updateIndicator = (0, import_react3.useCallback)(() => {
429
438
  if (!isWeb) return;
430
- const activeIndex = items.findIndex((item) => item.id === activeId);
431
439
  const activeItemEl = itemRefs.current[activeIndex];
432
440
  const containerEl = containerRef.current;
433
- if (activeItemEl && containerEl) {
434
- const containerRect = containerEl.getBoundingClientRect();
435
- const itemRect = activeItemEl.getBoundingClientRect();
436
- setIndicatorStyle({
437
- left: itemRect.left - containerRect.left,
438
- width: itemRect.width,
439
- initialized: true
440
- });
441
- }
442
- }, [activeId, items]);
441
+ if (!activeItemEl || !containerEl) return;
442
+ const containerRect = containerEl.getBoundingClientRect();
443
+ const itemRect = activeItemEl.getBoundingClientRect();
444
+ const next = {
445
+ left: itemRect.left - containerRect.left - containerEl.clientLeft,
446
+ top: itemRect.top - containerRect.top - containerEl.clientTop,
447
+ width: itemRect.width,
448
+ height: itemRect.height
449
+ };
450
+ setIndicatorStyle(
451
+ (prev) => prev.initialized && prev.left === next.left && prev.top === next.top && prev.width === next.width && prev.height === next.height ? prev : { ...next, initialized: true }
452
+ );
453
+ }, [activeIndex]);
454
+ (0, import_react3.useEffect)(() => {
455
+ if (!isWeb) return;
456
+ updateIndicator();
457
+ if (typeof ResizeObserver === "undefined") return;
458
+ const observer = new ResizeObserver(() => updateIndicator());
459
+ if (containerRef.current) observer.observe(containerRef.current);
460
+ itemRefs.current.slice(0, items.length).forEach((el) => {
461
+ if (el) observer.observe(el);
462
+ });
463
+ return () => observer.disconnect();
464
+ }, [updateIndicator, items.length, overflow, size, fullWidth]);
465
+ (0, import_react3.useEffect)(() => {
466
+ if (!isWeb || typeof document === "undefined") return;
467
+ const fonts = document.fonts;
468
+ if (!fonts) return;
469
+ let cancelled = false;
470
+ fonts.ready.then(() => {
471
+ if (!cancelled) updateIndicator();
472
+ });
473
+ return () => {
474
+ cancelled = true;
475
+ };
476
+ }, [updateIndicator]);
443
477
  const enabledIndices = items.map((item, index2) => !item.disabled ? index2 : -1).filter((i) => i !== -1);
444
478
  const focusItem = (0, import_react3.useCallback)((index2) => {
445
479
  const element = itemRefs.current[index2];
@@ -480,6 +514,9 @@ var Segmented = ({
480
514
  },
481
515
  [enabledIndices, focusItem, onChange, items]
482
516
  );
517
+ const disableColors = theme.colors.control.disable;
518
+ const isActiveItemDisabled = items.find((item) => item.id === activeId)?.disabled === true;
519
+ const indicatorBg = isActiveItemDisabled ? disableColors.bgActive : theme.colors.control.segmented.bgActive;
483
520
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
484
521
  Box,
485
522
  {
@@ -490,11 +527,12 @@ var Segmented = ({
490
527
  containerRef.current = el;
491
528
  },
492
529
  flexDirection: "row",
493
- alignItems: "center",
530
+ alignItems: "stretch",
494
531
  flexShrink: 0,
495
532
  position: "relative",
496
533
  width: fullWidth ? "100%" : "fit-content",
497
- height: sizeStyles.height,
534
+ maxWidth: "100%",
535
+ minHeight: sizeStyles.height,
498
536
  backgroundColor: theme.colors.control.segmented.bg,
499
537
  borderRadius: sizeStyles.containerRadius,
500
538
  padding: sizeStyles.containerPadding,
@@ -508,13 +546,14 @@ var Segmented = ({
508
546
  {
509
547
  position: "absolute",
510
548
  zIndex: 0,
511
- height: sizeStyles.itemHeight,
512
- backgroundColor: theme.colors.control.segmented.bgActive,
549
+ backgroundColor: indicatorBg,
513
550
  borderRadius: sizeStyles.itemRadius,
514
551
  style: {
515
552
  left: indicatorStyle.left,
553
+ top: indicatorStyle.top,
516
554
  width: indicatorStyle.width,
517
- transition: "left 200ms ease-out, width 200ms ease-out",
555
+ height: indicatorStyle.height,
556
+ transition: "left 200ms ease-out, top 200ms ease-out, width 200ms ease-out, height 200ms ease-out",
518
557
  pointerEvents: "none"
519
558
  },
520
559
  "aria-hidden": true
@@ -529,7 +568,7 @@ var Segmented = ({
529
568
  onChange(item.id);
530
569
  }
531
570
  };
532
- const textColor = isDisabled ? theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
571
+ const textColor = isDisabled ? isActive ? disableColors.contentActive : theme.colors.control.segmented.textDisable : theme.colors.control.segmented.text;
533
572
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
534
573
  Box,
535
574
  {
@@ -547,17 +586,18 @@ var Segmented = ({
547
586
  onPress: handlePress,
548
587
  onKeyDown: (e) => handleKeyDown(e, index2),
549
588
  flex: fullWidth ? 1 : void 0,
550
- flexShrink: 0,
589
+ flexShrink: 1,
590
+ minWidth: 0,
551
591
  position: "relative",
552
592
  zIndex: 1,
553
- height: sizeStyles.itemHeight,
593
+ minHeight: sizeStyles.itemHeight,
554
594
  paddingHorizontal: sizeStyles.itemPaddingHorizontal,
555
595
  paddingVertical: sizeStyles.itemPaddingVertical,
556
596
  flexDirection: "row",
557
597
  alignItems: "center",
558
598
  justifyContent: "center",
559
599
  gap: sizeStyles.gap,
560
- backgroundColor: !isWeb && isActive ? theme.colors.control.segmented.bgActive : "transparent",
600
+ backgroundColor: !isWeb && isActive ? indicatorBg : "transparent",
561
601
  borderRadius: sizeStyles.itemRadius,
562
602
  cursor: isDisabled ? "not-allowed" : "pointer",
563
603
  hoverStyle: !isDisabled && !isActive ? {
@@ -569,17 +609,32 @@ var Segmented = ({
569
609
  outlineOffset: -2
570
610
  },
571
611
  children: [
572
- item.icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }),
573
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
612
+ item.icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { flexShrink: 0, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: sizeStyles.iconSize, color: textColor, "aria-hidden": true, children: item.icon }) }),
613
+ isWrap ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
614
+ Text,
615
+ {
616
+ color: textColor,
617
+ fontSize: sizeStyles.fontSize,
618
+ lineHeight: sizeStyles.lineHeight,
619
+ fontWeight: "500",
620
+ textAlign: "center",
621
+ whiteSpace: "normal",
622
+ style: isWeb ? {
623
+ minWidth: 0,
624
+ overflowWrap: "anywhere",
625
+ textWrap: "balance"
626
+ } : void 0,
627
+ children: item.label
628
+ }
629
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
574
630
  Text,
575
631
  {
576
632
  color: textColor,
577
633
  fontSize: sizeStyles.fontSize,
578
634
  fontWeight: "500",
579
635
  textAlign: "center",
580
- whiteSpace: "nowrap",
581
- overflow: "hidden",
582
- textOverflow: "ellipsis",
636
+ numberOfLines: 1,
637
+ title: item.label,
583
638
  children: item.label
584
639
  }
585
640
  )