Created
March 1, 2024 17:22
-
-
Save malerba118/102395407e90fbcdeb77a49b67499add to your computer and use it in GitHub Desktop.
props-tunnel-shadcn-cva
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import * as React from "react"; | |
import * as TabsPrimitive from "@radix-ui/react-tabs"; | |
import { cn } from "@/lib/utils"; | |
function createPropsTunnel<TunneledProps extends Record<string, any>>( | |
defaultProps: Required<TunneledProps> | |
) { | |
const PropsContext = React.createContext<TunneledProps | null>(null); | |
function withProvider<T extends React.ComponentType<any>>( | |
Component: T | |
): React.FC<React.ComponentProps<T> & TunneledProps> { | |
const WithProvider = React.forwardRef<React.ComponentProps<T>, any>( | |
(props, ref) => { | |
// Split tunneled props from the rest | |
const otherProps = Object.keys(props).reduce((acc, key) => { | |
if (key in defaultProps) { | |
// ignore | |
} else { | |
acc[key] = props[key]; | |
} | |
return acc; | |
}, {} as React.ComponentProps<T>); | |
const tunneledProps = Object.keys(defaultProps).reduce((acc, key) => { | |
acc[key as keyof TunneledProps] = props[key] || defaultProps[key]; | |
return acc; | |
}, {} as TunneledProps); | |
return ( | |
<PropsContext.Provider value={tunneledProps}> | |
<Component {...(otherProps as any)} ref={ref} /> | |
</PropsContext.Provider> | |
); | |
} | |
); | |
WithProvider.displayName = `WithProvider(${ | |
Component.displayName || Component.name || "Component" | |
})`; | |
return WithProvider as React.FC<React.ComponentProps<T> & TunneledProps>; | |
} | |
function useProps(): TunneledProps { | |
const context = React.useContext(PropsContext); | |
if (!context) { | |
throw new Error( | |
"useProps must be used within a component wrapped by withProvider" | |
); | |
} | |
return context; | |
} | |
return { withProvider, useProps }; | |
} | |
const tunnel = createPropsTunnel<{ | |
variant?: "underlined" | "solid"; | |
size?: "sm" | "lg"; | |
}>({ variant: "underlined", size: "sm" }); | |
const Tabs = tunnel.withProvider(TabsPrimitive.Root); | |
const TabsList = React.forwardRef< | |
React.ElementRef<typeof TabsPrimitive.List>, | |
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> | |
>(({ className, ...props }, ref) => { | |
const tunneledProps = tunnel.useProps(); | |
return ( | |
<TabsPrimitive.List | |
ref={ref} | |
className={cn( | |
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground", | |
className | |
)} | |
{...props} | |
/> | |
); | |
}); | |
TabsList.displayName = TabsPrimitive.List.displayName; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment