-
-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for dynamic per-class prefix #412
Comments
I see two paths to expose an API for this, not sure yet which one makes more sense. But I think I'm learning towards approach 2. 1.
|
I originally think the prefix function would look like this: const twMerge = createTailwindMerge({
prefix: (className: string): string | undefined => {
// return the prefix, e.g. `tw-`, if the className contains prefix.
// otherwise, return undefined
}
}) Would that be better? This way the function is not removing the prefix. That is still handled by the library. |
Ah yes, that makes sense. Thanks! I'm still worrying about how error-prone this pattern could be. It's not obvious what will happen if the returned prefix isn't in the actual className string. Still needs a bit more thought. 🤔 |
There is only three possible outcomes:
Personally, my preference would be 1 > 3 > 2. 3 is the cleanest. I would do that in most case actually. Failing early is almost always the best option. 1 is pragmatic. It is least intrusive and follows JavaScript behavior. // returns the string untouched when not matched
className.replace(/^prefix-/, '') |
Hi there, want to circle back to see have you decided on which path to take. |
Hey @unional, thanks for your patience. So far I couldn't figure out an API with the dynamic prefix that really fits. My problem is that the current idea is too imperative while the rest of the config is declarative. With tailwind-merge having so many users I can only release APIs that are rock-solid, because changing them later is very painful. So I'm especially cautious here. I think I'll go with exposing a low-level API from #385 so everyone can build their use-cases themselves. But that will require some time which I currently don't have unfortunately. If you need the dynamic prefix right now, I recommend that you either fork the repo or copy and paste the tailwind-merge code into your codebase and modify it so that you can use a dynamic prefix. Maybe you'll figure out a great API along the way which we could introduce back to tailwind-merge as well. |
Sure, completely understand. By the way, for declarative approach, one way is accepting a regex. But in general that is not as good as just accepting a function. |
Yeah I also thought about it, but that still limits you to a more or less hardcoded value. It's possible to create RegExes dynamically, but that's also quite painful to do. |
I take a look at the code and I can see your hesitation. It seems like the code is designed to populate the class/class group ahead of time. So adding this means the data structure types needs to change and also the resolution logic. What's your "low-level context API" is about? I don't want to make changes that makes it impossible to merge back in the future. |
Yeah, it would require some more work for sure.
The problem it's trying to solve is described with some detail in #385. My idea is to create a function similar to That way you could implement more custom behaviors or add missing features yourself, like a dynamic prefix. E.g. speaking of the dynamic prefixes, that could be done by modifying Also: The reason why I'm hesitant to add these hooks into |
@unional I just released a new feature in import { extendTailwindMerge } from 'tailwind-merge'
const twMerge = extendTailwindMerge({
experimentalParseClassName({ className, parseClassName }) {
const parsed = parseClassName(className)
return {
...parsed,
baseClassName: removeDynamicPrefix(parsed.baseClassName)
}
}
})
function removeDynamicPrefix(className: string) {
// remove dynamic prefix here
} More on that in #444. |
@unional I'm considering this issue resolved due to the |
Thanks, I'm going to use it this week. Will let you know if I discover anything. |
Hi, I just tried it out. Instead of "remove dynamic prefix", I actually need to convert it to return the const knownPrefixes = ['tw-', 'app1-']
const regex = new RegExp(`^(${knownPrefixes.join('|')})(.*)`)
createTailwindMerge(() => {
const defaultConfig = getDefaultConfig()
return {
...defaultConfig,
prefix,
experimentalParseClassName: ({ className, parseClassName }) => {
const parsed = parseClassName(className)
const match = parsed.baseClassName.match(regex)
return {
...parsed,
baseClassName: match ? `${prefix}${match[2]}` : parsed.baseClassName
}
}
}
}) |
Thanks @unional! This is very helpful. I ran into an issue with negative values and updated this script with the following const normalizedPrefix = "ui-";
const knownPrefixes = [normalizedPrefix, "web-"];
const regex = new RegExp(`^-?(${knownPrefixes.join("|")})(.*)`);
const twMerge = extendTailwindMerge({
prefix: normalizedPrefix,
experimentalParseClassName: ({ className, parseClassName }) => {
const parsed = parseClassName(className);
const match = parsed.baseClassName.match(regex);
const isNegative = match ? parsed.baseClassName.startsWith("-") : false;
const prefix = `${isNegative ? "-" : ""}${normalizedPrefix}`;
return {
...parsed,
// We need to normalize all known prefix'd classes for the parser to work correctly.
baseClassName: match ? `${prefix}${match[2]}` : parsed.baseClassName,
};
},
)); |
Awesome. I give it a try, it seems like only adjusting the regex with it('merges negative class with positive class', () => {
const p2 = '-twds-p-2'
expect(twMerge('twds-p-1 twds-bg-green-100', p2)).toEqual('twds-bg-green-100 -twds-p-2')
})
it('merges positive class with negative class', () => {
const p2 = 'twds-p-2'
expect(twMerge('-twds-p-1 twds-bg-green-100', p2)).toEqual('twds-bg-green-100 twds-p-2')
})
it('merges negative class with negative class', () => {
const p2 = '-twds-p-2'
expect(twMerge('-twds-p-1 twds-bg-green-100', p2)).toEqual('twds-bg-green-100 -twds-p-2')
}) Did I miss some case? |
Also note that technically tailwind supports 2 negative syntax: For me, I will only support the first syntax. If you want to support the second one, you can further adjust the regex. |
I don't think so! I was trying to solve a problem I ran into in my own project and tried to reconstruct the return value I was given. |
Comes from #405.
The idea is to resolve conflicts across multiple prefixes which aren't necessarily enumerable.
The text was updated successfully, but these errors were encountered: