Skip to content

Commit

Permalink
feat: add getMatchedRules for dev mode
Browse files Browse the repository at this point in the history
  • Loading branch information
LastLeaf committed Jul 23, 2024
1 parent adebace commit f529799
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 9 deletions.
93 changes: 88 additions & 5 deletions glass-easel/src/backend/current_window_backend_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
type MediaQueryStatus,
type Observer,
} from './shared'
import type * as shared from './shared'

const DELEGATE_EVENTS = [
'touchstart',
Expand Down Expand Up @@ -527,11 +528,7 @@ export class CurrentWindowBackendContext implements Context {
cb: (computedStyle: { properties: { name: string; value: string }[] }) => void,
): void {
const style = window.getComputedStyle(target as unknown as HTMLElement)
const properties: { name: string; value: string }[] = []
for (let i = 0; i < style.length; i += 1) {
const name = style[i]!
properties.push({ name, value: style.getPropertyValue(name) })
}
const properties = collectStyleSheetProperties(style)
cb({ properties })
}

Expand Down Expand Up @@ -563,4 +560,90 @@ export class CurrentWindowBackendContext implements Context {
})
}
}

getMatchedRules(target: Element, cb: (res: shared.GetMatchedRulesResponses) => void): void {
const elem = target as unknown as HTMLElement
const sheets = document.styleSheets
const rules: shared.CSSRule[] = []
for (let i = 0; i < sheets.length; i += 1) {
const sheet = sheets[i]!
rules.push(...queryMatchedRules(i, sheet, elem))
}
const inlineText = elem.style.cssText
const inline = collectStyleSheetProperties(elem.style)
cb({ inline, inlineText, rules })
}
}

const collectStyleSheetProperties = (style: CSSStyleDeclaration) => {
const properties: shared.CSSProperty[] = []
for (let i = 0; i < style.length; i += 1) {
const name = style[i]!
const important = style.getPropertyPriority(name) === 'important'
properties.push({ name, value: style.getPropertyValue(name), important })
}
return properties
}

const forEachStyleSheetRule = (
sheet: CSSStyleSheet,
f: (ruleIndex: number, cssRule: CSSRule, ancestors: CSSGroupingRule[]) => void,
) => {
let ruleIndex = 0
const ancestors: CSSGroupingRule[] = []
const rec = (rules: CSSRuleList) => {
for (let i = 0; i < rules.length; i += 1) {
const cssRule = rules[i]!
if (cssRule instanceof CSSConditionRule || cssRule instanceof CSSLayerBlockRule) {
ancestors.push(cssRule)
rec(cssRule.cssRules)
ancestors.pop()
} else {
f(ruleIndex, cssRule, ancestors)
ruleIndex += 1
}
}
}
rec(sheet.cssRules)
}

const queryMatchedRules = (
sheetIndex: number,
sheet: CSSStyleSheet,
elem: HTMLElement,
): shared.CSSRule[] => {
const rules: shared.CSSRule[] = []
forEachStyleSheetRule(sheet, (ruleIndex, cssRule, ancestors) => {
if (cssRule instanceof CSSStyleRule) {
let inactive = false
const mediaQueries: string[] = []
for (let i = 0; i < ancestors.length; i += 1) {
const cssRule = ancestors[i]!
if (cssRule instanceof CSSMediaRule) {
if (!matchMedia(cssRule.conditionText).matches) {
inactive = true
}
mediaQueries.push(`@media ${cssRule.conditionText}`)
} else if (cssRule instanceof CSSLayerBlockRule) {
mediaQueries.push(`@layer ${cssRule.name}`)
}
}
const selector = cssRule.selectorText
if (elem.matches(selector)) {
const propertyText = cssRule.style.cssText
const properties = collectStyleSheetProperties(cssRule.style)
rules.push({
sheetIndex,
ruleIndex,
mediaQueries,
selector,
properties,
propertyText,
weightHighBits: 0, // FIXME infer priority values
inactive,
})
}
}
})
return rules
}
12 changes: 8 additions & 4 deletions glass-easel/src/backend/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,25 @@ export type ScrollOffset = {
export type CSSProperty = {
name: string
value: string
disabled: boolean
invalid: boolean
disabled?: boolean
invalid?: boolean
important?: boolean
}
export type CSSRule = {
sheetIndex: number
ruleIndex: number
mediaQueries: string[]
selector: string
properties: CSSProperty[]
weightHighBits: number
weightLowBits: number
propertyText?: string
weightHighBits?: number // the priority value of the layer level (0 by default)
weightLowBits?: number // the priority value of the selector level (calculated from selector by default)
inactive?: boolean
}

export type GetMatchedRulesResponses = {
inline: CSSProperty[]
inlineText?: string
rules: CSSRule[]
}

Expand Down
4 changes: 4 additions & 0 deletions glass-easel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export {
backend,
composedBackend,
domlikeBackend,
BoundingClientRect,
ScrollOffset,
CSSProperty,
CSSRule,
} from './backend'
export { CurrentWindowBackendContext } from './backend/current_window_backend_context'
export { EmptyBackendContext } from './backend/empty_backend'
Expand Down

0 comments on commit f529799

Please sign in to comment.