|
1 | | -import React from 'react' |
2 | | -import injectSheetDefault from 'react-jss' |
| 1 | +import React, {PureComponent} from 'react' |
| 2 | + |
| 3 | +import {create as createJSS, getDynamicStyles} from 'jss' |
| 4 | +import preset from 'jss-preset-default' |
3 | 5 |
|
4 | 6 | import filterProps from './utils/filter-props' |
5 | 7 |
|
6 | 8 |
|
7 | | -type StyledElementType = Function & { Tag: string, styles: Object } |
8 | | -type TagNameOrStyledElementType = string | StyledElementType |
9 | | -type PrimitivePropsType = { |
| 9 | +const JSS = createJSS(preset()) |
| 10 | + |
| 11 | +type StyledElementAttrsT = { Tag: string, styles: Object } |
| 12 | +type StyledElementT = Function & StyledElementAttrsT |
| 13 | +type TagOrStyledElementT = string | StyledElementT |
| 14 | +type StyledElementPropsT = { |
10 | 15 | classes: Object, |
11 | 16 | children: ?any, |
12 | 17 | className: ?string, |
13 | 18 | } |
14 | 19 |
|
15 | 20 |
|
16 | | -export const createStyled = (injectSheet?: Function = injectSheetDefault) => |
17 | | - ( |
18 | | - TagNameOrStyledElement: TagNameOrStyledElementType, |
19 | | - styles: Object, |
20 | | - baseStyles?: Object = {}, |
21 | | - ): StyledElementType => { |
22 | | - const { |
23 | | - Tag, |
24 | | - styles: inheritStyles = {}, |
25 | | - }: { |
26 | | - Tag: string, |
27 | | - styles?: Object |
28 | | - } = typeof TagNameOrStyledElement === 'string' |
29 | | - ? {Tag: TagNameOrStyledElement} |
30 | | - : TagNameOrStyledElement |
31 | | - |
32 | | - const elementStyles = {...inheritStyles, ...styles} |
33 | | - |
34 | | - const Primitive = ({classes, children, className, ...attrs}: PrimitivePropsType) => { |
35 | | - const props = filterProps(attrs) |
36 | | - |
37 | | - return ( |
38 | | - <Tag className={className ? `${classes[Tag]} ${className}` : classes[Tag]} {...props}> |
39 | | - {children} |
40 | | - </Tag> |
41 | | - ) |
42 | | - } |
| 21 | +export const createStyled = (jss?: Function = JSS) => (baseStyles: Object = {}) => { |
| 22 | + let sheet |
| 23 | + |
| 24 | + let currentId = 0 |
| 25 | + |
| 26 | + return (TagOrStyledElement: TagOrStyledElementT, ownStyles: Object): StyledElementT => { |
| 27 | + const {Tag, styles}: StyledElementAttrsT = typeof TagOrStyledElement === 'string' |
| 28 | + ? {Tag: TagOrStyledElement, styles: {}} |
| 29 | + : TagOrStyledElement |
| 30 | + |
| 31 | + const elementStyles = {...styles, ...ownStyles} |
| 32 | + const dynamicStyles = getDynamicStyles(elementStyles) |
| 33 | + |
| 34 | + const staticTag = `${Tag}-${++currentId}` |
| 35 | + |
| 36 | + return class StyledElement extends PureComponent { |
| 37 | + props: StyledElementPropsT |
| 38 | + |
| 39 | + static Tag = Tag |
| 40 | + static styles = elementStyles |
| 41 | + |
| 42 | + tagScoped = '' |
| 43 | + dynamicSheet = null |
| 44 | + |
| 45 | + constructor(props) { |
| 46 | + super(props) |
43 | 47 |
|
44 | | - const StyledPrimitive = injectSheet({ |
45 | | - [Tag]: elementStyles, |
46 | | - ...baseStyles, |
47 | | - })(Primitive) |
| 48 | + this.tagScoped = `${Tag}-${++currentId}` |
| 49 | + } |
48 | 50 |
|
49 | | - return Object.assign(StyledPrimitive, {Tag, styles: elementStyles}) |
| 51 | + componentWillMount() { |
| 52 | + if (!sheet) { |
| 53 | + sheet = jss.createStyleSheet(baseStyles, { |
| 54 | + link: true, |
| 55 | + meta: 'StaticBaseSheet', |
| 56 | + }).attach() |
| 57 | + } |
| 58 | + |
| 59 | + if (!sheet.getRule(staticTag)) { |
| 60 | + sheet.addRule(staticTag, elementStyles) |
| 61 | + } |
| 62 | + |
| 63 | + if (dynamicStyles && !this.dynamicSheet) { |
| 64 | + this.dynamicSheet = jss.createStyleSheet({[this.tagScoped]: dynamicStyles}, { |
| 65 | + link: true, |
| 66 | + meta: `DynamicComponentSheet-${this.tagScoped}`, |
| 67 | + }).update(this.props).attach() |
| 68 | + } |
| 69 | + } |
| 70 | + |
| 71 | + componentWillReceiveProps(nextProps: StyledElementPropsT) { |
| 72 | + if (this.dynamicSheet) this.dynamicSheet.update(nextProps) |
| 73 | + } |
| 74 | + |
| 75 | + componentWillUnmount() { |
| 76 | + if (this.dynamicSheet) { |
| 77 | + jss.removeStyleSheet(this.dynamicSheet) |
| 78 | + this.dynamicSheet = null |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + render() { |
| 83 | + if (!sheet) return null |
| 84 | + |
| 85 | + const {children, className, ...attrs} = this.props |
| 86 | + |
| 87 | + const props = filterProps(attrs) |
| 88 | + const tagClass = [ |
| 89 | + sheet.classes[staticTag], |
| 90 | + this.dynamicSheet && this.dynamicSheet.classes[this.tagScoped], |
| 91 | + className, |
| 92 | + ] |
| 93 | + .filter(Boolean) |
| 94 | + .join(' ') |
| 95 | + |
| 96 | + return ( |
| 97 | + <Tag className={tagClass} {...props}> |
| 98 | + {children} |
| 99 | + </Tag> |
| 100 | + ) |
| 101 | + } |
| 102 | + } |
50 | 103 | } |
| 104 | +} |
51 | 105 |
|
52 | | -const defaultStyled = createStyled() |
| 106 | +const defaultStyledBased = createStyled() |
| 107 | +const defaultStyled = defaultStyledBased() |
53 | 108 |
|
54 | 109 | export {defaultStyled as styled} |
55 | 110 |
|
56 | | -export const createStyledCreator = (styled: Function = defaultStyled) => |
57 | | - (baseStyles: Object) => Object.assign( |
58 | | - (TagNameOrStyledElement: TagNameOrStyledElementType, styles: Object) => |
59 | | - styled(TagNameOrStyledElement, styles, baseStyles), |
60 | | - {styles: baseStyles}, |
61 | | - ) |
| 111 | +export const createStyledCreator = (styled: Function = defaultStyledBased) => |
| 112 | + (baseStyles: Object) => Object.assign(styled(baseStyles), {styles: baseStyles}) |
62 | 113 |
|
63 | 114 | export default createStyledCreator() |
0 commit comments