1 |
|
2 | import * as React from 'react'
|
3 | import {
|
4 | getDefaultShouldForwardProp,
|
5 | composeShouldForwardProps,
|
6 | type StyledOptions,
|
7 | type CreateStyled,
|
8 | type PrivateStyledComponent,
|
9 | type StyledElementType
|
10 | } from './utils'
|
11 | import { withEmotionCache, ThemeContext } from '@emotion/react'
|
12 | import {
|
13 | getRegisteredStyles,
|
14 | insertStyles,
|
15 | registerStyles
|
16 | } from '@emotion/utils'
|
17 | import { serializeStyles } from '@emotion/serialize'
|
18 | import useInsertionEffectMaybe from './useInsertionEffectMaybe'
|
19 |
|
20 | const ILLEGAL_ESCAPE_SEQUENCE_ERROR = `You have illegal escape sequence in your template literal, most likely inside content's property value.
|
21 | Because you write your CSS inside a JavaScript string you actually have to do double escaping, so for example "content: '\\00d7';" should become "content: '\\\\00d7';".
|
22 | You can read more about this here:
|
23 | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#ES2018_revision_of_illegal_escape_sequences`
|
24 |
|
25 | let isBrowser = typeof document !== 'undefined'
|
26 |
|
27 | const Insertion = ({ cache, serialized, isStringTag }) => {
|
28 | registerStyles(cache, serialized, isStringTag)
|
29 |
|
30 | const rules = useInsertionEffectMaybe(() =>
|
31 | insertStyles(cache, serialized, isStringTag)
|
32 | )
|
33 |
|
34 | if (!isBrowser && rules !== undefined) {
|
35 | let serializedNames = serialized.name
|
36 | let next = serialized.next
|
37 | while (next !== undefined) {
|
38 | serializedNames += ' ' + next.name
|
39 | next = next.next
|
40 | }
|
41 | return (
|
42 | <style
|
43 | {...{
|
44 | [`data-emotion`]: `${cache.key} ${serializedNames}`,
|
45 | dangerouslySetInnerHTML: { __html: rules },
|
46 | nonce: cache.sheet.nonce
|
47 | }}
|
48 | />
|
49 | )
|
50 | }
|
51 | return null
|
52 | }
|
53 |
|
54 | let createStyled: CreateStyled = (tag: any, options?: StyledOptions) => {
|
55 | if (process.env.NODE_ENV !== 'production') {
|
56 | if (tag === undefined) {
|
57 | throw new Error(
|
58 | 'You are trying to create a styled element with an undefined component.\nYou may have forgotten to import it.'
|
59 | )
|
60 | }
|
61 | }
|
62 | const isReal = tag.__emotion_real === tag
|
63 | const baseTag = (isReal && tag.__emotion_base) || tag
|
64 |
|
65 | let identifierName
|
66 | let targetClassName
|
67 | if (options !== undefined) {
|
68 | identifierName = options.label
|
69 | targetClassName = options.target
|
70 | }
|
71 |
|
72 | const shouldForwardProp = composeShouldForwardProps(tag, options, isReal)
|
73 | const defaultShouldForwardProp =
|
74 | shouldForwardProp || getDefaultShouldForwardProp(baseTag)
|
75 | const shouldUseAs = !defaultShouldForwardProp('as')
|
76 |
|
77 | return function <Props>(): PrivateStyledComponent<Props> {
|
78 | let args = arguments
|
79 | let styles =
|
80 | isReal && tag.__emotion_styles !== undefined
|
81 | ? tag.__emotion_styles.slice(0)
|
82 | : []
|
83 |
|
84 | if (identifierName !== undefined) {
|
85 | styles.push(`label:${identifierName};`)
|
86 | }
|
87 | if (args[0] == null || args[0].raw === undefined) {
|
88 | styles.push.apply(styles, args)
|
89 | } else {
|
90 | if (process.env.NODE_ENV !== 'production' && args[0][0] === undefined) {
|
91 | console.error(ILLEGAL_ESCAPE_SEQUENCE_ERROR)
|
92 | }
|
93 | styles.push(args[0][0])
|
94 | let len = args.length
|
95 | let i = 1
|
96 | for (; i < len; i++) {
|
97 | if (process.env.NODE_ENV !== 'production' && args[0][i] === undefined) {
|
98 | console.error(ILLEGAL_ESCAPE_SEQUENCE_ERROR)
|
99 | }
|
100 | styles.push(args[i], args[0][i])
|
101 | }
|
102 | }
|
103 |
|
104 |
|
105 | const Styled: PrivateStyledComponent<Props> = withEmotionCache(
|
106 | (props, cache, ref) => {
|
107 | const FinalTag = (shouldUseAs && props.as) || baseTag
|
108 |
|
109 | let className = ''
|
110 | let classInterpolations = []
|
111 | let mergedProps = props
|
112 | if (props.theme == null) {
|
113 | mergedProps = {}
|
114 | for (let key in props) {
|
115 | mergedProps[key] = props[key]
|
116 | }
|
117 | mergedProps.theme = React.useContext(ThemeContext)
|
118 | }
|
119 |
|
120 | if (typeof props.className === 'string') {
|
121 | className = getRegisteredStyles(
|
122 | cache.registered,
|
123 | classInterpolations,
|
124 | props.className
|
125 | )
|
126 | } else if (props.className != null) {
|
127 | className = `${props.className} `
|
128 | }
|
129 |
|
130 | const serialized = serializeStyles(
|
131 | styles.concat(classInterpolations),
|
132 | cache.registered,
|
133 | mergedProps
|
134 | )
|
135 | className += `${cache.key}-${serialized.name}`
|
136 | if (targetClassName !== undefined) {
|
137 | className += ` ${targetClassName}`
|
138 | }
|
139 |
|
140 | const finalShouldForwardProp =
|
141 | shouldUseAs && shouldForwardProp === undefined
|
142 | ? getDefaultShouldForwardProp(FinalTag)
|
143 | : defaultShouldForwardProp
|
144 |
|
145 | let newProps = {}
|
146 |
|
147 | for (let key in props) {
|
148 | if (shouldUseAs && key === 'as') continue
|
149 |
|
150 | if (
|
151 |
|
152 | finalShouldForwardProp(key)
|
153 | ) {
|
154 | newProps[key] = props[key]
|
155 | }
|
156 | }
|
157 |
|
158 | newProps.className = className
|
159 | newProps.ref = ref
|
160 |
|
161 | return (
|
162 | <>
|
163 | <Insertion
|
164 | cache={cache}
|
165 | serialized={serialized}
|
166 | isStringTag={typeof FinalTag === 'string'}
|
167 | />
|
168 | <FinalTag {...newProps} />
|
169 | </>
|
170 | )
|
171 | }
|
172 | )
|
173 |
|
174 | Styled.displayName =
|
175 | identifierName !== undefined
|
176 | ? identifierName
|
177 | : `Styled(${
|
178 | typeof baseTag === 'string'
|
179 | ? baseTag
|
180 | : baseTag.displayName || baseTag.name || 'Component'
|
181 | })`
|
182 |
|
183 | Styled.defaultProps = tag.defaultProps
|
184 | Styled.__emotion_real = Styled
|
185 | Styled.__emotion_base = baseTag
|
186 | Styled.__emotion_styles = styles
|
187 | Styled.__emotion_forwardProp = shouldForwardProp
|
188 |
|
189 | Object.defineProperty(Styled, 'toString', {
|
190 | value() {
|
191 | if (
|
192 | targetClassName === undefined &&
|
193 | process.env.NODE_ENV !== 'production'
|
194 | ) {
|
195 | return 'NO_COMPONENT_SELECTOR'
|
196 | }
|
197 |
|
198 | return `.${targetClassName}`
|
199 | }
|
200 | })
|
201 |
|
202 | Styled.withComponent = (
|
203 | nextTag: StyledElementType<Props>,
|
204 | nextOptions?: StyledOptions
|
205 | ) => {
|
206 | return createStyled(nextTag, {
|
207 | ...options,
|
208 |
|
209 | ...nextOptions,
|
210 | shouldForwardProp: composeShouldForwardProps(Styled, nextOptions, true)
|
211 | })(...styles)
|
212 | }
|
213 |
|
214 | return Styled
|
215 | }
|
216 | }
|
217 |
|
218 | export default createStyled
|