Last active
November 7, 2024 16:18
-
-
Save FezVrasta/1eedff6cf752088d80d74fa0745dc8ce to your computer and use it in GitHub Desktop.
TailwindCSS utility to convert objects into strings
This file contains 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
type Value = string | number | boolean; | |
interface Config extends Record<string, Value | Config> {} | |
/** | |
* Converts a Tailwind CSS config object to a list of class names. | |
* | |
* The config object properties and the values will be joined with | |
* a hyphen to form the class name. If the value is `false`, the class | |
* name will be omitted. If the value is `true`, the class name will | |
* be included as is. | |
* | |
* The `&` key can be used to join the parent class name with the | |
* child class name. This works similar to the `&` in SCSS. | |
* | |
* Examples: | |
* | |
* ```ts | |
* twj({ grid: { cols: 12, gap: 4 } }); // "grid-cols-12 grid-gap-4" | |
* twj({ grid: { "&": true, gap: 4 } }); // "grid grid-gap-4" | |
* twj({ group: { hover: { bg: "blue", text: "white" } } }); // "group-hover:bg-blue group-hover:text-white" | |
* ``` | |
* | |
* @param config The Tailwind CSS config object. | |
* @returns A list of class names. | |
*/ | |
function configToClassNamesList(config: Config): Array<Array<Value>> { | |
const result: Array<Array<Value>> = []; | |
function traverse(obj: Config, path: string[]) { | |
for (const key in obj) { | |
if (typeof obj[key] === 'object' && obj[key] !== null) { | |
// If the value is an object, recurse into it | |
traverse(obj[key], [...path, key]); | |
} else { | |
// If the value is not an object, push the path and value to the result | |
result.push([...path, key, obj[key]]); | |
} | |
} | |
} | |
traverse(config, []); | |
return result; | |
} | |
export function twj(config: Config): string { | |
const classNamesList = configToClassNamesList(config); | |
return classNamesList | |
.map((parts) => { | |
parts = parts | |
.filter((part) => part !== '&') | |
.reduce((parts, part) => { | |
if (typeof part === 'string' && part.startsWith('&')) { | |
return [ | |
...parts.slice(0, -1), | |
`${parts.at(-1) ?? ''}${part.slice(1)}`, | |
]; | |
} | |
return [...parts, part]; | |
}, [] as Value[]); | |
if (parts.at(-1) === false) { | |
return undefined; | |
} | |
if (parts.at(-1) === true) { | |
parts.pop(); | |
} | |
return parts.join('-'); | |
}) | |
.filter((className) => className !== undefined) | |
.join(' '); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment