@xsolla/xui-b2c-card-selector 0.218.1 → 0.219.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/native/index.js CHANGED
@@ -63,6 +63,7 @@ var toGapValue = (gap) => {
63
63
  const asNumber = Number(gap);
64
64
  return Number.isFinite(asNumber) ? asNumber : void 0;
65
65
  };
66
+ var toNativeDisplay = (display) => display === "none" ? "none" : void 0;
66
67
  var Box = ({
67
68
  children,
68
69
  onPress,
@@ -94,6 +95,7 @@ var Box = ({
94
95
  marginBottom,
95
96
  marginLeft,
96
97
  marginRight,
98
+ display,
97
99
  flexDirection,
98
100
  flexWrap,
99
101
  alignItems,
@@ -157,6 +159,7 @@ var Box = ({
157
159
  marginBottom,
158
160
  marginLeft,
159
161
  marginRight,
162
+ display: toNativeDisplay(display),
160
163
  flexDirection,
161
164
  flexWrap,
162
165
  alignItems,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.tsx","../../src/CardSelector.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/CardSelectorGroupContext.tsx","../../src/CardSelectorGroup.tsx"],"sourcesContent":["export { CardSelector } from \"./CardSelector\";\nexport { CardSelectorGroup } from \"./CardSelectorGroup\";\n\nexport type {\n CardSelectorControlPlacement,\n CardSelectorGroupProps,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorState,\n CardSelectorType,\n} from \"./types\";\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, radiusScale } from \"@xsolla/xui-core\";\nimport {\n useCardSelectorGroup,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorType,\n} from \"./types\";\n\ntype ResolvedTheme = ReturnType<typeof useResolvedTheme>[\"theme\"];\n\n/**\n * Figma `CardSelector` (3446:1761). The component set is fixed-width per\n * `Platform`; the exact frame widths could not be read back from the Figma\n * REST API (the node payload exceeds the tool's response budget), so the card\n * is fluid by default and the consumer sizes it via `width` or the grid cell.\n * `Platform` therefore drives the internal proportions only.\n */\ninterface PlatformMetrics {\n /** Maps onto the shared checkbox/radio size scale so the shape tokens line up. */\n controlSize: \"md\" | \"lg\";\n controlBox: number;\n controlIcon: number;\n titleFontSize: number;\n titleLineHeight: number;\n descriptionFontSize: number;\n descriptionLineHeight: number;\n padding: number;\n gap: number;\n radius: number;\n}\n\nconst platformMetrics: Record<CardSelectorPlatform, PlatformMetrics> = {\n mobile: {\n controlSize: \"md\",\n controlBox: 18,\n controlIcon: 12,\n titleFontSize: 14,\n titleLineHeight: 16,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 8,\n gap: 8,\n radius: 8,\n },\n desktop: {\n controlSize: \"lg\",\n controlBox: 20,\n controlIcon: 14,\n titleFontSize: 16,\n titleLineHeight: 20,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 10,\n gap: 8,\n radius: 8,\n },\n};\n\n/** Inline 12×12 glyph so the stroke scales with the control, mirroring Checkbox. */\nconst CheckGlyph: React.FC<{ size: number; color: string }> = ({\n size,\n color,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M10.6699 4.2959L6.00293 8.96387C5.72637 9.24043 5.27761 9.24031 5.00098 8.96387L2 5.96289L3.2959 4.66699L5.50098 6.87207L9.37402 3L10.6699 4.2959Z\"\n fill={color}\n />\n </svg>\n);\n\n/**\n * The selection control is decorative: `aria-checked` lives on the card\n * itself, so exposing a second checkbox/radio here would double up the\n * semantics. It exists purely so selection never relies on colour alone.\n */\nconst SelectionControl: React.FC<{\n type: CardSelectorType;\n checked: boolean;\n disabled: boolean;\n metrics: PlatformMetrics;\n theme: ResolvedTheme;\n}> = ({ type, checked, disabled, metrics, theme }) => {\n const check = theme.colors.control.check;\n const faint = theme.colors.control.faint;\n\n const backgroundColor = disabled\n ? check.bgDisable\n : checked\n ? check.bg\n : faint.bg;\n const borderColor = disabled\n ? check.borderDisable\n : checked\n ? check.border\n : faint.border;\n const iconColor = disabled ? theme.colors.control.text.disable : check.icon;\n\n return (\n <Box\n width={metrics.controlBox}\n height={metrics.controlBox}\n flexShrink={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={1}\n borderStyle=\"solid\"\n borderColor={borderColor}\n backgroundColor={backgroundColor}\n borderRadius={\n type === \"radio\"\n ? radiusScale[\"999\"]\n : theme.shape.checkbox[metrics.controlSize].borderRadius\n }\n aria-hidden=\"true\"\n testID=\"card-selector-control\"\n >\n {checked &&\n (type === \"radio\" ? (\n <Box\n width={metrics.controlIcon}\n height={metrics.controlIcon}\n borderRadius={radiusScale[\"999\"]}\n backgroundColor={iconColor}\n />\n ) : (\n <CheckGlyph size={metrics.controlIcon} color={iconColor} />\n ))}\n </Box>\n );\n};\n\nconst asPlainText = (value: React.ReactNode): string | undefined =>\n typeof value === \"string\" ? value : undefined;\n\nexport const CardSelector: React.FC<CardSelectorProps> = ({\n title,\n description,\n imageSrc,\n imageAlt = \"\",\n value,\n type: typeProp,\n control: controlProp,\n platform: platformProp,\n checked: checkedProp,\n defaultChecked = false,\n onChange,\n state = \"default\",\n disabled,\n width = \"100%\",\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const group = useCardSelectorGroup();\n const nodeRef = useRef<HTMLDivElement | null>(null);\n\n const type: CardSelectorType = typeProp ?? group?.type ?? \"radio\";\n const control: CardSelectorControlPlacement =\n controlProp ?? group?.control ?? \"text\";\n const platform: CardSelectorPlatform =\n platformProp ?? group?.platform ?? \"mobile\";\n const metrics = platformMetrics[platform];\n\n const isDisabled =\n Boolean(disabled) || state === \"disabled\" || Boolean(group?.disabled);\n\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isGroupControlled =\n group !== null && type === \"radio\" && value !== undefined;\n const isControlled = checkedProp !== undefined;\n const checked = isControlled\n ? Boolean(checkedProp)\n : isGroupControlled\n ? group?.selectedValue === value\n : internalChecked;\n\n // Radio cards in a group are mutually exclusive, so re-pressing the selected\n // card must not clear the group's selection.\n const toggle = useCallback(() => {\n if (isDisabled) return;\n if (type === \"radio\" && checked && isGroupControlled) return;\n\n const next = type === \"radio\" ? true : !checked;\n\n if (!isControlled && !isGroupControlled) {\n setInternalChecked(next);\n }\n if (group && isGroupControlled && value !== undefined) {\n group.select(value);\n }\n onChange?.(next, value);\n }, [\n checked,\n group,\n isControlled,\n isDisabled,\n isGroupControlled,\n onChange,\n type,\n value,\n ]);\n\n const focus = useCallback(() => {\n const node = nodeRef.current;\n if (node && typeof node.focus === \"function\") node.focus();\n }, []);\n\n // Registered once per mount so the group's Arrow-key order matches DOM\n // order; the entry's fields are refreshed in place on every render.\n const itemRef = useRef<CardSelectorGroupItem>({\n value: value ?? \"\",\n disabled: Boolean(isDisabled),\n focus,\n });\n\n useEffect(() => {\n itemRef.current.value = value ?? \"\";\n itemRef.current.disabled = isDisabled;\n itemRef.current.focus = focus;\n });\n\n const registerItem = group?.register;\n useEffect(() => {\n if (!registerItem) return undefined;\n return registerItem(itemRef.current);\n }, [registerItem]);\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n const key = event.key;\n if (\n key === \" \" ||\n key === \"Spacebar\" ||\n key === \"Space\" ||\n key === \"Enter\"\n ) {\n event.preventDefault();\n toggle();\n return;\n }\n\n // Within a radio group, Arrow keys move focus and selection between cards.\n if (!group || type !== \"radio\") return;\n const delta =\n key === \"ArrowRight\" || key === \"ArrowDown\"\n ? 1\n : key === \"ArrowLeft\" || key === \"ArrowUp\"\n ? -1\n : 0;\n if (delta === 0) return;\n event.preventDefault();\n group.moveFocus(value, delta);\n };\n\n const showControl = control === \"text\" || checked;\n const isOnImage = control === \"onImage\";\n\n // Selected takes visual priority over hover.\n const surfaceBackground = checked\n ? theme.colors.overlay.brand\n : state === \"press\"\n ? theme.colors.overlay.brand\n : state === \"hover\"\n ? theme.colors.overlay.monoHover\n : theme.colors.overlay.mono;\n\n const titleColor =\n isOnImage && checked\n ? theme.colors.control.check.bg\n : theme.colors.content.primary;\n\n const derivedLabel = [asPlainText(title), asPlainText(description)]\n .filter(Boolean)\n .join(\", \");\n\n const media = (\n <Box\n position=\"relative\"\n width=\"100%\"\n overflow=\"hidden\"\n borderRadius={isOnImage ? metrics.radius : 0}\n style={{ aspectRatio: 1 }}\n testID=\"card-selector-media\"\n >\n {imageSrc && (\n <Box\n as=\"img\"\n src={imageSrc}\n alt={imageAlt}\n position=\"absolute\"\n top={0}\n left={0}\n width=\"100%\"\n height=\"100%\"\n style={{ objectFit: \"cover\", pointerEvents: \"none\" }}\n testID=\"card-selector-image\"\n />\n )}\n\n {isOnImage && showControl && (\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n </Box>\n )}\n </Box>\n );\n\n const label = (\n <Box flexDirection=\"column\" gap={2} flexShrink={1} overflow=\"hidden\">\n <Text\n color={titleColor}\n fontFamily={theme.fonts.body}\n fontSize={metrics.titleFontSize}\n lineHeight={metrics.titleLineHeight}\n fontWeight=\"500\"\n numberOfLines={1}\n testID=\"card-selector-title\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={theme.colors.content.tertiary}\n fontFamily={theme.fonts.body}\n fontSize={metrics.descriptionFontSize}\n lineHeight={metrics.descriptionLineHeight}\n fontWeight=\"400\"\n numberOfLines={1}\n testID=\"card-selector-description\"\n >\n {description}\n </Text>\n )}\n </Box>\n );\n\n // Roving tabindex: exactly one radio in a group is tabbable — the selected\n // card, or the first enabled card while the group holds no selection.\n const hasGroupSelection = group?.selectedValue !== undefined;\n const isRovingTarget =\n checked || (!hasGroupSelection && Boolean(group?.isFirstEnabled(value)));\n const isRovingRadio = Boolean(group) && type === \"radio\";\n const tabIndex = isDisabled || (isRovingRadio && !isRovingTarget) ? -1 : 0;\n\n return (\n <Box\n ref={nodeRef}\n width={width}\n flexDirection=\"column\"\n gap={isOnImage ? metrics.gap : 0}\n opacity={isDisabled ? 0.48 : 1}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n role={type === \"radio\" ? \"radio\" : \"checkbox\"}\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel || derivedLabel || undefined}\n tabIndex={tabIndex}\n onPress={isDisabled ? undefined : toggle}\n onKeyDown={handleKeyDown}\n focusStyle={{\n outlineColor: theme.colors.border.brand,\n outlineWidth: 2,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n }}\n testID={testID || \"card-selector\"}\n >\n <Box\n flexDirection=\"column\"\n overflow=\"hidden\"\n borderRadius={metrics.radius}\n borderWidth={2}\n borderStyle=\"solid\"\n borderColor={checked ? theme.colors.border.brand : \"transparent\"}\n backgroundColor={surfaceBackground}\n hoverStyle={\n !isDisabled && !checked && !isOnImage\n ? { backgroundColor: theme.colors.overlay.monoHover }\n : undefined\n }\n pressStyle={\n !isDisabled && !isOnImage\n ? { backgroundColor: theme.colors.overlay.brand }\n : undefined\n }\n testID=\"card-selector-surface\"\n >\n {media}\n\n {!isOnImage && (\n <Box\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={metrics.gap}\n padding={metrics.padding}\n testID=\"card-selector-bar\"\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n {label}\n </Box>\n )}\n </Box>\n\n {isOnImage && label}\n </Box>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import { createContext, useContext } from \"react\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorType,\n} from \"./types\";\n\nexport interface CardSelectorGroupItem {\n value: string;\n disabled: boolean;\n focus: () => void;\n}\n\nexport interface CardSelectorGroupContextValue {\n type: CardSelectorType;\n platform?: CardSelectorPlatform;\n control?: CardSelectorControlPlacement;\n disabled: boolean;\n /** Selected value, `radio` only. */\n selectedValue?: string;\n /** Selects a value, `radio` only. */\n select: (value: string) => void;\n /** Registers a card so the group can drive roving focus. Returns an unregister fn. */\n register: (item: CardSelectorGroupItem) => () => void;\n /** Moves focus (and, for `radio`, selection) by `delta` positions, wrapping. */\n moveFocus: (fromValue: string | undefined, delta: number) => void;\n /** True when the group holds no selection — the first enabled card stays tabbable. */\n isFirstEnabled: (value: string | undefined) => boolean;\n}\n\nexport const CardSelectorGroupContext =\n createContext<CardSelectorGroupContextValue | null>(null);\n\nexport const useCardSelectorGroup = (): CardSelectorGroupContextValue | null =>\n useContext(CardSelectorGroupContext);\n","import React, { useCallback, useMemo, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CardSelectorGroupContext,\n type CardSelectorGroupContextValue,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type { CardSelectorGroupProps } from \"./types\";\n\n/**\n * Wraps a set of `CardSelector` cards.\n *\n * For `type=\"radio\"` it renders a `radiogroup`, keeps the cards mutually\n * exclusive, and implements the roving-tabindex + Arrow-key behaviour the\n * Figma spec requires (3446:1761 — \"within a Radio group, Arrow keys move\n * focus and selection between cards\"). For `type=\"check\"` the cards toggle\n * independently and the wrapper is a plain `group`.\n */\nexport const CardSelectorGroup: React.FC<CardSelectorGroupProps> = ({\n children,\n type = \"radio\",\n value,\n defaultValue,\n onValueChange,\n platform,\n control,\n disabled = false,\n gap = 12,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n // Resolved so the group participates in the same theme scope as its cards.\n useResolvedTheme({ themeMode, themeProductContext });\n\n // Registration lives in state, not a ref, so the roving tabindex is correct\n // on the render right after the cards mount.\n const [items, setItems] = useState<CardSelectorGroupItem[]>([]);\n const [internalValue, setInternalValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const selectedValue = isControlled ? value : internalValue;\n\n const register = useCallback((item: CardSelectorGroupItem) => {\n setItems((prev) => [...prev, item]);\n return () => setItems((prev) => prev.filter((entry) => entry !== item));\n }, []);\n\n const select = useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange]\n );\n\n const moveFocus = useCallback(\n (fromValue: string | undefined, delta: number) => {\n const enabled = items.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex(\n (item) => item.value === fromValue\n );\n const nextIndex =\n currentIndex === -1\n ? 0\n : (currentIndex + delta + enabled.length) % enabled.length;\n const next = enabled[nextIndex];\n if (!next) return;\n\n next.focus();\n if (type === \"radio\" && next.value) select(next.value);\n },\n [items, select, type]\n );\n\n const isFirstEnabled = useCallback(\n (candidate: string | undefined) => {\n const first = items.find((item) => !item.disabled);\n return Boolean(first) && first?.value === candidate;\n },\n [items]\n );\n\n const contextValue = useMemo<CardSelectorGroupContextValue>(\n () => ({\n type,\n platform,\n control,\n disabled,\n selectedValue,\n select,\n register,\n moveFocus,\n isFirstEnabled,\n }),\n [\n control,\n disabled,\n isFirstEnabled,\n moveFocus,\n platform,\n register,\n select,\n selectedValue,\n type,\n ]\n );\n\n return (\n <CardSelectorGroupContext.Provider value={contextValue}>\n <Box\n role={type === \"radio\" ? \"radiogroup\" : \"group\"}\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n alignItems=\"flex-start\"\n gap={gap}\n testID={testID || \"card-selector-group\"}\n >\n {children}\n </Box>\n </CardSelectorGroupContext.Provider>\n );\n};\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","import_react","import_xui_core","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/index.tsx","../../src/CardSelector.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/CardSelectorGroupContext.tsx","../../src/CardSelectorGroup.tsx"],"sourcesContent":["export { CardSelector } from \"./CardSelector\";\nexport { CardSelectorGroup } from \"./CardSelectorGroup\";\n\nexport type {\n CardSelectorControlPlacement,\n CardSelectorGroupProps,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorState,\n CardSelectorType,\n} from \"./types\";\n","import React, { useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, radiusScale } from \"@xsolla/xui-core\";\nimport {\n useCardSelectorGroup,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorType,\n} from \"./types\";\n\ntype ResolvedTheme = ReturnType<typeof useResolvedTheme>[\"theme\"];\n\n/**\n * Figma `CardSelector` (3446:1761). The component set is fixed-width per\n * `Platform`; the exact frame widths could not be read back from the Figma\n * REST API (the node payload exceeds the tool's response budget), so the card\n * is fluid by default and the consumer sizes it via `width` or the grid cell.\n * `Platform` therefore drives the internal proportions only.\n */\ninterface PlatformMetrics {\n /** Maps onto the shared checkbox/radio size scale so the shape tokens line up. */\n controlSize: \"md\" | \"lg\";\n controlBox: number;\n controlIcon: number;\n titleFontSize: number;\n titleLineHeight: number;\n descriptionFontSize: number;\n descriptionLineHeight: number;\n padding: number;\n gap: number;\n radius: number;\n}\n\nconst platformMetrics: Record<CardSelectorPlatform, PlatformMetrics> = {\n mobile: {\n controlSize: \"md\",\n controlBox: 18,\n controlIcon: 12,\n titleFontSize: 14,\n titleLineHeight: 16,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 8,\n gap: 8,\n radius: 8,\n },\n desktop: {\n controlSize: \"lg\",\n controlBox: 20,\n controlIcon: 14,\n titleFontSize: 16,\n titleLineHeight: 20,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 10,\n gap: 8,\n radius: 8,\n },\n};\n\n/** Inline 12×12 glyph so the stroke scales with the control, mirroring Checkbox. */\nconst CheckGlyph: React.FC<{ size: number; color: string }> = ({\n size,\n color,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M10.6699 4.2959L6.00293 8.96387C5.72637 9.24043 5.27761 9.24031 5.00098 8.96387L2 5.96289L3.2959 4.66699L5.50098 6.87207L9.37402 3L10.6699 4.2959Z\"\n fill={color}\n />\n </svg>\n);\n\n/**\n * The selection control is decorative: `aria-checked` lives on the card\n * itself, so exposing a second checkbox/radio here would double up the\n * semantics. It exists purely so selection never relies on colour alone.\n */\nconst SelectionControl: React.FC<{\n type: CardSelectorType;\n checked: boolean;\n disabled: boolean;\n metrics: PlatformMetrics;\n theme: ResolvedTheme;\n}> = ({ type, checked, disabled, metrics, theme }) => {\n const check = theme.colors.control.check;\n const faint = theme.colors.control.faint;\n\n const backgroundColor = disabled\n ? check.bgDisable\n : checked\n ? check.bg\n : faint.bg;\n const borderColor = disabled\n ? check.borderDisable\n : checked\n ? check.border\n : faint.border;\n const iconColor = disabled ? theme.colors.control.text.disable : check.icon;\n\n return (\n <Box\n width={metrics.controlBox}\n height={metrics.controlBox}\n flexShrink={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={1}\n borderStyle=\"solid\"\n borderColor={borderColor}\n backgroundColor={backgroundColor}\n borderRadius={\n type === \"radio\"\n ? radiusScale[\"999\"]\n : theme.shape.checkbox[metrics.controlSize].borderRadius\n }\n aria-hidden=\"true\"\n testID=\"card-selector-control\"\n >\n {checked &&\n (type === \"radio\" ? (\n <Box\n width={metrics.controlIcon}\n height={metrics.controlIcon}\n borderRadius={radiusScale[\"999\"]}\n backgroundColor={iconColor}\n />\n ) : (\n <CheckGlyph size={metrics.controlIcon} color={iconColor} />\n ))}\n </Box>\n );\n};\n\nconst asPlainText = (value: React.ReactNode): string | undefined =>\n typeof value === \"string\" ? value : undefined;\n\nexport const CardSelector: React.FC<CardSelectorProps> = ({\n title,\n description,\n imageSrc,\n imageAlt = \"\",\n value,\n type: typeProp,\n control: controlProp,\n platform: platformProp,\n checked: checkedProp,\n defaultChecked = false,\n onChange,\n state = \"default\",\n disabled,\n width = \"100%\",\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const group = useCardSelectorGroup();\n const nodeRef = useRef<HTMLDivElement | null>(null);\n\n const type: CardSelectorType = typeProp ?? group?.type ?? \"radio\";\n const control: CardSelectorControlPlacement =\n controlProp ?? group?.control ?? \"text\";\n const platform: CardSelectorPlatform =\n platformProp ?? group?.platform ?? \"mobile\";\n const metrics = platformMetrics[platform];\n\n const isDisabled =\n Boolean(disabled) || state === \"disabled\" || Boolean(group?.disabled);\n\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isGroupControlled =\n group !== null && type === \"radio\" && value !== undefined;\n const isControlled = checkedProp !== undefined;\n const checked = isControlled\n ? Boolean(checkedProp)\n : isGroupControlled\n ? group?.selectedValue === value\n : internalChecked;\n\n // Radio cards in a group are mutually exclusive, so re-pressing the selected\n // card must not clear the group's selection.\n const toggle = useCallback(() => {\n if (isDisabled) return;\n if (type === \"radio\" && checked && isGroupControlled) return;\n\n const next = type === \"radio\" ? true : !checked;\n\n if (!isControlled && !isGroupControlled) {\n setInternalChecked(next);\n }\n if (group && isGroupControlled && value !== undefined) {\n group.select(value);\n }\n onChange?.(next, value);\n }, [\n checked,\n group,\n isControlled,\n isDisabled,\n isGroupControlled,\n onChange,\n type,\n value,\n ]);\n\n const focus = useCallback(() => {\n const node = nodeRef.current;\n if (node && typeof node.focus === \"function\") node.focus();\n }, []);\n\n // Registered once per mount so the group's Arrow-key order matches DOM\n // order; the entry's fields are refreshed in place on every render.\n const itemRef = useRef<CardSelectorGroupItem>({\n value: value ?? \"\",\n disabled: Boolean(isDisabled),\n focus,\n });\n\n useEffect(() => {\n itemRef.current.value = value ?? \"\";\n itemRef.current.disabled = isDisabled;\n itemRef.current.focus = focus;\n });\n\n const registerItem = group?.register;\n useEffect(() => {\n if (!registerItem) return undefined;\n return registerItem(itemRef.current);\n }, [registerItem]);\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n const key = event.key;\n if (\n key === \" \" ||\n key === \"Spacebar\" ||\n key === \"Space\" ||\n key === \"Enter\"\n ) {\n event.preventDefault();\n toggle();\n return;\n }\n\n // Within a radio group, Arrow keys move focus and selection between cards.\n if (!group || type !== \"radio\") return;\n const delta =\n key === \"ArrowRight\" || key === \"ArrowDown\"\n ? 1\n : key === \"ArrowLeft\" || key === \"ArrowUp\"\n ? -1\n : 0;\n if (delta === 0) return;\n event.preventDefault();\n group.moveFocus(value, delta);\n };\n\n const showControl = control === \"text\" || checked;\n const isOnImage = control === \"onImage\";\n\n // Selected takes visual priority over hover.\n const surfaceBackground = checked\n ? theme.colors.overlay.brand\n : state === \"press\"\n ? theme.colors.overlay.brand\n : state === \"hover\"\n ? theme.colors.overlay.monoHover\n : theme.colors.overlay.mono;\n\n const titleColor =\n isOnImage && checked\n ? theme.colors.control.check.bg\n : theme.colors.content.primary;\n\n const derivedLabel = [asPlainText(title), asPlainText(description)]\n .filter(Boolean)\n .join(\", \");\n\n const media = (\n <Box\n position=\"relative\"\n width=\"100%\"\n overflow=\"hidden\"\n borderRadius={isOnImage ? metrics.radius : 0}\n style={{ aspectRatio: 1 }}\n testID=\"card-selector-media\"\n >\n {imageSrc && (\n <Box\n as=\"img\"\n src={imageSrc}\n alt={imageAlt}\n position=\"absolute\"\n top={0}\n left={0}\n width=\"100%\"\n height=\"100%\"\n style={{ objectFit: \"cover\", pointerEvents: \"none\" }}\n testID=\"card-selector-image\"\n />\n )}\n\n {isOnImage && showControl && (\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n </Box>\n )}\n </Box>\n );\n\n const label = (\n <Box flexDirection=\"column\" gap={2} flexShrink={1} overflow=\"hidden\">\n <Text\n color={titleColor}\n fontFamily={theme.fonts.body}\n fontSize={metrics.titleFontSize}\n lineHeight={metrics.titleLineHeight}\n fontWeight=\"500\"\n numberOfLines={1}\n testID=\"card-selector-title\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={theme.colors.content.tertiary}\n fontFamily={theme.fonts.body}\n fontSize={metrics.descriptionFontSize}\n lineHeight={metrics.descriptionLineHeight}\n fontWeight=\"400\"\n numberOfLines={1}\n testID=\"card-selector-description\"\n >\n {description}\n </Text>\n )}\n </Box>\n );\n\n // Roving tabindex: exactly one radio in a group is tabbable — the selected\n // card, or the first enabled card while the group holds no selection.\n const hasGroupSelection = group?.selectedValue !== undefined;\n const isRovingTarget =\n checked || (!hasGroupSelection && Boolean(group?.isFirstEnabled(value)));\n const isRovingRadio = Boolean(group) && type === \"radio\";\n const tabIndex = isDisabled || (isRovingRadio && !isRovingTarget) ? -1 : 0;\n\n return (\n <Box\n ref={nodeRef}\n width={width}\n flexDirection=\"column\"\n gap={isOnImage ? metrics.gap : 0}\n opacity={isDisabled ? 0.48 : 1}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n role={type === \"radio\" ? \"radio\" : \"checkbox\"}\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel || derivedLabel || undefined}\n tabIndex={tabIndex}\n onPress={isDisabled ? undefined : toggle}\n onKeyDown={handleKeyDown}\n focusStyle={{\n outlineColor: theme.colors.border.brand,\n outlineWidth: 2,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n }}\n testID={testID || \"card-selector\"}\n >\n <Box\n flexDirection=\"column\"\n overflow=\"hidden\"\n borderRadius={metrics.radius}\n borderWidth={2}\n borderStyle=\"solid\"\n borderColor={checked ? theme.colors.border.brand : \"transparent\"}\n backgroundColor={surfaceBackground}\n hoverStyle={\n !isDisabled && !checked && !isOnImage\n ? { backgroundColor: theme.colors.overlay.monoHover }\n : undefined\n }\n pressStyle={\n !isDisabled && !isOnImage\n ? { backgroundColor: theme.colors.overlay.brand }\n : undefined\n }\n testID=\"card-selector-surface\"\n >\n {media}\n\n {!isOnImage && (\n <Box\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={metrics.gap}\n padding={metrics.padding}\n testID=\"card-selector-bar\"\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n {label}\n </Box>\n )}\n </Box>\n\n {isOnImage && label}\n </Box>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import { createContext, useContext } from \"react\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorType,\n} from \"./types\";\n\nexport interface CardSelectorGroupItem {\n value: string;\n disabled: boolean;\n focus: () => void;\n}\n\nexport interface CardSelectorGroupContextValue {\n type: CardSelectorType;\n platform?: CardSelectorPlatform;\n control?: CardSelectorControlPlacement;\n disabled: boolean;\n /** Selected value, `radio` only. */\n selectedValue?: string;\n /** Selects a value, `radio` only. */\n select: (value: string) => void;\n /** Registers a card so the group can drive roving focus. Returns an unregister fn. */\n register: (item: CardSelectorGroupItem) => () => void;\n /** Moves focus (and, for `radio`, selection) by `delta` positions, wrapping. */\n moveFocus: (fromValue: string | undefined, delta: number) => void;\n /** True when the group holds no selection — the first enabled card stays tabbable. */\n isFirstEnabled: (value: string | undefined) => boolean;\n}\n\nexport const CardSelectorGroupContext =\n createContext<CardSelectorGroupContextValue | null>(null);\n\nexport const useCardSelectorGroup = (): CardSelectorGroupContextValue | null =>\n useContext(CardSelectorGroupContext);\n","import React, { useCallback, useMemo, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CardSelectorGroupContext,\n type CardSelectorGroupContextValue,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type { CardSelectorGroupProps } from \"./types\";\n\n/**\n * Wraps a set of `CardSelector` cards.\n *\n * For `type=\"radio\"` it renders a `radiogroup`, keeps the cards mutually\n * exclusive, and implements the roving-tabindex + Arrow-key behaviour the\n * Figma spec requires (3446:1761 — \"within a Radio group, Arrow keys move\n * focus and selection between cards\"). For `type=\"check\"` the cards toggle\n * independently and the wrapper is a plain `group`.\n */\nexport const CardSelectorGroup: React.FC<CardSelectorGroupProps> = ({\n children,\n type = \"radio\",\n value,\n defaultValue,\n onValueChange,\n platform,\n control,\n disabled = false,\n gap = 12,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n // Resolved so the group participates in the same theme scope as its cards.\n useResolvedTheme({ themeMode, themeProductContext });\n\n // Registration lives in state, not a ref, so the roving tabindex is correct\n // on the render right after the cards mount.\n const [items, setItems] = useState<CardSelectorGroupItem[]>([]);\n const [internalValue, setInternalValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const selectedValue = isControlled ? value : internalValue;\n\n const register = useCallback((item: CardSelectorGroupItem) => {\n setItems((prev) => [...prev, item]);\n return () => setItems((prev) => prev.filter((entry) => entry !== item));\n }, []);\n\n const select = useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange]\n );\n\n const moveFocus = useCallback(\n (fromValue: string | undefined, delta: number) => {\n const enabled = items.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex(\n (item) => item.value === fromValue\n );\n const nextIndex =\n currentIndex === -1\n ? 0\n : (currentIndex + delta + enabled.length) % enabled.length;\n const next = enabled[nextIndex];\n if (!next) return;\n\n next.focus();\n if (type === \"radio\" && next.value) select(next.value);\n },\n [items, select, type]\n );\n\n const isFirstEnabled = useCallback(\n (candidate: string | undefined) => {\n const first = items.find((item) => !item.disabled);\n return Boolean(first) && first?.value === candidate;\n },\n [items]\n );\n\n const contextValue = useMemo<CardSelectorGroupContextValue>(\n () => ({\n type,\n platform,\n control,\n disabled,\n selectedValue,\n select,\n register,\n moveFocus,\n isFirstEnabled,\n }),\n [\n control,\n disabled,\n isFirstEnabled,\n moveFocus,\n platform,\n register,\n select,\n selectedValue,\n type,\n ]\n );\n\n return (\n <CardSelectorGroupContext.Provider value={contextValue}>\n <Box\n role={type === \"radio\" ? \"radiogroup\" : \"group\"}\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n alignItems=\"flex-start\"\n gap={gap}\n testID={testID || \"card-selector-group\"}\n >\n {children}\n </Box>\n </CardSelectorGroupContext.Provider>\n );\n};\n"],"mappings":"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","names":["import_react","import_react_native","import_jsx_runtime","RNText","import_jsx_runtime","import_react","import_xui_core","import_jsx_runtime"]}
package/native/index.mjs CHANGED
@@ -40,6 +40,7 @@ var toGapValue = (gap) => {
40
40
  const asNumber = Number(gap);
41
41
  return Number.isFinite(asNumber) ? asNumber : void 0;
42
42
  };
43
+ var toNativeDisplay = (display) => display === "none" ? "none" : void 0;
43
44
  var Box = ({
44
45
  children,
45
46
  onPress,
@@ -71,6 +72,7 @@ var Box = ({
71
72
  marginBottom,
72
73
  marginLeft,
73
74
  marginRight,
75
+ display,
74
76
  flexDirection,
75
77
  flexWrap,
76
78
  alignItems,
@@ -134,6 +136,7 @@ var Box = ({
134
136
  marginBottom,
135
137
  marginLeft,
136
138
  marginRight,
139
+ display: toNativeDisplay(display),
137
140
  flexDirection,
138
141
  flexWrap,
139
142
  alignItems,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/CardSelector.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/CardSelectorGroupContext.tsx","../../src/CardSelectorGroup.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, radiusScale } from \"@xsolla/xui-core\";\nimport {\n useCardSelectorGroup,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorType,\n} from \"./types\";\n\ntype ResolvedTheme = ReturnType<typeof useResolvedTheme>[\"theme\"];\n\n/**\n * Figma `CardSelector` (3446:1761). The component set is fixed-width per\n * `Platform`; the exact frame widths could not be read back from the Figma\n * REST API (the node payload exceeds the tool's response budget), so the card\n * is fluid by default and the consumer sizes it via `width` or the grid cell.\n * `Platform` therefore drives the internal proportions only.\n */\ninterface PlatformMetrics {\n /** Maps onto the shared checkbox/radio size scale so the shape tokens line up. */\n controlSize: \"md\" | \"lg\";\n controlBox: number;\n controlIcon: number;\n titleFontSize: number;\n titleLineHeight: number;\n descriptionFontSize: number;\n descriptionLineHeight: number;\n padding: number;\n gap: number;\n radius: number;\n}\n\nconst platformMetrics: Record<CardSelectorPlatform, PlatformMetrics> = {\n mobile: {\n controlSize: \"md\",\n controlBox: 18,\n controlIcon: 12,\n titleFontSize: 14,\n titleLineHeight: 16,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 8,\n gap: 8,\n radius: 8,\n },\n desktop: {\n controlSize: \"lg\",\n controlBox: 20,\n controlIcon: 14,\n titleFontSize: 16,\n titleLineHeight: 20,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 10,\n gap: 8,\n radius: 8,\n },\n};\n\n/** Inline 12×12 glyph so the stroke scales with the control, mirroring Checkbox. */\nconst CheckGlyph: React.FC<{ size: number; color: string }> = ({\n size,\n color,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M10.6699 4.2959L6.00293 8.96387C5.72637 9.24043 5.27761 9.24031 5.00098 8.96387L2 5.96289L3.2959 4.66699L5.50098 6.87207L9.37402 3L10.6699 4.2959Z\"\n fill={color}\n />\n </svg>\n);\n\n/**\n * The selection control is decorative: `aria-checked` lives on the card\n * itself, so exposing a second checkbox/radio here would double up the\n * semantics. It exists purely so selection never relies on colour alone.\n */\nconst SelectionControl: React.FC<{\n type: CardSelectorType;\n checked: boolean;\n disabled: boolean;\n metrics: PlatformMetrics;\n theme: ResolvedTheme;\n}> = ({ type, checked, disabled, metrics, theme }) => {\n const check = theme.colors.control.check;\n const faint = theme.colors.control.faint;\n\n const backgroundColor = disabled\n ? check.bgDisable\n : checked\n ? check.bg\n : faint.bg;\n const borderColor = disabled\n ? check.borderDisable\n : checked\n ? check.border\n : faint.border;\n const iconColor = disabled ? theme.colors.control.text.disable : check.icon;\n\n return (\n <Box\n width={metrics.controlBox}\n height={metrics.controlBox}\n flexShrink={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={1}\n borderStyle=\"solid\"\n borderColor={borderColor}\n backgroundColor={backgroundColor}\n borderRadius={\n type === \"radio\"\n ? radiusScale[\"999\"]\n : theme.shape.checkbox[metrics.controlSize].borderRadius\n }\n aria-hidden=\"true\"\n testID=\"card-selector-control\"\n >\n {checked &&\n (type === \"radio\" ? (\n <Box\n width={metrics.controlIcon}\n height={metrics.controlIcon}\n borderRadius={radiusScale[\"999\"]}\n backgroundColor={iconColor}\n />\n ) : (\n <CheckGlyph size={metrics.controlIcon} color={iconColor} />\n ))}\n </Box>\n );\n};\n\nconst asPlainText = (value: React.ReactNode): string | undefined =>\n typeof value === \"string\" ? value : undefined;\n\nexport const CardSelector: React.FC<CardSelectorProps> = ({\n title,\n description,\n imageSrc,\n imageAlt = \"\",\n value,\n type: typeProp,\n control: controlProp,\n platform: platformProp,\n checked: checkedProp,\n defaultChecked = false,\n onChange,\n state = \"default\",\n disabled,\n width = \"100%\",\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const group = useCardSelectorGroup();\n const nodeRef = useRef<HTMLDivElement | null>(null);\n\n const type: CardSelectorType = typeProp ?? group?.type ?? \"radio\";\n const control: CardSelectorControlPlacement =\n controlProp ?? group?.control ?? \"text\";\n const platform: CardSelectorPlatform =\n platformProp ?? group?.platform ?? \"mobile\";\n const metrics = platformMetrics[platform];\n\n const isDisabled =\n Boolean(disabled) || state === \"disabled\" || Boolean(group?.disabled);\n\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isGroupControlled =\n group !== null && type === \"radio\" && value !== undefined;\n const isControlled = checkedProp !== undefined;\n const checked = isControlled\n ? Boolean(checkedProp)\n : isGroupControlled\n ? group?.selectedValue === value\n : internalChecked;\n\n // Radio cards in a group are mutually exclusive, so re-pressing the selected\n // card must not clear the group's selection.\n const toggle = useCallback(() => {\n if (isDisabled) return;\n if (type === \"radio\" && checked && isGroupControlled) return;\n\n const next = type === \"radio\" ? true : !checked;\n\n if (!isControlled && !isGroupControlled) {\n setInternalChecked(next);\n }\n if (group && isGroupControlled && value !== undefined) {\n group.select(value);\n }\n onChange?.(next, value);\n }, [\n checked,\n group,\n isControlled,\n isDisabled,\n isGroupControlled,\n onChange,\n type,\n value,\n ]);\n\n const focus = useCallback(() => {\n const node = nodeRef.current;\n if (node && typeof node.focus === \"function\") node.focus();\n }, []);\n\n // Registered once per mount so the group's Arrow-key order matches DOM\n // order; the entry's fields are refreshed in place on every render.\n const itemRef = useRef<CardSelectorGroupItem>({\n value: value ?? \"\",\n disabled: Boolean(isDisabled),\n focus,\n });\n\n useEffect(() => {\n itemRef.current.value = value ?? \"\";\n itemRef.current.disabled = isDisabled;\n itemRef.current.focus = focus;\n });\n\n const registerItem = group?.register;\n useEffect(() => {\n if (!registerItem) return undefined;\n return registerItem(itemRef.current);\n }, [registerItem]);\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n const key = event.key;\n if (\n key === \" \" ||\n key === \"Spacebar\" ||\n key === \"Space\" ||\n key === \"Enter\"\n ) {\n event.preventDefault();\n toggle();\n return;\n }\n\n // Within a radio group, Arrow keys move focus and selection between cards.\n if (!group || type !== \"radio\") return;\n const delta =\n key === \"ArrowRight\" || key === \"ArrowDown\"\n ? 1\n : key === \"ArrowLeft\" || key === \"ArrowUp\"\n ? -1\n : 0;\n if (delta === 0) return;\n event.preventDefault();\n group.moveFocus(value, delta);\n };\n\n const showControl = control === \"text\" || checked;\n const isOnImage = control === \"onImage\";\n\n // Selected takes visual priority over hover.\n const surfaceBackground = checked\n ? theme.colors.overlay.brand\n : state === \"press\"\n ? theme.colors.overlay.brand\n : state === \"hover\"\n ? theme.colors.overlay.monoHover\n : theme.colors.overlay.mono;\n\n const titleColor =\n isOnImage && checked\n ? theme.colors.control.check.bg\n : theme.colors.content.primary;\n\n const derivedLabel = [asPlainText(title), asPlainText(description)]\n .filter(Boolean)\n .join(\", \");\n\n const media = (\n <Box\n position=\"relative\"\n width=\"100%\"\n overflow=\"hidden\"\n borderRadius={isOnImage ? metrics.radius : 0}\n style={{ aspectRatio: 1 }}\n testID=\"card-selector-media\"\n >\n {imageSrc && (\n <Box\n as=\"img\"\n src={imageSrc}\n alt={imageAlt}\n position=\"absolute\"\n top={0}\n left={0}\n width=\"100%\"\n height=\"100%\"\n style={{ objectFit: \"cover\", pointerEvents: \"none\" }}\n testID=\"card-selector-image\"\n />\n )}\n\n {isOnImage && showControl && (\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n </Box>\n )}\n </Box>\n );\n\n const label = (\n <Box flexDirection=\"column\" gap={2} flexShrink={1} overflow=\"hidden\">\n <Text\n color={titleColor}\n fontFamily={theme.fonts.body}\n fontSize={metrics.titleFontSize}\n lineHeight={metrics.titleLineHeight}\n fontWeight=\"500\"\n numberOfLines={1}\n testID=\"card-selector-title\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={theme.colors.content.tertiary}\n fontFamily={theme.fonts.body}\n fontSize={metrics.descriptionFontSize}\n lineHeight={metrics.descriptionLineHeight}\n fontWeight=\"400\"\n numberOfLines={1}\n testID=\"card-selector-description\"\n >\n {description}\n </Text>\n )}\n </Box>\n );\n\n // Roving tabindex: exactly one radio in a group is tabbable — the selected\n // card, or the first enabled card while the group holds no selection.\n const hasGroupSelection = group?.selectedValue !== undefined;\n const isRovingTarget =\n checked || (!hasGroupSelection && Boolean(group?.isFirstEnabled(value)));\n const isRovingRadio = Boolean(group) && type === \"radio\";\n const tabIndex = isDisabled || (isRovingRadio && !isRovingTarget) ? -1 : 0;\n\n return (\n <Box\n ref={nodeRef}\n width={width}\n flexDirection=\"column\"\n gap={isOnImage ? metrics.gap : 0}\n opacity={isDisabled ? 0.48 : 1}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n role={type === \"radio\" ? \"radio\" : \"checkbox\"}\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel || derivedLabel || undefined}\n tabIndex={tabIndex}\n onPress={isDisabled ? undefined : toggle}\n onKeyDown={handleKeyDown}\n focusStyle={{\n outlineColor: theme.colors.border.brand,\n outlineWidth: 2,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n }}\n testID={testID || \"card-selector\"}\n >\n <Box\n flexDirection=\"column\"\n overflow=\"hidden\"\n borderRadius={metrics.radius}\n borderWidth={2}\n borderStyle=\"solid\"\n borderColor={checked ? theme.colors.border.brand : \"transparent\"}\n backgroundColor={surfaceBackground}\n hoverStyle={\n !isDisabled && !checked && !isOnImage\n ? { backgroundColor: theme.colors.overlay.monoHover }\n : undefined\n }\n pressStyle={\n !isDisabled && !isOnImage\n ? { backgroundColor: theme.colors.overlay.brand }\n : undefined\n }\n testID=\"card-selector-surface\"\n >\n {media}\n\n {!isOnImage && (\n <Box\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={metrics.gap}\n padding={metrics.padding}\n testID=\"card-selector-bar\"\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n {label}\n </Box>\n )}\n </Box>\n\n {isOnImage && label}\n </Box>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import { createContext, useContext } from \"react\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorType,\n} from \"./types\";\n\nexport interface CardSelectorGroupItem {\n value: string;\n disabled: boolean;\n focus: () => void;\n}\n\nexport interface CardSelectorGroupContextValue {\n type: CardSelectorType;\n platform?: CardSelectorPlatform;\n control?: CardSelectorControlPlacement;\n disabled: boolean;\n /** Selected value, `radio` only. */\n selectedValue?: string;\n /** Selects a value, `radio` only. */\n select: (value: string) => void;\n /** Registers a card so the group can drive roving focus. Returns an unregister fn. */\n register: (item: CardSelectorGroupItem) => () => void;\n /** Moves focus (and, for `radio`, selection) by `delta` positions, wrapping. */\n moveFocus: (fromValue: string | undefined, delta: number) => void;\n /** True when the group holds no selection — the first enabled card stays tabbable. */\n isFirstEnabled: (value: string | undefined) => boolean;\n}\n\nexport const CardSelectorGroupContext =\n createContext<CardSelectorGroupContextValue | null>(null);\n\nexport const useCardSelectorGroup = (): CardSelectorGroupContextValue | null =>\n useContext(CardSelectorGroupContext);\n","import React, { useCallback, useMemo, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CardSelectorGroupContext,\n type CardSelectorGroupContextValue,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type { CardSelectorGroupProps } from \"./types\";\n\n/**\n * Wraps a set of `CardSelector` cards.\n *\n * For `type=\"radio\"` it renders a `radiogroup`, keeps the cards mutually\n * exclusive, and implements the roving-tabindex + Arrow-key behaviour the\n * Figma spec requires (3446:1761 — \"within a Radio group, Arrow keys move\n * focus and selection between cards\"). For `type=\"check\"` the cards toggle\n * independently and the wrapper is a plain `group`.\n */\nexport const CardSelectorGroup: React.FC<CardSelectorGroupProps> = ({\n children,\n type = \"radio\",\n value,\n defaultValue,\n onValueChange,\n platform,\n control,\n disabled = false,\n gap = 12,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n // Resolved so the group participates in the same theme scope as its cards.\n useResolvedTheme({ themeMode, themeProductContext });\n\n // Registration lives in state, not a ref, so the roving tabindex is correct\n // on the render right after the cards mount.\n const [items, setItems] = useState<CardSelectorGroupItem[]>([]);\n const [internalValue, setInternalValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const selectedValue = isControlled ? value : internalValue;\n\n const register = useCallback((item: CardSelectorGroupItem) => {\n setItems((prev) => [...prev, item]);\n return () => setItems((prev) => prev.filter((entry) => entry !== item));\n }, []);\n\n const select = useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange]\n );\n\n const moveFocus = useCallback(\n (fromValue: string | undefined, delta: number) => {\n const enabled = items.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex(\n (item) => item.value === fromValue\n );\n const nextIndex =\n currentIndex === -1\n ? 0\n : (currentIndex + delta + enabled.length) % enabled.length;\n const next = enabled[nextIndex];\n if (!next) return;\n\n next.focus();\n if (type === \"radio\" && next.value) select(next.value);\n },\n [items, select, type]\n );\n\n const isFirstEnabled = useCallback(\n (candidate: string | undefined) => {\n const first = items.find((item) => !item.disabled);\n return Boolean(first) && first?.value === candidate;\n },\n [items]\n );\n\n const contextValue = useMemo<CardSelectorGroupContextValue>(\n () => ({\n type,\n platform,\n control,\n disabled,\n selectedValue,\n select,\n register,\n moveFocus,\n isFirstEnabled,\n }),\n [\n control,\n disabled,\n isFirstEnabled,\n moveFocus,\n platform,\n register,\n select,\n selectedValue,\n type,\n ]\n );\n\n return (\n <CardSelectorGroupContext.Provider value={contextValue}>\n <Box\n role={type === \"radio\" ? \"radiogroup\" : \"group\"}\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n alignItems=\"flex-start\"\n gap={gap}\n testID={testID || \"card-selector-group\"}\n >\n {children}\n </Box>\n </CardSelectorGroupContext.Provider>\n );\n};\n"],"mappings":"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","names":["jsx","jsx","useCallback","useState","useResolvedTheme","jsx","useResolvedTheme","useState","useCallback"]}
1
+ {"version":3,"sources":["../../src/CardSelector.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../../../foundation/primitives-native/src/Text.tsx","../../src/CardSelectorGroupContext.tsx","../../src/CardSelectorGroup.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box, Text } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme, radiusScale } from \"@xsolla/xui-core\";\nimport {\n useCardSelectorGroup,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorProps,\n CardSelectorType,\n} from \"./types\";\n\ntype ResolvedTheme = ReturnType<typeof useResolvedTheme>[\"theme\"];\n\n/**\n * Figma `CardSelector` (3446:1761). The component set is fixed-width per\n * `Platform`; the exact frame widths could not be read back from the Figma\n * REST API (the node payload exceeds the tool's response budget), so the card\n * is fluid by default and the consumer sizes it via `width` or the grid cell.\n * `Platform` therefore drives the internal proportions only.\n */\ninterface PlatformMetrics {\n /** Maps onto the shared checkbox/radio size scale so the shape tokens line up. */\n controlSize: \"md\" | \"lg\";\n controlBox: number;\n controlIcon: number;\n titleFontSize: number;\n titleLineHeight: number;\n descriptionFontSize: number;\n descriptionLineHeight: number;\n padding: number;\n gap: number;\n radius: number;\n}\n\nconst platformMetrics: Record<CardSelectorPlatform, PlatformMetrics> = {\n mobile: {\n controlSize: \"md\",\n controlBox: 18,\n controlIcon: 12,\n titleFontSize: 14,\n titleLineHeight: 16,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 8,\n gap: 8,\n radius: 8,\n },\n desktop: {\n controlSize: \"lg\",\n controlBox: 20,\n controlIcon: 14,\n titleFontSize: 16,\n titleLineHeight: 20,\n descriptionFontSize: 12,\n descriptionLineHeight: 16,\n padding: 10,\n gap: 8,\n radius: 8,\n },\n};\n\n/** Inline 12×12 glyph so the stroke scales with the control, mirroring Checkbox. */\nconst CheckGlyph: React.FC<{ size: number; color: string }> = ({\n size,\n color,\n}) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M10.6699 4.2959L6.00293 8.96387C5.72637 9.24043 5.27761 9.24031 5.00098 8.96387L2 5.96289L3.2959 4.66699L5.50098 6.87207L9.37402 3L10.6699 4.2959Z\"\n fill={color}\n />\n </svg>\n);\n\n/**\n * The selection control is decorative: `aria-checked` lives on the card\n * itself, so exposing a second checkbox/radio here would double up the\n * semantics. It exists purely so selection never relies on colour alone.\n */\nconst SelectionControl: React.FC<{\n type: CardSelectorType;\n checked: boolean;\n disabled: boolean;\n metrics: PlatformMetrics;\n theme: ResolvedTheme;\n}> = ({ type, checked, disabled, metrics, theme }) => {\n const check = theme.colors.control.check;\n const faint = theme.colors.control.faint;\n\n const backgroundColor = disabled\n ? check.bgDisable\n : checked\n ? check.bg\n : faint.bg;\n const borderColor = disabled\n ? check.borderDisable\n : checked\n ? check.border\n : faint.border;\n const iconColor = disabled ? theme.colors.control.text.disable : check.icon;\n\n return (\n <Box\n width={metrics.controlBox}\n height={metrics.controlBox}\n flexShrink={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n borderWidth={1}\n borderStyle=\"solid\"\n borderColor={borderColor}\n backgroundColor={backgroundColor}\n borderRadius={\n type === \"radio\"\n ? radiusScale[\"999\"]\n : theme.shape.checkbox[metrics.controlSize].borderRadius\n }\n aria-hidden=\"true\"\n testID=\"card-selector-control\"\n >\n {checked &&\n (type === \"radio\" ? (\n <Box\n width={metrics.controlIcon}\n height={metrics.controlIcon}\n borderRadius={radiusScale[\"999\"]}\n backgroundColor={iconColor}\n />\n ) : (\n <CheckGlyph size={metrics.controlIcon} color={iconColor} />\n ))}\n </Box>\n );\n};\n\nconst asPlainText = (value: React.ReactNode): string | undefined =>\n typeof value === \"string\" ? value : undefined;\n\nexport const CardSelector: React.FC<CardSelectorProps> = ({\n title,\n description,\n imageSrc,\n imageAlt = \"\",\n value,\n type: typeProp,\n control: controlProp,\n platform: platformProp,\n checked: checkedProp,\n defaultChecked = false,\n onChange,\n state = \"default\",\n disabled,\n width = \"100%\",\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const group = useCardSelectorGroup();\n const nodeRef = useRef<HTMLDivElement | null>(null);\n\n const type: CardSelectorType = typeProp ?? group?.type ?? \"radio\";\n const control: CardSelectorControlPlacement =\n controlProp ?? group?.control ?? \"text\";\n const platform: CardSelectorPlatform =\n platformProp ?? group?.platform ?? \"mobile\";\n const metrics = platformMetrics[platform];\n\n const isDisabled =\n Boolean(disabled) || state === \"disabled\" || Boolean(group?.disabled);\n\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isGroupControlled =\n group !== null && type === \"radio\" && value !== undefined;\n const isControlled = checkedProp !== undefined;\n const checked = isControlled\n ? Boolean(checkedProp)\n : isGroupControlled\n ? group?.selectedValue === value\n : internalChecked;\n\n // Radio cards in a group are mutually exclusive, so re-pressing the selected\n // card must not clear the group's selection.\n const toggle = useCallback(() => {\n if (isDisabled) return;\n if (type === \"radio\" && checked && isGroupControlled) return;\n\n const next = type === \"radio\" ? true : !checked;\n\n if (!isControlled && !isGroupControlled) {\n setInternalChecked(next);\n }\n if (group && isGroupControlled && value !== undefined) {\n group.select(value);\n }\n onChange?.(next, value);\n }, [\n checked,\n group,\n isControlled,\n isDisabled,\n isGroupControlled,\n onChange,\n type,\n value,\n ]);\n\n const focus = useCallback(() => {\n const node = nodeRef.current;\n if (node && typeof node.focus === \"function\") node.focus();\n }, []);\n\n // Registered once per mount so the group's Arrow-key order matches DOM\n // order; the entry's fields are refreshed in place on every render.\n const itemRef = useRef<CardSelectorGroupItem>({\n value: value ?? \"\",\n disabled: Boolean(isDisabled),\n focus,\n });\n\n useEffect(() => {\n itemRef.current.value = value ?? \"\";\n itemRef.current.disabled = isDisabled;\n itemRef.current.focus = focus;\n });\n\n const registerItem = group?.register;\n useEffect(() => {\n if (!registerItem) return undefined;\n return registerItem(itemRef.current);\n }, [registerItem]);\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (isDisabled) return;\n\n const key = event.key;\n if (\n key === \" \" ||\n key === \"Spacebar\" ||\n key === \"Space\" ||\n key === \"Enter\"\n ) {\n event.preventDefault();\n toggle();\n return;\n }\n\n // Within a radio group, Arrow keys move focus and selection between cards.\n if (!group || type !== \"radio\") return;\n const delta =\n key === \"ArrowRight\" || key === \"ArrowDown\"\n ? 1\n : key === \"ArrowLeft\" || key === \"ArrowUp\"\n ? -1\n : 0;\n if (delta === 0) return;\n event.preventDefault();\n group.moveFocus(value, delta);\n };\n\n const showControl = control === \"text\" || checked;\n const isOnImage = control === \"onImage\";\n\n // Selected takes visual priority over hover.\n const surfaceBackground = checked\n ? theme.colors.overlay.brand\n : state === \"press\"\n ? theme.colors.overlay.brand\n : state === \"hover\"\n ? theme.colors.overlay.monoHover\n : theme.colors.overlay.mono;\n\n const titleColor =\n isOnImage && checked\n ? theme.colors.control.check.bg\n : theme.colors.content.primary;\n\n const derivedLabel = [asPlainText(title), asPlainText(description)]\n .filter(Boolean)\n .join(\", \");\n\n const media = (\n <Box\n position=\"relative\"\n width=\"100%\"\n overflow=\"hidden\"\n borderRadius={isOnImage ? metrics.radius : 0}\n style={{ aspectRatio: 1 }}\n testID=\"card-selector-media\"\n >\n {imageSrc && (\n <Box\n as=\"img\"\n src={imageSrc}\n alt={imageAlt}\n position=\"absolute\"\n top={0}\n left={0}\n width=\"100%\"\n height=\"100%\"\n style={{ objectFit: \"cover\", pointerEvents: \"none\" }}\n testID=\"card-selector-image\"\n />\n )}\n\n {isOnImage && showControl && (\n <Box\n position=\"absolute\"\n top={0}\n left={0}\n right={0}\n bottom={0}\n alignItems=\"center\"\n justifyContent=\"center\"\n zIndex={1}\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n </Box>\n )}\n </Box>\n );\n\n const label = (\n <Box flexDirection=\"column\" gap={2} flexShrink={1} overflow=\"hidden\">\n <Text\n color={titleColor}\n fontFamily={theme.fonts.body}\n fontSize={metrics.titleFontSize}\n lineHeight={metrics.titleLineHeight}\n fontWeight=\"500\"\n numberOfLines={1}\n testID=\"card-selector-title\"\n >\n {title}\n </Text>\n {description && (\n <Text\n color={theme.colors.content.tertiary}\n fontFamily={theme.fonts.body}\n fontSize={metrics.descriptionFontSize}\n lineHeight={metrics.descriptionLineHeight}\n fontWeight=\"400\"\n numberOfLines={1}\n testID=\"card-selector-description\"\n >\n {description}\n </Text>\n )}\n </Box>\n );\n\n // Roving tabindex: exactly one radio in a group is tabbable — the selected\n // card, or the first enabled card while the group holds no selection.\n const hasGroupSelection = group?.selectedValue !== undefined;\n const isRovingTarget =\n checked || (!hasGroupSelection && Boolean(group?.isFirstEnabled(value)));\n const isRovingRadio = Boolean(group) && type === \"radio\";\n const tabIndex = isDisabled || (isRovingRadio && !isRovingTarget) ? -1 : 0;\n\n return (\n <Box\n ref={nodeRef}\n width={width}\n flexDirection=\"column\"\n gap={isOnImage ? metrics.gap : 0}\n opacity={isDisabled ? 0.48 : 1}\n cursor={isDisabled ? \"not-allowed\" : \"pointer\"}\n role={type === \"radio\" ? \"radio\" : \"checkbox\"}\n aria-checked={checked}\n aria-disabled={isDisabled || undefined}\n aria-label={ariaLabel || derivedLabel || undefined}\n tabIndex={tabIndex}\n onPress={isDisabled ? undefined : toggle}\n onKeyDown={handleKeyDown}\n focusStyle={{\n outlineColor: theme.colors.border.brand,\n outlineWidth: 2,\n outlineOffset: 2,\n outlineStyle: \"solid\",\n }}\n testID={testID || \"card-selector\"}\n >\n <Box\n flexDirection=\"column\"\n overflow=\"hidden\"\n borderRadius={metrics.radius}\n borderWidth={2}\n borderStyle=\"solid\"\n borderColor={checked ? theme.colors.border.brand : \"transparent\"}\n backgroundColor={surfaceBackground}\n hoverStyle={\n !isDisabled && !checked && !isOnImage\n ? { backgroundColor: theme.colors.overlay.monoHover }\n : undefined\n }\n pressStyle={\n !isDisabled && !isOnImage\n ? { backgroundColor: theme.colors.overlay.brand }\n : undefined\n }\n testID=\"card-selector-surface\"\n >\n {media}\n\n {!isOnImage && (\n <Box\n flexDirection=\"row\"\n alignItems=\"center\"\n gap={metrics.gap}\n padding={metrics.padding}\n testID=\"card-selector-bar\"\n >\n <SelectionControl\n type={type}\n checked={checked}\n disabled={isDisabled}\n metrics={metrics}\n theme={theme}\n />\n {label}\n </Box>\n )}\n </Box>\n\n {isOnImage && label}\n </Box>\n );\n};\n","import React from \"react\";\nimport {\n View,\n Pressable,\n Image,\n ViewStyle,\n ImageStyle,\n DimensionValue,\n AnimatableNumericValue,\n AccessibilityRole,\n} from \"react-native\";\nimport { BoxProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleToAccessibilityRole: Record<string, AccessibilityRole> = {\n button: \"button\",\n link: \"link\",\n img: \"image\",\n image: \"image\",\n summary: \"summary\",\n switch: \"switch\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n heading: \"header\",\n text: \"text\",\n search: \"search\",\n alert: \"alert\",\n none: \"none\",\n presentation: \"none\",\n progressbar: \"progressbar\",\n scrollbar: \"scrollbar\",\n adjustable: \"adjustable\",\n tab: \"tab\",\n menu: \"menu\",\n menuitem: \"menuitem\",\n list: \"list\",\n timer: \"timer\",\n};\n\n/** RN ViewStyle.gap is typed as number (Yoga gap), not DimensionValue. */\nconst toGapValue = (gap: number | string | undefined): number | undefined => {\n if (gap === undefined) return undefined;\n if (typeof gap === \"number\") return gap;\n const pxMatch = /^(\\d+(?:\\.\\d+)?)px$/.exec(gap);\n if (pxMatch) return Number(pxMatch[1]);\n const asNumber = Number(gap);\n return Number.isFinite(asNumber) ? asNumber : undefined;\n};\n\n/**\n * Yoga has no inline formatting context, so RN's ViewStyle.display only\n * accepts \"flex\" | \"none\". Every inline-ish web value collapses to a plain\n * flex container — the box already fills only its content on native, which is\n * what those callers wanted on web anyway. Returning undefined (rather than\n * \"flex\") leaves RN's own default in place.\n */\nconst toNativeDisplay = (\n display: BoxProps[\"display\"]\n): ViewStyle[\"display\"] => (display === \"none\" ? \"none\" : undefined);\n\nexport const Box: React.FC<BoxProps> = ({\n children,\n onPress,\n onLayout,\n onMoveShouldSetResponder,\n onResponderGrant,\n onResponderMove,\n onResponderRelease,\n onResponderTerminate,\n backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius,\n borderStyle,\n height,\n padding,\n paddingHorizontal,\n paddingVertical,\n margin,\n marginTop,\n marginBottom,\n marginLeft,\n marginRight,\n display,\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position,\n top,\n bottom,\n left,\n right,\n width,\n minWidth,\n minHeight,\n maxWidth,\n maxHeight,\n flex,\n flexShrink,\n gap,\n overflow,\n opacity,\n zIndex,\n hoverStyle,\n pressStyle,\n style,\n disabled,\n \"data-testid\": dataTestId,\n testID,\n as,\n src,\n alt,\n ...rest\n}) => {\n const getContainerStyle = (pressed?: boolean): ViewStyle => ({\n backgroundColor:\n pressed && pressStyle?.backgroundColor\n ? pressStyle.backgroundColor\n : backgroundColor,\n borderColor,\n borderWidth,\n borderBottomWidth,\n borderBottomColor,\n borderTopWidth,\n borderTopColor,\n borderLeftWidth,\n borderLeftColor,\n borderRightWidth,\n borderRightColor,\n borderRadius: borderRadius as AnimatableNumericValue,\n borderStyle: borderStyle as ViewStyle[\"borderStyle\"],\n overflow,\n opacity: opacity ?? (disabled ? 0.5 : undefined),\n zIndex,\n height: height as DimensionValue,\n width: width as DimensionValue,\n minWidth: minWidth as DimensionValue,\n minHeight: minHeight as DimensionValue,\n maxWidth: maxWidth as DimensionValue,\n maxHeight: maxHeight as DimensionValue,\n padding: padding as DimensionValue,\n paddingHorizontal: paddingHorizontal as DimensionValue,\n paddingVertical: paddingVertical as DimensionValue,\n margin: margin as DimensionValue,\n marginTop: marginTop as DimensionValue,\n marginBottom: marginBottom as DimensionValue,\n marginLeft: marginLeft as DimensionValue,\n marginRight: marginRight as DimensionValue,\n display: toNativeDisplay(display),\n flexDirection,\n flexWrap,\n alignItems,\n justifyContent,\n alignSelf,\n position: position as ViewStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n flex,\n flexShrink,\n gap: toGapValue(gap),\n ...(style as ViewStyle),\n });\n\n const finalTestID = dataTestId || testID;\n\n // Map web a11y props; drop the rest of the web-only attributes before\n // spreading onto RN hosts (unknown props warn at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const {\n role,\n tabIndex,\n onKeyDown,\n onKeyUp,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": _ariaLabelledBy,\n \"aria-current\": _ariaCurrent,\n \"aria-disabled\": ariaDisabled,\n \"aria-live\": ariaLive,\n \"aria-busy\": ariaBusy,\n className,\n \"data-testid\": _dataTestId,\n cursor: _cursor,\n type: _type,\n ...nativeRest\n } = rest as Record<string, unknown>;\n\n const accessibilityRole =\n (typeof role === \"string\" && roleToAccessibilityRole[role]) ||\n (as === \"button\" ? \"button\" : undefined);\n const isDisabled = Boolean(disabled || ariaDisabled);\n const accessibilityState = {\n disabled: isDisabled || undefined,\n busy: Boolean(ariaBusy) || undefined,\n };\n const accessibilityLiveRegion =\n ariaLive === \"polite\" || ariaLive === \"assertive\" ? ariaLive : undefined;\n\n // Handle as=\"img\" for React Native\n if (as === \"img\" && src) {\n const imageStyle: ImageStyle = {\n width: width as DimensionValue,\n height: height as DimensionValue,\n borderRadius: borderRadius as number,\n position: position as ImageStyle[\"position\"],\n top: top as DimensionValue,\n bottom: bottom as DimensionValue,\n left: left as DimensionValue,\n right: right as DimensionValue,\n ...(style as ImageStyle),\n };\n\n return (\n <Image\n source={{ uri: src }}\n style={imageStyle}\n testID={finalTestID}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : alt}\n resizeMode=\"cover\"\n {...nativeRest}\n />\n );\n }\n\n // Prefer Pressable whenever the node is interactive or explicitly a button,\n // so disabled / aria-disabled controls stay in the a11y tree as buttons.\n if (onPress || as === \"button\") {\n return (\n <Pressable\n onPress={!isDisabled && onPress ? onPress : undefined}\n disabled={isDisabled}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n style={({ pressed }) => getContainerStyle(pressed)}\n testID={finalTestID}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={\n typeof ariaLabel === \"string\" ? ariaLabel : undefined\n }\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </Pressable>\n );\n }\n\n return (\n <View\n style={getContainerStyle()}\n testID={finalTestID}\n onLayout={onLayout}\n onMoveShouldSetResponder={onMoveShouldSetResponder}\n onResponderGrant={onResponderGrant}\n onResponderMove={onResponderMove}\n onResponderRelease={onResponderRelease}\n onResponderTerminate={onResponderTerminate}\n accessibilityRole={accessibilityRole}\n accessibilityLabel={typeof ariaLabel === \"string\" ? ariaLabel : undefined}\n accessibilityState={accessibilityState}\n accessibilityLiveRegion={accessibilityLiveRegion}\n {...nativeRest}\n >\n {children}\n </View>\n );\n};\n","import React from \"react\";\nimport {\n Text as RNText,\n TextStyle,\n AccessibilityRole,\n StyleSheet,\n} from \"react-native\";\nimport { TextProps } from \"@xsolla/xui-primitives-core\";\n\nconst roleMap: Record<string, AccessibilityRole> = {\n alert: \"alert\",\n heading: \"header\",\n button: \"button\",\n link: \"link\",\n text: \"text\",\n};\n\nconst parseNumericValue = (\n value: string | number | undefined\n): number | undefined => {\n if (value === undefined) return undefined;\n if (typeof value === \"number\") return value;\n const parsed = parseFloat(value);\n return isNaN(parsed) ? undefined : parsed;\n};\n\nexport const Text: React.FC<TextProps> = ({\n children,\n color,\n fontSize,\n fontWeight,\n fontFamily,\n textAlign,\n lineHeight,\n numberOfLines,\n id,\n role,\n testID,\n \"data-testid\": dataTestId,\n style: styleProp,\n ...props\n}) => {\n let resolvedFontFamily = fontFamily\n ? fontFamily.split(\",\")[0].replace(/['\"]/g, \"\").trim()\n : undefined;\n\n if (\n resolvedFontFamily === \"Pilat Wide\" ||\n resolvedFontFamily === \"Pilat Wide Bold\" ||\n resolvedFontFamily === \"Aktiv Grotesk\"\n ) {\n resolvedFontFamily = undefined;\n }\n\n const incomingStyle = StyleSheet.flatten(styleProp) as TextStyle | undefined;\n\n const baseStyle: TextStyle = {\n color: color ?? incomingStyle?.color,\n fontSize: typeof fontSize === \"number\" ? fontSize : undefined,\n fontWeight: fontWeight as TextStyle[\"fontWeight\"],\n fontFamily: resolvedFontFamily,\n textDecorationLine: props.textDecoration as TextStyle[\"textDecorationLine\"],\n textAlign: textAlign ?? incomingStyle?.textAlign,\n lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),\n marginTop: parseNumericValue(\n incomingStyle?.marginTop as number | string | undefined\n ),\n marginBottom: parseNumericValue(\n incomingStyle?.marginBottom as number | string | undefined\n ),\n };\n\n const accessibilityRole = role ? roleMap[role] : undefined;\n\n return (\n <RNText\n style={baseStyle}\n numberOfLines={numberOfLines}\n testID={dataTestId || testID || id}\n accessibilityRole={accessibilityRole}\n >\n {children}\n </RNText>\n );\n};\n","import { createContext, useContext } from \"react\";\nimport type {\n CardSelectorControlPlacement,\n CardSelectorPlatform,\n CardSelectorType,\n} from \"./types\";\n\nexport interface CardSelectorGroupItem {\n value: string;\n disabled: boolean;\n focus: () => void;\n}\n\nexport interface CardSelectorGroupContextValue {\n type: CardSelectorType;\n platform?: CardSelectorPlatform;\n control?: CardSelectorControlPlacement;\n disabled: boolean;\n /** Selected value, `radio` only. */\n selectedValue?: string;\n /** Selects a value, `radio` only. */\n select: (value: string) => void;\n /** Registers a card so the group can drive roving focus. Returns an unregister fn. */\n register: (item: CardSelectorGroupItem) => () => void;\n /** Moves focus (and, for `radio`, selection) by `delta` positions, wrapping. */\n moveFocus: (fromValue: string | undefined, delta: number) => void;\n /** True when the group holds no selection — the first enabled card stays tabbable. */\n isFirstEnabled: (value: string | undefined) => boolean;\n}\n\nexport const CardSelectorGroupContext =\n createContext<CardSelectorGroupContextValue | null>(null);\n\nexport const useCardSelectorGroup = (): CardSelectorGroupContextValue | null =>\n useContext(CardSelectorGroupContext);\n","import React, { useCallback, useMemo, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { useResolvedTheme } from \"@xsolla/xui-core\";\nimport {\n CardSelectorGroupContext,\n type CardSelectorGroupContextValue,\n type CardSelectorGroupItem,\n} from \"./CardSelectorGroupContext\";\nimport type { CardSelectorGroupProps } from \"./types\";\n\n/**\n * Wraps a set of `CardSelector` cards.\n *\n * For `type=\"radio\"` it renders a `radiogroup`, keeps the cards mutually\n * exclusive, and implements the roving-tabindex + Arrow-key behaviour the\n * Figma spec requires (3446:1761 — \"within a Radio group, Arrow keys move\n * focus and selection between cards\"). For `type=\"check\"` the cards toggle\n * independently and the wrapper is a plain `group`.\n */\nexport const CardSelectorGroup: React.FC<CardSelectorGroupProps> = ({\n children,\n type = \"radio\",\n value,\n defaultValue,\n onValueChange,\n platform,\n control,\n disabled = false,\n gap = 12,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledBy,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n}) => {\n // Resolved so the group participates in the same theme scope as its cards.\n useResolvedTheme({ themeMode, themeProductContext });\n\n // Registration lives in state, not a ref, so the roving tabindex is correct\n // on the render right after the cards mount.\n const [items, setItems] = useState<CardSelectorGroupItem[]>([]);\n const [internalValue, setInternalValue] = useState(defaultValue);\n const isControlled = value !== undefined;\n const selectedValue = isControlled ? value : internalValue;\n\n const register = useCallback((item: CardSelectorGroupItem) => {\n setItems((prev) => [...prev, item]);\n return () => setItems((prev) => prev.filter((entry) => entry !== item));\n }, []);\n\n const select = useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange]\n );\n\n const moveFocus = useCallback(\n (fromValue: string | undefined, delta: number) => {\n const enabled = items.filter((item) => !item.disabled);\n if (enabled.length === 0) return;\n\n const currentIndex = enabled.findIndex(\n (item) => item.value === fromValue\n );\n const nextIndex =\n currentIndex === -1\n ? 0\n : (currentIndex + delta + enabled.length) % enabled.length;\n const next = enabled[nextIndex];\n if (!next) return;\n\n next.focus();\n if (type === \"radio\" && next.value) select(next.value);\n },\n [items, select, type]\n );\n\n const isFirstEnabled = useCallback(\n (candidate: string | undefined) => {\n const first = items.find((item) => !item.disabled);\n return Boolean(first) && first?.value === candidate;\n },\n [items]\n );\n\n const contextValue = useMemo<CardSelectorGroupContextValue>(\n () => ({\n type,\n platform,\n control,\n disabled,\n selectedValue,\n select,\n register,\n moveFocus,\n isFirstEnabled,\n }),\n [\n control,\n disabled,\n isFirstEnabled,\n moveFocus,\n platform,\n register,\n select,\n selectedValue,\n type,\n ]\n );\n\n return (\n <CardSelectorGroupContext.Provider value={contextValue}>\n <Box\n role={type === \"radio\" ? \"radiogroup\" : \"group\"}\n aria-label={ariaLabelledBy ? undefined : ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-disabled={disabled || undefined}\n flexDirection=\"row\"\n flexWrap=\"wrap\"\n alignItems=\"flex-start\"\n gap={gap}\n testID={testID || \"card-selector-group\"}\n >\n {children}\n </Box>\n </CardSelectorGroupContext.Provider>\n );\n};\n"],"mappings":"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","names":["jsx","jsx","useCallback","useState","useResolvedTheme","jsx","useResolvedTheme","useState","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-b2c-card-selector",
3
- "version": "0.218.1",
3
+ "version": "0.219.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
@@ -13,8 +13,8 @@
13
13
  "test:coverage": "vitest run --coverage"
14
14
  },
15
15
  "dependencies": {
16
- "@xsolla/xui-core": "0.218.1",
17
- "@xsolla/xui-primitives-core": "0.218.1"
16
+ "@xsolla/xui-core": "0.219.0",
17
+ "@xsolla/xui-primitives-core": "0.219.0"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "react": ">=16.8.0"
package/web/index.js CHANGED
@@ -3577,6 +3577,10 @@ var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
3577
3577
  "fontWeight",
3578
3578
  "fontFamily",
3579
3579
  "textDecoration",
3580
+ // `display` is a presentation attribute on SVG, so isPropValid lets it
3581
+ // through — but Box consumes it purely as CSS. Without this it lands on the
3582
+ // DOM node as a bogus `display="inline-flex"` attribute.
3583
+ "display",
3580
3584
  // Cross-platform layout prop that Text consumes as CSS, never as an
3581
3585
  // attribute. Pinned explicitly because it is now spread into the styled
3582
3586
  // component rather than destructured away before it can reach the DOM.
@@ -3611,7 +3615,7 @@ function createFilteredElement(defaultTag) {
3611
3615
  var import_jsx_runtime = require("react/jsx-runtime");
3612
3616
  var FilteredDiv = createFilteredElement("div");
3613
3617
  var StyledBox = styled_components_esm_default(FilteredDiv)`
3614
- display: flex;
3618
+ display: ${(props) => props.display || "flex"};
3615
3619
  box-sizing: border-box;
3616
3620
  background-color: ${(props) => props.backgroundColor || "transparent"};
3617
3621
  border-color: ${(props) => props.borderColor || "transparent"};
@@ -3747,7 +3751,7 @@ var Box = import_react3.default.forwardRef(
3747
3751
  onLoad,
3748
3752
  "data-testid": dataTestId || testID,
3749
3753
  style: {
3750
- display: "block",
3754
+ display: props.display || "block",
3751
3755
  objectFit: "cover",
3752
3756
  width: typeof props.width === "number" ? `${props.width}px` : props.width,
3753
3757
  height: typeof props.height === "number" ? `${props.height}px` : props.height,