145 lines
4.0 KiB
JavaScript
145 lines
4.0 KiB
JavaScript
// @flow
|
|
import * as React from 'react'
|
|
import { withEmotionCache, ThemeContext } from './context'
|
|
import {
|
|
type EmotionCache,
|
|
type SerializedStyles,
|
|
insertStyles
|
|
} from '@emotion/utils'
|
|
import { isBrowser } from './utils'
|
|
|
|
import { StyleSheet } from '@emotion/sheet'
|
|
import { serializeStyles } from '@emotion/serialize'
|
|
|
|
type Styles = Object | Array<Object>
|
|
|
|
type GlobalProps = {
|
|
+styles: Styles | (Object => Styles)
|
|
}
|
|
|
|
let warnedAboutCssPropForGlobal = false
|
|
|
|
export let Global: React.StatelessFunctionalComponent<
|
|
GlobalProps
|
|
> = /* #__PURE__ */ withEmotionCache((props: GlobalProps, cache) => {
|
|
if (
|
|
process.env.NODE_ENV !== 'production' &&
|
|
!warnedAboutCssPropForGlobal &&
|
|
// check for className as well since the user is
|
|
// probably using the custom createElement which
|
|
// means it will be turned into a className prop
|
|
// $FlowFixMe I don't really want to add it to the type since it shouldn't be used
|
|
(props.className || props.css)
|
|
) {
|
|
console.error(
|
|
"It looks like you're using the css prop on Global, did you mean to use the styles prop instead?"
|
|
)
|
|
warnedAboutCssPropForGlobal = true
|
|
}
|
|
let styles = props.styles
|
|
|
|
if (typeof styles === 'function') {
|
|
return (
|
|
<ThemeContext.Consumer>
|
|
{theme => {
|
|
let serialized = serializeStyles([styles(theme)])
|
|
|
|
return <InnerGlobal serialized={serialized} cache={cache} />
|
|
}}
|
|
</ThemeContext.Consumer>
|
|
)
|
|
}
|
|
let serialized = serializeStyles([styles])
|
|
|
|
return <InnerGlobal serialized={serialized} cache={cache} />
|
|
})
|
|
|
|
type InnerGlobalProps = {
|
|
serialized: SerializedStyles,
|
|
cache: EmotionCache
|
|
}
|
|
|
|
// maintain place over rerenders.
|
|
// initial render from browser, insertBefore context.sheet.tags[0] or if a style hasn't been inserted there yet, appendChild
|
|
// initial client-side render from SSR, use place of hydrating tag
|
|
|
|
class InnerGlobal extends React.Component<InnerGlobalProps> {
|
|
sheet: StyleSheet
|
|
componentDidMount() {
|
|
this.sheet = new StyleSheet({
|
|
key: `${this.props.cache.key}-global`,
|
|
nonce: this.props.cache.sheet.nonce,
|
|
container: this.props.cache.sheet.container
|
|
})
|
|
// $FlowFixMe
|
|
let node: HTMLStyleElement | null = document.querySelector(
|
|
`style[data-emotion-${this.props.cache.key}="${
|
|
this.props.serialized.name
|
|
}"]`
|
|
)
|
|
|
|
if (node !== null) {
|
|
this.sheet.tags.push(node)
|
|
}
|
|
if (this.props.cache.sheet.tags.length) {
|
|
this.sheet.before = this.props.cache.sheet.tags[0]
|
|
}
|
|
this.insertStyles()
|
|
}
|
|
componentDidUpdate(prevProps) {
|
|
if (prevProps.serialized.name !== this.props.serialized.name) {
|
|
this.insertStyles()
|
|
}
|
|
}
|
|
insertStyles() {
|
|
if (this.props.serialized.next !== undefined) {
|
|
// insert keyframes
|
|
insertStyles(this.props.cache, this.props.serialized.next, true)
|
|
}
|
|
if (this.sheet.tags.length) {
|
|
// if this doesn't exist then it will be null so the style element will be appended
|
|
let element = this.sheet.tags[this.sheet.tags.length - 1]
|
|
.nextElementSibling
|
|
this.sheet.before = ((element: any): Element | null)
|
|
this.sheet.flush()
|
|
}
|
|
this.props.cache.insert(``, this.props.serialized, this.sheet, false)
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this.sheet.flush()
|
|
}
|
|
render() {
|
|
if (!isBrowser) {
|
|
let { serialized } = this.props
|
|
|
|
let serializedNames = serialized.name
|
|
let serializedStyles = serialized.styles
|
|
let next = serialized.next
|
|
while (next !== undefined) {
|
|
serializedNames += ' ' + next.name
|
|
serializedStyles += next.styles
|
|
next = next.next
|
|
}
|
|
|
|
let rules = this.props.cache.insert(
|
|
``,
|
|
{ name: serializedNames, styles: serializedStyles },
|
|
this.sheet,
|
|
false
|
|
)
|
|
|
|
return (
|
|
<style
|
|
{...{
|
|
[`data-emotion-${this.props.cache.key}`]: serializedNames,
|
|
dangerouslySetInnerHTML: { __html: rules },
|
|
nonce: this.props.cache.sheet.nonce
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
}
|