mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
84 lines
2.4 KiB
TypeScript
84 lines
2.4 KiB
TypeScript
import { addComponentListener, removeComponentListener } from '../settings'
|
|
import { deleteValue, getRandomId } from '../utils'
|
|
import { ShadowDomEntry } from './dom-entry'
|
|
import { ShadowDomObserver, shadowDomObserver } from './dom-observer'
|
|
|
|
export interface ShadowRootStyleEntry {
|
|
id: string
|
|
styleSheet: CSSStyleSheet
|
|
adoptedShadowDoms: ShadowDomEntry[]
|
|
remove: () => void
|
|
}
|
|
export interface ShadowRootStyleDefinition {
|
|
id?: string
|
|
style: string
|
|
}
|
|
export class ShadowRootStyles {
|
|
observer: ShadowDomObserver = shadowDomObserver
|
|
protected stylesMap = new Map<string, ShadowRootStyleEntry>()
|
|
|
|
protected addEntry(id: string, entry: ShadowRootStyleEntry) {
|
|
this.stylesMap.set(id, entry)
|
|
}
|
|
|
|
protected removeEntry(id: string) {
|
|
if (this.stylesMap.has(id)) {
|
|
this.stylesMap.delete(id)
|
|
}
|
|
}
|
|
|
|
async addStyle(definition: ShadowRootStyleDefinition) {
|
|
const { id, style } = definition
|
|
const entryId = `shadow-dom-style-${id !== undefined ? lodash.kebabCase(id) : getRandomId()}`
|
|
const styleSheet = new CSSStyleSheet()
|
|
await styleSheet.replace(style)
|
|
|
|
const adoptedShadowDoms: ShadowDomEntry[] = []
|
|
const destroy = this.observer.watchShadowDom({
|
|
added: async shadowDom => {
|
|
shadowDom.shadowRoot.adoptedStyleSheets.push(styleSheet)
|
|
adoptedShadowDoms.push(shadowDom)
|
|
},
|
|
})
|
|
|
|
const entry: ShadowRootStyleEntry = {
|
|
id: entryId,
|
|
styleSheet,
|
|
adoptedShadowDoms,
|
|
remove: () => {
|
|
destroy()
|
|
adoptedShadowDoms.forEach(it =>
|
|
deleteValue(it.shadowRoot.adoptedStyleSheets, sheet => sheet === styleSheet),
|
|
)
|
|
this.removeEntry(id)
|
|
},
|
|
}
|
|
this.addEntry(id, entry)
|
|
return entry
|
|
}
|
|
|
|
removeStyle(id: string) {
|
|
if (this.stylesMap.has(id)) {
|
|
const style = this.stylesMap.get(id)
|
|
style.remove()
|
|
this.removeEntry(id)
|
|
}
|
|
}
|
|
|
|
toggleWithComponent(path: string, definition: ShadowRootStyleDefinition) {
|
|
let entry: ShadowRootStyleEntry | undefined
|
|
const handler = async (rawValue: unknown) => {
|
|
const value = Boolean(rawValue)
|
|
if (value) {
|
|
entry = await this.addStyle(definition)
|
|
} else if (entry !== undefined) {
|
|
this.removeStyle(entry.id)
|
|
}
|
|
}
|
|
addComponentListener(path, handler, true)
|
|
return () => removeComponentListener(path, handler)
|
|
}
|
|
}
|
|
|
|
export const shadowRootStyles = new ShadowRootStyles()
|