{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "swipeable",
  "title": "Swipeable",
  "description": "Swipe leading and trailing actions onto row content.",
  "dependencies": [
    "react-native-gesture-handler",
    "react-native-reanimated"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "ui/swipeable.tsx",
      "content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport {\n  Children,\n  cloneElement,\n  createContext,\n  isValidElement,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n} from \"react\";\nimport { Text, useWindowDimensions, View } from \"react-native\";\nimport { Pressable as GestureHandlerPressable } from \"react-native-gesture-handler\";\nimport ReanimatedSwipeable, {\n  type SwipeableProps as ReanimatedSwipeableProps,\n  type SwipeableMethods,\n  SwipeDirection,\n} from \"react-native-gesture-handler/ReanimatedSwipeable\";\nimport Animated, {\n  type SharedValue,\n  useAnimatedReaction,\n  useAnimatedStyle,\n  useSharedValue,\n  withTiming,\n} from \"react-native-reanimated\";\nimport { withUniwind } from \"uniwind\";\nimport { cn } from \"@/lib/utils\";\n\nexport type { SwipeableMethods } from \"react-native-gesture-handler/ReanimatedSwipeable\";\n\nconst Pressable = withUniwind(GestureHandlerPressable);\n\n// Types\ntype SwipeableSlot = \"content\" | \"action-group\";\n\ntype SwipeableSlotComponent = {\n  slot?: SwipeableSlot;\n};\n\nexport type SwipeableEdge = \"leading\" | \"trailing\";\n\nexport type SwipeableListProps = {\n  children: React.ReactNode;\n  /** When true, opening one row closes every other registered row. */\n  exclusive?: boolean;\n};\n\nexport type SwipeableContentProps = {\n  children: React.ReactNode;\n  className?: string;\n  /** Prefer this over a nested RN Pressable — RNGH press cancels on swipe. */\n  onPress?: React.ComponentProps<typeof Pressable>[\"onPress\"];\n  disabled?: boolean;\n};\n\nexport type SwipeableActionGroupProps = {\n  edge: SwipeableEdge;\n  allowsFullSwipe?: boolean;\n  children?: React.ReactNode;\n  className?: string;\n};\n\ntype SwipeableActionVariant = VariantProps<\n  typeof swipeableActionVariants\n>[\"variant\"];\n\nexport type SwipeableActionProps = Omit<\n  React.ComponentProps<typeof Pressable>,\n  \"children\"\n> &\n  VariantProps<typeof swipeableActionVariants> & {\n    children?: React.ReactNode;\n  };\n\ntype SwipeableActionContextValue = {\n  foregroundOpacity: SharedValue<number>;\n  variant: SwipeableActionVariant;\n};\n\ntype SwipeableExclusiveMember = {\n  close: () => void;\n};\n\ntype SwipeableListContextValue = {\n  join: (member: SwipeableExclusiveMember) => () => void;\n  opened: (member: SwipeableExclusiveMember) => void;\n  closeAll: () => void;\n};\n\ntype ClassNameElement = React.ReactElement<{ className?: string }>;\n\ntype ParsedSwipeableSlots = {\n  content?: React.ReactNode;\n  contentClassName?: string;\n  contentDisabled?: boolean;\n  contentOnPress?: SwipeableContentProps[\"onPress\"];\n  leadingActions?: React.ReactElement<SwipeableActionGroupProps>;\n  trailingActions?: React.ReactElement<SwipeableActionGroupProps>;\n};\n\ntype FullSwipeRole = \"outermost\" | \"secondary\";\n\n// Constants\n/** Arm when |translation| crosses this fraction of the window width. */\nconst FULL_SWIPE_TRIGGER_RATIO = 0.5;\n/** Disarm when pulled back below this fraction (while dragging). */\nconst FULL_SWIPE_CANCEL_RATIO = 0.45;\nconst FULL_SWIPE_DIM_OPACITY = 0.1;\nconst FULL_SWIPE_DIM_MS = 160;\n\nconst DEFAULT_ANIMATION_OPTIONS = {\n  damping: 14,\n  mass: 0.75,\n  overshootClamping: false,\n  stiffness: 200,\n};\n\n/** Multi-action overshoot resistance (RNGH: drag maps 1:friction past open). */\nconst DEFAULT_OVERSHOOT_FRICTION = 1.25;\n/** Single-action overshoot — still reaches half-screen, feels less loose. */\nconst SINGLE_ACTION_OVERSHOOT_FRICTION = 2;\n\n// Context\nconst SwipeableActionContext =\n  createContext<SwipeableActionContextValue | null>(null);\nconst SwipeableMethodsContext = createContext<(() => void) | null>(null);\nconst SwipeableFullSwipeContext = createContext<{\n  armed: SharedValue<boolean>;\n  role: FullSwipeRole;\n} | null>(null);\nconst SwipeableListContext = createContext<SwipeableListContextValue | null>(\n  null\n);\n\nconst useSwipeableActionContext = () => {\n  const context = useContext(SwipeableActionContext);\n  if (!context) {\n    throw new Error(\n      \"SwipeableAction parts must be used within a SwipeableAction\"\n    );\n  }\n  return context;\n};\n\n// Utils\nconst getSlot = (type: string | React.JSXElementConstructor<unknown>) =>\n  typeof type === \"string\" ? undefined : (type as SwipeableSlotComponent).slot;\n\nconst parseSlots = (children: React.ReactNode): ParsedSwipeableSlots => {\n  const slots: ParsedSwipeableSlots = {};\n\n  for (const child of Children.toArray(children)) {\n    if (!isValidElement(child)) {\n      continue;\n    }\n\n    const slot = getSlot(child.type);\n\n    if (slot === \"content\") {\n      const props = child.props as SwipeableContentProps;\n      slots.content = props.children;\n      slots.contentClassName = props.className;\n      slots.contentDisabled = props.disabled;\n      slots.contentOnPress = props.onPress;\n      continue;\n    }\n\n    if (slot !== \"action-group\") {\n      continue;\n    }\n\n    const group = child as React.ReactElement<SwipeableActionGroupProps>;\n    if (group.props.edge === \"leading\") {\n      slots.leadingActions = group;\n    } else {\n      slots.trailingActions = group;\n    }\n  }\n\n  return slots;\n};\n\nconst getActions = (children?: React.ReactNode) =>\n  Children.toArray(children).filter(\n    (child) => isValidElement(child) && child.type === SwipeableAction\n  ) as React.ReactElement<SwipeableActionProps>[];\n\nconst getOutermostAction = (\n  edge: SwipeableEdge,\n  actions: React.ReactElement<SwipeableActionProps>[]\n) => (edge === \"trailing\" ? actions.at(-1) : actions.at(0));\n\nconst invokeOutermostAction = (\n  edge: SwipeableEdge,\n  children?: React.ReactNode\n) => {\n  const action = getOutermostAction(edge, getActions(children));\n  action?.props.onPress?.(\n    {} as Parameters<NonNullable<SwipeableActionProps[\"onPress\"]>>[0]\n  );\n};\n\nconst allowsFullSwipe = (\n  group?: React.ReactElement<SwipeableActionGroupProps>\n) => group?.props.allowsFullSwipe ?? true;\n\nconst actionCount = (group?: React.ReactElement<SwipeableActionGroupProps>) =>\n  group ? getActions(group.props.children).length : 0;\n\n/** Lock measured width so overshoot bleed grows outside the tile cluster. */\nconst useLockedWidthStyle = () => {\n  const width = useSharedValue(0);\n  const style = useAnimatedStyle(() =>\n    width.value > 0 ? { width: width.value } : {}\n  );\n  const onLayout = (event: { nativeEvent: { layout: { width: number } } }) => {\n    if (width.value === 0) {\n      width.value = event.nativeEvent.layout.width;\n    }\n  };\n  return { onLayout, style, width };\n};\n\n// Full-swipe arming (UI thread)\n/**\n * Arm/disarm from absolute translation vs screen width — only while dragging.\n * Freezes after release so the open-snap spring cannot clear a valid arm\n * (single narrow actions spring below the cancel distance before willOpen).\n */\nconst FullSwipeArmer = ({\n  armed,\n  cancelDistance,\n  enabled,\n  isDragging,\n  translation,\n  triggerDistance,\n}: {\n  armed: SharedValue<boolean>;\n  cancelDistance: number;\n  enabled: boolean;\n  isDragging: SharedValue<boolean>;\n  translation: SharedValue<number>;\n  triggerDistance: number;\n}) => {\n  useAnimatedReaction(\n    () => ({\n      distance: Math.abs(translation.value),\n      dragging: isDragging.value,\n    }),\n    ({ distance, dragging }) => {\n      if (!enabled) {\n        armed.value = false;\n        return;\n      }\n      if (!dragging) {\n        return;\n      }\n      if (distance >= triggerDistance) {\n        armed.value = true;\n        return;\n      }\n      if (distance < cancelDistance) {\n        armed.value = false;\n      }\n    },\n    [armed, cancelDistance, enabled, isDragging, triggerDistance]\n  );\n\n  return null;\n};\n\n// Action panel\nconst ActionTile = ({ children }: { children: React.ReactNode }) => {\n  const { onLayout, style } = useLockedWidthStyle();\n\n  return (\n    <Animated.View\n      className=\"h-full shrink-0 overflow-hidden\"\n      onLayout={onLayout}\n      style={style}\n    >\n      {children}\n    </Animated.View>\n  );\n};\n\nconst ActionGroupPanel = ({\n  armed,\n  allowsFullSwipe: fullSwipeEnabled,\n  edge,\n  translation,\n  className,\n  children,\n}: {\n  armed: SharedValue<boolean>;\n  allowsFullSwipe: boolean;\n  edge: SwipeableEdge;\n  translation: SharedValue<number>;\n  className?: string;\n  children: React.ReactNode;\n}) => {\n  const naturalWidth = useSharedValue(0);\n\n  const panelStyle = useAnimatedStyle(() => {\n    if (naturalWidth.value === 0) {\n      return { opacity: 0 };\n    }\n\n    const width = Math.max(naturalWidth.value, Math.abs(translation.value));\n    const offset =\n      edge === \"trailing\"\n        ? translation.value + width\n        : translation.value - width;\n\n    return {\n      opacity: 1,\n      transform: [{ translateX: offset }],\n      width,\n    };\n  });\n\n  const actions = getActions(children);\n  const outermostIndex = edge === \"trailing\" ? actions.length - 1 : 0;\n  const fillVariant =\n    getOutermostAction(edge, actions)?.props.variant ?? \"default\";\n\n  const outermostCtx = useMemo(\n    () => ({ armed, role: \"outermost\" as const }),\n    [armed]\n  );\n  const secondaryCtx = useMemo(\n    () => ({ armed, role: \"secondary\" as const }),\n    [armed]\n  );\n\n  return (\n    <Animated.View\n      className={cn(\n        \"h-full flex-row items-stretch\",\n        edge === \"leading\" && \"justify-end\",\n        swipeablePanelFillVariants({ variant: fillVariant }),\n        className\n      )}\n      data-slot={\n        edge === \"trailing\"\n          ? \"swipeable-actions-trailing\"\n          : \"swipeable-actions-leading\"\n      }\n      onLayout={(event) => {\n        if (naturalWidth.value === 0) {\n          naturalWidth.value = event.nativeEvent.layout.width;\n        }\n      }}\n      style={panelStyle}\n    >\n      {actions.map((action, index) => {\n        const node = fullSwipeEnabled ? (\n          <SwipeableFullSwipeContext.Provider\n            value={index === outermostIndex ? outermostCtx : secondaryCtx}\n          >\n            {action}\n          </SwipeableFullSwipeContext.Provider>\n        ) : (\n          action\n        );\n\n        return (\n          <ActionTile key={action.key ?? `swipeable-action-${index}`}>\n            {node}\n          </ActionTile>\n        );\n      })}\n    </Animated.View>\n  );\n};\n\n// List\nexport const SwipeableList = ({\n  children,\n  exclusive = true,\n}: SwipeableListProps) => {\n  const members = useRef(new Set<SwipeableExclusiveMember>());\n\n  const context = useMemo<SwipeableListContextValue>(\n    () => ({\n      closeAll: () => {\n        for (const row of members.current) {\n          row.close();\n        }\n      },\n      join: (row) => {\n        members.current.add(row);\n        return () => {\n          members.current.delete(row);\n        };\n      },\n      opened: (row) => {\n        if (!exclusive) {\n          return;\n        }\n        for (const other of members.current) {\n          if (other !== row) {\n            other.close();\n          }\n        }\n      },\n    }),\n    [exclusive]\n  );\n\n  return (\n    <SwipeableListContext.Provider value={context}>\n      {children}\n    </SwipeableListContext.Provider>\n  );\n};\nSwipeableList.displayName = \"SwipeableList\";\n\n/** Close every row in the enclosing `SwipeableList`. No-op outside one. */\nexport const useSwipeableList = () => {\n  const list = useContext(SwipeableListContext);\n  return {\n    closeAll: useCallback(() => list?.closeAll(), [list]),\n  };\n};\n\n// Row\nexport type SwipeableProps = Omit<\n  ReanimatedSwipeableProps,\n  | \"children\"\n  | \"overshootLeft\"\n  | \"overshootRight\"\n  | \"renderLeftActions\"\n  | \"renderRightActions\"\n> & {\n  children: React.ReactNode;\n};\n\nexport const Swipeable = ({\n  children,\n  friction = 1,\n  overshootFriction,\n  animationOptions,\n  onSwipeableOpenStartDrag,\n  onSwipeableCloseStartDrag,\n  onSwipeableWillOpen,\n  onSwipeableWillClose,\n  ref,\n  ...props\n}: SwipeableProps) => {\n  const { width: windowWidth } = useWindowDimensions();\n  const triggerDistance = windowWidth * FULL_SWIPE_TRIGGER_RATIO;\n  const cancelDistance = windowWidth * FULL_SWIPE_CANCEL_RATIO;\n\n  const methodsRef = useRef<SwipeableMethods | null>(null);\n  const leadingArmed = useSharedValue(false);\n  const trailingArmed = useSharedValue(false);\n  const isDragging = useSharedValue(false);\n\n  const list = useContext(SwipeableListContext);\n  const closeRef = useRef(() => methodsRef.current?.close());\n  closeRef.current = () => methodsRef.current?.close();\n  const member = useRef<SwipeableExclusiveMember>({\n    close: () => closeRef.current(),\n  }).current;\n\n  useEffect(() => list?.join(member), [list, member]);\n\n  const announceOpen = useCallback(() => {\n    list?.opened(member);\n  }, [list, member]);\n\n  const {\n    content,\n    contentClassName,\n    contentDisabled,\n    contentOnPress,\n    leadingActions,\n    trailingActions,\n  } = useMemo(() => parseSlots(children), [children]);\n\n  if (__DEV__ && content === undefined) {\n    throw new Error(\"Swipeable: SwipeableContent is required.\");\n  }\n\n  const leadingFullSwipe = allowsFullSwipe(leadingActions);\n  const trailingFullSwipe = allowsFullSwipe(trailingActions);\n  const resolvedOvershootFriction =\n    overshootFriction ??\n    ((leadingFullSwipe && actionCount(leadingActions) === 1) ||\n    (trailingFullSwipe && actionCount(trailingActions) === 1)\n      ? SINGLE_ACTION_OVERSHOOT_FRICTION\n      : DEFAULT_OVERSHOOT_FRICTION);\n\n  const resolvedAnimationOptions = useMemo(\n    () => ({ ...DEFAULT_ANIMATION_OPTIONS, ...animationOptions }),\n    [animationOptions]\n  );\n\n  const setMethodsRef = useCallback(\n    (methods: SwipeableMethods | null) => {\n      methodsRef.current = methods;\n      if (typeof ref === \"function\") {\n        ref(methods);\n        return;\n      }\n      if (ref) {\n        ref.current = methods;\n      }\n    },\n    [ref]\n  );\n\n  const commitFullSwipe = useCallback(\n    (\n      edge: SwipeableEdge,\n      armed: SharedValue<boolean>,\n      group?: React.ReactElement<SwipeableActionGroupProps>\n    ) => {\n      if (!(allowsFullSwipe(group) && armed.value)) {\n        return;\n      }\n      armed.value = false;\n      invokeOutermostAction(edge, group?.props.children);\n      methodsRef.current?.close();\n    },\n    []\n  );\n\n  const handleOpenStartDrag = useCallback(\n    (direction: SwipeDirection) => {\n      isDragging.value = true;\n      onSwipeableOpenStartDrag?.(direction);\n      announceOpen();\n    },\n    [announceOpen, isDragging, onSwipeableOpenStartDrag]\n  );\n\n  const handleCloseStartDrag = useCallback(\n    (direction: SwipeDirection) => {\n      isDragging.value = true;\n      onSwipeableCloseStartDrag?.(direction);\n    },\n    [isDragging, onSwipeableCloseStartDrag]\n  );\n\n  const handleWillOpen = useCallback(\n    (direction: SwipeDirection) => {\n      // Freeze arm state before the open-snap spring runs.\n      isDragging.value = false;\n      onSwipeableWillOpen?.(direction);\n      announceOpen();\n\n      if (direction === SwipeDirection.LEFT) {\n        commitFullSwipe(\"trailing\", trailingArmed, trailingActions);\n        return;\n      }\n      commitFullSwipe(\"leading\", leadingArmed, leadingActions);\n    },\n    [\n      announceOpen,\n      commitFullSwipe,\n      isDragging,\n      leadingActions,\n      leadingArmed,\n      onSwipeableWillOpen,\n      trailingActions,\n      trailingArmed,\n    ]\n  );\n\n  const handleWillClose = useCallback(\n    (direction: SwipeDirection) => {\n      isDragging.value = false;\n      leadingArmed.value = false;\n      trailingArmed.value = false;\n      onSwipeableWillClose?.(direction);\n    },\n    [isDragging, leadingArmed, onSwipeableWillClose, trailingArmed]\n  );\n\n  const renderActions = useCallback(\n    (\n      edge: SwipeableEdge,\n      group: React.ReactElement<SwipeableActionGroupProps> | undefined,\n      armed: SharedValue<boolean>,\n      translation: SharedValue<number>,\n      methods: SwipeableMethods\n    ) => {\n      if (!group) {\n        return null;\n      }\n\n      methodsRef.current = methods;\n\n      return (\n        <SwipeableMethodsContext.Provider value={methods.close}>\n          <FullSwipeArmer\n            armed={armed}\n            cancelDistance={cancelDistance}\n            enabled={allowsFullSwipe(group)}\n            isDragging={isDragging}\n            translation={translation}\n            triggerDistance={triggerDistance}\n          />\n          <ActionGroupPanel\n            allowsFullSwipe={allowsFullSwipe(group)}\n            armed={armed}\n            className={group.props.className}\n            edge={edge}\n            translation={translation}\n          >\n            {group.props.children}\n          </ActionGroupPanel>\n        </SwipeableMethodsContext.Provider>\n      );\n    },\n    [cancelDistance, isDragging, triggerDistance]\n  );\n\n  const renderLeftActions = useCallback(\n    (\n      _progress: SharedValue<number>,\n      translation: SharedValue<number>,\n      methods: SwipeableMethods\n    ) =>\n      renderActions(\n        \"leading\",\n        leadingActions,\n        leadingArmed,\n        translation,\n        methods\n      ),\n    [leadingActions, leadingArmed, renderActions]\n  );\n\n  const renderRightActions = useCallback(\n    (\n      _progress: SharedValue<number>,\n      translation: SharedValue<number>,\n      methods: SwipeableMethods\n    ) =>\n      renderActions(\n        \"trailing\",\n        trailingActions,\n        trailingArmed,\n        translation,\n        methods\n      ),\n    [renderActions, trailingActions, trailingArmed]\n  );\n\n  return (\n    <ReanimatedSwipeable\n      animationOptions={resolvedAnimationOptions}\n      friction={friction}\n      onSwipeableCloseStartDrag={handleCloseStartDrag}\n      onSwipeableOpenStartDrag={handleOpenStartDrag}\n      onSwipeableWillClose={handleWillClose}\n      onSwipeableWillOpen={handleWillOpen}\n      overshootFriction={resolvedOvershootFriction}\n      overshootLeft={Boolean(leadingActions)}\n      overshootRight={Boolean(trailingActions)}\n      ref={setMethodsRef}\n      renderLeftActions={leadingActions ? renderLeftActions : undefined}\n      renderRightActions={trailingActions ? renderRightActions : undefined}\n      {...props}\n    >\n      {contentOnPress ? (\n        <Pressable\n          accessibilityRole=\"button\"\n          className={cn(\"w-full bg-card\", contentClassName)}\n          data-slot=\"swipeable\"\n          disabled={contentDisabled}\n          onPress={contentOnPress}\n        >\n          {content}\n        </Pressable>\n      ) : (\n        <View\n          className={cn(\"w-full bg-card\", contentClassName)}\n          data-slot=\"swipeable\"\n        >\n          {content}\n        </View>\n      )}\n    </ReanimatedSwipeable>\n  );\n};\nSwipeable.displayName = \"Swipeable\";\n\n// Slots\nexport const SwipeableContent = (_props: SwipeableContentProps) => null;\nSwipeableContent.displayName = \"SwipeableContent\";\nSwipeableContent.slot = \"content\" as const;\n\nexport const SwipeableActionGroup = (_props: SwipeableActionGroupProps) => null;\nSwipeableActionGroup.displayName = \"SwipeableActionGroup\";\nSwipeableActionGroup.slot = \"action-group\" as const;\n\nexport const SwipeableAction = ({\n  children,\n  className,\n  variant = \"default\",\n  accessibilityRole = \"button\",\n  onPress,\n  ...props\n}: SwipeableActionProps) => {\n  const close = useContext(SwipeableMethodsContext);\n  const fullSwipe = useContext(SwipeableFullSwipeContext);\n  const foregroundOpacity = useSharedValue(1);\n\n  const ctx = useMemo(\n    () => ({ foregroundOpacity, variant }),\n    [foregroundOpacity, variant]\n  );\n\n  useAnimatedReaction(\n    () => fullSwipe?.armed.value ?? false,\n    (isArmed, previous) => {\n      if (isArmed === previous) {\n        return;\n      }\n      // Dim label/icon only — keep the tile fill opaque over bleed.\n      if (fullSwipe?.role !== \"secondary\") {\n        foregroundOpacity.value = 1;\n        return;\n      }\n      foregroundOpacity.value = withTiming(\n        isArmed ? FULL_SWIPE_DIM_OPACITY : 1,\n        {\n          duration: FULL_SWIPE_DIM_MS,\n        }\n      );\n    },\n    [fullSwipe, foregroundOpacity]\n  );\n\n  return (\n    <SwipeableActionContext.Provider value={ctx}>\n      <Pressable\n        accessibilityRole={accessibilityRole}\n        className={cn(swipeableActionVariants({ variant }), className)}\n        data-slot=\"swipeable-action\"\n        {...props}\n        onPress={(event) => {\n          onPress?.(event);\n          close?.();\n        }}\n      >\n        {Children.map(children, (child) =>\n          typeof child === \"string\" ? (\n            <SwipeableActionText>{child}</SwipeableActionText>\n          ) : (\n            child\n          )\n        )}\n      </Pressable>\n    </SwipeableActionContext.Provider>\n  );\n};\nSwipeableAction.displayName = \"SwipeableAction\";\n\nconst Foreground = ({ children }: { children: React.ReactNode }) => {\n  const { foregroundOpacity } = useSwipeableActionContext();\n  const style = useAnimatedStyle(() => ({\n    opacity: foregroundOpacity.value,\n  }));\n  return <Animated.View style={style}>{children}</Animated.View>;\n};\n\nexport const SwipeableActionText = ({\n  className,\n  numberOfLines = 1,\n  ...props\n}: React.ComponentProps<typeof Text>) => {\n  const { variant } = useSwipeableActionContext();\n\n  return (\n    <Foreground>\n      <Text\n        className={cn(swipeableActionTextVariants({ variant }), className)}\n        data-slot=\"swipeable-action-text\"\n        numberOfLines={numberOfLines}\n        {...props}\n      />\n    </Foreground>\n  );\n};\nSwipeableActionText.displayName = \"SwipeableActionText\";\n\nexport const SwipeableActionIcon = ({\n  children,\n  className,\n  ...props\n}: {\n  children: React.ReactNode;\n  className?: string;\n}) => {\n  const { variant } = useSwipeableActionContext();\n  const child = Children.only(children);\n\n  if (!child) {\n    if (__DEV__) {\n      throw new Error(\n        \"SwipeableActionIcon expects a single React element as children\"\n      );\n    }\n    return null;\n  }\n\n  const element = child as ClassNameElement;\n\n  return (\n    <Foreground>\n      {cloneElement(element, {\n        ...props,\n        className: cn(\n          swipeableActionIconVariants({ variant }),\n          className,\n          element.props.className\n        ),\n      })}\n    </Foreground>\n  );\n};\nSwipeableActionIcon.displayName = \"SwipeableActionIcon\";\n\n// Styles\nconst swipeablePanelFillVariants = cva(\"\", {\n  defaultVariants: { variant: \"default\" },\n  variants: {\n    variant: {\n      default: \"bg-primary\",\n      destructive: \"bg-destructive\",\n      secondary: \"bg-secondary\",\n    },\n  },\n});\n\nconst swipeableActionVariants = cva(\n  \"h-full w-full min-w-[4.5rem] shrink-0 flex-col items-center justify-center gap-1 overflow-hidden px-3\",\n  {\n    defaultVariants: { variant: \"default\" },\n    variants: {\n      variant: {\n        default: \"bg-primary active:bg-primary/90\",\n        destructive: \"bg-destructive active:bg-destructive/90\",\n        secondary: \"bg-secondary active:bg-secondary/90\",\n      },\n    },\n  }\n);\n\nconst swipeableActionTextVariants = cva(\n  \"shrink-0 text-center font-medium text-sm\",\n  {\n    defaultVariants: { variant: \"default\" },\n    variants: {\n      variant: {\n        default: \"text-primary-foreground\",\n        destructive: \"text-white\",\n        secondary: \"text-secondary-foreground\",\n      },\n    },\n  }\n);\n\nconst swipeableActionIconVariants = cva(\"size-5\", {\n  defaultVariants: { variant: \"default\" },\n  variants: {\n    variant: {\n      default: \"text-primary-foreground\",\n      destructive: \"text-white\",\n      secondary: \"text-secondary-foreground\",\n    },\n  },\n});\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
