Skip to content

Commit ca8f9b2

Browse files
authored
Merge pull request #6 from cssinjs/feature/injectStyled
Implement injectStyled
2 parents 1819075 + 775dcbd commit ca8f9b2

16 files changed

Lines changed: 553 additions & 133 deletions

.babelrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
presets: [
3+
'es2015',
34
'react',
45
],
56
plugins: [
6-
'transform-es2015-modules-commonjs',
7+
'transform-flow-strip-types',
78
'transform-class-properties',
89
['transform-object-rest-spread', { useBuiltIns: true }],
910
]

.eslintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ parser: babel-eslint
33

44
env:
55
jest: true
6+
7+
globals:
8+
ReactClass: true

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ const PrimaryButton = styled(Button, {
3131
Using base Style Sheet we can share classes between styled primitives.
3232

3333
```js
34-
import { Styled } from 'styled-jss'
35-
import injectSheet from 'react-jss'
34+
import { Styled, injectStyled } from 'styled-jss'
3635

3736
// Base styles, like a regular jss object.
3837
const styled = Styled({
3938
root: {
40-
margin: 10
39+
margin: 10,
40+
'& $baseButton': {
41+
fontSize: 16
42+
}
4143
},
4244
baseButton: {
43-
padding: 10
45+
padding: 10,
46+
'& + &': {
47+
marginLeft; 10
48+
}
4449
}
4550
})
4651

@@ -58,20 +63,24 @@ const PrimaryButton = styled(NormalButton, {
5863
// One can use classes AND styled primitives.
5964
const MyComponent = ({classes}) => (
6065
<div className={classes.root}>
66+
<NormalButton>normal button</NormalButton>
6167
<PrimaryButton>primary button</PrimaryButton>
6268
</div>
6369
)
6470

65-
const MyStyledComponent = injectSheet(styled.styles)(MyComponent)
71+
const MyStyledComponent = injectStyled(styled)(MyComponent)
6672
```
6773

6874
### With custom JSS setup:
6975

76+
`styled-jss` use [jss-preset-default](https://github.com/cssinjs/jss-preset-default) by default.
77+
But you can require `createStyled` and provide your custom jss instance.
78+
7079
```js
7180
import { create as createJss } from 'jss'
7281
import vendorPrefixer from 'jss-vendor-prefixer'
7382

74-
import { createStyled } from 'styled-jss'
83+
import createStyled from 'styled-jss/createStyled'
7584

7685
const jss = createJss()
7786
jss.use(vendorPrefixer())

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@
5353
"babel-core": "^6.23.1",
5454
"babel-eslint": "^7.2.2",
5555
"babel-plugin-transform-class-properties": "^6.23.0",
56-
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
56+
"babel-plugin-transform-flow-strip-types": "^6.22.0",
5757
"babel-plugin-transform-object-rest-spread": "^6.23.0",
58+
"babel-preset-es2015": "^6.24.1",
5859
"babel-preset-react": "^6.23.0",
5960
"eslint": "^3.13.0",
6061
"eslint-config-airbnb": "^14.1.0",
@@ -78,5 +79,8 @@
7879
"git add"
7980
]
8081
},
81-
"pre-commit": "lint:staged"
82+
"pre-commit": [
83+
"lint:staged",
84+
"test"
85+
]
8286
}

src/createStyled.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import styled from './styled'
2+
3+
import type {
4+
BaseStylesType,
5+
ComponentStyleType,
6+
StyledType,
7+
StyledElementAttrsType,
8+
StyledElementType,
9+
TagNameOrStyledElementType
10+
} from './types'
11+
12+
const createStyled = (jss: Function) => (
13+
baseStyles: BaseStylesType = {}
14+
): StyledType => {
15+
let staticSheet
16+
let dynamicSheet
17+
18+
const mountSheets = () => {
19+
if (!staticSheet) {
20+
staticSheet = jss.createStyleSheet(baseStyles, {
21+
meta: 'StaticBaseSheet',
22+
}).attach()
23+
24+
dynamicSheet = jss.createStyleSheet({}, {
25+
link: true,
26+
meta: 'DynamicComponentSheet',
27+
}).attach()
28+
}
29+
30+
return {staticSheet, dynamicSheet}
31+
}
32+
33+
return Object.assign((
34+
tagNameOrStyledElement: TagNameOrStyledElementType,
35+
ownStyle: ComponentStyleType
36+
): StyledElementType => {
37+
const {tagName, style}: StyledElementAttrsType = typeof tagNameOrStyledElement === 'string'
38+
? {tagName: tagNameOrStyledElement, style: {}}
39+
: tagNameOrStyledElement
40+
41+
const elementStyle = {...style, ...ownStyle}
42+
43+
return styled({tagName, baseStyles, elementStyle, mountSheets})
44+
}, {mountSheets, styles: baseStyles})
45+
}
46+
47+
export default createStyled

src/index.js

Lines changed: 6 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,11 @@
1-
import {PureComponent, createElement} from 'react'
2-
import {create as createJss, getDynamicStyles} from 'jss'
1+
import {create as createJss} from 'jss'
32
import preset from 'jss-preset-default'
4-
import filterProps from './utils/filter-props'
53

6-
const jssDefault = createJss(preset())
4+
import createStyled from './createStyled'
75

8-
type StyledElementAttrsType = { tag: string, styles: Object }
9-
type StyledElementType = Function & StyledElementAttrsType
10-
type tagOrStyledElementTypeype = string | StyledElementType
11-
type StyledElementPropsType = {
12-
classes: Object,
13-
children: ?any,
14-
className: ?string,
15-
}
6+
const jss: Function = createJss(preset())
167

17-
const createStyled = (jss?: Function = jssDefault) => (baseStyles: Object = {}) => {
18-
let sheet
19-
let dynamicSheet
20-
let counter = 0
8+
export const Styled = createStyled(jss)
9+
export default Styled()
2110

22-
const styled = (
23-
tagOrStyledElement: tagOrStyledElementTypeype,
24-
ownStyles: Object
25-
): StyledElementType => {
26-
const {tag, styles}: StyledElementAttrsType = typeof tagOrStyledElement === 'string'
27-
? {tag: tagOrStyledElement, styles: {}}
28-
: tagOrStyledElement
29-
30-
const elementStyles = {...styles, ...ownStyles}
31-
const dynamicStyles = getDynamicStyles(elementStyles)
32-
const staticTag = `${tag}-${++counter}`
33-
34-
return class StyledElement extends PureComponent {
35-
static tag = tag
36-
37-
static styles = elementStyles
38-
39-
props: StyledElementPropsType
40-
41-
tagScoped = ''
42-
43-
constructor(props) {
44-
super(props)
45-
this.tagScoped = `${tag}-${++counter}`
46-
}
47-
48-
componentWillMount() {
49-
if (!sheet) {
50-
sheet = jss.createStyleSheet(baseStyles, {
51-
link: true,
52-
meta: 'StaticBaseSheet',
53-
}).attach()
54-
55-
dynamicSheet = jss.createStyleSheet({}, {
56-
link: true,
57-
meta: 'DynamicComponentSheet',
58-
}).attach()
59-
}
60-
61-
if (!sheet.getRule(staticTag)) {
62-
sheet.addRule(staticTag, elementStyles)
63-
}
64-
65-
if (dynamicStyles && !dynamicSheet.getRule(this.tagScoped)) {
66-
dynamicSheet
67-
.detach()
68-
.addRule(this.tagScoped, dynamicStyles)
69-
dynamicSheet
70-
.update(this.tagScoped, this.props)
71-
.attach()
72-
.link()
73-
}
74-
}
75-
76-
componentWillReceiveProps(nextProps: StyledElementPropsType) {
77-
if (dynamicStyles) {
78-
dynamicSheet.update(this.tagScoped, nextProps)
79-
}
80-
}
81-
82-
componentWillUnmount() {
83-
dynamicSheet.deleteRule(this.tagScoped)
84-
}
85-
86-
render() {
87-
if (!sheet) return null
88-
89-
const {children, className, ...attrs} = this.props
90-
91-
const props = filterProps(attrs)
92-
const tagClass = [
93-
sheet.classes[staticTag],
94-
dynamicSheet.classes[this.tagScoped],
95-
className,
96-
]
97-
.filter(Boolean)
98-
.join(' ')
99-
100-
return createElement(tag, {...props, className: tagClass}, children)
101-
}
102-
}
103-
}
104-
105-
return Object.assign(styled, {styles: baseStyles})
106-
}
107-
108-
const defaultStyledCreator = createStyled()
109-
const defaultStyled = defaultStyledCreator()
110-
111-
export {
112-
createStyled,
113-
defaultStyledCreator as Styled,
114-
}
115-
116-
export default defaultStyled
11+
export {default as injectStyled} from './injectStyled'

src/injectStyled.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {createElement} from 'react'
2+
3+
import composeClasses from './utils/composeClasses'
4+
import type {StyledType} from './types'
5+
6+
const injectStyled = (styled: StyledType) => (InnerComponent: ReactClass<any>) => {
7+
const {staticSheet, dynamicSheet} = styled.mountSheets()
8+
9+
const classNames = Object.keys({...staticSheet.classes, ...dynamicSheet.classes})
10+
11+
const classes = [...classNames]
12+
.reduce((acc, name) => ({
13+
...acc,
14+
[name]: composeClasses(staticSheet.classes[name], dynamicSheet.classes[name]),
15+
}), {})
16+
17+
return (props: Object) => createElement(InnerComponent, {classes, ...props})
18+
}
19+
20+
export default injectStyled

src/styled.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import {PureComponent, createElement} from 'react'
2+
import {getDynamicStyles} from 'jss'
3+
4+
import filterProps from './utils/filterProps'
5+
import composeClasses from './utils/composeClasses'
6+
import generateTagName from './utils/generateTagName'
7+
8+
import type {
9+
JssStaticSheet,
10+
JssDynamicSheet,
11+
ComponentStyleType,
12+
StyledElementPropsType
13+
} from './types'
14+
15+
type StyledArgs = {
16+
tagName: string,
17+
elementStyle: ComponentStyleType,
18+
mountSheets: Function
19+
}
20+
21+
const styled = ({tagName, elementStyle, mountSheets}: StyledArgs) => {
22+
const dynamicStyle = getDynamicStyles(elementStyle)
23+
const staticTagName = generateTagName(tagName)
24+
25+
return class StyledElement extends PureComponent {
26+
static tagName: string = tagName
27+
static style: ComponentStyleType = elementStyle
28+
29+
props: StyledElementPropsType
30+
31+
dynamicTagName = ''
32+
staticSheet: JssStaticSheet
33+
dynamicSheet: JssDynamicSheet
34+
35+
constructor(props: StyledElementPropsType) {
36+
super(props)
37+
if (!this.dynamicTagName) {
38+
this.dynamicTagName = generateTagName(tagName)
39+
}
40+
}
41+
42+
componentWillMount() {
43+
Object.assign(this, mountSheets())
44+
45+
if (!this.staticSheet.getRule(staticTagName)) {
46+
this.staticSheet.addRule(staticTagName, elementStyle)
47+
}
48+
49+
if (dynamicStyle && !this.dynamicSheet.getRule(this.dynamicTagName)) {
50+
this.dynamicSheet
51+
.detach()
52+
.addRule(this.dynamicTagName, dynamicStyle)
53+
this.dynamicSheet
54+
.update(this.dynamicTagName, this.props)
55+
.attach()
56+
.link()
57+
}
58+
}
59+
60+
componentWillReceiveProps(nextProps: StyledElementPropsType) {
61+
if (dynamicStyle) {
62+
this.dynamicSheet.update(this.dynamicTagName, nextProps)
63+
}
64+
}
65+
66+
render() {
67+
if (!this.staticSheet) return null
68+
69+
const {children, className, ...attrs} = this.props
70+
71+
const props = filterProps(attrs)
72+
const tagClass = composeClasses(
73+
this.staticSheet.classes[staticTagName],
74+
this.dynamicSheet.classes[this.dynamicTagName],
75+
className
76+
)
77+
78+
return createElement(tagName, {...props, className: tagClass}, children)
79+
}
80+
}
81+
}
82+
83+
export default styled

src/tests/App.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
2+
import type {StyledType} from '../types'
23

3-
4-
export default (styled: Function) => {
4+
export default (styled: StyledType) => {
55
const App = styled('div', {
66
margin: 50,
77
})

0 commit comments

Comments
 (0)