{
  "name": "swap",
  "type": "registry:ui",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "@diceui/use-as-ref",
    "@diceui/use-isomorphic-layout-effect",
    "@diceui/use-lazy-ref"
  ],
  "files": [
    {
      "path": "ui/swap.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useAsRef } from \"@/registry/bases/radix/hooks/use-as-ref\";\nimport { useIsomorphicLayoutEffect } from \"@/registry/bases/radix/hooks/use-isomorphic-layout-effect\";\nimport { useLazyRef } from \"@/registry/bases/radix/hooks/use-lazy-ref\";\n\ninterface DivProps extends React.ComponentProps<\"div\"> {\n  asChild?: boolean;\n}\n\nfunction getDataState(swapped: boolean) {\n  return swapped ? \"on\" : \"off\";\n}\n\ninterface StoreState {\n  swapped: boolean;\n}\n\ninterface Store {\n  subscribe: (callback: () => void) => () => void;\n  getState: () => StoreState;\n  setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void;\n  notify: () => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStore<T>(\n  selector: (state: StoreState) => T,\n  ogStore?: Store | null,\n): T {\n  const contextStore = React.useContext(StoreContext);\n\n  const store = ogStore ?? contextStore;\n\n  if (!store) {\n    throw new Error(`\\`useStore\\` must be used within \\`Swap\\``);\n  }\n\n  const getSnapshot = React.useCallback(\n    () => selector(store.getState()),\n    [store, selector],\n  );\n\n  return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface SwapProps extends DivProps {\n  swapped?: boolean;\n  defaultSwapped?: boolean;\n  onSwappedChange?: (swapped: boolean) => void;\n  activationMode?: \"click\" | \"hover\";\n  animation?: \"fade\" | \"rotate\" | \"flip\" | \"scale\";\n  disabled?: boolean;\n}\n\nfunction Swap(props: SwapProps) {\n  const {\n    swapped: swappedProp,\n    defaultSwapped,\n    onSwappedChange,\n    activationMode = \"click\",\n    animation = \"fade\",\n    disabled,\n    asChild,\n    className,\n    onClick: onClickProp,\n    onMouseEnter: onMouseEnterProp,\n    onMouseLeave: onMouseLeaveProp,\n    onKeyDown: onKeyDownProp,\n    ...rootProps\n  } = props;\n\n  const listenersRef = useLazyRef(() => new Set<() => void>());\n  const stateRef = useLazyRef<StoreState>(() => ({\n    swapped: swappedProp ?? defaultSwapped ?? false,\n  }));\n\n  const propsRef = useAsRef({\n    activationMode,\n    animation,\n    disabled,\n    onSwappedChange,\n    onClick: onClickProp,\n    onMouseEnter: onMouseEnterProp,\n    onMouseLeave: onMouseLeaveProp,\n    onKeyDown: onKeyDownProp,\n  });\n\n  const isClickMode = activationMode === \"click\";\n\n  const store = React.useMemo<Store>(() => {\n    return {\n      subscribe: (cb) => {\n        listenersRef.current.add(cb);\n        return () => listenersRef.current.delete(cb);\n      },\n      getState: () => stateRef.current,\n      setState: (key, value) => {\n        if (Object.is(stateRef.current[key], value)) return;\n\n        if (key === \"swapped\" && typeof value === \"boolean\") {\n          stateRef.current.swapped = value;\n          propsRef.current.onSwappedChange?.(value);\n        } else {\n          stateRef.current[key] = value;\n        }\n\n        store.notify();\n      },\n      notify: () => {\n        for (const cb of listenersRef.current) {\n          cb();\n        }\n      },\n    };\n  }, [listenersRef, stateRef, propsRef]);\n\n  const swapped = useStore((state) => state.swapped, store);\n\n  useIsomorphicLayoutEffect(() => {\n    if (swappedProp !== undefined) {\n      store.setState(\"swapped\", swappedProp);\n    }\n  }, [swappedProp]);\n\n  const onToggle = React.useCallback(() => {\n    if (propsRef.current.disabled) return;\n\n    store.setState(\"swapped\", !store.getState().swapped);\n  }, [store, propsRef]);\n\n  const onClick = React.useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      propsRef.current.onClick?.(event);\n      if (event.defaultPrevented || propsRef.current.activationMode !== \"click\")\n        return;\n\n      onToggle();\n    },\n    [propsRef, onToggle],\n  );\n\n  const onMouseEnter = React.useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      propsRef.current.onMouseEnter?.(event);\n      if (\n        event.defaultPrevented ||\n        activationMode !== \"hover\" ||\n        propsRef.current.disabled\n      )\n        return;\n\n      store.setState(\"swapped\", true);\n    },\n    [propsRef, activationMode, store],\n  );\n\n  const onMouseLeave = React.useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      propsRef.current.onMouseLeave?.(event);\n      if (\n        event.defaultPrevented ||\n        activationMode !== \"hover\" ||\n        propsRef.current.disabled\n      )\n        return;\n\n      store.setState(\"swapped\", false);\n    },\n    [propsRef, activationMode, store],\n  );\n\n  const onKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\n      propsRef.current.onKeyDown?.(event);\n      if (\n        event.defaultPrevented ||\n        propsRef.current.activationMode !== \"click\" ||\n        propsRef.current.disabled\n      )\n        return;\n\n      if (event.key === \"Enter\" || event.key === \" \") {\n        event.preventDefault();\n        onToggle();\n      }\n    },\n    [propsRef, onToggle],\n  );\n\n  const RootPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <StoreContext.Provider value={store}>\n      <RootPrimitive\n        role={isClickMode ? \"button\" : undefined}\n        aria-pressed={isClickMode ? swapped : undefined}\n        aria-disabled={disabled}\n        data-slot=\"swap\"\n        data-animation={animation}\n        data-state={getDataState(swapped)}\n        data-disabled={disabled ? \"\" : undefined}\n        tabIndex={isClickMode && !disabled ? 0 : undefined}\n        {...rootProps}\n        className={cn(\n          \"relative inline-flex cursor-pointer items-center justify-center select-none data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n          className,\n        )}\n        onClick={onClick}\n        onMouseEnter={onMouseEnter}\n        onMouseLeave={onMouseLeave}\n        onKeyDown={onKeyDown}\n      />\n    </StoreContext.Provider>\n  );\n}\n\nfunction SwapOn(props: DivProps) {\n  const { asChild, className, ...onProps } = props;\n\n  const swapped = useStore((state) => state.swapped);\n\n  const OnPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <OnPrimitive\n      data-slot=\"swap-on\"\n      data-state={getDataState(swapped)}\n      {...onProps}\n      className={cn(\n        \"transition-all duration-300 data-[state=off]:absolute data-[state=off]:opacity-0 data-[state=on]:opacity-100 motion-reduce:transition-none\",\n        \"[*[data-animation=rotate]_&]:data-[state=off]:rotate-180 [*[data-animation=rotate]_&]:data-[state=on]:rotate-0 motion-reduce:[*[data-animation=rotate]_&]:data-[state=off]:rotate-0\",\n        \"[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(180deg)] [*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(0deg)] motion-reduce:[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(0deg)]\",\n        \"[*[data-animation=scale]_&]:data-[state=off]:scale-0 [*[data-animation=scale]_&]:data-[state=on]:scale-100 motion-reduce:[*[data-animation=scale]_&]:data-[state=off]:scale-100\",\n        className,\n      )}\n    />\n  );\n}\n\nfunction SwapOff(props: DivProps) {\n  const { asChild, className, ...offProps } = props;\n\n  const swapped = useStore((state) => state.swapped);\n\n  const OffPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <OffPrimitive\n      data-slot=\"swap-off\"\n      data-state={getDataState(swapped)}\n      {...offProps}\n      className={cn(\n        \"transition-all duration-300 data-[state=off]:opacity-100 data-[state=on]:absolute data-[state=on]:opacity-0 motion-reduce:transition-none\",\n        \"[*[data-animation=rotate]_&]:data-[state=off]:rotate-0 [*[data-animation=rotate]_&]:data-[state=on]:rotate-180 motion-reduce:[*[data-animation=rotate]_&]:data-[state=on]:rotate-0\",\n        \"[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(0deg)] [*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(180deg)] motion-reduce:[*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(0deg)]\",\n        \"[*[data-animation=scale]_&]:data-[state=off]:scale-100 [*[data-animation=scale]_&]:data-[state=on]:scale-0 motion-reduce:[*[data-animation=scale]_&]:data-[state=on]:scale-100\",\n        className,\n      )}\n    />\n  );\n}\n\nexport { Swap, SwapOff, SwapOn, type SwapProps, useStore as useSwap };\n",
      "target": ""
    }
  ]
}