Skip to content
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

Utility for conditionally joining classNames together #7

Closed
dashed opened this issue Jun 2, 2016 · 6 comments
Closed

Utility for conditionally joining classNames together #7

dashed opened this issue Jun 2, 2016 · 6 comments

Comments

@dashed
Copy link
Contributor

dashed commented Jun 2, 2016

I had a need for something like https://github.com/JedWatson/classnames

I came up with the following macro:

macro_rules! classnames {

    // base cases

    ($name: expr) => (
        format!("{}", $name)
    );

    ($name:expr => $should_include:expr) => (
        if $should_include {
            format!("{}", $name)
        } else {
            format!("")
        }
    );

    // expansions

    ($name:expr, $($tail:tt)+) => {
        format!("{} {}", $name, classnames!($($tail)*))

    };

    ($name:expr => $should_include:expr, $($tail:tt)+) => {
        if $should_include {
            format!("{} {}", $name, classnames!($($tail)*))
        } else {
            classnames!($($tail)*)
        };
    };

}

// usage:
html!{

    div(class = classnames!("active" => true, "button-style")) {
        : "this is a button"
    }

    div(class = classnames!("active" => false, "button-style")) {
        : "this is a button"
    }
}

rust playground with examples: https://is.gd/e6Jahb

I just learned rust macros to make this; it can probably be refactored into something more nicer.


I'm wondering if this utility macro would be something worth adding to horrorshow. I can try to PR.

@Stebalien
Copy link
Owner

That definitely sounds useful.

Personally, I would call it something like labels! or items! because it can be used for more than class names. I'd also use a guard syntax (labels!("a", "b" if false, "c")) as it makes it clear that the booleans are conditionals. Finally, the recursive calls to format! will have very poor performance. Instead, consider implementing the Render traits for some helper struct (you can even make this generic over items implementing Render); this will allow you to write directly to the template buffer instead of allocating intermediate strings.

At the end of the day, it should be possible to write:

    div(class = labels!("active" if false, "button-style", format_args!("nth-{}", i /* a number */)) {
        : "this is a button"
    }

and get (assuming i = 10):

<div class="button-style nth-10">this is a button</div>

@Stebalien
Copy link
Owner

If you need help with any of this, just ask. If you don't feel up for doing all of it, I can finish up anything you don't implement (i.e., partial PRs welcome).

@dashed
Copy link
Contributor Author

dashed commented Jun 3, 2016

The additional suggestions sounds good.

I'm not totally clear on how to allocate TemplateBuffer.

@Stebalien
Copy link
Owner

Stebalien commented Jun 3, 2016

You don't. Just implement Render, RenderMut, and RenderOnce (modeled after Fn, FnMut, and FnOnce) and an &mut TemplateBuffer will get passed to you (it's an analog of Display's &mut Formatter).

@Stebalien
Copy link
Owner

Actually, you can probably just generate a closure wrapped in an FnRenderer. That is, FnRenderer::new(|tmpl| { /* append appropriate labels to template */ }).

@dashed
Copy link
Contributor Author

dashed commented Jun 4, 2016

Thanks. I created a PR attempt based on your suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants