1 |
|
2 |
|
3 | import * as React from 'react'
|
4 | import isPropValid from '@emotion/is-prop-valid'
|
5 | import {ThemeContext as DefaultThemeContext} from 'theming'
|
6 |
|
7 | import createUseStyles from './createUseStyles'
|
8 |
|
9 |
|
10 | const parseStyles = (args) => {
|
11 | const dynamicStyles = []
|
12 | let staticStyle
|
13 | const labels = []
|
14 |
|
15 |
|
16 | for (const key in args) {
|
17 | const style = args[key]
|
18 | if (!style) continue
|
19 | if (typeof style === 'function') {
|
20 | dynamicStyles.push(style)
|
21 | } else {
|
22 | if (!staticStyle) staticStyle = {}
|
23 | Object.assign(staticStyle, style)
|
24 | const {label} = staticStyle
|
25 | if (label) {
|
26 | if (labels.indexOf(label) === -1) labels.push(label)
|
27 | }
|
28 | }
|
29 | }
|
30 |
|
31 | const styles = {}
|
32 | const label = labels.length === 0 ? 'sc' : labels.join('-')
|
33 |
|
34 | if (staticStyle) {
|
35 |
|
36 | if ('label' in staticStyle) delete staticStyle.label
|
37 | styles[label] = staticStyle
|
38 | }
|
39 |
|
40 |
|
41 | if (dynamicStyles.length === 1) {
|
42 | styles.scd = dynamicStyles[0]
|
43 | }
|
44 |
|
45 |
|
46 |
|
47 | if (dynamicStyles.length > 1) {
|
48 | styles.scd = (props) => {
|
49 | const merged = {}
|
50 | for (let i = 0; i < dynamicStyles.length; i++) {
|
51 | const dynamicStyle = dynamicStyles[i](props)
|
52 | if (dynamicStyle) Object.assign(merged, dynamicStyle)
|
53 | }
|
54 | return merged
|
55 | }
|
56 | }
|
57 |
|
58 | return {styles, label}
|
59 | }
|
60 |
|
61 | const shouldForwardPropSymbol = Symbol('react-jss-styled')
|
62 |
|
63 | const getShouldForwardProp = (tagOrComponent, options) => {
|
64 | const {shouldForwardProp} = options
|
65 | const childShouldForwardProp = tagOrComponent[shouldForwardPropSymbol]
|
66 | let finalShouldForwardProp = shouldForwardProp || childShouldForwardProp
|
67 | if (shouldForwardProp && childShouldForwardProp) {
|
68 | finalShouldForwardProp = (prop) => childShouldForwardProp(prop) && shouldForwardProp(prop)
|
69 | }
|
70 | return finalShouldForwardProp
|
71 | }
|
72 |
|
73 | const getChildProps = (props, shouldForwardProp, isTag) => {
|
74 | const childProps = {}
|
75 |
|
76 | for (const prop in props) {
|
77 | if (shouldForwardProp) {
|
78 | if (shouldForwardProp(prop) === true) {
|
79 | childProps[prop] = props[prop]
|
80 | }
|
81 | continue
|
82 | }
|
83 |
|
84 |
|
85 | if (isTag) {
|
86 | if (isPropValid(prop)) {
|
87 | childProps[prop] = props[prop]
|
88 | }
|
89 | continue
|
90 | }
|
91 |
|
92 | childProps[prop] = props[prop]
|
93 | }
|
94 |
|
95 | return childProps
|
96 | }
|
97 |
|
98 |
|
99 | const configureStyled = (tagOrComponent, options = {}) => {
|
100 | const {theming} = options
|
101 | const isTag = typeof tagOrComponent === 'string'
|
102 |
|
103 | const ThemeContext = theming ? theming.context : DefaultThemeContext
|
104 | const shouldForwardProp = getShouldForwardProp(tagOrComponent, options)
|
105 | const {shouldForwardProp: _, ...hookOptions} = options
|
106 |
|
107 | return function createStyledComponent() {
|
108 |
|
109 | const {styles, label} = parseStyles(arguments)
|
110 | const useStyles = createUseStyles(styles, hookOptions)
|
111 |
|
112 | const Styled = (props) => {
|
113 | const {as, className} = props
|
114 | const theme = React.useContext(ThemeContext)
|
115 | const propsWithTheme = Object.assign({theme}, props)
|
116 | const classes = useStyles(propsWithTheme)
|
117 | const childProps = getChildProps(props, shouldForwardProp, isTag)
|
118 |
|
119 | const classNames = `${classes[label] || classes.sc || ''} ${classes.scd || ''}`.trim()
|
120 | childProps.className = className ? `${className} ${classNames}` : classNames
|
121 |
|
122 | if (!isTag && shouldForwardProp) {
|
123 | tagOrComponent[shouldForwardPropSymbol] = shouldForwardProp
|
124 | }
|
125 |
|
126 | if (isTag && as) {
|
127 | return React.createElement(as, childProps)
|
128 | }
|
129 |
|
130 | return React.createElement(tagOrComponent, childProps)
|
131 | }
|
132 |
|
133 | return Styled
|
134 | }
|
135 | }
|
136 |
|
137 | export default configureStyled
|