@odjs/classes
Version:
Classname management for @odjs/dom
280 lines (193 loc) • 6.97 kB
Markdown
# @odjs/classes
[](https://circleci.com/gh/odjs/classes) [](https://www.npmjs.com/package/@odjs/classes) [](https://codecov.io/gh/odjs/classes) [](https://www.jsdelivr.com/package/npm/@odjs/classes) [](https://david-dm.org/odjs/classes) [](https://david-dm.org/odjs/classes?type=dev) [](https://packagephobia.now.sh/result?p=@odjs/classes) [](https://bundlephobia.com/result?p=@odjs/classes) [](https://github.com/microsoft/typescript) [](https://snyk.io/test/github/odjs/classes?targetFile=package.json) [](LICENSE)
Classname management for [@odjs/dom](https://github.com/odjs/dom)
*While this project has been created to be used internally in [@odjs/dom](https://github.com/odjs/dom), it can be used as standalone both in Node.js and in the browser.*
## Install
```bash
npm i @odjs/classes
```
*For the browser you can use one of our [cdn](#cdn) scripts, or you can use a tool like Webpack, Browserify or Parcel.*
[*see usage section...*](#usage)
## API
***syntax***
```typescript
classes(...names: ClassName[]): string;
```
*Accepts any number of [ClassName](#classname) arguments and returns [normalized classname](#object-normalization) string.*
***example***
```javascript
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);
```
```console
> "btn is-small is-red is-rounded"
```
*note that* `"has-border"` *is not present in the resulting string, see [object normalization feature](#object-normalization) for more information.*
## Types
### ClassName
```typescript
type ClassName = string | ClassObject | ClassArray;
```
### ClassObject
```typescript
type IsClassPresent = (current: { [key: string]: boolean }) => any;
interface ClassObject {
[key: string]: IsClassPresent | any;
}
```
*see [Conditional Class](#conditional-class) for more info.*
### ClassArray
```typescript
interface ClassArray {
[index: number]: ClassName;
}
```
## CDN
### jsDelivr
```html
<script src="https://cdn.jsdelivr.net/npm/@odjs/classes@latest/dist/map.umd.js"></script>
```
*or for production...*
```html
<script src="https://cdn.jsdelivr.net/npm/@odjs/classes@latest/dist/map.umd.min.js"></script>
```
*[more options...](https://www.jsdelivr.com/package/npm/@odjs/classes?version=latest)*
### unpkg
```html
<script src="https://unpkg.com/@odjs/classes@latest/dist/map.umd.js"></script>
```
*for production...*
```html
<script src="https://unpkg.com/@odjs/classes@latest/dist/map.umd.min.js"></script>
```
*[more options...](https://unpkg.com/@odjs/classes@latest/)*
## Usage
### Node.js
```javascript
const classes = require("@odjs/classes");
element.className = classes("btn", { red: true });
```
### Browser
*After including the* `script` *tag in your html file,* `classes` *will be available globally.*
```javascript
element.className = classes("btn", { red: true });
```
## Iteration Order
*Object key iteration order is implementation dependent, which means there are no guarantees the iteration will happen in the order you declared them.*
***example***
```javascript
// 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.*
```javascript
classes(
"btn is-red",
{ "is-red": false }
);
```
## Features
### Object Normalization
*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***
```javascript
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);
```
```console
> "btn is-enabled is-medium"
```
*Using this feature within a single is a bad idea. see [Iteration Order](#iteration-order) for more info. However, it will work most of the times.*
***example***
```javascript
// this is a bad idea
const classObj = {
"button is-rounded is-enabled": true,
"is-rounded": false,
};
const classname = classes(classObj);
console.log(classname);
```
```console
> "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.*
```javascript
const base = "button is-rounded is-enabled";
const cond = {
"is-rounded": false,
};
const classname = classes(base, cond);
console.log(classname);
```
```console
> "button is-enabled"
...always!
```
### Conditional Class
*When using a [ClassObject](#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.*
> *:warning:* ***DO NOT*** *rely on classnames within the same object to be included in the normalized object. see [Iteration Order](#iteration-order) for more info.*
***example***
```javascript
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);
```
```console
> "button is-enabled is-red"
```
## License
[MIT](LICENSE) © [Manuel Fernández](https://github.com/manferlo81)