1 | Classnames
|
2 | ===========
|
3 |
|
4 | [![Version](http://img.shields.io/npm/v/classnames.svg)](https://www.npmjs.org/package/classnames)
|
5 | [![Build Status](https://travis-ci.org/JedWatson/classnames.svg?branch=master)](https://travis-ci.org/JedWatson/classnames)
|
6 | [![Supported by Thinkmill](https://thinkmill.github.io/badge/heart.svg)](http://thinkmill.com.au/?utm_source=github&utm_medium=badge&utm_campaign=classnames)
|
7 |
|
8 | A simple JavaScript utility for conditionally joining classNames together.
|
9 |
|
10 | Install with [npm](https://www.npmjs.com/), [Bower](https://bower.io/), or [Yarn](https://yarnpkg.com/):
|
11 |
|
12 | npm:
|
13 | ```sh
|
14 | npm install classnames --save
|
15 | ```
|
16 |
|
17 | Bower:
|
18 | ```sh
|
19 | bower install classnames --save
|
20 | ```
|
21 |
|
22 | Yarn (note that `yarn add` automatically saves the package to the `dependencies` in `package.json`):
|
23 | ```sh
|
24 | yarn add classnames
|
25 | ```
|
26 |
|
27 | Use with [Node.js](https://nodejs.org/en/), [Browserify](http://browserify.org/), or [webpack](https://webpack.github.io/):
|
28 |
|
29 | ```js
|
30 | var classNames = require('classnames');
|
31 | classNames('foo', 'bar'); // => 'foo bar'
|
32 | ```
|
33 |
|
34 | Alternatively, you can simply include `index.js` on your page with a standalone `<script>` tag and it will export a global `classNames` method, or define the module if you are using RequireJS.
|
35 |
|
36 | ### Project philosophy
|
37 |
|
38 | We take the stability and performance of this package seriously, because it is run millions of times a day in browsers all around the world. Updates are thoroughly reviewed for performance impacts before being released, and we have a comprehensive test suite.
|
39 |
|
40 | Classnames follows the [SemVer](http://semver.org/) standard for versioning.
|
41 |
|
42 | There is also a [Changelog](https://github.com/JedWatson/classnames/blob/master/HISTORY.md).
|
43 |
|
44 | ## Usage
|
45 |
|
46 | The `classNames` function takes any number of arguments which can be a string or object.
|
47 | The argument `'foo'` is short for `{ foo: true }`. If the value associated with a given key is falsy, that key won't be included in the output.
|
48 |
|
49 | ```js
|
50 | classNames('foo', 'bar'); // => 'foo bar'
|
51 | classNames('foo', { bar: true }); // => 'foo bar'
|
52 | classNames({ 'foo-bar': true }); // => 'foo-bar'
|
53 | classNames({ 'foo-bar': false }); // => ''
|
54 | classNames({ foo: true }, { bar: true }); // => 'foo bar'
|
55 | classNames({ foo: true, bar: true }); // => 'foo bar'
|
56 |
|
57 | // lots of arguments of various types
|
58 | classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
|
59 |
|
60 | // other falsy values are just ignored
|
61 | classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'
|
62 | ```
|
63 |
|
64 | Arrays will be recursively flattened as per the rules above:
|
65 |
|
66 | ```js
|
67 | var arr = ['b', { c: true, d: false }];
|
68 | classNames('a', arr); // => 'a b c'
|
69 | ```
|
70 |
|
71 | ### Dynamic class names with ES2015
|
72 |
|
73 | If you're in an environment that supports [computed keys](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) (available in ES2015 and Babel) you can use dynamic class names:
|
74 |
|
75 | ```js
|
76 | let buttonType = 'primary';
|
77 | classNames({ [`btn-${buttonType}`]: true });
|
78 | ```
|
79 |
|
80 | ### Usage with React.js
|
81 |
|
82 | This package is the official replacement for `classSet`, which was originally shipped in the React.js Addons bundle.
|
83 |
|
84 | One of its primary use cases is to make dynamic and conditional `className` props simpler to work with (especially more so than conditional string manipulation). So where you may have the following code to generate a `className` prop for a `<button>` in React:
|
85 |
|
86 | ```js
|
87 | var Button = React.createClass({
|
88 | // ...
|
89 | render () {
|
90 | var btnClass = 'btn';
|
91 | if (this.state.isPressed) btnClass += ' btn-pressed';
|
92 | else if (this.state.isHovered) btnClass += ' btn-over';
|
93 | return <button className={btnClass}>{this.props.label}</button>;
|
94 | }
|
95 | });
|
96 | ```
|
97 |
|
98 | You can express the conditional classes more simply as an object:
|
99 |
|
100 | ```js
|
101 | var classNames = require('classnames');
|
102 |
|
103 | var Button = React.createClass({
|
104 | // ...
|
105 | render () {
|
106 | var btnClass = classNames({
|
107 | btn: true,
|
108 | 'btn-pressed': this.state.isPressed,
|
109 | 'btn-over': !this.state.isPressed && this.state.isHovered
|
110 | });
|
111 | return <button className={btnClass}>{this.props.label}</button>;
|
112 | }
|
113 | });
|
114 | ```
|
115 |
|
116 | Because you can mix together object, array and string arguments, supporting optional `className` props is also simpler as only truthy arguments get included in the result:
|
117 |
|
118 | ```js
|
119 | var btnClass = classNames('btn', this.props.className, {
|
120 | 'btn-pressed': this.state.isPressed,
|
121 | 'btn-over': !this.state.isPressed && this.state.isHovered
|
122 | });
|
123 | ```
|
124 |
|
125 |
|
126 | ### Alternate `dedupe` version
|
127 |
|
128 | There is an alternate version of `classNames` available which correctly dedupes classes and ensures that falsy classes specified in later arguments are excluded from the result set.
|
129 |
|
130 | This version is slower (about 5x) so it is offered as an opt-in.
|
131 |
|
132 | To use the dedupe version with Node.js, Browserify, or webpack:
|
133 |
|
134 | ```js
|
135 | var classNames = require('classnames/dedupe');
|
136 |
|
137 | classNames('foo', 'foo', 'bar'); // => 'foo bar'
|
138 | classNames('foo', { foo: false, bar: true }); // => 'bar'
|
139 | ```
|
140 |
|
141 | For standalone (global / AMD) use, include `dedupe.js` in a `<script>` tag on your page.
|
142 |
|
143 |
|
144 | ### Alternate `bind` version (for [css-modules](https://github.com/css-modules/css-modules))
|
145 |
|
146 | If you are using [css-modules](https://github.com/css-modules/css-modules), or a similar approach to abstract class "names" and the real `className` values that are actually output to the DOM, you may want to use the `bind` variant.
|
147 |
|
148 | _Note that in ES2015 environments, it may be better to use the "dynamic class names" approach documented above._
|
149 |
|
150 | ```js
|
151 | var classNames = require('classnames/bind');
|
152 |
|
153 | var styles = {
|
154 | foo: 'abc',
|
155 | bar: 'def',
|
156 | baz: 'xyz'
|
157 | };
|
158 |
|
159 | var cx = classNames.bind(styles);
|
160 |
|
161 | var className = cx('foo', ['bar'], { baz: true }); // => "abc def xyz"
|
162 | ```
|
163 |
|
164 | Real-world example:
|
165 |
|
166 | ```js
|
167 | /* components/submit-button.js */
|
168 | import { Component } from 'react';
|
169 | import classNames from 'classnames/bind';
|
170 | import styles from './submit-button.css';
|
171 |
|
172 | let cx = classNames.bind(styles);
|
173 |
|
174 | export default class SubmitButton extends Component {
|
175 | render () {
|
176 | let text = this.props.store.submissionInProgress ? 'Processing...' : 'Submit';
|
177 | let className = cx({
|
178 | base: true,
|
179 | inProgress: this.props.store.submissionInProgress,
|
180 | error: this.props.store.errorOccurred,
|
181 | disabled: this.props.form.valid,
|
182 | });
|
183 | return <button className={className}>{text}</button>;
|
184 | }
|
185 | };
|
186 |
|
187 | ```
|
188 |
|
189 |
|
190 | ## Polyfills needed to support older browsers
|
191 |
|
192 | #### `classNames >=2.0.0`
|
193 |
|
194 | `Array.isArray`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill.
|
195 |
|
196 | `Object.keys`: see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) for details about unsupported older browsers (e.g. <= IE8) and a simple polyfill. This is only used in `dedupe.js`.
|
197 |
|
198 | ## License
|
199 |
|
200 | [MIT](LICENSE). Copyright (c) 2017 Jed Watson.
|
201 |
|
\ | No newline at end of file |