Created
September 16, 2022 09:01
-
-
Save jhesgodi/ba0039008fa0d24eef32f93b467d8d9c to your computer and use it in GitHub Desktop.
Variant class names typing
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
// Playground Link | |
// https://www.typescriptlang.org/play?#code/C4TwDgpgBAaghgJwJZwHbAM4HkBmAeAFQBooAFKAXigAkB7WgawzywFslgDwI8AlCAI4BXJAggATQgD4SAMSGoAxsCS1UMqABIAggBtdUqZSgBvAFBRLUANoMoSVFAYQQtHGQC6AfgBcZWx5QEAAewBCo4hhQAJIRIVBephZWKdZI9o6kAd5+8MhomLh4dIzMbBx4pCQMhkYAPlAYwMioAOYA3MmWAL5QDU0trVB+WQyBIWERUQQIQsAAFiAJUNoYAEL0uhBoeSjoeAMOQw27Bdj4JUws7MCV1bVGfqgQAG4QCJ3dZmagkFAzc0WxgARptto4GrRgQArCDKPpQeRKFRqb6-aCxcTxKiHNoIjAgVig3QI1BCInvTo-bgrdZgnaIPa3UhGKgmKAAcmaQggHJGUF6DXZHJwcF0GF5-MFjWaRzRNJ0+mM7M0Yt0flxrRIqt0AHc4CAMBrZXivtS-poACKiKJURUkgBkUAAohx5u88CrUGoIMbBgKSF6kOqZYNtd7gKhg36jgKpPK-pdmMQoJbjFabaz-lAnZaE9BypxuIQSCzlV0bHYHE4XG5s3BpgEgqFwpEyMtnm8EMMnB4-AQAmYzeioFhULoQCWyFn2bYMjXXO4CH3-gF2gKc6ZK-PnIvPL4oJ3KQLOiPXQsPSmy1QxxOp2WGrfJ1V-lIqWYAPQfqAAOVoYT8aIoEUWghF0cRUC5KBWDgZwoAvKBxFEOEVDeKJgWgaEhCaUMjhIDBaHsYAoFcIR4IQJZgEImC4IvVgoDQKAoVheEQNYMBg3eKBdTUYi2LAXQ4AcEgFloCVP2-DhuN4picBwLi4DmQj+K2FFHAgN13gAOigABlQj0nEGSJWgKSGwYqBgSQIYsTFLTvm+L9vwAWlc5z-ggJpYzc1yHJHXSkAAL2gKgACJggwUKEVCjBWCihpQtYcR4qgULdFaFLwt0UKqRHNY5io1BSAQWgwFtJJLCQcQD01TpLHEOBgDgAhPOAaJqpjNo6uAwSMAwH84FYCAapNDpvksEDdFoBAD1C4FdB5TKxGS6L5rgRQGBy5IXkZApZrAZAYIozKJRAiJEBATKwgQFQLsy4RGveVA7uihQmhALZku6jAguGvwAuC7r5jEHARsGb6HuWg9QVoLY0G6pCMDgeaJGh+lUG6nAwN0AB1KqFjR2HwW6xQ4A4prdF+jrLPR7q1AAYUpjaDwACl9KAAFlQIlZ03nQABKSgjAUBhvV1DHkjULnsIgZ10HeVn2elnm+eAQWKGF1BRdocXuqa4FMRCA8yQpD5khKrZZuBAq1Eyymta2yxdoAGWRiBdDBo5ut24rPIlamYbhiXLBUYBLc6sbLCmuAkLaQmg8+Kkzpwnb8nQI1YF29Oiny4BCuK0qMBnCsdXVCtQpwXjnKR1Aokr9Bq4gdhiXEci0B+tTEKEBBGtUVBnIAdgAVigEqFCxcRq-ogoUEphsJDbjajmc3UqugOHY9aZzvWeGngmc0EECxbsHHtiBnJwLZgiIpuMGcxRwmuqAsK8nAQHvx+uIwMB1vP-eACZQpEAcpYH6wU-DmBSBFPwiUHDOXmIPeCExnIRSgGAd+ABGNBACgEVlijA9g-cEEAE4kGhBQVEdBzl-7YOcgAZlwSkJKBC4EIIwQABjIcAChaD37-y0iPMA+8GHAJSOlFhRDnIYJoWEchqCqF0NoSIiswQQywMkRggALFwg+89eHOW0UIgxjCAzjUaJDCQECKzcnZpAlIjRfpWPsVYaBqUEED24oPExzj8FuOcqQ3U-jvH2OYX4jhniOHBLEa0GBbCaGBOkVElxai2HaISZopJ3RRFWCyckXJ20s7ACcVYA6SAjogGKSkUuMDZHcN1PMDg0BD7HySYjZGn1Kn2NFOKWxFZnGTWmp05xlh5o8hgc094B8FpNK3qM6A8xaBdh8MCWZ0z6HsPYUk5xy1xnTWPs5Zalkt6HIWUslZByJDrM2dk4ZVg1obV2UfSZrQe7v2IRso5zkXkGn8R8057xllb2+e-AeGytk5JufY3JtybHjK3m0lGrdSrrQ4O-TRnDanOQRZ9TJkL8kpFOmoBqFEhlaDVI8lpkL6pICRoi0llhukSnpVYapbjFkAsxfUxp4KJqw0GRVW5dzpkUsmXMnRYr-kIEBVMxaVLhk7NShMhAFzW6YpOeyqV5zlo8qFetBgIrlXAt+Ri5BRr3mcMldKs1YK5UpGhcM+1zjYWKr2c815VymI-0UKigx1y+mmLtcA6x7xbokoFSkbFljw1dLFEy6N-S+VSvjcMuZNTkESo1T4TFcyrk6ssAq0KaqF6WqLZPOhNr-XOPufq1KmLrUWszXW91oK-WCoDQ6oNMLZjs0Laa915bOHIu9aAX1uKKz4qsA9a6z0w12IjTS9pUa50xp6cy3lU0k3Ltuay0KRqAAcFa20jOFWypZ2bpl5tHlG0KJbkHattfY6tsTG19p+eanVjqoWduGc63t5CjUDs9Sikd6Kx2BrMZYTQUYQxbrJfoA1NMnmGvdf-D5Ta32HvsZG8QzLGU9stUqmV8zM3nIzWe5BXKwjgt-UOn1oG5WfvtRO6Om8YFgFoA4a6zlXiPzvjvGZWKF2Ip0dhxD+z+O4OSFjfQeNxALE6b+wJ0nsrZInaTcmYoqYKe7TAoQYBIAIFJhKExE7sPabGalRQ3cCLKojM5NUOsJDBLwzAqzCAbPOXY5x94JngFmiAA | |
type VariantsOf<T, P = Hooks<OmitType<Required<T>, Function>, $All>> = { | |
[k in keyof P]?: P[k] extends Index ? { | |
[i in P[k]]?: VariantsOf<Hooks<Omit<P, k>>> | string; | |
} | string : P[k] extends Truthy ? AsBooleanVariant<string | VariantsOf<Hooks<Omit<P, k>>>> : never; | |
} | |
type Truthy = boolean | object | Function | |
type Index = string | symbol | number; | |
type AsBooleanVariant<P> = { 'true': P } | { 'false': P } | string | |
type $All = { $all: string, $always: string } | |
type $Dirs = $All & Either<{ $none: string }, { $nil: string, $notnil: string }> | |
type Hooks<T, D = $Dirs> = T & D | |
type OmitType<T, P> = { | |
[k in keyof T as T[k] extends P ? never : k]: T[k] | |
} | |
type Only<T, P> = { [k in keyof T]: T[k]; } & { [k in keyof P]?: never; }; | |
type Either<T, P> = Only<T, P> | Only<P, T>; | |
// Note: I couldn't make the directives be just string, so it you try to make them an object compiler wont complain, those | |
// it wont offer auto completion either. So i dont see it as a big deal. | |
/// ---- Testing ----- | |
type Size = "xs" | "sm" | "md" | "lg" | "xl"; | |
type ButtonProps = { | |
id?: string; | |
dataTestId?: string; | |
className?: string; | |
color?: "blue" | "red" | "black"; | |
variant?: "primary" | "secondary" | "tertiary" | "quaternary" | "unstyled"; | |
size?: Size; | |
href?: string; | |
squared?: boolean; | |
disabled?: boolean; | |
fullWidth?: boolean; | |
capitalized?: boolean; | |
onClick?: (e: MouseEvent) => unknown; | |
onMouseEnter?: (e: MouseEvent) => unknown; | |
tabIndex?: number; | |
role?: "button" | "link"; | |
ariaLabel?: string; | |
ariaPressed?: boolean; | |
title?: string; | |
loading?: boolean; | |
}; | |
const variants: VariantsOf<ButtonProps> = { | |
$all: | |
"font-sans font-semibold transition duration-75 rounded-sm antialiased tracking-wide leading-none box-border inline-flex items-center justify-center space-x-2", | |
size: { | |
xs: "min-h-7 text-xs py-1 px-2", | |
sm: "min-h-9 text-xs py-2 px-3", | |
md: "min-h-10 text-xs py-2.5 px-3", | |
lg: "min-h-12 text-xs py-3 px-3", | |
xl: "min-h-14 text-base py-4 px-4", | |
}, | |
squared: { | |
true: { | |
size: { | |
xs: "h-7 w-7", | |
sm: "h-9 w-9", | |
md: "h-10 w-10", | |
lg: "h-12 w-12", | |
xl: "h-14 w-14", | |
}, | |
}, | |
}, | |
variant: { | |
primary: { | |
$all: "text-white border", | |
disabled: { | |
false: { | |
color: { | |
blue: "border-blue bg-blue hover:bg-blue-300", | |
red: "border-red bg-red hover:bg-red-300", | |
black: "border-gray-900 bg-gray-900 hover:bg-gray-700", | |
}, | |
}, | |
true: "bg-disabled opacity-40 text-disabled", | |
}, | |
}, | |
secondary: { | |
$all: "border", | |
disabled: { | |
false: { | |
$all: "hover:text-white", | |
color: { | |
blue: "border-blue text-blue hover:bg-blue", | |
red: "border-red text-red hover:bg-red", | |
black: "border-gray-900 text-gray-900 hover:bg-gray-900", | |
}, | |
}, | |
true: "border-gray-300 opacity-40", | |
}, | |
}, | |
tertiary: { | |
disabled: { | |
false: { | |
color: { | |
blue: "text-blue hover:text-blue-300", | |
red: "text-red hover:text-red-300", | |
black: "text-gray-900 hover:text-gray-700", | |
}, | |
}, | |
true: "text-gray-300 opacity-40", | |
}, | |
}, | |
quaternary: { | |
disabled: { | |
false: { | |
color: { | |
$all: "gray-800", | |
blue: "hover:text-blue", | |
red: "hover:text-red", | |
black: "hover:text-gray-900", | |
}, | |
}, | |
true: "text-gray-300 opacity-40", | |
}, | |
}, | |
$nil: { | |
$all: "border border-gray-200 text-gray-900", | |
disabled: { | |
false: "hover:border-blue hover:bg-blue hover:text-white", | |
true: "opacity-40", | |
}, | |
}, | |
}, | |
loading: "pointer-events-none bg-disabled text-disabled border-none", | |
fullWidth: { | |
true: "w-full", | |
}, | |
capitalized: { | |
true: "uppercase", | |
}, | |
disabled: { | |
true: "cursor-not-allowed", | |
false: "cursor-pointer", | |
}, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment