unpkg/public/@emotion/core@10.0.6/src/global.js

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
}
}