Open geoctrl opened 5 months ago
here's a first-pass at the new utilities:
export const css = (...args: (string | undefined)[]): string => {
return args.filter(Boolean).join(" ");
};
export const maybe = (
enabled: boolean | undefined | null,
...className: string[]
): string => {
return enabled ? className.join(" ") : "";
};
export const toggle = (
enabled: boolean | undefined | null,
className1: string | string[],
className2: string | string[],
): string => {
const one = Array.isArray(className1) ? className1.join(" ") : className1;
const two = Array.isArray(className2) ? className2.join(" ") : className2;
return enabled ? one : two;
};
export { css as c, maybe as m, toggle as t };
and usage examples:
import { a, m, t } from "@kremlingjs/react";
<div className={a(
"one",
m(condition, "two"),
t(condition, "three", "four"),
)} />
I've been hard at work moving kremling over to a monorepo (i'll write another issue here about that soon), and it's really close! the last thing left is figuring out what to do with the classname utilities:
always() maybe() toggle()
Background
This utility uses a string object instead of a string primitive so we can pass values around and make it chainable. It works, albeit there’s some weird caveats like sometimes requiring
toString()
if you’re using prop types, or if you’re working in other libraries like solidjs, this solution just flat out fails. Also, typing it with typescript is a major headache - i’ve spent too much time arguing with chatgpt about the best approach to solving this... tldr: we’d have to assert the type every time we use it, or we usetoString()
at the end of every implementation 🤮option 1 - leave it be
we keep it as-is and don’t convert it to typescript - also keep the caveats I mentioned above ^ - this works because the type definitions that were manually written for it extend the output of each method with
& String
which satisfies react’s className typeoption 2 - proposal:
we rewrite it:
this looks very similar to the old way… except when we compose them together - instead of chaining, we pipe them in with
always
:the
always
method is crucial because it allows infinite string arguments:a(...strArgs: string[]): string
joining all the strings together leaving a simple string primitive!benefits:
toString()
t()
, it’ll be tree-shaken outdownsides:
always
still requiresalways
to combine them:a(m(), m())
(although i would argue that not usinga()
is an edge case in this scenario)BONUS QUESTION:
do we keep classname helpers in kremling? is this useful enough to pull out and make it's own library?