Helper to apply classNames to a dynamic element?
Would be nice to have a className-like helper when you want to dynamically apply classes according to a variant config.
In my case, I'm playing with a basic <Button />component that would have an icon prop that could receive an ReactElement:
<Button size="xs" icon={<XMarkIcon />}>
Default
</Button>
In this case I'd like to apply some size-based variants to the icon as well, something like:
const iconClassNames = variantClassNames({
variants: {
size: {
xs: "w-4 h-4 -ml-0.5 mr-1",
sm: "w-4 h-4 -ml-0.5 mr-2",
md: "w-5 h-5 -ml-1 mr-2",
lg: "w-6 h-6 -ml-1 mr-3",
xl: "w-6 h-6 -ml-1 mr-3",
}
},
defaultVariants: {
size: "md",
},
});
That could be used this way:
<button>
{icon ? cloneElement(icon, {className: iconClassNames(props)}) : null}
{children}
</button>
Do you think this could be useful or would you do it differently (eg. nested styles, etc.)?
The className is passed to the components, so you can just apply it wherever you need it:
function MyCustomComponent(props) {
const {className} = props;
return (
<button>
{icon ? cloneElement(icon, {className}) : null}
{children}
</button>
);
}
const MyStyledCustomComponent = styled(MyCustomComponent, {
variants: {
size: {
xs: "w-4 h-4 -ml-0.5 mr-1",
sm: "w-4 h-4 -ml-0.5 mr-2",
md: "w-5 h-5 -ml-1 mr-2",
lg: "w-6 h-6 -ml-1 mr-3",
xl: "w-6 h-6 -ml-1 mr-3",
}
},
defaultVariants: {
size: "md",
},
});
Does that work?
Or another option could be to create a wrapper component that passes the className. Something like this should work (haven't test it):
function IconWrapper(props: {
children: (className: string) => ReactElement,
className: string
}) {
const {children, className} = props;
<>
{children(className)}
</>
}
const StyledIcon = styled(IconWrapper, {
variants: {
size: {
xs: "w-4 h-4 -ml-0.5 mr-1",
sm: "w-4 h-4 -ml-0.5 mr-2",
md: "w-5 h-5 -ml-1 mr-2",
lg: "w-6 h-6 -ml-1 mr-3",
xl: "w-6 h-6 -ml-1 mr-3",
}
},
defaultVariants: {
size: "md",
},
}
function Button(props) {
const {icon, ...rest} = props;
return (
<button>
{icon ? (
<StyledIcon {...rest}>
{(className) => cloneElement(icon, {className})}
</StyledIcon>
: null}
{children}
</button>
)
}