@xsolla/xui-b2c-popover 0.218.0 → 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
@@ -68,6 +68,7 @@ var toGapValue = (gap) => {
68
68
  const asNumber = Number(gap);
69
69
  return Number.isFinite(asNumber) ? asNumber : void 0;
70
70
  };
71
+ var toNativeDisplay = (display) => display === "none" ? "none" : void 0;
71
72
  var Box = ({
72
73
  children,
73
74
  onPress,
@@ -99,6 +100,7 @@ var Box = ({
99
100
  marginBottom,
100
101
  marginLeft,
101
102
  marginRight,
103
+ display,
102
104
  flexDirection,
103
105
  flexWrap,
104
106
  alignItems,
@@ -162,6 +164,7 @@ var Box = ({
162
164
  marginBottom,
163
165
  marginLeft,
164
166
  marginRight,
167
+ display: toNativeDisplay(display),
165
168
  flexDirection,
166
169
  flexWrap,
167
170
  alignItems,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.tsx","../../src/Popover.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../src/PopoverRoot.native.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["export { Popover } from \"./Popover\";\nexport { PopoverTitleBar } from \"./PopoverTitleBar\";\nexport { usePopover } from \"./usePopover\";\nexport { usePopoverDrag } from \"./usePopoverDrag\";\nexport { useDeviceType, resolveDevice, POPOVER_BREAKPOINTS } from \"./useDeviceType\";\nexport type {\n PopoverProps,\n PopoverDevice,\n PopoverPosition,\n PopoverDragHandleProps,\n PopoverRootProps,\n PopoverTitleBarProps,\n} from \"./types\";\nexport type { UsePopoverOptions, UsePopoverReturn } from \"./usePopover\";\nexport type {\n UsePopoverDragOptions,\n UsePopoverDragReturn,\n} from \"./usePopoverDrag\";\n","import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\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 { memo } from \"react\";\nimport type { PopoverRootProps } from \"./types\";\n\n// Web portal not available on React Native.\n// A native popover would present through RN's own Modal / Animated APIs.\nexport const PopoverRoot = memo((_props: PopoverRootProps) => {\n return null;\n});\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","handleKeyDown","import_react"]}
1
+ {"version":3,"sources":["../../src/index.tsx","../../src/Popover.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../src/PopoverRoot.native.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["export { Popover } from \"./Popover\";\nexport { PopoverTitleBar } from \"./PopoverTitleBar\";\nexport { usePopover } from \"./usePopover\";\nexport { usePopoverDrag } from \"./usePopoverDrag\";\nexport { useDeviceType, resolveDevice, POPOVER_BREAKPOINTS } from \"./useDeviceType\";\nexport type {\n PopoverProps,\n PopoverDevice,\n PopoverPosition,\n PopoverDragHandleProps,\n PopoverRootProps,\n PopoverTitleBarProps,\n} from \"./types\";\nexport type { UsePopoverOptions, UsePopoverReturn } from \"./usePopover\";\nexport type {\n UsePopoverDragOptions,\n UsePopoverDragReturn,\n} from \"./usePopoverDrag\";\n","import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\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 { memo } from \"react\";\nimport type { PopoverRootProps } from \"./types\";\n\n// Web portal not available on React Native.\n// A native popover would present through RN's own Modal / Animated APIs.\nexport const PopoverRoot = memo((_props: PopoverRootProps) => {\n return null;\n});\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","handleKeyDown","import_react"]}
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/Popover.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../src/PopoverRoot.native.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\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 { memo } from \"react\";\nimport type { PopoverRootProps } from \"./types\";\n\n// Web portal not available on React Native.\n// A native popover would present through RN's own Modal / Animated APIs.\nexport const PopoverRoot = memo((_props: PopoverRootProps) => {\n return null;\n});\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":";AAAA,SAAS,YAAY,eAAAA,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACCrE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AAwMD;AArMN,IAAM,0BAA6D;AAAA,EACjE,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,KAAK;AAAA,EACL,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AACT;AAGA,IAAM,aAAa,CAAC,QAAyD;AAC3E,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,UAAU,sBAAsB,KAAK,GAAG;AAC9C,MAAI,QAAS,QAAO,OAAO,QAAQ,CAAC,CAAC;AACrC,QAAM,WAAW,OAAO,GAAG;AAC3B,SAAO,OAAO,SAAS,QAAQ,IAAI,WAAW;AAChD;AAEO,IAAM,MAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,QAAM,oBAAoB,CAAC,aAAkC;AAAA,IAC3D,iBACE,WAAW,YAAY,kBACnB,WAAW,kBACX;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,YAAY,WAAW,MAAM;AAAA,IACtC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,WAAW,GAAG;AAAA,IACnB,GAAI;AAAA,EACN;AAEA,QAAM,cAAc,cAAc;AAKlC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,aAAa;AAAA,IACb;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,oBACH,OAAO,SAAS,YAAY,wBAAwB,IAAI,MACxD,OAAO,WAAW,WAAW;AAChC,QAAM,aAAa,QAAQ,YAAY,YAAY;AACnD,QAAM,qBAAqB;AAAA,IACzB,UAAU,cAAc;AAAA,IACxB,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC7B;AACA,QAAM,0BACJ,aAAa,YAAY,aAAa,cAAc,WAAW;AAGjE,MAAI,OAAO,SAAS,KAAK;AACvB,UAAM,aAAyB;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI;AAAA,IACN;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,QAChE,YAAW;AAAA,QACV,GAAG;AAAA;AAAA,IACN;AAAA,EAEJ;AAIA,MAAI,WAAW,OAAO,UAAU;AAC9B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,CAAC,cAAc,UAAU,UAAU;AAAA,QAC5C,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,CAAC,EAAE,QAAQ,MAAM,kBAAkB,OAAO;AAAA,QACjD,QAAQ;AAAA,QACR;AAAA,QACA,oBACE,OAAO,cAAc,WAAW,YAAY;AAAA,QAE9C;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,kBAAkB;AAAA,MACzB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,oBAAoB,OAAO,cAAc,WAAW,YAAY;AAAA,MAChE;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;;;ADzQA,SAAS,aAAa,wBAAwB;;;AEH9C,SAAS,YAAY;AAKd,IAAM,cAAc,KAAK,CAAC,WAA6B;AAC5D,SAAO;AACT,CAAC;AAED,YAAY,cAAc;;;ACT1B,SAAS,QAAAC,aAAY;AACrB,SAAS,kBAAkB;AAC3B,SAAS,MAAM,KAAK,QAAQ,aAAa;AACzC,SAAS,kBAAkB;AAsBvB,SAqBM,OAAAC,MArBN;AAVG,IAAM,kBAAkBD;AAAA,EAC7B,CAAC;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAa;AAAA,MACb,OAAO;AAAA,QACL,YAAY;AAAA,QACZ,OAAO;AAAA,QACP,SAAS;AAAA,QACT,eAAe;AAAA,QACf,YAAY;AAAA,QACZ,KAAK;AAAA,QACL,QAAQ,YAAY,SAAS;AAAA,MAC/B;AAAA,MACC,GAAI,YAAY,kBAAkB,CAAC;AAAA,MAEnC;AAAA,qBACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,gCAA6B;AAAA,YAC7B,MAAK;AAAA,YACL,UAAU;AAAA,YACV,cAAW;AAAA,YACX,OAAO,EAAE,SAAS,eAAe,YAAY,EAAE;AAAA,YAE/C,0BAAAA,KAAC,QAAK;AAAA;AAAA,QACR;AAAA,QAGF,gBAAAA,KAAC,SAAI,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,SAAS,GACpD,mBACC,gBAAAA,KAAC,cAAW,SAAQ,gBAAe,QAAM,MACtC,iBACH,GAEJ;AAAA,QAEC,kBACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,MAAK;AAAA,YACL,MAAM,SAAS,gBAAAA,KAAC,SAAM,IAAK,gBAAAA,KAAC,OAAI;AAAA,YAChC,SAAS,MAAM,eAAe,CAAC,MAAM;AAAA,YACrC,cAAY,SAAS,kBAAkB;AAAA,YACvC,gBAAc;AAAA;AAAA,QAChB;AAAA,QAGD,WACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,MAAK;AAAA,YACL,MAAM,gBAAAA,KAAC,UAAO;AAAA,YACd,SAAS;AAAA,YACT,cAAW;AAAA;AAAA,QACb;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,gBAAgB,cAAc;;;ACpF9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAsBP,IAAM,gBAAgB;AAEtB,IAAM,cAAc;AAGpB,IAAM,gBAAgB,CAAC,WACrB,kBAAkB,WAAW,OAAO,QAAQ,kBAAkB,MAAM;AAEtE,IAAM,kBAAkB,CACtB,UACA,UACoB;AACpB,MAAI,CAAC,SAAS,OAAO,WAAW,YAAa,QAAO;AACpD,QAAM,EAAE,OAAO,OAAO,IAAI,MAAM,sBAAsB;AACtD,QAAM,OAAO,KAAK,IAAI,aAAa,OAAO,aAAa,QAAQ,WAAW;AAC1E,QAAM,OAAO,KAAK,IAAI,aAAa,OAAO,cAAc,SAAS,WAAW;AAC5E,SAAO;AAAA,IACL,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,GAAG,WAAW,GAAG,IAAI;AAAA,IACnD,GAAG,KAAK,IAAI,KAAK,IAAI,SAAS,GAAG,WAAW,GAAG,IAAI;AAAA,EACrD;AACF;AASO,IAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,MAAmD;AACjD,QAAM,WAAW,OAAuB,IAAI;AAC5C,QAAM,CAAC,UAAU,WAAW,IAAI;AAAA,IAC9B;AAAA,EACF;AACA,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAGlD,QAAM,aAAa,OAAwB,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;AAKzD,YAAU,MAAM;AACd,QAAI,CAAC,QAAS,aAAY,MAAS;AAAA,EACrC,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,SAAS;AAAA,IACb,CAAC,SAA0B;AACzB,YAAM,UAAU,gBAAgB,MAAM,SAAS,OAAO;AACtD,kBAAY,OAAO;AACnB,yBAAmB,OAAO;AAAA,IAC5B;AAAA,IACA,CAAC,gBAAgB;AAAA,EACnB;AAEA,QAAM,gBAAgB;AAAA,IACpB,CAAC,UAA6B;AAC5B,UAAI,CAAC,WAAW,MAAM,WAAW,EAAG;AAGpC,UAAI,cAAc,MAAM,MAAM,EAAG;AACjC,YAAM,QAAQ,SAAS;AACvB,UAAI,CAAC,MAAO;AAEZ,YAAM,eAAe;AACrB,YAAM,OAAO,MAAM,sBAAsB;AACzC,iBAAW,UAAU;AAAA,QACnB,GAAG,MAAM,UAAU,KAAK;AAAA,QACxB,GAAG,MAAM,UAAU,KAAK;AAAA,MAC1B;AACA,oBAAc,IAAI;AAAA,IACpB;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,UAA8B;AAC7B,UAAI,CAAC,QAAS;AAGd,UACE,EAAE,MAAM,kBAAkB,YAC1B,CAAC,MAAM,OAAO,QAAQ,gCAAgC,GACtD;AACA;AAAA,MACF;AACA,YAAM,SAA0C;AAAA,QAC9C,SAAS,EAAE,GAAG,GAAG,GAAG,CAAC,cAAc;AAAA,QACnC,WAAW,EAAE,GAAG,GAAG,GAAG,cAAc;AAAA,QACpC,WAAW,EAAE,GAAG,CAAC,eAAe,GAAG,EAAE;AAAA,QACrC,YAAY,EAAE,GAAG,eAAe,GAAG,EAAE;AAAA,MACvC;AACA,YAAM,QAAQ,OAAO,MAAM,GAAG;AAC9B,UAAI,CAAC,MAAO;AAEZ,YAAM,eAAe;AAGrB,YAAM,OAAO,SAAS,SAAS,sBAAsB;AACrD,YAAM,OAAO,YAAY,EAAE,GAAG,MAAM,QAAQ,GAAG,GAAG,MAAM,OAAO,EAAE;AACjE,aAAO,EAAE,GAAG,KAAK,IAAI,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AAAA,IACrD;AAAA,IACA,CAAC,QAAQ,SAAS,QAAQ;AAAA,EAC5B;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,OAAO,aAAa,YAAa;AAEpD,UAAM,aAAa,CAAC,UAAwB;AAC1C,aAAO;AAAA,QACL,GAAG,MAAM,UAAU,WAAW,QAAQ;AAAA,QACtC,GAAG,MAAM,UAAU,WAAW,QAAQ;AAAA,MACxC,CAAC;AAAA,IACH;AACA,UAAM,OAAO,MAAM,cAAc,KAAK;AAEtC,aAAS,iBAAiB,eAAe,UAAU;AACnD,aAAS,iBAAiB,aAAa,IAAI;AAC3C,aAAS,iBAAiB,iBAAiB,IAAI;AAC/C,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,UAAU;AACtD,eAAS,oBAAoB,aAAa,IAAI;AAC9C,eAAS,oBAAoB,iBAAiB,IAAI;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL,UAAU,UAAU,WAAW;AAAA,IAC/B;AAAA,IACA,iBAAiB,EAAE,eAAe,UAAU;AAAA,IAC5C;AAAA,EACF;AACF;;;ACtKA,SAAS,aAAAC,YAAW,YAAAC,iBAAgB;AAO7B,IAAM,sBAAsB;AAAA;AAAA,EAEjC,WAAW;AAAA;AAAA,EAEX,WAAW;AACb;AAGO,IAAM,gBAAgB,CAAC,UAAiC;AAC7D,MAAI,SAAS,oBAAoB,UAAW,QAAO;AACnD,MAAI,SAAS,oBAAoB,UAAW,QAAO;AACnD,SAAO;AACT;AAUO,IAAM,gBAAgB,CAAC,aAA4C;AACxE,QAAM,CAAC,QAAQ,SAAS,IAAIA;AAAA,IAAwB,MAClD,OAAO,WAAW,cAAc,YAAY,cAAc,OAAO,UAAU;AAAA,EAC7E;AAEA,EAAAD,WAAU,MAAM;AACd,QAAI,YAAY,OAAO,WAAW,YAAa;AAE/C,UAAM,SAAS,MAAM,UAAU,cAAc,OAAO,UAAU,CAAC;AAE/D,WAAO;AAEP,WAAO,iBAAiB,UAAU,MAAM;AACxC,WAAO,MAAM,OAAO,oBAAoB,UAAU,MAAM;AAAA,EAC1D,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAO,YAAY;AACrB;;;AL+JQ,SA0CM,OAAAE,MA1CN,QAAAC,aAAA;AAnMR,IAAM,sBACJ;AAGF,IAAM,gBAAgB;AAEtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AAEpB,IAAM,eAAe,YAAY,KAAK;AAEtC,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAE3B,IAAM,mBAAmB,CAAC,OAA6B;AACrD,MAAI,GAAG,iBAAiB,QAAQ,iBAAiB,EAAE,EAAE,aAAa;AAChE,WAAO;AACT,QAAM,QAAQ,iBAAiB,EAAE;AACjC,SAAO,MAAM,eAAe,YAAY,MAAM,YAAY;AAC5D;AAEA,IAAM,uBAAuB,CAAC,cAC5B,MAAM;AAAA,EACJ,UAAU,iBAA8B,mBAAmB;AAC7D,EAAE,OAAO,gBAAgB;AA4BpB,IAAM,UAAU;AAAA,EACrB,CACE;AAAA,IACE,OAAO;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,EACxB,GACA,QACG;AACH,UAAM,EAAE,MAAM,IAAI,iBAAiB,EAAE,WAAW,oBAAoB,CAAC;AACrE,UAAM,SAAS,cAAc,UAAU;AACvC,UAAM,aAAa,WAAW;AAG9B,UAAM,UAAU,aAAa,CAAC;AAG9B,UAAM,iBAAiB,uBAAuB,CAAC,UAAU,CAAC;AAE1D,UAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,IAAI;AAC3C,UAAM,YAAYC,QAAuB,IAAI;AAC7C,UAAM,wBAAwBA,QAA2B,IAAI;AAE7D,UAAM,EAAE,UAAU,UAAU,gBAAgB,IAAI,eAAe;AAAA,MAC7D,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF,CAAC;AAED,IAAAC,WAAU,MAAM;AACd,UAAI,KAAM,YAAW,IAAI;AAAA,IAC3B,GAAG,CAAC,IAAI,CAAC;AAET,IAAAA,WAAU,MAAM;AACd,UAAI,CAAC,QAAQ,OAAO,aAAa,YAAa;AAE9C,4BAAsB,UAAU,SAAS;AACzC,YAAM,cACJ,iBAAiB,WAChB,UAAU,WAAW,qBAAqB,UAAU,OAAO,EAAE,CAAC;AAEjE,UAAI,YAAa,aAAY,MAAM;AAAA,UAC9B,WAAU,SAAS,MAAM;AAAA,IAIhC,GAAG,CAAC,MAAM,eAAe,CAAC;AAE1B,IAAAA,WAAU,MAAM;AACd,UAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAS;AACzC,UAAI,OAAO,aAAa,YAAa;AACrC,YAAMC,iBAAgB,CAAC,UAAyB;AAC9C,YAAI,MAAM,QAAQ,SAAU,SAAQ;AAAA,MACtC;AACA,eAAS,iBAAiB,WAAWA,cAAa;AAClD,aAAO,MAAM,SAAS,oBAAoB,WAAWA,cAAa;AAAA,IACpE,GAAG,CAAC,MAAM,eAAe,OAAO,CAAC;AAEjC,UAAM,gBAAgBC,aAAY,CAAC,UAA+B;AAChE,UAAI,MAAM,QAAQ,SAAS,CAAC,UAAU,QAAS;AAC/C,UAAI,OAAO,aAAa,YAAa;AAErC,YAAM,oBAAoB,qBAAqB,UAAU,OAAO;AAChE,YAAM,eAAe,kBAAkB,CAAC;AACxC,YAAM,cAAc,kBAAkB,kBAAkB,SAAS,CAAC;AAElE,UAAI,CAAC,cAAc;AACjB,cAAM,eAAe;AACrB;AAAA,MACF;AAEA,UAAI,MAAM,YAAY,SAAS,kBAAkB,cAAc;AAC7D,cAAM,eAAe;AACrB,oBAAY,MAAM;AAAA,MACpB,WAAW,CAAC,MAAM,YAAY,SAAS,kBAAkB,aAAa;AACpE,cAAM,eAAe;AACrB,qBAAa,MAAM;AAAA,MACrB;AAAA,IACF,GAAG,CAAC,CAAC;AAIL,UAAM,eAAeA;AAAA,MACnB,CAAC,SAAgC;AAE/B,QAAC,UAAyB,UAAU;AACpC,QAAC,SAAwB,UAAU;AACnC,YAAI,OAAO,QAAQ,WAAY,KAAI,IAAI;AAAA,iBAC9B,IAAK,CAAC,IAAmB,UAAU;AAAA,MAC9C;AAAA,MACA,CAAC,UAAU,GAAG;AAAA,IAChB;AAEA,UAAM,eAAeA,aAAY,MAAM;AACrC,iBAAW,KAAK;AAChB,4BAAsB,SAAS,MAAM;AAAA,IACvC,GAAG,CAAC,CAAC;AAEL,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,eAAe,kBAAkB,UAAU;AACjD,UAAM,qBACJ,CAAC,aAAa,SAAS,gBAAgB;AACzC,UAAM,iBACJ,cAAc,OAAO,UAAU,WAAW,QAAQ;AAEpD,WACE,gBAAAN;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU;AAAA,QACV,iBAAiB,iBAAiB,UAAU;AAAA,QAC5C,YACE,SAAS,CAAC,aACL,MAAM,OAAO,OAAO,SAAS,wBAC9B;AAAA,QAEN;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,QAEnB,0BAAAC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,KAAK;AAAA,YACL,MAAK;AAAA,YACL,cAAY,cAAc,QAAQ,SAAS;AAAA,YAC3C,cAAY;AAAA,YACZ,uBAAqB;AAAA,YACrB,uBAAqB,SAAS,SAAS;AAAA,YACvC,UAAU;AAAA,YACV,WAAW;AAAA,YACX,OAAO;AAAA,cACL,WAAW;AAAA,cACX,SAAS;AAAA,cACT,eAAe;AAAA,cACf,YAAY;AAAA,cACZ,KAAK;AAAA,cACL,OAAO,aAAa,SAAS;AAAA,cAC7B,UAAU;AAAA,cACV,QAAQ,aAAa,SAAS;AAAA,cAC9B,WAAW;AAAA,cACX,SAAS;AAAA,cACT,OAAO,MAAM,OAAO,QAAQ;AAAA,cAC5B,iBAAiB,mBAAmB,MAAM,OAAO,WAAW;AAAA,cAC5D,aAAa;AAAA,cACb,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,cAKd,YAAY,aAAa,IAAI;AAAA,cAC7B,eAAe;AAAA,cACf,cAAc,aAAa,IAAI;AAAA,cAC/B,QAAQ,aACJ,SACA,aAAa,MAAM,OAAO,OAAO,SAAS;AAAA,cAC9C,WAAW,aAAa,SAAY,MAAM,OAAO;AAAA,cACjD,gBAAgB,CAAC,cAAc,OAAO,gBAAgB;AAAA,cACtD,GAAG;AAAA,YACL;AAAA,YAEC;AAAA,2BACE,qBACC,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA,WAAW;AAAA,kBACX;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,kBACT,QAAQ,SAAS,GAAG,MAAM,eAAe;AAAA;AAAA,cAC3C,IACE;AAAA,cAEL,UACC,gBAAAA,KAAC,SAAI,OAAO,EAAE,YAAY,GAAG,OAAO,OAAO,GAAI,kBAAO;AAAA,cAGxD,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,oBACL,MAAM;AAAA,oBACN,WAAW;AAAA,oBACX,OAAO;AAAA,oBACP,WAAW;AAAA,oBACX,oBAAoB;AAAA,kBACtB;AAAA,kBAEC;AAAA;AAAA,cACH;AAAA,cAEC,UACC,gBAAAA,KAAC,SAAI,OAAO,EAAE,YAAY,GAAG,OAAO,OAAO,GAAI,kBAAO;AAAA;AAAA;AAAA,QAE1D;AAAA;AAAA,IACF;AAAA,EAEJ;AACF;AAEA,QAAQ,cAAc;;;AM3RtB,SAAS,eAAAO,cAAa,YAAAC,iBAAgB;AAe/B,SAAS,WAAW,SAA+C;AACxE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAE1C,QAAM,OAAOD,aAAY,MAAM;AAC7B,cAAU,IAAI;AACd,aAAS,SAAS;AAAA,EACpB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,QAAQA,aAAY,MAAM;AAC9B,cAAU,KAAK;AACf,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,SAASA,aAAY,MAAM;AAC/B,cAAU,CAAC,aAAa;AACtB,UAAI,SAAU,UAAS,UAAU;AAAA,UAC5B,UAAS,SAAS;AACvB,aAAO,CAAC;AAAA,IACV,CAAC;AAAA,EACH,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,QAAQ,MAAM,OAAO,OAAO;AACvC;","names":["useCallback","useEffect","useRef","useState","memo","jsx","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","handleKeyDown","useCallback","useCallback","useState"]}
1
+ {"version":3,"sources":["../../src/Popover.tsx","../../../../foundation/primitives-native/src/Box.tsx","../../src/PopoverRoot.native.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\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 { memo } from \"react\";\nimport type { PopoverRootProps } from \"./types\";\n\n// Web portal not available on React Native.\n// A native popover would present through RN's own Modal / Animated APIs.\nexport const PopoverRoot = memo((_props: PopoverRootProps) => {\n return null;\n});\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","memo","jsx","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","handleKeyDown","useCallback","useCallback","useState"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-b2c-popover",
3
- "version": "0.218.0",
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,11 +13,11 @@
13
13
  "test:coverage": "vitest run --coverage"
14
14
  },
15
15
  "dependencies": {
16
- "@xsolla/xui-button": "0.218.0",
17
- "@xsolla/xui-core": "0.218.0",
18
- "@xsolla/xui-icons-base": "0.218.0",
19
- "@xsolla/xui-primitives-core": "0.218.0",
20
- "@xsolla/xui-typography": "0.218.0"
16
+ "@xsolla/xui-button": "0.219.0",
17
+ "@xsolla/xui-core": "0.219.0",
18
+ "@xsolla/xui-icons-base": "0.219.0",
19
+ "@xsolla/xui-primitives-core": "0.219.0",
20
+ "@xsolla/xui-typography": "0.219.0"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "react": ">=16.8.0",
package/web/index.js CHANGED
@@ -91,6 +91,10 @@ var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
91
91
  "fontWeight",
92
92
  "fontFamily",
93
93
  "textDecoration",
94
+ // `display` is a presentation attribute on SVG, so isPropValid lets it
95
+ // through — but Box consumes it purely as CSS. Without this it lands on the
96
+ // DOM node as a bogus `display="inline-flex"` attribute.
97
+ "display",
94
98
  // Cross-platform layout prop that Text consumes as CSS, never as an
95
99
  // attribute. Pinned explicitly because it is now spread into the styled
96
100
  // component rather than destructured away before it can reach the DOM.
@@ -125,7 +129,7 @@ function createFilteredElement(defaultTag) {
125
129
  var import_jsx_runtime = require("react/jsx-runtime");
126
130
  var FilteredDiv = createFilteredElement("div");
127
131
  var StyledBox = (0, import_styled_components.default)(FilteredDiv)`
128
- display: flex;
132
+ display: ${(props) => props.display || "flex"};
129
133
  box-sizing: border-box;
130
134
  background-color: ${(props) => props.backgroundColor || "transparent"};
131
135
  border-color: ${(props) => props.borderColor || "transparent"};
@@ -261,7 +265,7 @@ var Box = import_react2.default.forwardRef(
261
265
  onLoad,
262
266
  "data-testid": dataTestId || testID,
263
267
  style: {
264
- display: "block",
268
+ display: props.display || "block",
265
269
  objectFit: "cover",
266
270
  width: typeof props.width === "number" ? `${props.width}px` : props.width,
267
271
  height: typeof props.height === "number" ? `${props.height}px` : props.height,
package/web/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.tsx","../../src/Popover.tsx","../../../../foundation/primitives-web/src/Box.tsx","../../../../foundation/primitives-web/src/filterDOMProps.ts","../../../../../node_modules/@emotion/memoize/dist/memoize.esm.js","../../../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js","../../src/PopoverRoot.web.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["export { Popover } from \"./Popover\";\nexport { PopoverTitleBar } from \"./PopoverTitleBar\";\nexport { usePopover } from \"./usePopover\";\nexport { usePopoverDrag } from \"./usePopoverDrag\";\nexport { useDeviceType, resolveDevice, POPOVER_BREAKPOINTS } from \"./useDeviceType\";\nexport type {\n PopoverProps,\n PopoverDevice,\n PopoverPosition,\n PopoverDragHandleProps,\n PopoverRootProps,\n PopoverTitleBarProps,\n} from \"./types\";\nexport type { UsePopoverOptions, UsePopoverReturn } from \"./usePopover\";\nexport type {\n UsePopoverDragOptions,\n UsePopoverDragReturn,\n} from \"./usePopoverDrag\";\n","import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport type { BoxProps } from \"@xsolla/xui-primitives-core\";\nimport { createFilteredElement } from \"./filterDOMProps\";\n\nconst FilteredDiv = createFilteredElement(\"div\");\n\nconst StyledBox = styled(FilteredDiv)<BoxProps>`\n display: flex;\n box-sizing: border-box;\n background-color: ${(props) => props.backgroundColor || \"transparent\"};\n border-color: ${(props) => props.borderColor || \"transparent\"};\n border-width: ${(props) =>\n typeof props.borderWidth === \"number\"\n ? `${props.borderWidth}px`\n : props.borderWidth || 0};\n\n ${(props) =>\n props.borderBottomWidth !== undefined &&\n `\n border-bottom-width: ${typeof props.borderBottomWidth === \"number\" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};\n border-bottom-color: ${props.borderBottomColor || props.borderColor || \"transparent\"};\n border-bottom-style: solid;\n `}\n ${(props) =>\n props.borderTopWidth !== undefined &&\n `\n border-top-width: ${typeof props.borderTopWidth === \"number\" ? `${props.borderTopWidth}px` : props.borderTopWidth};\n border-top-color: ${props.borderTopColor || props.borderColor || \"transparent\"};\n border-top-style: solid;\n `}\n ${(props) =>\n props.borderLeftWidth !== undefined &&\n `\n border-left-width: ${typeof props.borderLeftWidth === \"number\" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};\n border-left-color: ${props.borderLeftColor || props.borderColor || \"transparent\"};\n border-left-style: solid;\n `}\n ${(props) =>\n props.borderRightWidth !== undefined &&\n `\n border-right-width: ${typeof props.borderRightWidth === \"number\" ? `${props.borderRightWidth}px` : props.borderRightWidth};\n border-right-color: ${props.borderRightColor || props.borderColor || \"transparent\"};\n border-right-style: solid;\n `}\n\n border-style: ${(props) =>\n props.borderStyle ||\n (props.borderWidth ||\n props.borderBottomWidth ||\n props.borderTopWidth ||\n props.borderLeftWidth ||\n props.borderRightWidth\n ? \"solid\"\n : \"none\")};\n border-radius: ${(props) =>\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius || 0};\n height: ${(props) =>\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height || \"auto\"};\n width: ${(props) =>\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width || \"auto\"};\n min-width: ${(props) =>\n typeof props.minWidth === \"number\"\n ? `${props.minWidth}px`\n : props.minWidth || \"auto\"};\n min-height: ${(props) =>\n typeof props.minHeight === \"number\"\n ? `${props.minHeight}px`\n : props.minHeight || \"auto\"};\n max-width: ${(props) =>\n typeof props.maxWidth === \"number\"\n ? `${props.maxWidth}px`\n : props.maxWidth || \"none\"};\n max-height: ${(props) =>\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight || \"none\"};\n\n padding: ${(props) =>\n typeof props.padding === \"number\"\n ? `${props.padding}px`\n : props.padding || 0};\n ${(props) =>\n props.paddingHorizontal &&\n `\n padding-left: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n padding-right: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n `}\n ${(props) =>\n props.paddingVertical &&\n `\n padding-top: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n padding-bottom: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n `}\n ${(props) =>\n props.paddingTop !== undefined &&\n `padding-top: ${typeof props.paddingTop === \"number\" ? `${props.paddingTop}px` : props.paddingTop};`}\n ${(props) =>\n props.paddingBottom !== undefined &&\n `padding-bottom: ${typeof props.paddingBottom === \"number\" ? `${props.paddingBottom}px` : props.paddingBottom};`}\n ${(props) =>\n props.paddingLeft !== undefined &&\n `padding-left: ${typeof props.paddingLeft === \"number\" ? `${props.paddingLeft}px` : props.paddingLeft};`}\n ${(props) =>\n props.paddingRight !== undefined &&\n `padding-right: ${typeof props.paddingRight === \"number\" ? `${props.paddingRight}px` : props.paddingRight};`}\n\n margin: ${(props) =>\n typeof props.margin === \"number\" ? `${props.margin}px` : props.margin || 0};\n ${(props) =>\n props.marginTop !== undefined &&\n `margin-top: ${typeof props.marginTop === \"number\" ? `${props.marginTop}px` : props.marginTop};`}\n ${(props) =>\n props.marginBottom !== undefined &&\n `margin-bottom: ${typeof props.marginBottom === \"number\" ? `${props.marginBottom}px` : props.marginBottom};`}\n ${(props) =>\n props.marginLeft !== undefined &&\n `margin-left: ${typeof props.marginLeft === \"number\" ? `${props.marginLeft}px` : props.marginLeft};`}\n ${(props) =>\n props.marginRight !== undefined &&\n `margin-right: ${typeof props.marginRight === \"number\" ? `${props.marginRight}px` : props.marginRight};`}\n\n flex-direction: ${(props) => props.flexDirection || \"column\"};\n flex-wrap: ${(props) => props.flexWrap || \"nowrap\"};\n align-items: ${(props) => props.alignItems || \"stretch\"};\n justify-content: ${(props) => props.justifyContent || \"flex-start\"};\n cursor: ${(props) =>\n props.cursor\n ? props.cursor\n : props.onClick || props.onPress\n ? \"pointer\"\n : \"inherit\"};\n position: ${(props) => props.position || \"static\"};\n top: ${(props) =>\n typeof props.top === \"number\" ? `${props.top}px` : props.top};\n bottom: ${(props) =>\n typeof props.bottom === \"number\" ? `${props.bottom}px` : props.bottom};\n left: ${(props) =>\n typeof props.left === \"number\" ? `${props.left}px` : props.left};\n right: ${(props) =>\n typeof props.right === \"number\" ? `${props.right}px` : props.right};\n flex: ${(props) => props.flex};\n flex-shrink: ${(props) => props.flexShrink ?? 1};\n gap: ${(props) =>\n typeof props.gap === \"number\" ? `${props.gap}px` : props.gap || 0};\n align-self: ${(props) => props.alignSelf || \"auto\"};\n /* Only emit overflow-x/y when explicitly set. Defaulting them to\n visible after overflow would override the shorthand and break\n clipping (e.g. OtherCard bottom-right promo image). */\n overflow: ${(props) => props.overflow || \"visible\"};\n ${(props) =>\n props.overflowX != null ? `overflow-x: ${props.overflowX};` : \"\"}\n ${(props) =>\n props.overflowY != null ? `overflow-y: ${props.overflowY};` : \"\"}\n z-index: ${(props) => props.zIndex};\n opacity: ${(props) =>\n props.opacity != null ? props.opacity : props.disabled ? 0.5 : 1};\n pointer-events: ${(props) => (props.disabled ? \"none\" : \"auto\")};\n\n /* \\`background\\` is emitted before \\`background-color\\` so a caller passing\n both still gets backgroundColor as the winner. Use the shorthand for a\n layered fill (e.g. an overlay tint over a resting surface colour), which\n a single background-color cannot express. */\n &:hover {\n ${(props) =>\n props.hoverStyle?.background &&\n `background: ${props.hoverStyle.background};`}\n ${(props) =>\n props.hoverStyle?.backgroundColor &&\n `background-color: ${props.hoverStyle.backgroundColor};`}\n ${(props) =>\n props.hoverStyle?.borderColor &&\n `border-color: ${props.hoverStyle.borderColor};`}\n }\n\n &:active {\n ${(props) =>\n props.pressStyle?.background &&\n `background: ${props.pressStyle.background};`}\n ${(props) =>\n props.pressStyle?.backgroundColor &&\n `background-color: ${props.pressStyle.backgroundColor};`}\n }\n`;\n\nexport const Box = React.forwardRef<\n HTMLDivElement | HTMLButtonElement,\n BoxProps\n>(\n (\n {\n children,\n onPress,\n onKeyDown,\n onKeyUp,\n role,\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 \"aria-describedby\": ariaDescribedBy,\n \"aria-expanded\": ariaExpanded,\n \"aria-haspopup\": ariaHasPopup,\n \"aria-pressed\": ariaPressed,\n \"aria-controls\": ariaControls,\n tabIndex,\n as,\n src,\n alt,\n onError,\n onLoad,\n type,\n disabled,\n id,\n testID,\n \"data-testid\": dataTestId,\n ...props\n },\n ref\n ) => {\n // Handle as=\"img\" for rendering images with proper border-radius\n if (as === \"img\" && src) {\n return (\n <img\n src={src}\n alt={alt || \"\"}\n onError={onError}\n onLoad={onLoad}\n data-testid={dataTestId || testID}\n style={{\n display: \"block\",\n objectFit: \"cover\",\n width:\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width,\n height:\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height,\n borderRadius:\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius,\n position: props.position,\n top: typeof props.top === \"number\" ? `${props.top}px` : props.top,\n left:\n typeof props.left === \"number\" ? `${props.left}px` : props.left,\n right:\n typeof props.right === \"number\"\n ? `${props.right}px`\n : props.right,\n bottom:\n typeof props.bottom === \"number\"\n ? `${props.bottom}px`\n : props.bottom,\n ...props.style,\n }}\n />\n );\n }\n\n return (\n <StyledBox\n ref={ref}\n elementType={as}\n id={id}\n type={as === \"button\" ? type || \"button\" : undefined}\n disabled={as === \"button\" ? disabled : undefined}\n onClick={onPress}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-current={ariaCurrent}\n aria-disabled={ariaDisabled}\n aria-busy={ariaBusy}\n aria-describedby={ariaDescribedBy}\n aria-expanded={ariaExpanded}\n aria-haspopup={ariaHasPopup}\n aria-pressed={ariaPressed}\n aria-controls={ariaControls}\n aria-live={ariaLive}\n tabIndex={tabIndex !== undefined ? tabIndex : undefined}\n data-testid={dataTestId || testID}\n {...props}\n >\n {children}\n </StyledBox>\n );\n }\n);\n\nBox.displayName = \"Box\";\n","import React from \"react\";\nimport isPropValid from \"@emotion/is-prop-valid\";\n\n// Props that @emotion/is-prop-valid incorrectly treats as valid HTML.\n// These are React Native or component-specific props that match\n// valid HTML patterns (on* event handlers, SVG attributes).\nexport const ADDITIONAL_BLOCKED_PROPS = new Set([\n // RN-only event handlers (pass isPropValid's on* pattern)\n \"onPress\",\n \"onChangeText\",\n \"onLayout\",\n \"onMoveShouldSetResponder\",\n \"onResponderGrant\",\n \"onResponderMove\",\n \"onResponderRelease\",\n \"onResponderTerminate\",\n // SVG attributes that pass isPropValid\n \"strokeWidth\",\n // CSS properties that pass isPropValid but are used as component props\n \"overflow\",\n \"opacity\",\n \"cursor\",\n \"fontSize\",\n \"fontWeight\",\n \"fontFamily\",\n \"textDecoration\",\n // Cross-platform layout prop that Text consumes as CSS, never as an\n // attribute. Pinned explicitly because it is now spread into the styled\n // component rather than destructured away before it can reach the DOM.\n \"numberOfLines\",\n]);\n\nfunction shouldForwardProp(key: string): boolean {\n if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;\n return isPropValid(key);\n}\n\n/**\n * Creates a React component that renders the given HTML tag\n * but filters out non-HTML props before they reach the DOM.\n *\n * Uses @emotion/is-prop-valid (same library styled-components v4\n * uses internally) to automatically block invalid HTML attributes,\n * plus a small blocklist for false positives (RN on* handlers, SVG attrs).\n *\n * Usage: `const FilteredDiv = createFilteredElement(\"div\");`\n * Then: `const StyledBox = styled(FilteredDiv)<BoxProps>\\`...\\`;`\n *\n * styled-components can still read ALL props for CSS interpolation,\n * but only valid HTML attributes are forwarded to the DOM element.\n */\nexport function createFilteredElement(defaultTag: string) {\n const Component = React.forwardRef<HTMLElement, Record<string, unknown>>(\n ({ children, elementType, ...props }, ref) => {\n const Tag = (elementType as string) || defaultTag;\n const htmlProps: Record<string, unknown> = {};\n for (const key of Object.keys(props)) {\n if (shouldForwardProp(key)) {\n htmlProps[key] = props[key];\n }\n }\n return React.createElement(\n Tag,\n { ref, ...htmlProps },\n children as React.ReactNode\n );\n }\n );\n Component.displayName = `Filtered(${defaultTag})`;\n return Component;\n}\n","function memoize(fn) {\n var cache = {};\n return function (arg) {\n if (cache[arg] === undefined) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\n\nexport default memoize;\n","import memoize from '@emotion/memoize';\n\nvar reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/; // https://esbench.com/bench/5bfee68a4cd7e6009ef61d23\n\nvar index = memoize(function (prop) {\n return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111\n /* o */\n && prop.charCodeAt(1) === 110\n /* n */\n && prop.charCodeAt(2) < 91;\n}\n/* Z+1 */\n);\n\nexport default index;\n","import { memo, useEffect, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport {\n useOverlayLayer,\n OverlayLayerProvider,\n MODAL_LAYER_STEP,\n} from \"@xsolla/xui-core\";\nimport type { PopoverRootProps } from \"./types\";\n\n/** Breathing room between a centred floating panel and the viewport edges. */\nconst OUTER_PADDING = 20;\n\nexport const PopoverRoot = memo(\n ({\n children,\n open,\n onExited,\n onBackdropClick,\n scrimColor,\n fullScreen,\n position,\n animationDuration,\n testID,\n }: PopoverRootProps) => {\n const [visible, setVisible] = useState(false);\n const popoverLayer = useOverlayLayer() + MODAL_LAYER_STEP;\n\n // Keyed off `open` rather than mount so a rapid close/reopen still plays\n // the enter transition.\n useEffect(() => {\n if (!open) return;\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, [open]);\n\n useEffect(() => {\n if (open) return;\n setVisible(false);\n const timer = setTimeout(onExited, animationDuration);\n return () => clearTimeout(timer);\n }, [open, animationDuration, onExited]);\n\n // A full-screen popover replaces the page and a scrim blocks it, so\n // neither should scroll underneath. A scrim-less floating panel is the\n // opposite case — the page stays usable, so leave its scroll alone.\n const locksScroll = fullScreen || Boolean(scrimColor);\n useEffect(() => {\n if (!locksScroll || typeof document === \"undefined\") return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }, [locksScroll]);\n\n if (typeof document === \"undefined\") return null;\n\n const positioned = !fullScreen && position !== undefined;\n\n return ReactDOM.createPortal(\n <div\n data-testid={testID}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: popoverLayer,\n padding: fullScreen || positioned ? 0 : OUTER_PADDING,\n display: \"flex\",\n alignItems: fullScreen ? \"stretch\" : \"center\",\n justifyContent: \"center\",\n // Without a scrim the page behind stays interactive, so the\n // full-viewport wrapper must not swallow presses meant for it.\n // The panel itself re-enables pointer events below.\n pointerEvents: scrimColor ? undefined : \"none\",\n }}\n >\n {scrimColor && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n background: scrimColor,\n opacity: visible ? 1 : 0,\n cursor: onBackdropClick ? \"pointer\" : \"default\",\n transition: `opacity ${animationDuration}ms ease`,\n }}\n onClick={onBackdropClick}\n aria-hidden=\"true\"\n />\n )}\n <div\n style={{\n position: positioned ? \"absolute\" : \"relative\",\n left: positioned ? position.x : undefined,\n top: positioned ? position.y : undefined,\n zIndex: 1,\n pointerEvents: \"auto\",\n display: \"flex\",\n maxHeight: \"100%\",\n width: fullScreen ? \"100%\" : undefined,\n opacity: visible ? 1 : 0,\n transform: visible ? \"scale(1)\" : \"scale(0.95)\",\n transition: `opacity ${animationDuration}ms ease, transform ${animationDuration}ms ease`,\n }}\n >\n <OverlayLayerProvider value={popoverLayer}>\n {children}\n </OverlayLayerProvider>\n </div>\n </div>,\n document.body,\n );\n },\n);\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["import_react","import_react","React","styled","React","import_xui_core","import_react","import_jsx_runtime","ReactDOM","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","handleKeyDown","import_react"]}
1
+ {"version":3,"sources":["../../src/index.tsx","../../src/Popover.tsx","../../../../foundation/primitives-web/src/Box.tsx","../../../../foundation/primitives-web/src/filterDOMProps.ts","../../../../../node_modules/@emotion/memoize/dist/memoize.esm.js","../../../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js","../../src/PopoverRoot.web.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["export { Popover } from \"./Popover\";\nexport { PopoverTitleBar } from \"./PopoverTitleBar\";\nexport { usePopover } from \"./usePopover\";\nexport { usePopoverDrag } from \"./usePopoverDrag\";\nexport { useDeviceType, resolveDevice, POPOVER_BREAKPOINTS } from \"./useDeviceType\";\nexport type {\n PopoverProps,\n PopoverDevice,\n PopoverPosition,\n PopoverDragHandleProps,\n PopoverRootProps,\n PopoverTitleBarProps,\n} from \"./types\";\nexport type { UsePopoverOptions, UsePopoverReturn } from \"./usePopover\";\nexport type {\n UsePopoverDragOptions,\n UsePopoverDragReturn,\n} from \"./usePopoverDrag\";\n","import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport type { BoxProps } from \"@xsolla/xui-primitives-core\";\nimport { createFilteredElement } from \"./filterDOMProps\";\n\nconst FilteredDiv = createFilteredElement(\"div\");\n\nconst StyledBox = styled(FilteredDiv)<BoxProps>`\n display: ${(props) => props.display || \"flex\"};\n box-sizing: border-box;\n background-color: ${(props) => props.backgroundColor || \"transparent\"};\n border-color: ${(props) => props.borderColor || \"transparent\"};\n border-width: ${(props) =>\n typeof props.borderWidth === \"number\"\n ? `${props.borderWidth}px`\n : props.borderWidth || 0};\n\n ${(props) =>\n props.borderBottomWidth !== undefined &&\n `\n border-bottom-width: ${typeof props.borderBottomWidth === \"number\" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};\n border-bottom-color: ${props.borderBottomColor || props.borderColor || \"transparent\"};\n border-bottom-style: solid;\n `}\n ${(props) =>\n props.borderTopWidth !== undefined &&\n `\n border-top-width: ${typeof props.borderTopWidth === \"number\" ? `${props.borderTopWidth}px` : props.borderTopWidth};\n border-top-color: ${props.borderTopColor || props.borderColor || \"transparent\"};\n border-top-style: solid;\n `}\n ${(props) =>\n props.borderLeftWidth !== undefined &&\n `\n border-left-width: ${typeof props.borderLeftWidth === \"number\" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};\n border-left-color: ${props.borderLeftColor || props.borderColor || \"transparent\"};\n border-left-style: solid;\n `}\n ${(props) =>\n props.borderRightWidth !== undefined &&\n `\n border-right-width: ${typeof props.borderRightWidth === \"number\" ? `${props.borderRightWidth}px` : props.borderRightWidth};\n border-right-color: ${props.borderRightColor || props.borderColor || \"transparent\"};\n border-right-style: solid;\n `}\n\n border-style: ${(props) =>\n props.borderStyle ||\n (props.borderWidth ||\n props.borderBottomWidth ||\n props.borderTopWidth ||\n props.borderLeftWidth ||\n props.borderRightWidth\n ? \"solid\"\n : \"none\")};\n border-radius: ${(props) =>\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius || 0};\n height: ${(props) =>\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height || \"auto\"};\n width: ${(props) =>\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width || \"auto\"};\n min-width: ${(props) =>\n typeof props.minWidth === \"number\"\n ? `${props.minWidth}px`\n : props.minWidth || \"auto\"};\n min-height: ${(props) =>\n typeof props.minHeight === \"number\"\n ? `${props.minHeight}px`\n : props.minHeight || \"auto\"};\n max-width: ${(props) =>\n typeof props.maxWidth === \"number\"\n ? `${props.maxWidth}px`\n : props.maxWidth || \"none\"};\n max-height: ${(props) =>\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight || \"none\"};\n\n padding: ${(props) =>\n typeof props.padding === \"number\"\n ? `${props.padding}px`\n : props.padding || 0};\n ${(props) =>\n props.paddingHorizontal &&\n `\n padding-left: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n padding-right: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n `}\n ${(props) =>\n props.paddingVertical &&\n `\n padding-top: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n padding-bottom: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n `}\n ${(props) =>\n props.paddingTop !== undefined &&\n `padding-top: ${typeof props.paddingTop === \"number\" ? `${props.paddingTop}px` : props.paddingTop};`}\n ${(props) =>\n props.paddingBottom !== undefined &&\n `padding-bottom: ${typeof props.paddingBottom === \"number\" ? `${props.paddingBottom}px` : props.paddingBottom};`}\n ${(props) =>\n props.paddingLeft !== undefined &&\n `padding-left: ${typeof props.paddingLeft === \"number\" ? `${props.paddingLeft}px` : props.paddingLeft};`}\n ${(props) =>\n props.paddingRight !== undefined &&\n `padding-right: ${typeof props.paddingRight === \"number\" ? `${props.paddingRight}px` : props.paddingRight};`}\n\n margin: ${(props) =>\n typeof props.margin === \"number\" ? `${props.margin}px` : props.margin || 0};\n ${(props) =>\n props.marginTop !== undefined &&\n `margin-top: ${typeof props.marginTop === \"number\" ? `${props.marginTop}px` : props.marginTop};`}\n ${(props) =>\n props.marginBottom !== undefined &&\n `margin-bottom: ${typeof props.marginBottom === \"number\" ? `${props.marginBottom}px` : props.marginBottom};`}\n ${(props) =>\n props.marginLeft !== undefined &&\n `margin-left: ${typeof props.marginLeft === \"number\" ? `${props.marginLeft}px` : props.marginLeft};`}\n ${(props) =>\n props.marginRight !== undefined &&\n `margin-right: ${typeof props.marginRight === \"number\" ? `${props.marginRight}px` : props.marginRight};`}\n\n flex-direction: ${(props) => props.flexDirection || \"column\"};\n flex-wrap: ${(props) => props.flexWrap || \"nowrap\"};\n align-items: ${(props) => props.alignItems || \"stretch\"};\n justify-content: ${(props) => props.justifyContent || \"flex-start\"};\n cursor: ${(props) =>\n props.cursor\n ? props.cursor\n : props.onClick || props.onPress\n ? \"pointer\"\n : \"inherit\"};\n position: ${(props) => props.position || \"static\"};\n top: ${(props) =>\n typeof props.top === \"number\" ? `${props.top}px` : props.top};\n bottom: ${(props) =>\n typeof props.bottom === \"number\" ? `${props.bottom}px` : props.bottom};\n left: ${(props) =>\n typeof props.left === \"number\" ? `${props.left}px` : props.left};\n right: ${(props) =>\n typeof props.right === \"number\" ? `${props.right}px` : props.right};\n flex: ${(props) => props.flex};\n flex-shrink: ${(props) => props.flexShrink ?? 1};\n gap: ${(props) =>\n typeof props.gap === \"number\" ? `${props.gap}px` : props.gap || 0};\n align-self: ${(props) => props.alignSelf || \"auto\"};\n /* Only emit overflow-x/y when explicitly set. Defaulting them to\n visible after overflow would override the shorthand and break\n clipping (e.g. OtherCard bottom-right promo image). */\n overflow: ${(props) => props.overflow || \"visible\"};\n ${(props) =>\n props.overflowX != null ? `overflow-x: ${props.overflowX};` : \"\"}\n ${(props) =>\n props.overflowY != null ? `overflow-y: ${props.overflowY};` : \"\"}\n z-index: ${(props) => props.zIndex};\n opacity: ${(props) =>\n props.opacity != null ? props.opacity : props.disabled ? 0.5 : 1};\n pointer-events: ${(props) => (props.disabled ? \"none\" : \"auto\")};\n\n /* \\`background\\` is emitted before \\`background-color\\` so a caller passing\n both still gets backgroundColor as the winner. Use the shorthand for a\n layered fill (e.g. an overlay tint over a resting surface colour), which\n a single background-color cannot express. */\n &:hover {\n ${(props) =>\n props.hoverStyle?.background &&\n `background: ${props.hoverStyle.background};`}\n ${(props) =>\n props.hoverStyle?.backgroundColor &&\n `background-color: ${props.hoverStyle.backgroundColor};`}\n ${(props) =>\n props.hoverStyle?.borderColor &&\n `border-color: ${props.hoverStyle.borderColor};`}\n }\n\n &:active {\n ${(props) =>\n props.pressStyle?.background &&\n `background: ${props.pressStyle.background};`}\n ${(props) =>\n props.pressStyle?.backgroundColor &&\n `background-color: ${props.pressStyle.backgroundColor};`}\n }\n`;\n\nexport const Box = React.forwardRef<\n HTMLDivElement | HTMLButtonElement,\n BoxProps\n>(\n (\n {\n children,\n onPress,\n onKeyDown,\n onKeyUp,\n role,\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 \"aria-describedby\": ariaDescribedBy,\n \"aria-expanded\": ariaExpanded,\n \"aria-haspopup\": ariaHasPopup,\n \"aria-pressed\": ariaPressed,\n \"aria-controls\": ariaControls,\n tabIndex,\n as,\n src,\n alt,\n onError,\n onLoad,\n type,\n disabled,\n id,\n testID,\n \"data-testid\": dataTestId,\n ...props\n },\n ref\n ) => {\n // Handle as=\"img\" for rendering images with proper border-radius\n if (as === \"img\" && src) {\n return (\n <img\n src={src}\n alt={alt || \"\"}\n onError={onError}\n onLoad={onLoad}\n data-testid={dataTestId || testID}\n style={{\n display: props.display || \"block\",\n objectFit: \"cover\",\n width:\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width,\n height:\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height,\n borderRadius:\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius,\n position: props.position,\n top: typeof props.top === \"number\" ? `${props.top}px` : props.top,\n left:\n typeof props.left === \"number\" ? `${props.left}px` : props.left,\n right:\n typeof props.right === \"number\"\n ? `${props.right}px`\n : props.right,\n bottom:\n typeof props.bottom === \"number\"\n ? `${props.bottom}px`\n : props.bottom,\n ...props.style,\n }}\n />\n );\n }\n\n return (\n <StyledBox\n ref={ref}\n elementType={as}\n id={id}\n type={as === \"button\" ? type || \"button\" : undefined}\n disabled={as === \"button\" ? disabled : undefined}\n onClick={onPress}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-current={ariaCurrent}\n aria-disabled={ariaDisabled}\n aria-busy={ariaBusy}\n aria-describedby={ariaDescribedBy}\n aria-expanded={ariaExpanded}\n aria-haspopup={ariaHasPopup}\n aria-pressed={ariaPressed}\n aria-controls={ariaControls}\n aria-live={ariaLive}\n tabIndex={tabIndex !== undefined ? tabIndex : undefined}\n data-testid={dataTestId || testID}\n {...props}\n >\n {children}\n </StyledBox>\n );\n }\n);\n\nBox.displayName = \"Box\";\n","import React from \"react\";\nimport isPropValid from \"@emotion/is-prop-valid\";\n\n// Props that @emotion/is-prop-valid incorrectly treats as valid HTML.\n// These are React Native or component-specific props that match\n// valid HTML patterns (on* event handlers, SVG attributes).\nexport const ADDITIONAL_BLOCKED_PROPS = new Set([\n // RN-only event handlers (pass isPropValid's on* pattern)\n \"onPress\",\n \"onChangeText\",\n \"onLayout\",\n \"onMoveShouldSetResponder\",\n \"onResponderGrant\",\n \"onResponderMove\",\n \"onResponderRelease\",\n \"onResponderTerminate\",\n // SVG attributes that pass isPropValid\n \"strokeWidth\",\n // CSS properties that pass isPropValid but are used as component props\n \"overflow\",\n \"opacity\",\n \"cursor\",\n \"fontSize\",\n \"fontWeight\",\n \"fontFamily\",\n \"textDecoration\",\n // `display` is a presentation attribute on SVG, so isPropValid lets it\n // through — but Box consumes it purely as CSS. Without this it lands on the\n // DOM node as a bogus `display=\"inline-flex\"` attribute.\n \"display\",\n // Cross-platform layout prop that Text consumes as CSS, never as an\n // attribute. Pinned explicitly because it is now spread into the styled\n // component rather than destructured away before it can reach the DOM.\n \"numberOfLines\",\n]);\n\nfunction shouldForwardProp(key: string): boolean {\n if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;\n return isPropValid(key);\n}\n\n/**\n * Creates a React component that renders the given HTML tag\n * but filters out non-HTML props before they reach the DOM.\n *\n * Uses @emotion/is-prop-valid (same library styled-components v4\n * uses internally) to automatically block invalid HTML attributes,\n * plus a small blocklist for false positives (RN on* handlers, SVG attrs).\n *\n * Usage: `const FilteredDiv = createFilteredElement(\"div\");`\n * Then: `const StyledBox = styled(FilteredDiv)<BoxProps>\\`...\\`;`\n *\n * styled-components can still read ALL props for CSS interpolation,\n * but only valid HTML attributes are forwarded to the DOM element.\n */\nexport function createFilteredElement(defaultTag: string) {\n const Component = React.forwardRef<HTMLElement, Record<string, unknown>>(\n ({ children, elementType, ...props }, ref) => {\n const Tag = (elementType as string) || defaultTag;\n const htmlProps: Record<string, unknown> = {};\n for (const key of Object.keys(props)) {\n if (shouldForwardProp(key)) {\n htmlProps[key] = props[key];\n }\n }\n return React.createElement(\n Tag,\n { ref, ...htmlProps },\n children as React.ReactNode\n );\n }\n );\n Component.displayName = `Filtered(${defaultTag})`;\n return Component;\n}\n","function memoize(fn) {\n var cache = {};\n return function (arg) {\n if (cache[arg] === undefined) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\n\nexport default memoize;\n","import memoize from '@emotion/memoize';\n\nvar reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/; // https://esbench.com/bench/5bfee68a4cd7e6009ef61d23\n\nvar index = memoize(function (prop) {\n return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111\n /* o */\n && prop.charCodeAt(1) === 110\n /* n */\n && prop.charCodeAt(2) < 91;\n}\n/* Z+1 */\n);\n\nexport default index;\n","import { memo, useEffect, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport {\n useOverlayLayer,\n OverlayLayerProvider,\n MODAL_LAYER_STEP,\n} from \"@xsolla/xui-core\";\nimport type { PopoverRootProps } from \"./types\";\n\n/** Breathing room between a centred floating panel and the viewport edges. */\nconst OUTER_PADDING = 20;\n\nexport const PopoverRoot = memo(\n ({\n children,\n open,\n onExited,\n onBackdropClick,\n scrimColor,\n fullScreen,\n position,\n animationDuration,\n testID,\n }: PopoverRootProps) => {\n const [visible, setVisible] = useState(false);\n const popoverLayer = useOverlayLayer() + MODAL_LAYER_STEP;\n\n // Keyed off `open` rather than mount so a rapid close/reopen still plays\n // the enter transition.\n useEffect(() => {\n if (!open) return;\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, [open]);\n\n useEffect(() => {\n if (open) return;\n setVisible(false);\n const timer = setTimeout(onExited, animationDuration);\n return () => clearTimeout(timer);\n }, [open, animationDuration, onExited]);\n\n // A full-screen popover replaces the page and a scrim blocks it, so\n // neither should scroll underneath. A scrim-less floating panel is the\n // opposite case — the page stays usable, so leave its scroll alone.\n const locksScroll = fullScreen || Boolean(scrimColor);\n useEffect(() => {\n if (!locksScroll || typeof document === \"undefined\") return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }, [locksScroll]);\n\n if (typeof document === \"undefined\") return null;\n\n const positioned = !fullScreen && position !== undefined;\n\n return ReactDOM.createPortal(\n <div\n data-testid={testID}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: popoverLayer,\n padding: fullScreen || positioned ? 0 : OUTER_PADDING,\n display: \"flex\",\n alignItems: fullScreen ? \"stretch\" : \"center\",\n justifyContent: \"center\",\n // Without a scrim the page behind stays interactive, so the\n // full-viewport wrapper must not swallow presses meant for it.\n // The panel itself re-enables pointer events below.\n pointerEvents: scrimColor ? undefined : \"none\",\n }}\n >\n {scrimColor && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n background: scrimColor,\n opacity: visible ? 1 : 0,\n cursor: onBackdropClick ? \"pointer\" : \"default\",\n transition: `opacity ${animationDuration}ms ease`,\n }}\n onClick={onBackdropClick}\n aria-hidden=\"true\"\n />\n )}\n <div\n style={{\n position: positioned ? \"absolute\" : \"relative\",\n left: positioned ? position.x : undefined,\n top: positioned ? position.y : undefined,\n zIndex: 1,\n pointerEvents: \"auto\",\n display: \"flex\",\n maxHeight: \"100%\",\n width: fullScreen ? \"100%\" : undefined,\n opacity: visible ? 1 : 0,\n transform: visible ? \"scale(1)\" : \"scale(0.95)\",\n transition: `opacity ${animationDuration}ms ease, transform ${animationDuration}ms ease`,\n }}\n >\n <OverlayLayerProvider value={popoverLayer}>\n {children}\n </OverlayLayerProvider>\n </div>\n </div>,\n document.body,\n );\n },\n);\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["import_react","import_react","React","styled","React","import_xui_core","import_react","import_jsx_runtime","ReactDOM","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","handleKeyDown","import_react"]}
package/web/index.mjs CHANGED
@@ -49,6 +49,10 @@ var ADDITIONAL_BLOCKED_PROPS = /* @__PURE__ */ new Set([
49
49
  "fontWeight",
50
50
  "fontFamily",
51
51
  "textDecoration",
52
+ // `display` is a presentation attribute on SVG, so isPropValid lets it
53
+ // through — but Box consumes it purely as CSS. Without this it lands on the
54
+ // DOM node as a bogus `display="inline-flex"` attribute.
55
+ "display",
52
56
  // Cross-platform layout prop that Text consumes as CSS, never as an
53
57
  // attribute. Pinned explicitly because it is now spread into the styled
54
58
  // component rather than destructured away before it can reach the DOM.
@@ -83,7 +87,7 @@ function createFilteredElement(defaultTag) {
83
87
  import { jsx } from "react/jsx-runtime";
84
88
  var FilteredDiv = createFilteredElement("div");
85
89
  var StyledBox = styled(FilteredDiv)`
86
- display: flex;
90
+ display: ${(props) => props.display || "flex"};
87
91
  box-sizing: border-box;
88
92
  background-color: ${(props) => props.backgroundColor || "transparent"};
89
93
  border-color: ${(props) => props.borderColor || "transparent"};
@@ -219,7 +223,7 @@ var Box = React2.forwardRef(
219
223
  onLoad,
220
224
  "data-testid": dataTestId || testID,
221
225
  style: {
222
- display: "block",
226
+ display: props.display || "block",
223
227
  objectFit: "cover",
224
228
  width: typeof props.width === "number" ? `${props.width}px` : props.width,
225
229
  height: typeof props.height === "number" ? `${props.height}px` : props.height,
package/web/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Popover.tsx","../../../../foundation/primitives-web/src/Box.tsx","../../../../foundation/primitives-web/src/filterDOMProps.ts","../../../../../node_modules/@emotion/memoize/dist/memoize.esm.js","../../../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js","../../src/PopoverRoot.web.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport type { BoxProps } from \"@xsolla/xui-primitives-core\";\nimport { createFilteredElement } from \"./filterDOMProps\";\n\nconst FilteredDiv = createFilteredElement(\"div\");\n\nconst StyledBox = styled(FilteredDiv)<BoxProps>`\n display: flex;\n box-sizing: border-box;\n background-color: ${(props) => props.backgroundColor || \"transparent\"};\n border-color: ${(props) => props.borderColor || \"transparent\"};\n border-width: ${(props) =>\n typeof props.borderWidth === \"number\"\n ? `${props.borderWidth}px`\n : props.borderWidth || 0};\n\n ${(props) =>\n props.borderBottomWidth !== undefined &&\n `\n border-bottom-width: ${typeof props.borderBottomWidth === \"number\" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};\n border-bottom-color: ${props.borderBottomColor || props.borderColor || \"transparent\"};\n border-bottom-style: solid;\n `}\n ${(props) =>\n props.borderTopWidth !== undefined &&\n `\n border-top-width: ${typeof props.borderTopWidth === \"number\" ? `${props.borderTopWidth}px` : props.borderTopWidth};\n border-top-color: ${props.borderTopColor || props.borderColor || \"transparent\"};\n border-top-style: solid;\n `}\n ${(props) =>\n props.borderLeftWidth !== undefined &&\n `\n border-left-width: ${typeof props.borderLeftWidth === \"number\" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};\n border-left-color: ${props.borderLeftColor || props.borderColor || \"transparent\"};\n border-left-style: solid;\n `}\n ${(props) =>\n props.borderRightWidth !== undefined &&\n `\n border-right-width: ${typeof props.borderRightWidth === \"number\" ? `${props.borderRightWidth}px` : props.borderRightWidth};\n border-right-color: ${props.borderRightColor || props.borderColor || \"transparent\"};\n border-right-style: solid;\n `}\n\n border-style: ${(props) =>\n props.borderStyle ||\n (props.borderWidth ||\n props.borderBottomWidth ||\n props.borderTopWidth ||\n props.borderLeftWidth ||\n props.borderRightWidth\n ? \"solid\"\n : \"none\")};\n border-radius: ${(props) =>\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius || 0};\n height: ${(props) =>\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height || \"auto\"};\n width: ${(props) =>\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width || \"auto\"};\n min-width: ${(props) =>\n typeof props.minWidth === \"number\"\n ? `${props.minWidth}px`\n : props.minWidth || \"auto\"};\n min-height: ${(props) =>\n typeof props.minHeight === \"number\"\n ? `${props.minHeight}px`\n : props.minHeight || \"auto\"};\n max-width: ${(props) =>\n typeof props.maxWidth === \"number\"\n ? `${props.maxWidth}px`\n : props.maxWidth || \"none\"};\n max-height: ${(props) =>\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight || \"none\"};\n\n padding: ${(props) =>\n typeof props.padding === \"number\"\n ? `${props.padding}px`\n : props.padding || 0};\n ${(props) =>\n props.paddingHorizontal &&\n `\n padding-left: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n padding-right: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n `}\n ${(props) =>\n props.paddingVertical &&\n `\n padding-top: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n padding-bottom: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n `}\n ${(props) =>\n props.paddingTop !== undefined &&\n `padding-top: ${typeof props.paddingTop === \"number\" ? `${props.paddingTop}px` : props.paddingTop};`}\n ${(props) =>\n props.paddingBottom !== undefined &&\n `padding-bottom: ${typeof props.paddingBottom === \"number\" ? `${props.paddingBottom}px` : props.paddingBottom};`}\n ${(props) =>\n props.paddingLeft !== undefined &&\n `padding-left: ${typeof props.paddingLeft === \"number\" ? `${props.paddingLeft}px` : props.paddingLeft};`}\n ${(props) =>\n props.paddingRight !== undefined &&\n `padding-right: ${typeof props.paddingRight === \"number\" ? `${props.paddingRight}px` : props.paddingRight};`}\n\n margin: ${(props) =>\n typeof props.margin === \"number\" ? `${props.margin}px` : props.margin || 0};\n ${(props) =>\n props.marginTop !== undefined &&\n `margin-top: ${typeof props.marginTop === \"number\" ? `${props.marginTop}px` : props.marginTop};`}\n ${(props) =>\n props.marginBottom !== undefined &&\n `margin-bottom: ${typeof props.marginBottom === \"number\" ? `${props.marginBottom}px` : props.marginBottom};`}\n ${(props) =>\n props.marginLeft !== undefined &&\n `margin-left: ${typeof props.marginLeft === \"number\" ? `${props.marginLeft}px` : props.marginLeft};`}\n ${(props) =>\n props.marginRight !== undefined &&\n `margin-right: ${typeof props.marginRight === \"number\" ? `${props.marginRight}px` : props.marginRight};`}\n\n flex-direction: ${(props) => props.flexDirection || \"column\"};\n flex-wrap: ${(props) => props.flexWrap || \"nowrap\"};\n align-items: ${(props) => props.alignItems || \"stretch\"};\n justify-content: ${(props) => props.justifyContent || \"flex-start\"};\n cursor: ${(props) =>\n props.cursor\n ? props.cursor\n : props.onClick || props.onPress\n ? \"pointer\"\n : \"inherit\"};\n position: ${(props) => props.position || \"static\"};\n top: ${(props) =>\n typeof props.top === \"number\" ? `${props.top}px` : props.top};\n bottom: ${(props) =>\n typeof props.bottom === \"number\" ? `${props.bottom}px` : props.bottom};\n left: ${(props) =>\n typeof props.left === \"number\" ? `${props.left}px` : props.left};\n right: ${(props) =>\n typeof props.right === \"number\" ? `${props.right}px` : props.right};\n flex: ${(props) => props.flex};\n flex-shrink: ${(props) => props.flexShrink ?? 1};\n gap: ${(props) =>\n typeof props.gap === \"number\" ? `${props.gap}px` : props.gap || 0};\n align-self: ${(props) => props.alignSelf || \"auto\"};\n /* Only emit overflow-x/y when explicitly set. Defaulting them to\n visible after overflow would override the shorthand and break\n clipping (e.g. OtherCard bottom-right promo image). */\n overflow: ${(props) => props.overflow || \"visible\"};\n ${(props) =>\n props.overflowX != null ? `overflow-x: ${props.overflowX};` : \"\"}\n ${(props) =>\n props.overflowY != null ? `overflow-y: ${props.overflowY};` : \"\"}\n z-index: ${(props) => props.zIndex};\n opacity: ${(props) =>\n props.opacity != null ? props.opacity : props.disabled ? 0.5 : 1};\n pointer-events: ${(props) => (props.disabled ? \"none\" : \"auto\")};\n\n /* \\`background\\` is emitted before \\`background-color\\` so a caller passing\n both still gets backgroundColor as the winner. Use the shorthand for a\n layered fill (e.g. an overlay tint over a resting surface colour), which\n a single background-color cannot express. */\n &:hover {\n ${(props) =>\n props.hoverStyle?.background &&\n `background: ${props.hoverStyle.background};`}\n ${(props) =>\n props.hoverStyle?.backgroundColor &&\n `background-color: ${props.hoverStyle.backgroundColor};`}\n ${(props) =>\n props.hoverStyle?.borderColor &&\n `border-color: ${props.hoverStyle.borderColor};`}\n }\n\n &:active {\n ${(props) =>\n props.pressStyle?.background &&\n `background: ${props.pressStyle.background};`}\n ${(props) =>\n props.pressStyle?.backgroundColor &&\n `background-color: ${props.pressStyle.backgroundColor};`}\n }\n`;\n\nexport const Box = React.forwardRef<\n HTMLDivElement | HTMLButtonElement,\n BoxProps\n>(\n (\n {\n children,\n onPress,\n onKeyDown,\n onKeyUp,\n role,\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 \"aria-describedby\": ariaDescribedBy,\n \"aria-expanded\": ariaExpanded,\n \"aria-haspopup\": ariaHasPopup,\n \"aria-pressed\": ariaPressed,\n \"aria-controls\": ariaControls,\n tabIndex,\n as,\n src,\n alt,\n onError,\n onLoad,\n type,\n disabled,\n id,\n testID,\n \"data-testid\": dataTestId,\n ...props\n },\n ref\n ) => {\n // Handle as=\"img\" for rendering images with proper border-radius\n if (as === \"img\" && src) {\n return (\n <img\n src={src}\n alt={alt || \"\"}\n onError={onError}\n onLoad={onLoad}\n data-testid={dataTestId || testID}\n style={{\n display: \"block\",\n objectFit: \"cover\",\n width:\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width,\n height:\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height,\n borderRadius:\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius,\n position: props.position,\n top: typeof props.top === \"number\" ? `${props.top}px` : props.top,\n left:\n typeof props.left === \"number\" ? `${props.left}px` : props.left,\n right:\n typeof props.right === \"number\"\n ? `${props.right}px`\n : props.right,\n bottom:\n typeof props.bottom === \"number\"\n ? `${props.bottom}px`\n : props.bottom,\n ...props.style,\n }}\n />\n );\n }\n\n return (\n <StyledBox\n ref={ref}\n elementType={as}\n id={id}\n type={as === \"button\" ? type || \"button\" : undefined}\n disabled={as === \"button\" ? disabled : undefined}\n onClick={onPress}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-current={ariaCurrent}\n aria-disabled={ariaDisabled}\n aria-busy={ariaBusy}\n aria-describedby={ariaDescribedBy}\n aria-expanded={ariaExpanded}\n aria-haspopup={ariaHasPopup}\n aria-pressed={ariaPressed}\n aria-controls={ariaControls}\n aria-live={ariaLive}\n tabIndex={tabIndex !== undefined ? tabIndex : undefined}\n data-testid={dataTestId || testID}\n {...props}\n >\n {children}\n </StyledBox>\n );\n }\n);\n\nBox.displayName = \"Box\";\n","import React from \"react\";\nimport isPropValid from \"@emotion/is-prop-valid\";\n\n// Props that @emotion/is-prop-valid incorrectly treats as valid HTML.\n// These are React Native or component-specific props that match\n// valid HTML patterns (on* event handlers, SVG attributes).\nexport const ADDITIONAL_BLOCKED_PROPS = new Set([\n // RN-only event handlers (pass isPropValid's on* pattern)\n \"onPress\",\n \"onChangeText\",\n \"onLayout\",\n \"onMoveShouldSetResponder\",\n \"onResponderGrant\",\n \"onResponderMove\",\n \"onResponderRelease\",\n \"onResponderTerminate\",\n // SVG attributes that pass isPropValid\n \"strokeWidth\",\n // CSS properties that pass isPropValid but are used as component props\n \"overflow\",\n \"opacity\",\n \"cursor\",\n \"fontSize\",\n \"fontWeight\",\n \"fontFamily\",\n \"textDecoration\",\n // Cross-platform layout prop that Text consumes as CSS, never as an\n // attribute. Pinned explicitly because it is now spread into the styled\n // component rather than destructured away before it can reach the DOM.\n \"numberOfLines\",\n]);\n\nfunction shouldForwardProp(key: string): boolean {\n if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;\n return isPropValid(key);\n}\n\n/**\n * Creates a React component that renders the given HTML tag\n * but filters out non-HTML props before they reach the DOM.\n *\n * Uses @emotion/is-prop-valid (same library styled-components v4\n * uses internally) to automatically block invalid HTML attributes,\n * plus a small blocklist for false positives (RN on* handlers, SVG attrs).\n *\n * Usage: `const FilteredDiv = createFilteredElement(\"div\");`\n * Then: `const StyledBox = styled(FilteredDiv)<BoxProps>\\`...\\`;`\n *\n * styled-components can still read ALL props for CSS interpolation,\n * but only valid HTML attributes are forwarded to the DOM element.\n */\nexport function createFilteredElement(defaultTag: string) {\n const Component = React.forwardRef<HTMLElement, Record<string, unknown>>(\n ({ children, elementType, ...props }, ref) => {\n const Tag = (elementType as string) || defaultTag;\n const htmlProps: Record<string, unknown> = {};\n for (const key of Object.keys(props)) {\n if (shouldForwardProp(key)) {\n htmlProps[key] = props[key];\n }\n }\n return React.createElement(\n Tag,\n { ref, ...htmlProps },\n children as React.ReactNode\n );\n }\n );\n Component.displayName = `Filtered(${defaultTag})`;\n return Component;\n}\n","function memoize(fn) {\n var cache = {};\n return function (arg) {\n if (cache[arg] === undefined) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\n\nexport default memoize;\n","import memoize from '@emotion/memoize';\n\nvar reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/; // https://esbench.com/bench/5bfee68a4cd7e6009ef61d23\n\nvar index = memoize(function (prop) {\n return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111\n /* o */\n && prop.charCodeAt(1) === 110\n /* n */\n && prop.charCodeAt(2) < 91;\n}\n/* Z+1 */\n);\n\nexport default index;\n","import { memo, useEffect, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport {\n useOverlayLayer,\n OverlayLayerProvider,\n MODAL_LAYER_STEP,\n} from \"@xsolla/xui-core\";\nimport type { PopoverRootProps } from \"./types\";\n\n/** Breathing room between a centred floating panel and the viewport edges. */\nconst OUTER_PADDING = 20;\n\nexport const PopoverRoot = memo(\n ({\n children,\n open,\n onExited,\n onBackdropClick,\n scrimColor,\n fullScreen,\n position,\n animationDuration,\n testID,\n }: PopoverRootProps) => {\n const [visible, setVisible] = useState(false);\n const popoverLayer = useOverlayLayer() + MODAL_LAYER_STEP;\n\n // Keyed off `open` rather than mount so a rapid close/reopen still plays\n // the enter transition.\n useEffect(() => {\n if (!open) return;\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, [open]);\n\n useEffect(() => {\n if (open) return;\n setVisible(false);\n const timer = setTimeout(onExited, animationDuration);\n return () => clearTimeout(timer);\n }, [open, animationDuration, onExited]);\n\n // A full-screen popover replaces the page and a scrim blocks it, so\n // neither should scroll underneath. A scrim-less floating panel is the\n // opposite case — the page stays usable, so leave its scroll alone.\n const locksScroll = fullScreen || Boolean(scrimColor);\n useEffect(() => {\n if (!locksScroll || typeof document === \"undefined\") return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }, [locksScroll]);\n\n if (typeof document === \"undefined\") return null;\n\n const positioned = !fullScreen && position !== undefined;\n\n return ReactDOM.createPortal(\n <div\n data-testid={testID}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: popoverLayer,\n padding: fullScreen || positioned ? 0 : OUTER_PADDING,\n display: \"flex\",\n alignItems: fullScreen ? \"stretch\" : \"center\",\n justifyContent: \"center\",\n // Without a scrim the page behind stays interactive, so the\n // full-viewport wrapper must not swallow presses meant for it.\n // The panel itself re-enables pointer events below.\n pointerEvents: scrimColor ? undefined : \"none\",\n }}\n >\n {scrimColor && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n background: scrimColor,\n opacity: visible ? 1 : 0,\n cursor: onBackdropClick ? \"pointer\" : \"default\",\n transition: `opacity ${animationDuration}ms ease`,\n }}\n onClick={onBackdropClick}\n aria-hidden=\"true\"\n />\n )}\n <div\n style={{\n position: positioned ? \"absolute\" : \"relative\",\n left: positioned ? position.x : undefined,\n top: positioned ? position.y : undefined,\n zIndex: 1,\n pointerEvents: \"auto\",\n display: \"flex\",\n maxHeight: \"100%\",\n width: fullScreen ? \"100%\" : undefined,\n opacity: visible ? 1 : 0,\n transform: visible ? \"scale(1)\" : \"scale(0.95)\",\n transition: `opacity ${animationDuration}ms ease, transform ${animationDuration}ms ease`,\n }}\n >\n <OverlayLayerProvider value={popoverLayer}>\n {children}\n </OverlayLayerProvider>\n </div>\n </div>,\n document.body,\n );\n },\n);\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","React","React","jsx","memo","jsx","jsxs","useEffect","useState","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","handleKeyDown","useCallback","useCallback","useState"]}
1
+ {"version":3,"sources":["../../src/Popover.tsx","../../../../foundation/primitives-web/src/Box.tsx","../../../../foundation/primitives-web/src/filterDOMProps.ts","../../../../../node_modules/@emotion/memoize/dist/memoize.esm.js","../../../../../node_modules/@emotion/is-prop-valid/dist/is-prop-valid.esm.js","../../src/PopoverRoot.web.tsx","../../src/PopoverTitleBar.tsx","../../src/usePopoverDrag.ts","../../src/useDeviceType.ts","../../src/usePopover.ts"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n// @ts-expect-error - this will be resolved at build time\nimport { Box } from \"@xsolla/xui-primitives\";\nimport { radiusScale, useResolvedTheme } from \"@xsolla/xui-core\";\nimport type { PopoverProps } from \"./types\";\nimport { PopoverRoot } from \"./PopoverRoot\";\nimport { PopoverTitleBar } from \"./PopoverTitleBar\";\nimport { usePopoverDrag } from \"./usePopoverDrag\";\nimport { useDeviceType } from \"./useDeviceType\";\n\nconst FOCUSABLE_SELECTORS =\n 'button:not([disabled]), [href], input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Figma `idPopover`: 400px fixed panel on desktop and tablet. */\nconst DEFAULT_WIDTH = 400;\n/** Figma `idPopover`: 16px inset, 24px between stacked sections. */\nconst PANEL_PADDING = 16;\nconst SECTION_GAP = 24;\n/** Radius of the `boxShadow&BlurPopover` surface — `radiusScale[\"200\"]`. */\nconst PANEL_RADIUS = radiusScale[\"200\"];\n/** Backdrop blur from the same Figma effect style. */\nconst BACKDROP_BLUR = \"blur(24px)\";\nconst ANIMATION_DURATION = 200;\n\nconst isElementVisible = (el: HTMLElement): boolean => {\n if (el.offsetParent === null && getComputedStyle(el).position !== \"fixed\")\n return false;\n const style = getComputedStyle(el);\n return style.visibility !== \"hidden\" && style.display !== \"none\";\n};\n\nconst getFocusableElements = (container: HTMLElement): HTMLElement[] =>\n Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter(isElementVisible);\n\n/**\n * Responsive overlay panel for player-facing surfaces — the container behind\n * the Xsolla ID profile popover.\n *\n * On `desktop` and `tablet` it presents as a 400px floating panel over a\n * scrim, optionally draggable by its title bar. On `mobile` the same content\n * takes over the whole screen as a page. The bucket is measured from the\n * viewport by `useDeviceType`; pass `device` only to override that.\n *\n * Everything inside is a slot — `titleBar`, `header`, `children`, `footer` —\n * which is how the Figma `idPopover` composes its variants (the component set\n * declares one property, `Device`; every other difference between the usage\n * examples is different content in the same slots).\n *\n * ```tsx\n * <Popover\n * open={open}\n * onClose={() => setOpen(false)}\n * title=\"Profile\"\n * header={<Greeting />}\n * footer={<SignOut />}\n * >\n * {sections}\n * </Popover>\n * ```\n */\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(\n (\n {\n open = false,\n onClose,\n device: deviceProp,\n width = DEFAULT_WIDTH,\n draggable = false,\n initialPosition,\n onPositionChange,\n title,\n pinned,\n onPinnedChange,\n showCloseButton = true,\n titleBar,\n header,\n children,\n footer,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n scrim = true,\n backgroundColor,\n blur = true,\n styled: styledProps,\n initialFocusRef,\n \"aria-label\": ariaLabel,\n testID,\n themeMode,\n themeProductContext = \"b2c\",\n },\n ref,\n ) => {\n const { theme } = useResolvedTheme({ themeMode, themeProductContext });\n const device = useDeviceType(deviceProp);\n const fullScreen = device === \"mobile\";\n // \"Buttons for floating are used when you can change the position of the\n // popover on a screen. Don't use it […] on mobile.\" — Figma spec.\n const canDrag = draggable && !fullScreen;\n // Pinning exists so a click on the page behind cannot dismiss the panel,\n // and a full-screen page has no outside to click.\n const dismissOnScrim = closeOnOverlayClick && !pinned && !fullScreen;\n\n const [mounted, setMounted] = useState(open);\n const dialogRef = useRef<HTMLDivElement>(null);\n const previousActiveElement = useRef<HTMLElement | null>(null);\n\n const { position, panelRef, dragHandleProps } = usePopoverDrag({\n enabled: canDrag,\n initialPosition,\n onPositionChange,\n });\n\n useEffect(() => {\n if (open) setMounted(true);\n }, [open]);\n\n useEffect(() => {\n if (!open || typeof document === \"undefined\") return;\n\n previousActiveElement.current = document.activeElement as HTMLElement;\n const focusTarget =\n initialFocusRef?.current ||\n (dialogRef.current && getFocusableElements(dialogRef.current)[0]);\n\n if (focusTarget) focusTarget.focus();\n else dialogRef.current?.focus();\n\n // Focus is restored in onExited so it does not jump back to the page\n // while the panel is still on screen.\n }, [open, initialFocusRef]);\n\n useEffect(() => {\n if (!open || !closeOnEscape || !onClose) return;\n if (typeof document === \"undefined\") return;\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, closeOnEscape, onClose]);\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent) => {\n if (event.key !== \"Tab\" || !dialogRef.current) return;\n if (typeof document === \"undefined\") return;\n\n const focusableElements = getFocusableElements(dialogRef.current);\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (!firstElement) {\n event.preventDefault();\n return;\n }\n\n if (event.shiftKey && document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n } else if (!event.shiftKey && document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }, []);\n\n // One node feeds three consumers: the focus-trap/Escape logic, the drag\n // hook's measurements, and the caller's forwarded ref.\n const setPanelNode = useCallback(\n (node: HTMLDivElement | null) => {\n type MutableRef = React.MutableRefObject<HTMLDivElement | null>;\n (dialogRef as MutableRef).current = node;\n (panelRef as MutableRef).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as MutableRef).current = node;\n },\n [panelRef, ref],\n );\n\n const handleExited = useCallback(() => {\n setMounted(false);\n previousActiveElement.current?.focus();\n }, []);\n\n if (!mounted) return null;\n\n const closeControl = showCloseButton ? onClose : undefined;\n const hasDefaultTitleBar =\n !titleBar && (title || closeControl || canDrag);\n const accessibleName =\n ariaLabel ?? (typeof title === \"string\" ? title : undefined);\n\n return (\n <PopoverRoot\n open={open}\n onExited={handleExited}\n onBackdropClick={dismissOnScrim ? onClose : undefined}\n scrimColor={\n scrim && !fullScreen\n ? (theme.colors.layer?.scrim ?? \"rgba(0, 0, 0, 0.75)\")\n : undefined\n }\n fullScreen={fullScreen}\n position={position}\n animationDuration={ANIMATION_DURATION}\n >\n <Box\n testID={testID}\n ref={setPanelNode}\n role=\"dialog\"\n aria-modal={fullScreen || scrim ? \"true\" : undefined}\n aria-label={accessibleName}\n data-popover-device={device}\n data-popover-pinned={pinned ? \"true\" : undefined}\n tabIndex={-1}\n onKeyDown={handleKeyDown}\n style={{\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SECTION_GAP,\n width: fullScreen ? \"100%\" : width,\n maxWidth: \"100%\",\n height: fullScreen ? \"100%\" : undefined,\n maxHeight: \"100%\",\n outline: \"none\",\n color: theme.colors.content.primary,\n backgroundColor: backgroundColor ?? theme.colors.background.primary,\n paddingLeft: PANEL_PADDING,\n paddingRight: PANEL_PADDING,\n // The Figma mobile frame carries only a bottom inset — its top bars\n // are full-bleed, and its children supply their own side padding.\n // A slot component cannot assume the caller re-pads for one\n // breakpoint, so the sides stay inset and only the top is dropped.\n paddingTop: fullScreen ? 0 : PANEL_PADDING,\n paddingBottom: PANEL_PADDING,\n borderRadius: fullScreen ? 0 : PANEL_RADIUS,\n border: fullScreen\n ? undefined\n : `1px solid ${theme.colors.border.secondary}`,\n boxShadow: fullScreen ? undefined : theme.shadow.popover,\n backdropFilter: !fullScreen && blur ? BACKDROP_BLUR : undefined,\n ...styledProps,\n }}\n >\n {titleBar ??\n (hasDefaultTitleBar ? (\n <PopoverTitleBar\n title={title}\n draggable={canDrag}\n dragHandleProps={dragHandleProps}\n pinned={pinned}\n onPinnedChange={onPinnedChange}\n onClose={closeControl}\n testID={testID ? `${testID}-title-bar` : undefined}\n />\n ) : null)}\n\n {header && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{header}</div>\n )}\n\n <div\n style={{\n flex: 1,\n minHeight: 0,\n width: \"100%\",\n overflowY: \"auto\",\n overscrollBehavior: \"contain\",\n }}\n >\n {children}\n </div>\n\n {footer && (\n <div style={{ flexShrink: 0, width: \"100%\" }}>{footer}</div>\n )}\n </Box>\n </PopoverRoot>\n );\n },\n);\n\nPopover.displayName = \"Popover\";\n","import React from \"react\";\nimport styled from \"styled-components\";\nimport type { BoxProps } from \"@xsolla/xui-primitives-core\";\nimport { createFilteredElement } from \"./filterDOMProps\";\n\nconst FilteredDiv = createFilteredElement(\"div\");\n\nconst StyledBox = styled(FilteredDiv)<BoxProps>`\n display: ${(props) => props.display || \"flex\"};\n box-sizing: border-box;\n background-color: ${(props) => props.backgroundColor || \"transparent\"};\n border-color: ${(props) => props.borderColor || \"transparent\"};\n border-width: ${(props) =>\n typeof props.borderWidth === \"number\"\n ? `${props.borderWidth}px`\n : props.borderWidth || 0};\n\n ${(props) =>\n props.borderBottomWidth !== undefined &&\n `\n border-bottom-width: ${typeof props.borderBottomWidth === \"number\" ? `${props.borderBottomWidth}px` : props.borderBottomWidth};\n border-bottom-color: ${props.borderBottomColor || props.borderColor || \"transparent\"};\n border-bottom-style: solid;\n `}\n ${(props) =>\n props.borderTopWidth !== undefined &&\n `\n border-top-width: ${typeof props.borderTopWidth === \"number\" ? `${props.borderTopWidth}px` : props.borderTopWidth};\n border-top-color: ${props.borderTopColor || props.borderColor || \"transparent\"};\n border-top-style: solid;\n `}\n ${(props) =>\n props.borderLeftWidth !== undefined &&\n `\n border-left-width: ${typeof props.borderLeftWidth === \"number\" ? `${props.borderLeftWidth}px` : props.borderLeftWidth};\n border-left-color: ${props.borderLeftColor || props.borderColor || \"transparent\"};\n border-left-style: solid;\n `}\n ${(props) =>\n props.borderRightWidth !== undefined &&\n `\n border-right-width: ${typeof props.borderRightWidth === \"number\" ? `${props.borderRightWidth}px` : props.borderRightWidth};\n border-right-color: ${props.borderRightColor || props.borderColor || \"transparent\"};\n border-right-style: solid;\n `}\n\n border-style: ${(props) =>\n props.borderStyle ||\n (props.borderWidth ||\n props.borderBottomWidth ||\n props.borderTopWidth ||\n props.borderLeftWidth ||\n props.borderRightWidth\n ? \"solid\"\n : \"none\")};\n border-radius: ${(props) =>\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius || 0};\n height: ${(props) =>\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height || \"auto\"};\n width: ${(props) =>\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width || \"auto\"};\n min-width: ${(props) =>\n typeof props.minWidth === \"number\"\n ? `${props.minWidth}px`\n : props.minWidth || \"auto\"};\n min-height: ${(props) =>\n typeof props.minHeight === \"number\"\n ? `${props.minHeight}px`\n : props.minHeight || \"auto\"};\n max-width: ${(props) =>\n typeof props.maxWidth === \"number\"\n ? `${props.maxWidth}px`\n : props.maxWidth || \"none\"};\n max-height: ${(props) =>\n typeof props.maxHeight === \"number\"\n ? `${props.maxHeight}px`\n : props.maxHeight || \"none\"};\n\n padding: ${(props) =>\n typeof props.padding === \"number\"\n ? `${props.padding}px`\n : props.padding || 0};\n ${(props) =>\n props.paddingHorizontal &&\n `\n padding-left: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n padding-right: ${typeof props.paddingHorizontal === \"number\" ? `${props.paddingHorizontal}px` : props.paddingHorizontal};\n `}\n ${(props) =>\n props.paddingVertical &&\n `\n padding-top: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n padding-bottom: ${typeof props.paddingVertical === \"number\" ? `${props.paddingVertical}px` : props.paddingVertical};\n `}\n ${(props) =>\n props.paddingTop !== undefined &&\n `padding-top: ${typeof props.paddingTop === \"number\" ? `${props.paddingTop}px` : props.paddingTop};`}\n ${(props) =>\n props.paddingBottom !== undefined &&\n `padding-bottom: ${typeof props.paddingBottom === \"number\" ? `${props.paddingBottom}px` : props.paddingBottom};`}\n ${(props) =>\n props.paddingLeft !== undefined &&\n `padding-left: ${typeof props.paddingLeft === \"number\" ? `${props.paddingLeft}px` : props.paddingLeft};`}\n ${(props) =>\n props.paddingRight !== undefined &&\n `padding-right: ${typeof props.paddingRight === \"number\" ? `${props.paddingRight}px` : props.paddingRight};`}\n\n margin: ${(props) =>\n typeof props.margin === \"number\" ? `${props.margin}px` : props.margin || 0};\n ${(props) =>\n props.marginTop !== undefined &&\n `margin-top: ${typeof props.marginTop === \"number\" ? `${props.marginTop}px` : props.marginTop};`}\n ${(props) =>\n props.marginBottom !== undefined &&\n `margin-bottom: ${typeof props.marginBottom === \"number\" ? `${props.marginBottom}px` : props.marginBottom};`}\n ${(props) =>\n props.marginLeft !== undefined &&\n `margin-left: ${typeof props.marginLeft === \"number\" ? `${props.marginLeft}px` : props.marginLeft};`}\n ${(props) =>\n props.marginRight !== undefined &&\n `margin-right: ${typeof props.marginRight === \"number\" ? `${props.marginRight}px` : props.marginRight};`}\n\n flex-direction: ${(props) => props.flexDirection || \"column\"};\n flex-wrap: ${(props) => props.flexWrap || \"nowrap\"};\n align-items: ${(props) => props.alignItems || \"stretch\"};\n justify-content: ${(props) => props.justifyContent || \"flex-start\"};\n cursor: ${(props) =>\n props.cursor\n ? props.cursor\n : props.onClick || props.onPress\n ? \"pointer\"\n : \"inherit\"};\n position: ${(props) => props.position || \"static\"};\n top: ${(props) =>\n typeof props.top === \"number\" ? `${props.top}px` : props.top};\n bottom: ${(props) =>\n typeof props.bottom === \"number\" ? `${props.bottom}px` : props.bottom};\n left: ${(props) =>\n typeof props.left === \"number\" ? `${props.left}px` : props.left};\n right: ${(props) =>\n typeof props.right === \"number\" ? `${props.right}px` : props.right};\n flex: ${(props) => props.flex};\n flex-shrink: ${(props) => props.flexShrink ?? 1};\n gap: ${(props) =>\n typeof props.gap === \"number\" ? `${props.gap}px` : props.gap || 0};\n align-self: ${(props) => props.alignSelf || \"auto\"};\n /* Only emit overflow-x/y when explicitly set. Defaulting them to\n visible after overflow would override the shorthand and break\n clipping (e.g. OtherCard bottom-right promo image). */\n overflow: ${(props) => props.overflow || \"visible\"};\n ${(props) =>\n props.overflowX != null ? `overflow-x: ${props.overflowX};` : \"\"}\n ${(props) =>\n props.overflowY != null ? `overflow-y: ${props.overflowY};` : \"\"}\n z-index: ${(props) => props.zIndex};\n opacity: ${(props) =>\n props.opacity != null ? props.opacity : props.disabled ? 0.5 : 1};\n pointer-events: ${(props) => (props.disabled ? \"none\" : \"auto\")};\n\n /* \\`background\\` is emitted before \\`background-color\\` so a caller passing\n both still gets backgroundColor as the winner. Use the shorthand for a\n layered fill (e.g. an overlay tint over a resting surface colour), which\n a single background-color cannot express. */\n &:hover {\n ${(props) =>\n props.hoverStyle?.background &&\n `background: ${props.hoverStyle.background};`}\n ${(props) =>\n props.hoverStyle?.backgroundColor &&\n `background-color: ${props.hoverStyle.backgroundColor};`}\n ${(props) =>\n props.hoverStyle?.borderColor &&\n `border-color: ${props.hoverStyle.borderColor};`}\n }\n\n &:active {\n ${(props) =>\n props.pressStyle?.background &&\n `background: ${props.pressStyle.background};`}\n ${(props) =>\n props.pressStyle?.backgroundColor &&\n `background-color: ${props.pressStyle.backgroundColor};`}\n }\n`;\n\nexport const Box = React.forwardRef<\n HTMLDivElement | HTMLButtonElement,\n BoxProps\n>(\n (\n {\n children,\n onPress,\n onKeyDown,\n onKeyUp,\n role,\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 \"aria-describedby\": ariaDescribedBy,\n \"aria-expanded\": ariaExpanded,\n \"aria-haspopup\": ariaHasPopup,\n \"aria-pressed\": ariaPressed,\n \"aria-controls\": ariaControls,\n tabIndex,\n as,\n src,\n alt,\n onError,\n onLoad,\n type,\n disabled,\n id,\n testID,\n \"data-testid\": dataTestId,\n ...props\n },\n ref\n ) => {\n // Handle as=\"img\" for rendering images with proper border-radius\n if (as === \"img\" && src) {\n return (\n <img\n src={src}\n alt={alt || \"\"}\n onError={onError}\n onLoad={onLoad}\n data-testid={dataTestId || testID}\n style={{\n display: props.display || \"block\",\n objectFit: \"cover\",\n width:\n typeof props.width === \"number\"\n ? `${props.width}px`\n : props.width,\n height:\n typeof props.height === \"number\"\n ? `${props.height}px`\n : props.height,\n borderRadius:\n typeof props.borderRadius === \"number\"\n ? `${props.borderRadius}px`\n : props.borderRadius,\n position: props.position,\n top: typeof props.top === \"number\" ? `${props.top}px` : props.top,\n left:\n typeof props.left === \"number\" ? `${props.left}px` : props.left,\n right:\n typeof props.right === \"number\"\n ? `${props.right}px`\n : props.right,\n bottom:\n typeof props.bottom === \"number\"\n ? `${props.bottom}px`\n : props.bottom,\n ...props.style,\n }}\n />\n );\n }\n\n return (\n <StyledBox\n ref={ref}\n elementType={as}\n id={id}\n type={as === \"button\" ? type || \"button\" : undefined}\n disabled={as === \"button\" ? disabled : undefined}\n onClick={onPress}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n role={role}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-current={ariaCurrent}\n aria-disabled={ariaDisabled}\n aria-busy={ariaBusy}\n aria-describedby={ariaDescribedBy}\n aria-expanded={ariaExpanded}\n aria-haspopup={ariaHasPopup}\n aria-pressed={ariaPressed}\n aria-controls={ariaControls}\n aria-live={ariaLive}\n tabIndex={tabIndex !== undefined ? tabIndex : undefined}\n data-testid={dataTestId || testID}\n {...props}\n >\n {children}\n </StyledBox>\n );\n }\n);\n\nBox.displayName = \"Box\";\n","import React from \"react\";\nimport isPropValid from \"@emotion/is-prop-valid\";\n\n// Props that @emotion/is-prop-valid incorrectly treats as valid HTML.\n// These are React Native or component-specific props that match\n// valid HTML patterns (on* event handlers, SVG attributes).\nexport const ADDITIONAL_BLOCKED_PROPS = new Set([\n // RN-only event handlers (pass isPropValid's on* pattern)\n \"onPress\",\n \"onChangeText\",\n \"onLayout\",\n \"onMoveShouldSetResponder\",\n \"onResponderGrant\",\n \"onResponderMove\",\n \"onResponderRelease\",\n \"onResponderTerminate\",\n // SVG attributes that pass isPropValid\n \"strokeWidth\",\n // CSS properties that pass isPropValid but are used as component props\n \"overflow\",\n \"opacity\",\n \"cursor\",\n \"fontSize\",\n \"fontWeight\",\n \"fontFamily\",\n \"textDecoration\",\n // `display` is a presentation attribute on SVG, so isPropValid lets it\n // through — but Box consumes it purely as CSS. Without this it lands on the\n // DOM node as a bogus `display=\"inline-flex\"` attribute.\n \"display\",\n // Cross-platform layout prop that Text consumes as CSS, never as an\n // attribute. Pinned explicitly because it is now spread into the styled\n // component rather than destructured away before it can reach the DOM.\n \"numberOfLines\",\n]);\n\nfunction shouldForwardProp(key: string): boolean {\n if (ADDITIONAL_BLOCKED_PROPS.has(key)) return false;\n return isPropValid(key);\n}\n\n/**\n * Creates a React component that renders the given HTML tag\n * but filters out non-HTML props before they reach the DOM.\n *\n * Uses @emotion/is-prop-valid (same library styled-components v4\n * uses internally) to automatically block invalid HTML attributes,\n * plus a small blocklist for false positives (RN on* handlers, SVG attrs).\n *\n * Usage: `const FilteredDiv = createFilteredElement(\"div\");`\n * Then: `const StyledBox = styled(FilteredDiv)<BoxProps>\\`...\\`;`\n *\n * styled-components can still read ALL props for CSS interpolation,\n * but only valid HTML attributes are forwarded to the DOM element.\n */\nexport function createFilteredElement(defaultTag: string) {\n const Component = React.forwardRef<HTMLElement, Record<string, unknown>>(\n ({ children, elementType, ...props }, ref) => {\n const Tag = (elementType as string) || defaultTag;\n const htmlProps: Record<string, unknown> = {};\n for (const key of Object.keys(props)) {\n if (shouldForwardProp(key)) {\n htmlProps[key] = props[key];\n }\n }\n return React.createElement(\n Tag,\n { ref, ...htmlProps },\n children as React.ReactNode\n );\n }\n );\n Component.displayName = `Filtered(${defaultTag})`;\n return Component;\n}\n","function memoize(fn) {\n var cache = {};\n return function (arg) {\n if (cache[arg] === undefined) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\n\nexport default memoize;\n","import memoize from '@emotion/memoize';\n\nvar reactPropsRegex = /^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|inert|itemProp|itemScope|itemType|itemID|itemRef|on|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/; // https://esbench.com/bench/5bfee68a4cd7e6009ef61d23\n\nvar index = memoize(function (prop) {\n return reactPropsRegex.test(prop) || prop.charCodeAt(0) === 111\n /* o */\n && prop.charCodeAt(1) === 110\n /* n */\n && prop.charCodeAt(2) < 91;\n}\n/* Z+1 */\n);\n\nexport default index;\n","import { memo, useEffect, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport {\n useOverlayLayer,\n OverlayLayerProvider,\n MODAL_LAYER_STEP,\n} from \"@xsolla/xui-core\";\nimport type { PopoverRootProps } from \"./types\";\n\n/** Breathing room between a centred floating panel and the viewport edges. */\nconst OUTER_PADDING = 20;\n\nexport const PopoverRoot = memo(\n ({\n children,\n open,\n onExited,\n onBackdropClick,\n scrimColor,\n fullScreen,\n position,\n animationDuration,\n testID,\n }: PopoverRootProps) => {\n const [visible, setVisible] = useState(false);\n const popoverLayer = useOverlayLayer() + MODAL_LAYER_STEP;\n\n // Keyed off `open` rather than mount so a rapid close/reopen still plays\n // the enter transition.\n useEffect(() => {\n if (!open) return;\n const frame = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(frame);\n }, [open]);\n\n useEffect(() => {\n if (open) return;\n setVisible(false);\n const timer = setTimeout(onExited, animationDuration);\n return () => clearTimeout(timer);\n }, [open, animationDuration, onExited]);\n\n // A full-screen popover replaces the page and a scrim blocks it, so\n // neither should scroll underneath. A scrim-less floating panel is the\n // opposite case — the page stays usable, so leave its scroll alone.\n const locksScroll = fullScreen || Boolean(scrimColor);\n useEffect(() => {\n if (!locksScroll || typeof document === \"undefined\") return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }, [locksScroll]);\n\n if (typeof document === \"undefined\") return null;\n\n const positioned = !fullScreen && position !== undefined;\n\n return ReactDOM.createPortal(\n <div\n data-testid={testID}\n style={{\n position: \"fixed\",\n inset: 0,\n zIndex: popoverLayer,\n padding: fullScreen || positioned ? 0 : OUTER_PADDING,\n display: \"flex\",\n alignItems: fullScreen ? \"stretch\" : \"center\",\n justifyContent: \"center\",\n // Without a scrim the page behind stays interactive, so the\n // full-viewport wrapper must not swallow presses meant for it.\n // The panel itself re-enables pointer events below.\n pointerEvents: scrimColor ? undefined : \"none\",\n }}\n >\n {scrimColor && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n background: scrimColor,\n opacity: visible ? 1 : 0,\n cursor: onBackdropClick ? \"pointer\" : \"default\",\n transition: `opacity ${animationDuration}ms ease`,\n }}\n onClick={onBackdropClick}\n aria-hidden=\"true\"\n />\n )}\n <div\n style={{\n position: positioned ? \"absolute\" : \"relative\",\n left: positioned ? position.x : undefined,\n top: positioned ? position.y : undefined,\n zIndex: 1,\n pointerEvents: \"auto\",\n display: \"flex\",\n maxHeight: \"100%\",\n width: fullScreen ? \"100%\" : undefined,\n opacity: visible ? 1 : 0,\n transform: visible ? \"scale(1)\" : \"scale(0.95)\",\n transition: `opacity ${animationDuration}ms ease, transform ${animationDuration}ms ease`,\n }}\n >\n <OverlayLayerProvider value={popoverLayer}>\n {children}\n </OverlayLayerProvider>\n </div>\n </div>,\n document.body,\n );\n },\n);\n\nPopoverRoot.displayName = \"PopoverRoot\";\n","import { memo } from \"react\";\nimport { IconButton } from \"@xsolla/xui-button\";\nimport { Drag, Pin, Remove, Unpin } from \"@xsolla/xui-icons-base\";\nimport { Typography } from \"@xsolla/xui-typography\";\nimport type { PopoverTitleBarProps } from \"./types\";\n\n/**\n * The floating title row from the Figma `idPopover` spec: grab handle, title,\n * pin toggle, close button.\n *\n * `dragHandleProps` is attached to the whole row rather than just the grip, so\n * the title text is draggable too. Presses that originate inside a `<button>`\n * (pin, close) are filtered out by `usePopoverDrag`, and the grip stays\n * focusable so the row can also be moved with the arrow keys.\n */\nexport const PopoverTitleBar = memo(\n ({\n title,\n draggable,\n dragHandleProps,\n pinned,\n onPinnedChange,\n onClose,\n testID,\n }: PopoverTitleBarProps) => (\n <div\n data-testid={testID}\n style={{\n flexShrink: 0,\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n gap: 8,\n cursor: draggable ? \"grab\" : undefined,\n }}\n {...(draggable ? dragHandleProps : {})}\n >\n {draggable && (\n <span\n data-xui-popover-drag-handle=\"\"\n role=\"button\"\n tabIndex={0}\n aria-label=\"Move popover\"\n style={{ display: \"inline-flex\", flexShrink: 0 }}\n >\n <Drag />\n </span>\n )}\n\n <div style={{ flex: 1, minWidth: 0, overflow: \"hidden\" }}>\n {title && (\n <Typography variant=\"bodyLgAccent\" noWrap>\n {title}\n </Typography>\n )}\n </div>\n\n {onPinnedChange && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={pinned ? <Unpin /> : <Pin />}\n onPress={() => onPinnedChange(!pinned)}\n aria-label={pinned ? \"Unpin popover\" : \"Pin popover\"}\n aria-pressed={pinned}\n />\n )}\n\n {onClose && (\n <IconButton\n variant=\"secondary\"\n tone=\"mono\"\n size=\"xs\"\n icon={<Remove />}\n onPress={onClose}\n aria-label=\"Close popover\"\n />\n )}\n </div>\n ),\n);\n\nPopoverTitleBar.displayName = \"PopoverTitleBar\";\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\nimport type { PopoverDragHandleProps, PopoverPosition } from \"./types\";\n\nexport interface UsePopoverDragOptions {\n /** Drag is a no-op while this is false (mobile, or `draggable={false}`). */\n enabled: boolean;\n /** Starting viewport offset. `undefined` leaves the panel centred. */\n initialPosition?: PopoverPosition;\n onPositionChange?: (position: PopoverPosition) => void;\n}\n\nexport interface UsePopoverDragReturn {\n /** `undefined` until the panel has been positioned, so it stays centred. */\n position?: PopoverPosition;\n /** Attach to the panel so the hook can measure it for clamping. */\n panelRef: RefObject<HTMLDivElement>;\n /** Spread onto the element that starts a drag. */\n dragHandleProps: PopoverDragHandleProps;\n isDragging: boolean;\n}\n\n/** Distance an arrow key moves the panel. */\nconst KEYBOARD_STEP = 16;\n/** Keeps a dragged panel from being pushed fully off-screen. */\nconst EDGE_MARGIN = 8;\n\n/** True when the event originated on a real control rather than the row. */\nconst isInteractive = (target: EventTarget | null): boolean =>\n target instanceof Element && target.closest(\"button, a, input\") !== null;\n\nconst clampToViewport = (\n position: PopoverPosition,\n panel: HTMLElement | null,\n): PopoverPosition => {\n if (!panel || typeof window === \"undefined\") return position;\n const { width, height } = panel.getBoundingClientRect();\n const maxX = Math.max(EDGE_MARGIN, window.innerWidth - width - EDGE_MARGIN);\n const maxY = Math.max(EDGE_MARGIN, window.innerHeight - height - EDGE_MARGIN);\n return {\n x: Math.min(Math.max(position.x, EDGE_MARGIN), maxX),\n y: Math.min(Math.max(position.y, EDGE_MARGIN), maxY),\n };\n};\n\n/**\n * Pointer- and keyboard-driven repositioning for a floating popover panel.\n *\n * The panel stays in its centred flow position until the first drag, at which\n * point the hook switches it to an absolute viewport offset. Positions are\n * always clamped so at least `EDGE_MARGIN` of the panel stays on screen.\n */\nexport const usePopoverDrag = ({\n enabled,\n initialPosition,\n onPositionChange,\n}: UsePopoverDragOptions): UsePopoverDragReturn => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState<PopoverPosition | undefined>(\n initialPosition,\n );\n const [isDragging, setIsDragging] = useState(false);\n // Offset between the pointer and the panel's top-left corner, so the panel\n // does not jump to centre itself under the cursor on the first move.\n const grabOffset = useRef<PopoverPosition>({ x: 0, y: 0 });\n\n // Dropping out of draggable mode (e.g. a resize into the mobile variant)\n // must release the absolute offset, otherwise the full-screen page inherits\n // a stale left/top and renders off-centre.\n useEffect(() => {\n if (!enabled) setPosition(undefined);\n }, [enabled]);\n\n const commit = useCallback(\n (next: PopoverPosition) => {\n const clamped = clampToViewport(next, panelRef.current);\n setPosition(clamped);\n onPositionChange?.(clamped);\n },\n [onPositionChange],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n if (!enabled || event.button !== 0) return;\n // The handlers sit on the whole title row, so a press on the pin or\n // close button would otherwise start a drag instead of clicking.\n if (isInteractive(event.target)) return;\n const panel = panelRef.current;\n if (!panel) return;\n\n event.preventDefault();\n const rect = panel.getBoundingClientRect();\n grabOffset.current = {\n x: event.clientX - rect.left,\n y: event.clientY - rect.top,\n };\n setIsDragging(true);\n },\n [enabled],\n );\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (!enabled) return;\n // Only the grip nudges the panel — arrow keys elsewhere in the row\n // belong to whatever control has focus.\n if (\n !(event.target instanceof Element) ||\n !event.target.closest(\"[data-xui-popover-drag-handle]\")\n ) {\n return;\n }\n const deltas: Record<string, PopoverPosition> = {\n ArrowUp: { x: 0, y: -KEYBOARD_STEP },\n ArrowDown: { x: 0, y: KEYBOARD_STEP },\n ArrowLeft: { x: -KEYBOARD_STEP, y: 0 },\n ArrowRight: { x: KEYBOARD_STEP, y: 0 },\n };\n const delta = deltas[event.key];\n if (!delta) return;\n\n event.preventDefault();\n // The first arrow key press has no offset yet, so anchor on wherever the\n // centred panel currently sits.\n const rect = panelRef.current?.getBoundingClientRect();\n const base = position ?? { x: rect?.left ?? 0, y: rect?.top ?? 0 };\n commit({ x: base.x + delta.x, y: base.y + delta.y });\n },\n [commit, enabled, position],\n );\n\n useEffect(() => {\n if (!isDragging || typeof document === \"undefined\") return;\n\n const handleMove = (event: PointerEvent) => {\n commit({\n x: event.clientX - grabOffset.current.x,\n y: event.clientY - grabOffset.current.y,\n });\n };\n const stop = () => setIsDragging(false);\n\n document.addEventListener(\"pointermove\", handleMove);\n document.addEventListener(\"pointerup\", stop);\n document.addEventListener(\"pointercancel\", stop);\n return () => {\n document.removeEventListener(\"pointermove\", handleMove);\n document.removeEventListener(\"pointerup\", stop);\n document.removeEventListener(\"pointercancel\", stop);\n };\n }, [commit, isDragging]);\n\n return {\n position: enabled ? position : undefined,\n panelRef,\n dragHandleProps: { onPointerDown, onKeyDown },\n isDragging,\n };\n};\n","import { useEffect, useState } from \"react\";\nimport type { PopoverDevice } from \"./types\";\n\n/**\n * Viewport thresholds from the Figma \"Breakpoints\" frame\n * (file `SOOtaT80JAVJf7PKlFnt7Q`, node `6393:1137`).\n */\nexport const POPOVER_BREAKPOINTS = {\n /** Widths at or below this are `mobile` (Figma: \"Mobile: 320-768px\"). */\n mobileMax: 768,\n /** Widths at or below this, but above `mobileMax`, are `tablet` (Figma: \"Tablet: 769-1023px\"). */\n tabletMax: 1023,\n} as const;\n\n/** Maps a viewport width in CSS pixels onto a {@link PopoverDevice} bucket. */\nexport const resolveDevice = (width: number): PopoverDevice => {\n if (width <= POPOVER_BREAKPOINTS.mobileMax) return \"mobile\";\n if (width <= POPOVER_BREAKPOINTS.tabletMax) return \"tablet\";\n return \"desktop\";\n};\n\n/**\n * Resolves the active device bucket from the viewport width, re-resolving on\n * resize. Pass `override` to short-circuit measurement entirely — the override\n * is returned verbatim and no listener is attached.\n *\n * Server-side (no `window`) the hook reports `desktop`, which matches the\n * Figma default variant, and corrects itself on the first client effect.\n */\nexport const useDeviceType = (override?: PopoverDevice): PopoverDevice => {\n const [device, setDevice] = useState<PopoverDevice>(() =>\n typeof window === \"undefined\" ? \"desktop\" : resolveDevice(window.innerWidth),\n );\n\n useEffect(() => {\n if (override || typeof window === \"undefined\") return;\n\n const update = () => setDevice(resolveDevice(window.innerWidth));\n // Re-read on mount: the initial state was computed before hydration on SSR.\n update();\n\n window.addEventListener(\"resize\", update);\n return () => window.removeEventListener(\"resize\", update);\n }, [override]);\n\n return override ?? device;\n};\n","import { useCallback, useState } from \"react\";\n\nexport interface UsePopoverOptions {\n onOpen?: () => void;\n onClose?: () => void;\n}\n\nexport interface UsePopoverReturn {\n isOpen: boolean;\n open: () => void;\n close: () => void;\n toggle: () => void;\n}\n\n/** Open/close state for a `Popover`, mirroring `useDrawer`. */\nexport function usePopover(options?: UsePopoverOptions): UsePopoverReturn {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback(() => {\n setIsOpen(true);\n options?.onOpen?.();\n }, [options]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n options?.onClose?.();\n }, [options]);\n\n const toggle = useCallback(() => {\n setIsOpen((previous) => {\n if (previous) options?.onClose?.();\n else options?.onOpen?.();\n return !previous;\n });\n }, [options]);\n\n return { isOpen, open, close, toggle };\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","React","React","jsx","memo","jsx","jsxs","useEffect","useState","useEffect","useState","jsx","jsxs","useState","useRef","useEffect","handleKeyDown","useCallback","useCallback","useState"]}