{ "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\";\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: (key: K, value: StoreState[K]) => void;\n notify: () => void;\n}\n\nconst StoreContext = React.createContext(null);\n\nfunction useStore(\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(() => ({\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(() => {\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) => {\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) => {\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) => {\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) => {\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 \n \n \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 \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 \n );\n}\n\nexport { Swap, SwapOff, SwapOn, type SwapProps, useStore as useSwap };\n", "target": "" } ] }