Created
April 21, 2019 03:04
-
-
Save justgage/0f4b5af9b40d39724579ffae9d5d76e7 to your computer and use it in GitHub Desktop.
BEM.re
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let whitespaceRegex = Js.Re.fromString("\\s+"); | |
/** | |
* This is a little helper to make sure that classNames are BEM style | |
* | |
* Example: | |
* module B = | |
* BEM.Block({ | |
* let block = "Legal"; | |
* }); | |
* | |
* B.block | |
* ->B.mobileModifier | |
* ->(B.modf("isActive", active)) | |
* | |
* will generate: "Legal Legal--mobile Legal--isActive" if it's both active and on a mobile phone | |
* but just "Legal" otherwise | |
* | |
* NOTE: See tests for more advanced usages. | |
* | |
* This is implemented using a "Functor". Basically they are | |
* a function that takes in a module and returns a module as | |
* it's return type. This is similar to an Elixir macro | |
* that generates a module. | |
* | |
* Read more about them here: https://reasonml.github.io/docs/en/module#module-functions-functors | |
*/ | |
module Block = (BlockDef: {let block: string;}) => { | |
open BlockDef; | |
/** | |
this is the block (the B in BEM) It's usually the component name. | |
If you're in LocationSelector it will be named "LocationSelector" | |
*/ | |
let block = block; | |
/* The line above doesn't look like it does anything, | |
but this is how you re-export something from another | |
module, in this case BlockDef above. It's very much | |
like defdelegate in Elixir */ | |
/** | |
* A sub element: `B.el("Account") == "Legal__Account"` | |
*/ | |
let el = element => block ++ "__" ++ element; | |
/** | |
* A helper function to add a non-standard classname to a BEM style thing. | |
*/ | |
let addClass = (base, extraClass) => base ++ " " ++ extraClass; | |
exception UnableToModifyBlankClassName; | |
/** | |
* Adds a modifier to a classname. | |
* | |
* This will turn a BEM modifier on and off based on the "cond" | |
* | |
* Example: | |
* | |
* <div className=B.block->(B.modif("isActive", active))> ... </div> | |
* | |
* will generate: "Legal Legal--isActive" if it's both active but just "Legal" otherwise | |
*/ | |
let modif = (existing: string, modifier: string, cond: bool) => | |
if (cond) { | |
let splitOnWhitespace = Js.String.splitByRe(whitespaceRegex); | |
let classNames = existing |> splitOnWhitespace |> Array.to_list; | |
let (root: string, rest: string) = | |
switch (classNames) { | |
| [Some(root), ...rest] => ( | |
root, | |
Js.String.concatMany( | |
rest | |
->Belt.List.map(x => Belt.Option.getWithDefault(x, "")) | |
->Belt.List.toArray, | |
"", | |
), | |
) | |
| _ => raise(UnableToModifyBlankClassName) | |
}; | |
let prefix = rest == "" ? root : {j|$root $rest|j}; | |
{j|$prefix $root--$modifier|j}; | |
} else { | |
existing; | |
}; | |
let mobileModifier = existing => | |
modif(existing, "isMobile", MobileHelper.isMobile()); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment