Classname management for @odjs/dom
While this project has been created to be used internally in @odjs/dom, it can be used as standalone both in Node.js and in the browser.
npm i @odjs/classes
For the browser you can use one of our cdn scripts, or you can use a tool like Webpack, Browserify or Parcel.
syntax
classes(...names: ClassName[]): string;
Accepts any number of ClassName arguments and returns normalized classname string.
example
const classname = classes(
"btn",
["is-small", "is-red", "is-enable"],
{
"is-rounded has-border": (current) => current["is-enable"],
"is-enable": false,
},
{ "has-border": false },
);
console.log(classname);
> "btn is-small is-red is-rounded"
note that "has-border"
is not present in the resulting string, see object normalization feature for more information.
type ClassName = string | ClassObject | ClassArray;
type IsClassPresent = (current: { [key: string]: boolean }) => any;
interface ClassObject {
[key: string]: IsClassPresent | any;
}
see Conditional Class for more info.
interface ClassArray {
[index: number]: ClassName;
}
<script src="https://cdn.jsdelivr.net/npm/@odjs/classes@latest/dist/map.umd.js"></script>
or for production...
<script src="https://cdn.jsdelivr.net/npm/@odjs/classes@latest/dist/map.umd.min.js"></script>
<script src="https://unpkg.com/@odjs/classes@latest/dist/map.umd.js"></script>
for production...
<script src="https://unpkg.com/@odjs/classes@latest/dist/map.umd.min.js"></script>
const classes = require("@odjs/classes");
element.className = classes("btn", { red: true });
After including the script
tag in your html file, classes
will be available globally.
element.className = classes("btn", { red: true });
Object key iteration order is implementation dependent, which means there are no guarantees the iteration will happen in the order you declared them.
example
// BAD IDEA
classes({
"btn is-red": true,
"is-red": false,
});
"is-red" may be present or it may not, use the following code for a predictable result.
classes(
"btn is-red",
{ "is-red": false }
);
Objects with "multi-class" keys (keys that contain spaces) and "multi-class" strings will be normalized, which allows to extend a "single-class" after it's been set from a "multi-class". It also trims any extra space in the object keys and ignore empty keys.
example
const classes1 = "btn is-small is-rounded is-enabled";
const classes2 = {
"is-small": false,
"is-medium": true,
};
const classes3 = {
"is-rounded": false,
};
const classname = classes(
classes1,
classes2,
classes3,
),
console.log(classname);
> "btn is-enabled is-medium"
Using this feature within a single is a bad idea. see Iteration Order for more info. However, it will work most of the times.
example
// this is a bad idea
const classObj = {
"button is-rounded is-enabled": true,
"is-rounded": false,
};
const classname = classes(classObj);
console.log(classname);
> "button is-enabled"
...most of the times!
however "is-rounded" may be present or it may not.
Use the following code for a predictable result.
const base = "button is-rounded is-enabled";
const cond = {
"is-rounded": false,
};
const classname = classes(base, cond);
console.log(classname);
> "button is-enabled"
...always!
When using a ClassObject the object key will be used as classname and the value will determine whether that classname should be present in the final result. If the value if a function, it will be called with an object as only argument containing the current normalized state of the result.
⚠️ DO NOT rely on classnames within the same object to be included in the normalized object. see Iteration Order for more info.
example
const classname = classes(
"button is-enabled",
"is-rounded",
{
"is-rounded": (current, classnames) => {
// current = {
// button: true,
// "is-enabled": true
// "is-rounded": true
// }
// classnames = ['is-rounded']
// note: is-rounded = true
// returning undefined will case
// the classname to be set to false
}
},
{
"is-red": (current, classnames) => {
// current = {
// button: true,
// "is-enabled": true
// "is-rounded": false
// }
// classnames = ['is-red']
// note: is-rounded = false
// because it was set to false in the previous step
return current["is-enabled"]
},
}
);
console.log(classname);
> "button is-enabled is-red"