styled-components 2.1.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -1
- package/CODE_OF_CONDUCT.md +1 -1
- package/README.md +12 -67
- package/dist/styled-components.es.js +273 -110
- package/dist/styled-components.js +325 -119
- package/dist/styled-components.min.js +2 -2
- package/lib/hoc/withTheme.js +13 -7
- package/lib/models/BrowserStyleSheet.js +11 -0
- package/lib/models/ComponentStyle.js +45 -2
- package/lib/models/InlineStyle.js +1 -1
- package/lib/models/ServerStyleSheet.js +33 -17
- package/lib/models/StyleSheet.js +9 -0
- package/lib/models/StyledComponent.js +82 -38
- package/lib/models/StyledNativeComponent.js +31 -15
- package/lib/models/ThemeProvider.js +44 -12
- package/lib/native/index.js +1 -1
- package/lib/test/utils.js +5 -2
- package/lib/utils/create-broadcast.js +34 -24
- package/lib/utils/domElements.js +1 -1
- package/lib/utils/flatten.js +4 -1
- package/lib/utils/generateAlphabeticName.js +1 -1
- package/lib/utils/nonce.js +10 -0
- package/lib/utils/once.js +17 -0
- package/package.json +10 -10
- package/src/hoc/withTheme.js +14 -7
- package/src/models/BrowserStyleSheet.js +8 -0
- package/src/models/ComponentStyle.js +42 -2
- package/src/models/InlineStyle.js +1 -1
- package/src/models/ServerStyleSheet.js +27 -12
- package/src/models/StyleSheet.js +9 -0
- package/src/models/StyledComponent.js +81 -26
- package/src/models/StyledNativeComponent.js +30 -10
- package/src/models/ThemeProvider.js +38 -9
- package/src/models/test/ThemeProvider.test.js +7 -8
- package/src/native/index.js +1 -1
- package/src/native/test/native.test.js +14 -0
- package/src/test/__snapshots__/ssr.test.js.snap +147 -0
- package/src/test/expanded-api.test.js +24 -0
- package/src/test/props.test.js +14 -3
- package/src/test/ssr.test.js +90 -123
- package/src/test/styles.test.js +52 -0
- package/src/test/utils.js +5 -2
- package/src/utils/create-broadcast.js +31 -17
- package/src/utils/domElements.js +1 -0
- package/src/utils/flatten.js +16 -6
- package/src/utils/generateAlphabeticName.js +1 -1
- package/src/utils/nonce.js +6 -0
- package/src/utils/once.js +12 -0
- package/typings/styled-components.d.ts +15 -21
- package/typings/tests/issue1068.tsx +226 -0
- package/typings/tests/main-test.tsx +1 -1
- package/typings/tests/string-tags-test.tsx +62 -0
- package/typings/tests/themed-tests/issue1068.tsx +226 -0
- package/typings/tests/themed-tests/mytheme-styled-components.tsx +1 -1
- package/typings/tests/themed-tests/with-theme-test.tsx +2 -1
- package/typings/tests/with-theme-test.tsx +17 -0
- package/lib/constructors/test/injectGlobal.test.js +0 -63
- package/lib/constructors/test/keyframes.test.js +0 -48
- package/lib/constructors/test/styled.test.js +0 -19
- package/lib/models/AbstractStyledComponent.js +0 -43
- package/lib/models/test/ThemeProvider.test.js +0 -200
- package/lib/native/test/native.test.js +0 -290
- package/lib/no-parser/test/basic.test.js +0 -46
- package/lib/no-parser/test/flatten.test.js +0 -125
- package/lib/no-parser/test/keyframes.test.js +0 -45
- package/lib/primitives/test/primitives.test.js +0 -289
- package/lib/test/attrs.test.js +0 -158
- package/lib/test/basic.test.js +0 -267
- package/lib/test/css.test.js +0 -43
- package/lib/test/expanded-api.test.js +0 -90
- package/lib/test/extending.test.js +0 -198
- package/lib/test/overriding.test.js +0 -35
- package/lib/test/props.test.js +0 -38
- package/lib/test/rehydration.test.js +0 -306
- package/lib/test/ssr.test.js +0 -187
- package/lib/test/styles.test.js +0 -146
- package/lib/test/theme.test.js +0 -497
- package/lib/test/warnTooManyClasses.test.js +0 -71
- package/lib/utils/test/extractCompsFromCSS.test.js +0 -46
- package/lib/utils/test/flatten.test.js +0 -109
- package/lib/utils/test/generateAlphabeticName.test.js +0 -14
- package/lib/utils/test/interleave.test.js +0 -22
- package/lib/utils/test/validAttr.test.js +0 -560
- package/src/models/AbstractStyledComponent.js +0 -21
- package/typings/tags.d.ts +0 -137
package/src/test/ssr.test.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment node
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import React from 'react'
|
|
2
|
-
import { renderToString } from
|
|
6
|
+
import { renderToString } from 'react-dom/server'
|
|
3
7
|
import ServerStyleSheet from '../models/ServerStyleSheet'
|
|
4
|
-
import { resetStyled
|
|
8
|
+
import { resetStyled } from './utils'
|
|
5
9
|
import _injectGlobal from '../constructors/injectGlobal'
|
|
6
10
|
import _keyframes from '../constructors/keyframes'
|
|
7
11
|
import stringifyRules from '../utils/stringifyRules'
|
|
8
12
|
import css from '../constructors/css'
|
|
13
|
+
|
|
14
|
+
jest.mock('../utils/nonce')
|
|
15
|
+
|
|
9
16
|
const injectGlobal = _injectGlobal(stringifyRules, css)
|
|
10
17
|
|
|
11
18
|
let index = 0
|
|
@@ -13,13 +20,11 @@ const keyframes = _keyframes(() => `keyframe_${index++}`, stringifyRules, css)
|
|
|
13
20
|
|
|
14
21
|
let styled
|
|
15
22
|
|
|
16
|
-
const format = css => stripWhitespace(css)
|
|
17
|
-
.replace(/ {/g, "{")
|
|
18
|
-
.replace(/(\*\/|[}>])/g, "$1\n")
|
|
19
|
-
.replace(/\n\s+/g, "\n")
|
|
20
|
-
|
|
21
23
|
describe('ssr', () => {
|
|
22
24
|
beforeEach(() => {
|
|
25
|
+
// eslint-disable-next-line
|
|
26
|
+
require('../utils/nonce').mockReset()
|
|
27
|
+
|
|
23
28
|
styled = resetStyled(true)
|
|
24
29
|
})
|
|
25
30
|
|
|
@@ -30,17 +35,10 @@ describe('ssr', () => {
|
|
|
30
35
|
|
|
31
36
|
const sheet = new ServerStyleSheet()
|
|
32
37
|
const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
|
|
33
|
-
const css =
|
|
34
|
-
|
|
35
|
-
expect(html).toEqual('<h1 class="sc-a b" data-reactroot="" data-reactid="1" data-react-checksum="197727696">Hello SSR!</h1>')
|
|
36
|
-
expect(css).toEqual(format(`
|
|
37
|
-
<style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
|
|
38
|
-
/* sc-component-id: sc-a */
|
|
39
|
-
.sc-a {}
|
|
40
|
-
.b { color: red; }
|
|
38
|
+
const css = sheet.getStyleTags()
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
expect(html).toMatchSnapshot()
|
|
41
|
+
expect(css).toMatchSnapshot()
|
|
44
42
|
})
|
|
45
43
|
|
|
46
44
|
it('should extract both global and local CSS', () => {
|
|
@@ -53,22 +51,29 @@ describe('ssr', () => {
|
|
|
53
51
|
|
|
54
52
|
const sheet = new ServerStyleSheet()
|
|
55
53
|
const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
|
|
56
|
-
const css =
|
|
54
|
+
const css = sheet.getStyleTags()
|
|
57
55
|
|
|
58
|
-
expect(html).
|
|
59
|
-
expect(css).
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
expect(html).toMatchSnapshot()
|
|
57
|
+
expect(css).toMatchSnapshot()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should add a nonce to the stylesheet if webpack nonce is detected in the global scope', () => {
|
|
61
|
+
// eslint-disable-next-line
|
|
62
|
+
require('../utils/nonce').mockImplementation(() => 'foo')
|
|
63
|
+
|
|
64
|
+
injectGlobal`
|
|
62
65
|
body { background: papayawhip; }
|
|
66
|
+
`
|
|
67
|
+
const Heading = styled.h1`
|
|
68
|
+
color: red;
|
|
69
|
+
`
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.sc-a {}
|
|
68
|
-
.b { color: red; }
|
|
71
|
+
const sheet = new ServerStyleSheet()
|
|
72
|
+
const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
|
|
73
|
+
const css = sheet.getStyleTags()
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
expect(html).toMatchSnapshot()
|
|
76
|
+
expect(css).toMatchSnapshot()
|
|
72
77
|
})
|
|
73
78
|
|
|
74
79
|
it('should render CSS in the order the components were defined, not rendered', () => {
|
|
@@ -86,20 +91,10 @@ describe('ssr', () => {
|
|
|
86
91
|
<ONE/>
|
|
87
92
|
</div>
|
|
88
93
|
))
|
|
89
|
-
const css =
|
|
90
|
-
|
|
91
|
-
expect(html).
|
|
92
|
-
expect(css).
|
|
93
|
-
<style type="text/css" data-styled-components="a b" data-styled-components-is-local="true">
|
|
94
|
-
/* sc-component-id: ONE */
|
|
95
|
-
.ONE {}
|
|
96
|
-
.b {color: red;}
|
|
97
|
-
/* sc-component-id: TWO */
|
|
98
|
-
.TWO {}
|
|
99
|
-
.a {color: blue;}
|
|
100
|
-
|
|
101
|
-
</style>
|
|
102
|
-
`))
|
|
94
|
+
const css = sheet.getStyleTags()
|
|
95
|
+
|
|
96
|
+
expect(html).toMatchSnapshot()
|
|
97
|
+
expect(css).toMatchSnapshot()
|
|
103
98
|
})
|
|
104
99
|
|
|
105
100
|
it('should share global styles but keep renders separate', () => {
|
|
@@ -115,37 +110,16 @@ describe('ssr', () => {
|
|
|
115
110
|
|
|
116
111
|
const sheetOne = new ServerStyleSheet()
|
|
117
112
|
const htmlOne = renderToString(sheetOne.collectStyles(<PageOne>Camera One!</PageOne>))
|
|
118
|
-
const cssOne =
|
|
113
|
+
const cssOne = sheetOne.getStyleTags()
|
|
119
114
|
|
|
120
115
|
const sheetTwo = new ServerStyleSheet()
|
|
121
116
|
const htmlTwo = renderToString(sheetTwo.collectStyles(<PageTwo>Camera Two!</PageTwo>))
|
|
122
|
-
const cssTwo =
|
|
117
|
+
const cssTwo = sheetTwo.getStyleTags()
|
|
123
118
|
|
|
124
|
-
expect(htmlOne).
|
|
125
|
-
expect(cssOne).
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
body { background: papayawhip; }
|
|
129
|
-
</style>
|
|
130
|
-
<style type="text/css" data-styled-components="a" data-styled-components-is-local="true">
|
|
131
|
-
/* sc-component-id: PageOne */
|
|
132
|
-
.PageOne {}
|
|
133
|
-
.a { color: red; }
|
|
134
|
-
</style>
|
|
135
|
-
`))
|
|
136
|
-
|
|
137
|
-
expect(htmlTwo).toEqual('<h2 class="PageTwo b" data-reactroot="" data-reactid="1" data-react-checksum="2124224444">Camera Two!</h2>')
|
|
138
|
-
expect(cssTwo).toEqual(format(`
|
|
139
|
-
<style type="text/css" data-styled-components="" data-styled-components-is-local="false">
|
|
140
|
-
/* sc-component-id: sc-global-2303210225 */
|
|
141
|
-
body { background: papayawhip; }
|
|
142
|
-
</style>
|
|
143
|
-
<style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
|
|
144
|
-
/* sc-component-id: PageTwo */
|
|
145
|
-
.PageTwo {}
|
|
146
|
-
.b { color: blue; }
|
|
147
|
-
</style>
|
|
148
|
-
`))
|
|
119
|
+
expect(htmlOne).toMatchSnapshot()
|
|
120
|
+
expect(cssOne).toMatchSnapshot()
|
|
121
|
+
expect(htmlTwo).toMatchSnapshot()
|
|
122
|
+
expect(cssTwo).toMatchSnapshot()
|
|
149
123
|
})
|
|
150
124
|
|
|
151
125
|
it('should allow global styles to be injected during rendering', () => {
|
|
@@ -160,54 +134,21 @@ describe('ssr', () => {
|
|
|
160
134
|
const sheetOne = new ServerStyleSheet()
|
|
161
135
|
const htmlOne = renderToString(sheetOne.collectStyles(<PageOne>Camera One!</PageOne>))
|
|
162
136
|
injectGlobal`html::before { content: 'During first render'; }`
|
|
163
|
-
const cssOne =
|
|
137
|
+
const cssOne = sheetOne.getStyleTags()
|
|
164
138
|
|
|
165
139
|
injectGlobal`html::before { content: 'Between renders'; }`
|
|
166
140
|
|
|
167
141
|
const sheetTwo = new ServerStyleSheet()
|
|
168
142
|
injectGlobal`html::before { content: 'During second render'; }`
|
|
169
143
|
const htmlTwo = renderToString(sheetTwo.collectStyles(<PageTwo>Camera Two!</PageTwo>))
|
|
170
|
-
const cssTwo =
|
|
144
|
+
const cssTwo = sheetTwo.getStyleTags()
|
|
171
145
|
|
|
172
146
|
injectGlobal`html::before { content: 'After both renders'; }`
|
|
173
147
|
|
|
174
|
-
expect(htmlOne).
|
|
175
|
-
expect(cssOne).
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
html::before { content: 'Before both renders'; }
|
|
179
|
-
</style>
|
|
180
|
-
<style type="text/css" data-styled-components="a" data-styled-components-is-local="true">
|
|
181
|
-
/* sc-component-id: PageOne */
|
|
182
|
-
.PageOne {}
|
|
183
|
-
.a { color: red; }
|
|
184
|
-
</style>
|
|
185
|
-
<style type="text/css" data-styled-components="" data-styled-components-is-local="false">
|
|
186
|
-
/* sc-component-id: sc-global-2914197427 */
|
|
187
|
-
html::before { content: 'During first render'; }
|
|
188
|
-
</style>
|
|
189
|
-
`))
|
|
190
|
-
|
|
191
|
-
expect(htmlTwo).toEqual('<h2 class="PageTwo b" data-reactroot="" data-reactid="1" data-react-checksum="2124224444">Camera Two!</h2>')
|
|
192
|
-
expect(cssTwo).toEqual(format(`
|
|
193
|
-
<style type="text/css" data-styled-components="" data-styled-components-is-local="false">
|
|
194
|
-
/* sc-component-id: sc-global-737874422 */
|
|
195
|
-
html::before { content: 'Before both renders'; }
|
|
196
|
-
</style>
|
|
197
|
-
<style type="text/css" data-styled-components="b" data-styled-components-is-local="true">
|
|
198
|
-
/* sc-component-id: PageTwo */
|
|
199
|
-
.PageTwo {}
|
|
200
|
-
.b { color: blue; }
|
|
201
|
-
</style>
|
|
202
|
-
<style type="text/css" data-styled-components="" data-styled-components-is-local="false">
|
|
203
|
-
/* sc-component-id: sc-global-2914197427 */
|
|
204
|
-
html::before { content: 'During first render'; }
|
|
205
|
-
/* sc-component-id: sc-global-1207956261 */
|
|
206
|
-
html::before { content: 'Between renders'; }
|
|
207
|
-
/* sc-component-id: sc-global-3990873394 */
|
|
208
|
-
html::before { content: 'During second render'; }
|
|
209
|
-
</style>
|
|
210
|
-
`))
|
|
148
|
+
expect(htmlOne).toMatchSnapshot()
|
|
149
|
+
expect(cssOne).toMatchSnapshot()
|
|
150
|
+
expect(htmlTwo).toMatchSnapshot()
|
|
151
|
+
expect(cssTwo).toMatchSnapshot()
|
|
211
152
|
})
|
|
212
153
|
|
|
213
154
|
it('should dispatch global styles to each ServerStyleSheet', () => {
|
|
@@ -222,21 +163,47 @@ describe('ssr', () => {
|
|
|
222
163
|
const html = renderToString(sheet.collectStyles(
|
|
223
164
|
<Header animation={keyframes`0% { opacity: 0; }`}/>
|
|
224
165
|
))
|
|
225
|
-
const css =
|
|
166
|
+
const css = sheet.getStyleTags()
|
|
167
|
+
|
|
168
|
+
expect(html).toMatchSnapshot()
|
|
169
|
+
expect(css).toMatchSnapshot()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should return a generated React style element', () => {
|
|
173
|
+
injectGlobal`
|
|
174
|
+
body { background: papayawhip; }
|
|
175
|
+
`
|
|
176
|
+
const Heading = styled.h1`
|
|
177
|
+
color: red;
|
|
178
|
+
`
|
|
179
|
+
|
|
180
|
+
const sheet = new ServerStyleSheet()
|
|
181
|
+
const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
|
|
182
|
+
const elements = sheet.getStyleElement()
|
|
183
|
+
|
|
184
|
+
expect(elements).toHaveLength(2);
|
|
185
|
+
|
|
186
|
+
expect(elements[0].props).toMatchSnapshot()
|
|
187
|
+
expect(elements[1].props).toMatchSnapshot()
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should return a generated React style element with nonce if webpack nonce is preset in the global scope', () => {
|
|
191
|
+
// eslint-disable-next-line
|
|
192
|
+
require('../utils/nonce').mockImplementation(() => 'foo')
|
|
226
193
|
|
|
227
|
-
|
|
228
|
-
expect(css).toEqual(format(`
|
|
229
|
-
<style type="text/css" data-styled-components="" data-styled-components-is-local="false">
|
|
230
|
-
/* sc-component-id: sc-global-2303210225 */
|
|
194
|
+
injectGlobal`
|
|
231
195
|
body { background: papayawhip; }
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
196
|
+
`
|
|
197
|
+
const Heading = styled.h1`
|
|
198
|
+
color: red;
|
|
199
|
+
`
|
|
200
|
+
|
|
201
|
+
const sheet = new ServerStyleSheet()
|
|
202
|
+
const html = renderToString(sheet.collectStyles(<Heading>Hello SSR!</Heading>))
|
|
203
|
+
const elements = sheet.getStyleElement()
|
|
204
|
+
|
|
205
|
+
expect(elements).toHaveLength(2);
|
|
206
|
+
expect(elements[0].props.nonce).toBe('foo');
|
|
207
|
+
expect(elements[1].props.nonce).toBe('foo');
|
|
241
208
|
})
|
|
242
209
|
})
|
package/src/test/styles.test.js
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { shallow } from 'enzyme'
|
|
4
4
|
|
|
5
|
+
import * as nonce from '../utils/nonce';
|
|
5
6
|
import { resetStyled, expectCSSMatches } from './utils'
|
|
7
|
+
import _injectGlobal from '../constructors/injectGlobal'
|
|
8
|
+
import stringifyRules from '../utils/stringifyRules'
|
|
9
|
+
import css from '../constructors/css'
|
|
10
|
+
const injectGlobal = _injectGlobal(stringifyRules, css)
|
|
11
|
+
|
|
12
|
+
jest.mock('../utils/nonce')
|
|
6
13
|
|
|
7
14
|
let styled
|
|
8
15
|
|
|
@@ -169,4 +176,49 @@ describe('with styles', () => {
|
|
|
169
176
|
}
|
|
170
177
|
`)
|
|
171
178
|
})
|
|
179
|
+
|
|
180
|
+
it('should add a webpack nonce to the style tags if one is available in the global scope', () => {
|
|
181
|
+
jest.spyOn(nonce, 'default').mockImplementation(() => 'foo')
|
|
182
|
+
|
|
183
|
+
const rule = 'color: blue;'
|
|
184
|
+
const Comp = styled.div`
|
|
185
|
+
${rule}
|
|
186
|
+
`
|
|
187
|
+
shallow(<Comp />)
|
|
188
|
+
expectCSSMatches(`
|
|
189
|
+
.sc-a {}
|
|
190
|
+
.b {
|
|
191
|
+
color: blue;
|
|
192
|
+
}
|
|
193
|
+
`)
|
|
194
|
+
|
|
195
|
+
Array.from(document.querySelectorAll('style')).forEach(el => expect(el.getAttribute('nonce')).toBe('foo'))
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should add a webpack nonce to the global style tags if one is available in the global scope', () => {
|
|
199
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
200
|
+
window.__webpack_nonce__ = 'foo'
|
|
201
|
+
|
|
202
|
+
injectGlobal`
|
|
203
|
+
html {
|
|
204
|
+
background: red;
|
|
205
|
+
}
|
|
206
|
+
`
|
|
207
|
+
const rule = 'color: blue;'
|
|
208
|
+
const Comp = styled.div`
|
|
209
|
+
${rule}
|
|
210
|
+
`
|
|
211
|
+
shallow(<Comp />)
|
|
212
|
+
expectCSSMatches(`
|
|
213
|
+
html {
|
|
214
|
+
background: red;
|
|
215
|
+
}
|
|
216
|
+
.sc-a {}
|
|
217
|
+
.b {
|
|
218
|
+
color: blue;
|
|
219
|
+
}
|
|
220
|
+
`)
|
|
221
|
+
|
|
222
|
+
Array.from(document.querySelectorAll('style')).forEach(el => expect(el.getAttribute('nonce')).toBe('foo'))
|
|
223
|
+
})
|
|
172
224
|
})
|
package/src/test/utils.js
CHANGED
|
@@ -23,8 +23,11 @@ const classNames = () => seededClassnames.shift() || String.fromCodePoint(97 + i
|
|
|
23
23
|
|
|
24
24
|
export const seedNextClassnames = (names: Array<string>) => seededClassnames = names
|
|
25
25
|
export const resetStyled = (isServer: boolean = false) => {
|
|
26
|
-
if (!
|
|
27
|
-
|
|
26
|
+
if (!isServer) {
|
|
27
|
+
if (!document.head) throw new Error("Missing document <head>")
|
|
28
|
+
document.head.innerHTML = ''
|
|
29
|
+
}
|
|
30
|
+
|
|
28
31
|
StyleSheet.reset(isServer)
|
|
29
32
|
index = 0
|
|
30
33
|
|
|
@@ -7,29 +7,43 @@
|
|
|
7
7
|
|
|
8
8
|
export type Broadcast = {
|
|
9
9
|
publish: (value: mixed) => void,
|
|
10
|
-
subscribe: (listener: (currentValue: mixed) => void) =>
|
|
10
|
+
subscribe: (listener: (currentValue: mixed) => void) => number,
|
|
11
|
+
unsubscribe: (number) => void,
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const createBroadcast = (
|
|
14
|
-
|
|
15
|
-
let
|
|
14
|
+
const createBroadcast = (initialState: mixed): Broadcast => {
|
|
15
|
+
const listeners = {}
|
|
16
|
+
let id = 0
|
|
17
|
+
let state = initialState
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
currentValue = value
|
|
20
|
-
listeners.forEach(listener => listener(currentValue))
|
|
21
|
-
},
|
|
22
|
-
subscribe(listener) {
|
|
23
|
-
listeners.push(listener)
|
|
19
|
+
function publish(nextState: mixed) {
|
|
20
|
+
state = nextState
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
// eslint-disable-next-line guard-for-in, no-restricted-syntax
|
|
23
|
+
for (const key in listeners) {
|
|
24
|
+
const listener = listeners[key]
|
|
25
|
+
if (listener === undefined) {
|
|
26
|
+
// eslint-disable-next-line no-continue
|
|
27
|
+
continue
|
|
30
28
|
}
|
|
31
|
-
|
|
29
|
+
|
|
30
|
+
listener(state)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function subscribe(listener) {
|
|
35
|
+
const currentId = id
|
|
36
|
+
listeners[currentId] = listener
|
|
37
|
+
id += 1
|
|
38
|
+
listener(state)
|
|
39
|
+
return currentId
|
|
32
40
|
}
|
|
41
|
+
|
|
42
|
+
function unsubscribe(unsubID: number) {
|
|
43
|
+
listeners[unsubID] = undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { publish, subscribe, unsubscribe }
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
export default createBroadcast
|
package/src/utils/domElements.js
CHANGED
package/src/utils/flatten.js
CHANGED
|
@@ -5,13 +5,23 @@ import isPlainObject from 'is-plain-object'
|
|
|
5
5
|
import type { Interpolation } from '../types'
|
|
6
6
|
|
|
7
7
|
export const objToCss = (obj: Object, prevKey?: string): string => {
|
|
8
|
-
const css = Object.keys(obj)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const css = Object.keys(obj)
|
|
9
|
+
.filter(key => {
|
|
10
|
+
const chunk = obj[key]
|
|
11
|
+
return (
|
|
12
|
+
chunk !== undefined && chunk !== null && chunk !== false && chunk !== ''
|
|
13
|
+
)
|
|
14
|
+
})
|
|
15
|
+
.map(key => {
|
|
16
|
+
if (isPlainObject(obj[key])) return objToCss(obj[key], key)
|
|
17
|
+
return `${hyphenate(key)}: ${obj[key]};`
|
|
18
|
+
})
|
|
19
|
+
.join(' ')
|
|
20
|
+
return prevKey
|
|
21
|
+
? `${prevKey} {
|
|
13
22
|
${css}
|
|
14
|
-
}`
|
|
23
|
+
}`
|
|
24
|
+
: css
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
const flatten = (chunks: Array<Interpolation>, executionContext: ?Object): Array<Interpolation> => (
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { StatelessComponent, ComponentClass, PureComponent, ReactElement } from "react";
|
|
3
3
|
|
|
4
|
-
import { HTMLTags, SVGTags } from "./tags";
|
|
5
|
-
|
|
6
4
|
type Component<P> = ComponentClass<P> | StatelessComponent<P>;
|
|
7
5
|
|
|
8
6
|
export interface ThemeProps<T> {
|
|
@@ -33,8 +31,7 @@ type Attrs<P, A extends Partial<P>, T> = {
|
|
|
33
31
|
export interface StyledComponentClass<P, T, O = P> extends ComponentClass<ThemedOuterStyledProps<O, T>> {
|
|
34
32
|
extend: ThemedStyledFunction<P, T, O>;
|
|
35
33
|
|
|
36
|
-
withComponent<K extends keyof
|
|
37
|
-
withComponent<K extends keyof SVGTags>(tag: K): StyledComponentClass<React.SVGAttributes<SVGTags[K]>, T, O>;
|
|
34
|
+
withComponent<K extends keyof JSX.IntrinsicElements>(tag: K): StyledComponentClass<JSX.IntrinsicElements[K], T, O>;
|
|
38
35
|
withComponent(element: ComponentClass<P>): StyledComponentClass<P, T, O>;
|
|
39
36
|
}
|
|
40
37
|
|
|
@@ -46,24 +43,16 @@ export interface ThemedStyledFunction<P, T, O = P> {
|
|
|
46
43
|
|
|
47
44
|
export type StyledFunction<P> = ThemedStyledFunction<P, any>;
|
|
48
45
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export type ThemedSvgStyledFunction<E extends SVGElement, T> = ThemedStyledFunction<React.SVGAttributes<E>, T>;
|
|
53
|
-
export type SvgStyledFunction<E extends SVGElement> = ThemedSvgStyledFunction<E, any>;
|
|
54
|
-
|
|
55
|
-
type ThemedStyledComponentFactoriesHTML<T> = {
|
|
56
|
-
[K in keyof HTMLTags]: ThemedHtmlStyledFunction<HTMLTags[K], T>;
|
|
46
|
+
type ThemedStyledComponentFactories<T> = {
|
|
47
|
+
[TTag in keyof JSX.IntrinsicElements]: ThemedStyledFunction<JSX.IntrinsicElements[TTag], T>;
|
|
57
48
|
};
|
|
58
49
|
|
|
59
|
-
type ThemedStyledComponentFactoriesSVG<T> = {
|
|
60
|
-
[K in keyof SVGTags]: ThemedSvgStyledFunction<SVGTags[K], T>;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
type ThemedStyledComponentFactories<T> = ThemedStyledComponentFactoriesHTML<T> & ThemedStyledComponentFactoriesSVG<T>;
|
|
64
|
-
|
|
65
50
|
export interface ThemedBaseStyledInterface<T> extends ThemedStyledComponentFactories<T> {
|
|
66
|
-
<P>(
|
|
51
|
+
<P, TTag extends keyof JSX.IntrinsicElements>(tag: TTag): ThemedStyledFunction<P, T, P & JSX.IntrinsicElements[TTag]>;
|
|
52
|
+
<P, O>(component: StyledComponentClass<P, T, O>): ThemedStyledFunction<P, T, O>;
|
|
53
|
+
<P extends { theme: T; }>(component: React.ComponentClass<P>): ThemedStyledFunction<P, T, WithOptionalTheme<P, T>>;
|
|
54
|
+
<P>(component: React.ComponentClass<P>): ThemedStyledFunction<P, T>;
|
|
55
|
+
<P extends { [prop: string]: any; theme?: T; }>(component: React.StatelessComponent<P>): ThemedStyledFunction<P, T, WithOptionalTheme<P, T>>;
|
|
67
56
|
}
|
|
68
57
|
export type BaseStyledInterface = ThemedBaseStyledInterface<any>;
|
|
69
58
|
|
|
@@ -80,13 +69,18 @@ export interface ThemedCssFunction<T> {
|
|
|
80
69
|
<P>(strings: TemplateStringsArray, ...interpolations: Interpolation<ThemedStyledProps<P, T>>[]): FlattenInterpolation<ThemedStyledProps<P, T>>[];
|
|
81
70
|
}
|
|
82
71
|
|
|
72
|
+
// Helper type operators
|
|
73
|
+
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
|
|
74
|
+
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
|
|
75
|
+
type WithOptionalTheme<P extends { theme?: T; }, T> = Omit<P, "theme"> & { theme?: T; };
|
|
76
|
+
|
|
83
77
|
export interface ThemedStyledComponentsModule<T> {
|
|
84
78
|
default: ThemedStyledInterface<T>;
|
|
85
79
|
|
|
86
80
|
css: ThemedCssFunction<T>;
|
|
87
81
|
keyframes(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): string;
|
|
88
82
|
injectGlobal(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): void;
|
|
89
|
-
withTheme<P extends { theme?: T; }
|
|
83
|
+
withTheme<P extends { theme?: T; }>(component: Component<P>): ComponentClass<WithOptionalTheme<P, T>>;
|
|
90
84
|
|
|
91
85
|
ThemeProvider: ThemeProviderComponent<T>;
|
|
92
86
|
}
|
|
@@ -94,7 +88,7 @@ export interface ThemedStyledComponentsModule<T> {
|
|
|
94
88
|
declare const styled: StyledInterface;
|
|
95
89
|
|
|
96
90
|
export const css: ThemedCssFunction<any>;
|
|
97
|
-
export function withTheme<P extends { theme?: T; }, T>(component: Component<P>): ComponentClass<P
|
|
91
|
+
export function withTheme<P extends { theme?: T; }, T>(component: Component<P>): ComponentClass<WithOptionalTheme<P, T>>;
|
|
98
92
|
|
|
99
93
|
export function keyframes(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): string;
|
|
100
94
|
export function injectGlobal(strings: TemplateStringsArray, ...interpolations: SimpleInterpolation[]): void;
|