Skip to content

Commit 0af646d

Browse files
authored
New css interface (#1129)
* initial verion of css innterface * use string based cache, which can be optimized later with a babel plugin * merge labels * support arrays * check cache early * implement composition * add flow types [ci skip] * fix tests, export types * Update packages/css-jss/src/index.js use createCss directly Co-Authored-By: Henri <henribeck.dev@gmail.com> * cr changes * dedupe labels * changelog * revert browsers.json changes
1 parent 59ea4db commit 0af646d

14 files changed

Lines changed: 421 additions & 16 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
- [react-jss] New experimental styled API (undocumented intentionally)([#1094](https://github.com/cssinjs/jss/pull/1094))
6+
- [css-jss] New experimental css() API (undocumented intentionally)([#1129](https://github.com/cssinjs/jss/pull/1129))
67

78
### Bug fixes
89

flow-typed/mocha.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
declare function describe(string, Function): void
22
declare function it(string, Function): void
3+
declare function beforeEach(Function): void
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"dist/css-jss.js": {
3+
"bundled": 56911,
4+
"minified": 20186,
5+
"gzipped": 6756
6+
},
7+
"dist/css-jss.min.js": {
8+
"bundled": 56157,
9+
"minified": 19727,
10+
"gzipped": 6538
11+
},
12+
"dist/css-jss.cjs.js": {
13+
"bundled": 2505,
14+
"minified": 1002,
15+
"gzipped": 569
16+
},
17+
"dist/css-jss.esm.js": {
18+
"bundled": 2287,
19+
"minified": 834,
20+
"gzipped": 491,
21+
"treeshaked": {
22+
"rollup": {
23+
"code": 98,
24+
"import_statements": 63
25+
},
26+
"webpack": {
27+
"code": 1151
28+
}
29+
}
30+
}
31+
}

packages/css-jss/LICENSE

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
The MIT License (MIT)
2+
Copyright (c) 2014-present Oleg Isonen (Slobodskoi) & contributors
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5+
6+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/css-jss/package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "css-jss",
3+
"description": "Implements css() interface on top of JSS",
4+
"version": "10.0.0-alpha.17",
5+
"license": "MIT",
6+
"homepage": "https://cssinjs.org/",
7+
"main": "dist/css-jss.cjs.js",
8+
"module": "dist/css-jss.esm.js",
9+
"unpkg": "dist/css-jss.bundle.js",
10+
"typings": "./src/index.d.ts",
11+
"author": "JSS Team",
12+
"repository": {
13+
"type": "git",
14+
"url": "https://github.com/cssinjs/jss"
15+
},
16+
"bugs": {
17+
"url": "https://github.com/cssinjs/jss/issues/new"
18+
},
19+
"files": [
20+
"dist",
21+
"src",
22+
"LICENSE"
23+
],
24+
"keywords": [
25+
"jss",
26+
"style",
27+
"sheet",
28+
"stylesheet",
29+
"css",
30+
"components",
31+
"composable",
32+
"css in js",
33+
"css-in-js"
34+
],
35+
"scripts": {
36+
"build": "node ../../scripts/build.js",
37+
"check-snapshot": "node ../../scripts/match-snapshot.js"
38+
},
39+
"dependencies": {
40+
"@babel/runtime": "^7.3.1",
41+
"jss": "10.0.0-alpha.17",
42+
"jss-preset-default": "10.0.0-alpha.17"
43+
}
44+
}

packages/css-jss/readme.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# css-jss
2+
3+
[![Version](https://img.shields.io/npm/v/css-jss.svg?style=flat)](https://npmjs.org/package/css-jss)
4+
[![License](https://img.shields.io/npm/l/css-jss.svg?style=flat)](https://github.com/cssinjs/jss/blob/master/LICENSE)
5+
[![Downlodas](https://img.shields.io/npm/dm/css-jss.svg?style=flat)](https://npmjs.org/package/css-jss)
6+
[![Size](https://img.shields.io/bundlephobia/minzip/css-jss.svg?style=flat)](https://npmjs.org/package/css-jss)
7+
[![Dependencies](https://img.shields.io/david/cssinjs/jss.svg?path=packages%2Fcss-jss&style=flat)](https://npmjs.org/package/css-jss)
8+
[![Gitter](https://badges.gitter.im/JoinChat.svg)](https://gitter.im/cssinjs/lobby)
9+
10+
> Implements css() interface on top of JSS
11+
12+
See our website [css-jss](https://cssinjs.org/css-jss?v=v10.0.0-alpha.17) for more information.
13+
14+
## Install
15+
16+
Using npm:
17+
18+
```sh
19+
npm install css-jss
20+
```
21+
22+
or using yarn:
23+
24+
```sh
25+
yarn add css-jss
26+
```

packages/css-jss/src/createCss.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// @flow
2+
import type {StyleSheet} from 'jss'
3+
// eslint-disable-next-line no-unused-vars
4+
import type {Css, StyleArg} from './types'
5+
6+
const createCss = (sheet: StyleSheet): Css => {
7+
const cache = new Map()
8+
let ruleIndex = 0
9+
10+
return function css(/* :: ..._: StyleArg[] */): string {
11+
// eslint-disable-next-line prefer-rest-params
12+
const args = arguments
13+
14+
// We can avoid the need for stringification with a babel plugin,
15+
// which could generate a hash at build time and add it to the object.
16+
const argsStr = JSON.stringify(args)
17+
const cached = cache.get(argsStr)
18+
if (cached) return cached.className
19+
20+
const flatArgs = []
21+
22+
// Flatten arguments which can be
23+
// - style objects
24+
// - array of style objects
25+
// - arrays of style objects
26+
for (const argIndex in args) {
27+
const arg = args[argIndex]
28+
if (!Array.isArray(arg)) {
29+
flatArgs.push(arg)
30+
continue
31+
}
32+
for (let innerArgIndex = 0; innerArgIndex < arg.length; innerArgIndex++) {
33+
flatArgs.push(arg[innerArgIndex])
34+
}
35+
}
36+
37+
const mergedStyle = {}
38+
const labels = []
39+
40+
for (let i = 0; i < flatArgs.length; i++) {
41+
let style = flatArgs[i]
42+
if (!style) continue
43+
// It can be a class name that css() has previously generated.
44+
if (typeof style === 'string') {
45+
// eslint-disable-next-line no-shadow
46+
const cached = cache.get(style)
47+
if (cached) {
48+
// eslint-disable-next-line prefer-spread
49+
if (cached.labels.length) labels.push.apply(labels, cached.labels)
50+
style = cached.style
51+
}
52+
}
53+
if (style.label && labels.indexOf(style.label) === -1) labels.push(style.label)
54+
Object.assign(mergedStyle, style)
55+
}
56+
delete mergedStyle.label
57+
const label = labels.length === 0 ? 'css' : labels.join('-')
58+
const key = `${label}-${ruleIndex++}`
59+
sheet.addRule(key, mergedStyle)
60+
const className = sheet.classes[key]
61+
const cacheValue = {style: mergedStyle, labels, className}
62+
cache.set(argsStr, cacheValue)
63+
cache.set(className, cacheValue)
64+
return className
65+
}
66+
}
67+
68+
export default createCss
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// @flow
2+
import expect from 'expect.js'
3+
import {stripIndent} from 'common-tags'
4+
import {create as createJss} from 'jss'
5+
import {createGenerateId} from '../../../tests/utils'
6+
import {create as createCss} from './index'
7+
8+
describe('css-jss', () => {
9+
let sheet
10+
let css
11+
12+
beforeEach(() => {
13+
sheet = createJss({createGenerateId}).createStyleSheet()
14+
css = createCss(sheet)
15+
})
16+
17+
it('should accept a single style object argument', () => {
18+
const className = css({color: 'red'})
19+
expect(className).to.be('css-0-id')
20+
expect(sheet.toString()).to.be(stripIndent`
21+
.css-0-id {
22+
color: red;
23+
}
24+
`)
25+
})
26+
27+
it('should accept multiple style object arguments', () => {
28+
const className = css({color: 'red'}, {background: 'green'})
29+
expect(className).to.be('css-0-id')
30+
expect(sheet.toString()).to.be(stripIndent`
31+
.css-0-id {
32+
color: red;
33+
background: green;
34+
}
35+
`)
36+
})
37+
38+
it('should accept multiple style object array', () => {
39+
const className = css([{color: 'red'}, {background: 'green'}])
40+
expect(className).to.be('css-0-id')
41+
expect(sheet.toString()).to.be(stripIndent`
42+
.css-0-id {
43+
color: red;
44+
background: green;
45+
}
46+
`)
47+
})
48+
49+
it('should accept multiple style object array and style objects', () => {
50+
const className = css([{color: 'red'}, {background: 'green'}], {float: 'left'})
51+
expect(className).to.be('css-0-id')
52+
expect(sheet.toString()).to.be(stripIndent`
53+
.css-0-id {
54+
color: red;
55+
background: green;
56+
float: left;
57+
}
58+
`)
59+
})
60+
61+
it('should accept multiple style object arrays', () => {
62+
const className = css([{color: 'red'}, {background: 'green'}], [{float: 'left'}])
63+
expect(className).to.be('css-0-id')
64+
expect(sheet.toString()).to.be(stripIndent`
65+
.css-0-id {
66+
color: red;
67+
background: green;
68+
float: left;
69+
}
70+
`)
71+
})
72+
73+
it('should compose css() calls', () => {
74+
const className = css(css({color: 'red'}), css({background: 'green'}))
75+
expect(className).to.be('css-2-id')
76+
expect(sheet.toString()).to.be(stripIndent`
77+
.css-0-id {
78+
color: red;
79+
}
80+
.css-1-id {
81+
background: green;
82+
}
83+
.css-2-id {
84+
color: red;
85+
background: green;
86+
}
87+
`)
88+
})
89+
90+
it('should compose css() calls inside of array arg', () => {
91+
const className = css([css({color: 'red'}), css({background: 'green'})])
92+
expect(className).to.be('css-2-id')
93+
expect(sheet.toString()).to.be(stripIndent`
94+
.css-0-id {
95+
color: red;
96+
}
97+
.css-1-id {
98+
background: green;
99+
}
100+
.css-2-id {
101+
color: red;
102+
background: green;
103+
}
104+
`)
105+
})
106+
107+
it('should compose css() calls from mixed array and strings', () => {
108+
const className = css([css({color: 'red'}), css({background: 'green'})], css({float: 'left'}))
109+
expect(className).to.be('css-3-id')
110+
expect(sheet.toString()).to.be(stripIndent`
111+
.css-0-id {
112+
color: red;
113+
}
114+
.css-1-id {
115+
background: green;
116+
}
117+
.css-2-id {
118+
float: left;
119+
}
120+
.css-3-id {
121+
color: red;
122+
background: green;
123+
float: left;
124+
}
125+
`)
126+
})
127+
128+
it('should ignore empty values', () => {
129+
const className = css(null, {color: 'red'}, '', {background: 'green'}, undefined)
130+
expect(className).to.be('css-0-id')
131+
expect(sheet.toString()).to.be(stripIndent`
132+
.css-0-id {
133+
color: red;
134+
background: green;
135+
}
136+
`)
137+
})
138+
139+
it('should accept label', () => {
140+
const className = css({color: 'red', label: 'xxx'}, {background: 'green'})
141+
expect(className).to.be('xxx-0-id')
142+
expect(sheet.toString()).to.be(stripIndent`
143+
.xxx-0-id {
144+
color: red;
145+
background: green;
146+
}
147+
`)
148+
})
149+
150+
it('should merge label', () => {
151+
const className = css(
152+
{color: 'red', label: 'xxx'},
153+
{background: 'green', label: 'yyy'},
154+
{float: 'left', label: 'yyy'}
155+
)
156+
expect(className).to.be('xxx-yyy-0-id')
157+
expect(sheet.toString()).to.be(stripIndent`
158+
.xxx-yyy-0-id {
159+
color: red;
160+
background: green;
161+
float: left;
162+
}
163+
`)
164+
})
165+
166+
it('should cache a single style', () => {
167+
const style = {color: 'red'}
168+
const className1 = css(style)
169+
const className2 = css(style)
170+
expect(className1).to.be('css-0-id')
171+
expect(className2).to.be('css-0-id')
172+
expect(sheet.toString()).to.be(stripIndent`
173+
.css-0-id {
174+
color: red;
175+
}
176+
`)
177+
})
178+
179+
it('should cache multiple styles', () => {
180+
const style1 = {color: 'red'}
181+
const style2 = {background: 'green'}
182+
const className1 = css(style1, style2)
183+
const className2 = css(style1, style2)
184+
expect(className1).to.be('css-0-id')
185+
expect(className2).to.be('css-0-id')
186+
expect(sheet.toString()).to.be(stripIndent`
187+
.css-0-id {
188+
color: red;
189+
background: green;
190+
}
191+
`)
192+
})
193+
194+
it('should try get the cached rule by using a ref first before trying to stringify', () => {})
195+
})

0 commit comments

Comments
 (0)