├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package.json ├── src └── index.js ├── tests ├── example.html └── stylenames.test.js └── yarn.lock /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: yarn install, lint, and test 21 | run: | 22 | yarn 23 | yarn lint 24 | yarn test 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | ### Node template 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Dependency directories 8 | node_modules 9 | 10 | .idea 11 | .babelrc 12 | .eslintrc 13 | .gitignore 14 | webpack.production.babel.js 15 | 16 | tests 17 | .dependabot 18 | .github 19 | src 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Kevin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![build](https://github.com/shatstack/stylenames/workflows/build/badge.svg) 2 | ![npm version](https://img.shields.io/npm/v/@shat/stylenames) 3 | ![npm bundle size (scoped)](https://img.shields.io/bundlephobia/min/@shat/stylenames) 4 | 5 | # @shat/stylenames 6 | 7 | A simple JavaScript utility for conditionally joining inline styles together. 8 | 9 | > This is a fork of the unmaintained [stylenames](https://github.com/kmathmann/stylenames) package 10 | 11 | ## What does stylenames do? 12 | 13 | In one short sentence: "stylenames converts an object to a css style string." 14 | 15 | Think [classNames](https://www.npmjs.com/package/classnames) but for inline styles. 16 | 17 | ## Install 18 | 19 | **From CDN:** Add the following script to the end of your `` section. 20 | ```html 21 | 22 | ``` 23 | 24 | That's it. It will initialize itself as `styleNames()`. 25 | 26 | **From NPM:** Install the package from NPM. 27 | ```js 28 | npm install @shat/stylenames 29 | ``` 30 | 31 | Include it in your script. 32 | 33 | ```javascript 34 | import styleNames from '@shat/stylenames'; 35 | ``` 36 | 37 | 38 | ## Quick Start 39 | 40 | Standalone: 41 | 42 | ```js 43 | styleNames({ 44 | backgroundColor: 'red', 45 | width: '120px', 46 | 47 | 'height':{ 48 | // If the condition is false the style does not get used. 49 | '200px': false, 50 | // Only the first value with true condition is used. 51 | '300px': true, 52 | '400px': true 53 | }, 54 | }); 55 | ``` 56 | 57 | With [Alpine.js](https://github.com/alpinejs/alpine): 58 | 59 | ```html 60 | 61 | 62 |
63 | 66 |
67 | 68 | 69 | ``` 70 | 71 | ## Examples 72 | 73 | ### Without conditions 74 | 75 | ```javascript 76 | let styles1 = styleNames({ 77 | height: '120px', 78 | width: '100px' 79 | }); 80 | console.log(styles1); //--> 'height:120px;width:100px;' 81 | ``` 82 | 83 | ### With one condition using a boolean toggle 84 | 85 | ```javascript 86 | let styles1 = styleNames({ 87 | height: '120px', 88 | width: { 89 | '200px': false 90 | } 91 | }); 92 | console.log(styles1); //--> 'height:120px ' 93 | 94 | let styles2 = styleNames({ 95 | height: '120px', 96 | width: { 97 | '200px': true 98 | } 99 | }); 100 | console.log(styles2); //--> 'height:120px;width:200px;' 101 | ``` 102 | 103 | ### With multiple rules with 1 boolean toggle 104 | 105 | ```js 106 | const styles = styleNames({ 107 | 'height:120px;width:100px;': true 108 | }); 109 | console.log(styles); //--> 'height:120px;width:100px;' 110 | ``` 111 | 112 | ### With camelcased rules 113 | 114 | ```js 115 | const styles = styleNames({backgroundColor: 'red'}); 116 | console.log(styles); //--> 'background-color:red;' 117 | ``` 118 | 119 | ### With array input 120 | 121 | ```js 122 | const styles = styleNames(['height:120px', 'width:100px']); 123 | console.log(styles); //--> 'height:120px;width:100px;' 124 | ``` 125 | 126 | ### With more than one condition using a function toggle 127 | 128 | ```javascript 129 | let itemCount = 0; 130 | 131 | let styleNamesConfig = { 132 | display: { 133 | 'none': () => itemCount === 0 134 | }, 135 | height: '120px', 136 | width: { 137 | '100px': () => itemCount <= 1, 138 | '200px': () => itemCount <= 2, 139 | '400px': () => itemCount <= 4, 140 | '100%': () => itemCount > 4 141 | } 142 | }; 143 | 144 | console.log(styleNames(styleNamesConfig)); //--> 'display:none;height:120px;width:100px;' 145 | 146 | itemCount++; //1 147 | console.log(styleNames(styleNamesConfig)); //--> 'height:120px;width:100px;' 148 | 149 | itemCount++; //2 150 | console.log(styleNames(styleNamesConfig)); //--> 'height:120px;width:200px;' 151 | 152 | itemCount++; //3 153 | console.log(styleNames(styleNamesConfig)); //--> 'height:120px;width:400px;' 154 | 155 | itemCount += 12; //15 156 | console.log(styleNames(styleNamesConfig)); //--> 'height:120px;width:100%;' 157 | ``` 158 | 159 | ## Contributing 160 | 161 | ### Requirements 162 | 163 | - Node 10 164 | - Yarn 1.x or npm 165 | 166 | ### Setup 167 | 168 | 1. Clone the repository 169 | 2. Run `yarn` or `npm install` installs all required dependencies. 170 | 171 | ### npm scripts 172 | 173 | > Equivalent `npm run 2 | 3 |
4 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/stylenames.test.js: -------------------------------------------------------------------------------- 1 | const styleNames = require('../lib/index.umd'); 2 | 3 | test('quickstart example', () => { 4 | expect( 5 | styleNames({ 6 | backgroundColor: 'red', 7 | width: '120px', 8 | 9 | height: { 10 | // If the condition is false the style does not becomes used. 11 | '200px': false, 12 | // Only the first value with true condition becomes used. 13 | '300px': true, 14 | '400px': true 15 | } 16 | }) 17 | ).toBe('background-color:red;width:120px;height:300px;'); 18 | }); 19 | 20 | test('returns empty string when called with nothing', () => { 21 | expect(styleNames()).toBe(''); 22 | }); 23 | 24 | test('returns empty string when not called with an object', () => { 25 | expect(styleNames(123)).toBe(''); 26 | }); 27 | 28 | test('works with JS stylenames', () => { 29 | expect(styleNames({backgroundColor: 'red'})).toBe('background-color:red;'); 30 | }); 31 | 32 | test('works with string values', () => { 33 | expect( 34 | styleNames({ 35 | height: '120px', 36 | width: '100px' 37 | }) 38 | ).toBe('height:120px;width:100px;'); 39 | }); 40 | 41 | test('works with multiple rules under 1 toggle', () => { 42 | expect( 43 | styleNames({ 44 | 'height:120px;width:100px;': true 45 | }) 46 | ).toBe('height:120px;width:100px;'); 47 | }); 48 | 49 | test('works with arrays', () => { 50 | expect(styleNames(['height:120px', 'width:100px'])).toBe( 51 | 'height:120px;width:100px;' 52 | ); 53 | }); 54 | 55 | test('works with a single true condition', () => { 56 | expect( 57 | styleNames({ 58 | height: '120px', 59 | width: { 60 | '200px': false 61 | } 62 | }) 63 | ).toBe('height:120px;'); 64 | }); 65 | 66 | test('works with a single false condition', () => { 67 | expect( 68 | styleNames({ 69 | height: '120px', 70 | width: { 71 | '200px': true 72 | } 73 | }) 74 | ).toBe('height:120px;width:200px;'); 75 | }); 76 | 77 | test('works with more than one condition & with function conditionals', () => { 78 | let itemCount = 0; 79 | 80 | const styleNamesConfig = { 81 | display: { 82 | none: () => itemCount === 0 83 | }, 84 | height: '120px', 85 | width: { 86 | '100px': () => itemCount <= 1, 87 | '200px': () => itemCount <= 2, 88 | '400px': () => itemCount <= 4, 89 | '100%': () => itemCount > 4 90 | } 91 | }; 92 | 93 | expect(styleNames(styleNamesConfig)).toBe( 94 | 'display:none;height:120px;width:100px;' 95 | ); 96 | itemCount++; // 1 97 | expect(styleNames(styleNamesConfig)).toBe('height:120px;width:100px;'); 98 | itemCount++; // 2 99 | expect(styleNames(styleNamesConfig)).toBe('height:120px;width:200px;'); 100 | itemCount++; // 3 101 | expect(styleNames(styleNamesConfig)).toBe('height:120px;width:400px;'); 102 | itemCount += 12; // 15 103 | expect(styleNames(styleNamesConfig)).toBe('height:120px;width:100%;'); 104 | }); 105 | 106 | test('respects whitespace in value', () => { 107 | expect(styleNames({height: `calc( 100% - 90px )`})).toBe( 108 | 'height:calc( 100% - 90px );' 109 | ); 110 | }); 111 | --------------------------------------------------------------------------------