1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
//! //! # Classnames //! //! An opinionated lLibrary for generating BEM style classnames, in Rust. //! //! If you don't know BEM, [BEM](http://getbem.com/naming/) it is a set //! of naming conventions for CSS names. //! //! ## Using Classnames //! //! There are two main things to import ... //! //! * `::classnames::classname` - A function for creating classnames. //! * `::classnames::Class` - A trait all classnames implement. Use this for when you want to pass classnames to places that you want to use them. //! //! The way classnames works, is internally it wraps each Class with //! a different Class type. Adding on "\_\_child" and "--attr" classes, //! and so on. //! //! This is to avoid needing to build lots of strings internally, //! in order to make it more efficient. //! //! ### The crux of using Classnames ... //! //! 1. You call `classname` to create a base classname. //! 2. You may then call `.el` to generate a child classname. //! 3. You may also call `.attr` to add on any BEM attributes. //! 4. You can then add classes together, to allow printing multiple different classnames in a component. //! 5. Finally all of the above; the base class, the child names, attributes, and classes added together. All support being turned into a `String`, or being printed with `::std::fmt::Display`. That's how you get the formatted classname out. //! //! Here is the above again in code ... //! //! ``` //! use ::classnames::Class; //! use ::classnames::classname; //! //! fn example() { //! // 1. Prints "banner" //! let base_class = classname("banner"); //! println!("{}", base_class); //! //! // 2. Prints "banner__header" //! let header = base_class.el("header"); //! println!("{}", header); //! //! // 3. Prints "banner__header banner__header--bold" //! let bold_header = base_class.el("header").attr("bold"); //! println!("{}", bold_header); //! //! // 4. Prints "banner pricing-banner" //! let pricing_banner = base_class + classname("pricing-banner"); //! println!("{}", pricing_banner); //! //! // 5. Prints out HTML with the classes included. //! format!(r#" //! <div class="{base_class}"> //! <h1 class="{header_class}">Pricing Information</h1> //! //! <p>Example copy</p> //! </div> //! "#, base_class = pricing_banner, header_class = bold_header); //! } //! ``` //! //! ### Passing classnames to other functions //! //! All of the internal Classname types implement `::classnames::Class`. //! They can be passed by using this type, which you can also wrap in an `Option` for convenience. //! //! For example ... //! //! ``` //! use ::classnames::Class; //! use ::classnames::classname; //! use ::render::{component, rsx, Render}; //! //! #[component] //! pub fn Card<C: Class, Children: Render>( //! class: Option<C>, //! children: Children, //! ) -> impl Render { //! let base_class = classname("card"); //! //! rsx! { //! <div //! class={base_class + class} //! > //! {children} //! </div> //! } //! } //! ``` //! // // Internally this crate works by structuring nodes in reverse order. // // i.e. The following code ... // // Node::new("home_page").append("content").append("button") // // Would produce a structure like this ... // // { // base_class: "button", // parent: { // base_class: "content", // parent: { // base_class: "home_page", // parent: None, // } // } // } // mod class; pub mod classes; pub use crate::class::classname; pub use crate::class::Class; #[cfg(test)] mod integration { use super::*; #[test] fn it_should_print_base_class() { let class = classname("home_page"); assert_eq!(class.to_string(), "home_page"); } #[test] fn it_should_print_base_class_children() { let class = classname("home_page").el("content"); assert_eq!(class.to_string(), "home_page__content"); } #[test] fn it_should_append_classes() { let class = classname("page") + "home_page"; assert_eq!(class.to_string(), "page home_page"); } #[test] fn it_should_append_different_classes() { let class = (classname("page") + classname("home_page").attr("large")) + classname("noscript").el("pane"); assert_eq!( class.to_string(), "page home_page home_page--large noscript__pane" ); } #[test] fn it_should_append_multiple_classes() { let class = classname("page") + "home_page" + "noscript"; assert_eq!(class.to_string(), "page home_page noscript"); } #[test] fn it_should_print_base_class_with_attributes() { let class = classname("home_page").attr("wide"); assert_eq!(class.to_string(), "home_page home_page--wide"); } #[test] fn it_should_print_base_class_with_multiple_attributes() { let class = classname("home_page").attr("wide").attr("bold"); assert_eq!( class.to_string(), "home_page home_page--wide home_page--bold" ); } #[test] fn it_should_print_base_class_children_with_attributes() { let class = classname("home_page").el("content").attr("large"); assert_eq!( class.to_string(), "home_page__content home_page__content--large" ); } #[test] fn it_should_print_base_class_children_with_attributes_with_additions() { let class = classname("home_page").el("content").attr("large") + "noscript"; assert_eq!( class.to_string(), "home_page__content home_page__content--large noscript" ); } #[test] fn it_should_not_print_added_optional_classes_when_none() { let base: Option<classes::BaseClass> = None; let class = classname("page") + "home_page" + base + "noscript"; assert_eq!(class.to_string(), "page home_page noscript"); } #[test] fn it_should_print_added_optional_classes_when_some() { let base = Some(classname("mobile")); let class = classname("page") + "home_page" + base + "noscript"; assert_eq!(class.to_string(), "page home_page mobile noscript"); } }