Spaces:
Sleeping
Sleeping
File size: 5,185 Bytes
56fda74 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
import isDevelopment from '#is-development'
/*
Based off glamor's StyleSheet, thanks Sunil ❤️
high performance StyleSheet for css-in-js systems
- uses multiple style tags behind the scenes for millions of rules
- uses `insertRule` for appending in production for *much* faster performance
// usage
import { StyleSheet } from '@emotion/sheet'
let styleSheet = new StyleSheet({ key: '', container: document.head })
styleSheet.insert('#box { border: 1px solid red; }')
- appends a css rule into the stylesheet
styleSheet.flush()
- empties the stylesheet of all its contents
*/
function sheetForTag(tag: HTMLStyleElement): CSSStyleSheet {
if (tag.sheet) {
return tag.sheet
}
// this weirdness brought to you by firefox
/* istanbul ignore next */
for (let i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i].ownerNode === tag) {
return document.styleSheets[i]
}
}
// this function should always return with a value
// TS can't understand it though so we make it stop complaining here
return undefined as any
}
export type Options = {
nonce?: string
key: string
container: Node
speedy?: boolean
prepend?: boolean
insertionPoint?: HTMLElement
}
function createStyleElement(options: Options): HTMLStyleElement {
let tag = document.createElement('style')
tag.setAttribute('data-emotion', options.key)
if (options.nonce !== undefined) {
tag.setAttribute('nonce', options.nonce)
}
tag.appendChild(document.createTextNode(''))
tag.setAttribute('data-s', '')
return tag
}
export class StyleSheet {
isSpeedy: boolean
ctr: number
tags: HTMLStyleElement[]
// Using Node instead of HTMLElement since container may be a ShadowRoot
container: Node
key: string
nonce: string | undefined
prepend: boolean | undefined
before: Element | null
insertionPoint: HTMLElement | undefined
private _alreadyInsertedOrderInsensitiveRule: boolean | undefined
constructor(options: Options) {
this.isSpeedy =
options.speedy === undefined ? !isDevelopment : options.speedy
this.tags = []
this.ctr = 0
this.nonce = options.nonce
// key is the value of the data-emotion attribute, it's used to identify different sheets
this.key = options.key
this.container = options.container
this.prepend = options.prepend
this.insertionPoint = options.insertionPoint
this.before = null
}
private _insertTag = (tag: HTMLStyleElement): void => {
let before
if (this.tags.length === 0) {
if (this.insertionPoint) {
before = this.insertionPoint.nextSibling
} else if (this.prepend) {
before = this.container.firstChild
} else {
before = this.before
}
} else {
before = this.tags[this.tags.length - 1].nextSibling
}
this.container.insertBefore(tag, before)
this.tags.push(tag)
}
hydrate(nodes: HTMLStyleElement[]): void {
nodes.forEach(this._insertTag)
}
insert(rule: string): void {
// the max length is how many rules we have per style tag, it's 65000 in speedy mode
// it's 1 in dev because we insert source maps that map a single rule to a location
// and you can only have one source map per style tag
if (this.ctr % (this.isSpeedy ? 65000 : 1) === 0) {
this._insertTag(createStyleElement(this))
}
const tag = this.tags[this.tags.length - 1]
if (isDevelopment) {
const isImportRule =
rule.charCodeAt(0) === 64 && rule.charCodeAt(1) === 105
if (isImportRule && this._alreadyInsertedOrderInsensitiveRule) {
// this would only cause problem in speedy mode
// but we don't want enabling speedy to affect the observable behavior
// so we report this error at all times
console.error(
`You're attempting to insert the following rule:\n` +
rule +
'\n\n`@import` rules must be before all other types of rules in a stylesheet but other rules have already been inserted. Please ensure that `@import` rules are before all other rules.'
)
}
this._alreadyInsertedOrderInsensitiveRule =
this._alreadyInsertedOrderInsensitiveRule || !isImportRule
}
if (this.isSpeedy) {
const sheet = sheetForTag(tag)
try {
// this is the ultrafast version, works across browsers
// the big drawback is that the css won't be editable in devtools
sheet.insertRule(rule, sheet.cssRules.length)
} catch (e) {
if (
isDevelopment &&
!/:(-moz-placeholder|-moz-focus-inner|-moz-focusring|-ms-input-placeholder|-moz-read-write|-moz-read-only|-ms-clear|-ms-expand|-ms-reveal){/.test(
rule
)
) {
console.error(
`There was a problem inserting the following rule: "${rule}"`,
e
)
}
}
} else {
tag.appendChild(document.createTextNode(rule))
}
this.ctr++
}
flush(): void {
this.tags.forEach(tag => tag.parentNode?.removeChild(tag))
this.tags = []
this.ctr = 0
if (isDevelopment) {
this._alreadyInsertedOrderInsensitiveRule = false
}
}
}
|