You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					144 lines
				
				4.4 KiB
			
		
		
			
		
	
	
					144 lines
				
				4.4 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								// @flow
							 | 
						||
| 
								 | 
							
								import * as React from 'react'
							 | 
						||
| 
								 | 
							
								import { withEmotionCache } from './context'
							 | 
						||
| 
								 | 
							
								import { ThemeContext } from './theming'
							 | 
						||
| 
								 | 
							
								import { insertStyles } from '@emotion/utils'
							 | 
						||
| 
								 | 
							
								import { isBrowser } from './utils'
							 | 
						||
| 
								 | 
							
								import { useInsertionEffectWithLayoutFallback } from '@emotion/use-insertion-effect-with-fallbacks'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import { serializeStyles } from '@emotion/serialize'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type Styles = Object | Array<Object>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type GlobalProps = {
							 | 
						||
| 
								 | 
							
								  +styles: Styles | (Object => Styles)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let warnedAboutCssPropForGlobal = false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export let Global: React.AbstractComponent<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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let serialized = serializeStyles(
							 | 
						||
| 
								 | 
							
								      [styles],
							 | 
						||
| 
								 | 
							
								      undefined,
							 | 
						||
| 
								 | 
							
								      React.useContext(ThemeContext)
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!isBrowser) {
							 | 
						||
| 
								 | 
							
								      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 shouldCache = cache.compat === true
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      let rules = cache.insert(
							 | 
						||
| 
								 | 
							
								        ``,
							 | 
						||
| 
								 | 
							
								        { name: serializedNames, styles: serializedStyles },
							 | 
						||
| 
								 | 
							
								        cache.sheet,
							 | 
						||
| 
								 | 
							
								        shouldCache
							 | 
						||
| 
								 | 
							
								      )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (shouldCache) {
							 | 
						||
| 
								 | 
							
								        return null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return (
							 | 
						||
| 
								 | 
							
								        <style
							 | 
						||
| 
								 | 
							
								          {...{
							 | 
						||
| 
								 | 
							
								            [`data-emotion`]: `${cache.key}-global ${serializedNames}`,
							 | 
						||
| 
								 | 
							
								            dangerouslySetInnerHTML: { __html: rules },
							 | 
						||
| 
								 | 
							
								            nonce: cache.sheet.nonce
							 | 
						||
| 
								 | 
							
								          }}
							 | 
						||
| 
								 | 
							
								        />
							 | 
						||
| 
								 | 
							
								      )
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // yes, i know these hooks are used conditionally
							 | 
						||
| 
								 | 
							
								    // but it is based on a constant that will never change at runtime
							 | 
						||
| 
								 | 
							
								    // it's effectively like having two implementations and switching them out
							 | 
						||
| 
								 | 
							
								    // so it's not actually breaking anything
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let sheetRef = React.useRef()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    useInsertionEffectWithLayoutFallback(() => {
							 | 
						||
| 
								 | 
							
								      const key = `${cache.key}-global`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // use case of https://github.com/emotion-js/emotion/issues/2675
							 | 
						||
| 
								 | 
							
								      let sheet = new cache.sheet.constructor({
							 | 
						||
| 
								 | 
							
								        key,
							 | 
						||
| 
								 | 
							
								        nonce: cache.sheet.nonce,
							 | 
						||
| 
								 | 
							
								        container: cache.sheet.container,
							 | 
						||
| 
								 | 
							
								        speedy: cache.sheet.isSpeedy
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      let rehydrating = false
							 | 
						||
| 
								 | 
							
								      // $FlowFixMe
							 | 
						||
| 
								 | 
							
								      let node: HTMLStyleElement | null = document.querySelector(
							 | 
						||
| 
								 | 
							
								        `style[data-emotion="${key} ${serialized.name}"]`
							 | 
						||
| 
								 | 
							
								      )
							 | 
						||
| 
								 | 
							
								      if (cache.sheet.tags.length) {
							 | 
						||
| 
								 | 
							
								        sheet.before = cache.sheet.tags[0]
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (node !== null) {
							 | 
						||
| 
								 | 
							
								        rehydrating = true
							 | 
						||
| 
								 | 
							
								        // clear the hash so this node won't be recognizable as rehydratable by other <Global/>s
							 | 
						||
| 
								 | 
							
								        node.setAttribute('data-emotion', key)
							 | 
						||
| 
								 | 
							
								        sheet.hydrate([node])
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      sheetRef.current = [sheet, rehydrating]
							 | 
						||
| 
								 | 
							
								      return () => {
							 | 
						||
| 
								 | 
							
								        sheet.flush()
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }, [cache])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    useInsertionEffectWithLayoutFallback(() => {
							 | 
						||
| 
								 | 
							
								      let sheetRefCurrent = (sheetRef.current: any)
							 | 
						||
| 
								 | 
							
								      let [sheet, rehydrating] = sheetRefCurrent
							 | 
						||
| 
								 | 
							
								      if (rehydrating) {
							 | 
						||
| 
								 | 
							
								        sheetRefCurrent[1] = false
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (serialized.next !== undefined) {
							 | 
						||
| 
								 | 
							
								        // insert keyframes
							 | 
						||
| 
								 | 
							
								        insertStyles(cache, serialized.next, true)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (sheet.tags.length) {
							 | 
						||
| 
								 | 
							
								        // if this doesn't exist then it will be null so the style element will be appended
							 | 
						||
| 
								 | 
							
								        let element = sheet.tags[sheet.tags.length - 1].nextElementSibling
							 | 
						||
| 
								 | 
							
								        sheet.before = ((element: any): Element | null)
							 | 
						||
| 
								 | 
							
								        sheet.flush()
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      cache.insert(``, serialized, sheet, false)
							 | 
						||
| 
								 | 
							
								    }, [cache, serialized.name])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return null
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (process.env.NODE_ENV !== 'production') {
							 | 
						||
| 
								 | 
							
								  Global.displayName = 'EmotionGlobal'
							 | 
						||
| 
								 | 
							
								}
							 |