-
Notifications
You must be signed in to change notification settings - Fork 424
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 [key]Classes method to better handle multiple CSS classes #344
Conversation
Would appreciate any input on this proposal @dhh -- thanks! |
Makes sense to support utility-first CSS conventions, but overloading the same data attribute to mean one or many class names seems problematic. If you can't trust |
@javan You don't like having fooClass just return the string and then fooClasses return an array where split has been called on the string? Then they're just two types of accessors. One always returns a string, the other always returns an array (even if it might only have one element, if there are no spaces). |
Yeah, I wasn't sure if it was preferable to have the singular property blow up when using classList APIs if you pass a string with spaces (to notify you that you very likely have a typo/bug) or have it silently not apply anything other than the first CSS class. Is throwing a Stimulus error (not the raw |
Using the example above: <div data-controller="highlight"
data-highlight-pulse-class="animate-pulse ring-8 ring-teal-600 ring-offset-8 rounded"> I don't like |
Yeah, that's a great point. I buy that. |
Singular properties should throw an error. They do currently: https://github.com/stimulusjs/stimulus/blob/11370bc37ac07753dbc4d676eebb8534bb03d3f8/packages/%40stimulus/core/src/class_properties.ts#L18-L26 Unsure if plural properties should do the same. |
Sorry, I was unclear. I meant should we throw a different error ("Expected a string without spaces, did you mean
Yeah, I'm not sure either. Plural targets do not check for missing the key (maybe that is intentional to support 0..N targets, not 1..N targets?). But in the utility CSS use-case, it would be nice if Stimulus errored if you typo the key or omit it. You're unlikely to use the singular property if you are expecting multiple classes so you are skipping the missing attribute checks. Trying to think if there is any use-case where you would intentionally want to pass in an empty string for the class value... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for taking this on! Requesting a few minor changes.
@javan thank you for reviewing. I've made the requested changes. |
Holler if there is anything else we want to do with this, I have some spare time during the holidays. Otherwise, I think it's ready for final review/merge. I can imagine the past few weeks have been pretty hectic so I appreciate the time to review this! |
Thanks, @swanson! We got a little sidelined getting hotwire out the door, and most of us are out for holidays now. Will resume soon! |
👋 Friendly bump :) |
This is at the top of our list when we return to Stimulus later this month 👍 |
Add [key]Classes method to better handle multiple CSS classes
For anyone wanting to use this functionality while waiting on the next Stimulus release, this is what I came up with: // app/javascripts/controllers/application_controller.js
import { Controller } from 'stimulus'
export default class extends Controller {
// TODO: Remove when https://github.com/hotwired/stimulus/pull/344 gets released.
initialize() {
this.constructor.classes.forEach(logicalClass => {
Object.defineProperty(this, `${logicalClass}Classes`, {
get() {
return this.[`${logicalClass}Class`].split(' ')
}
})
})
}
} Other controllers can subclass from this controller. |
You can also swap the "stimulus": "https://github.com/hotwired/dev-builds/archive/stimulus/848e0c7.tar.gz" |
@@ -9,3 +9,7 @@ export function capitalize(value: string) { | |||
export function dasherize(value: string) { | |||
return value.replace(/([A-Z])/g, (_, char) => `-${char.toLowerCase()}`) | |||
} | |||
|
|||
export function tokenize(value: string) { | |||
return value.trim().split(/\s+/).filter(content => content.length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A simpler (and slightly faster) method of doing this is
return value.match(/[^\s]+/g)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do add a PR 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR #430
Closes #341
👋
Utility-first CSS is becoming increasingly popular as an alternative to BEM (the convention used in the Handbook). This PR adds the
[key]Classes
property that maps a space separated list of classes into an array.This new property can be combined with the spread operator for more elegant
classList
manipulation when you need to change multiple classes at once.Here is an adapted example from my production codebase:
Pros:
${key}Classes
plural API mirrors target conventionsCons:
fooTargets
is mapped by multipledata-x-y-targets
attributes, whilefooClasses
is mapped by space separated string (too much magic?)I've added some basic test cases and happy to add docs if this is something you'd like added. Open to any and all feedback or alternatives :)