-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathvcss.ts
126 lines (116 loc) · 3.59 KB
/
vcss.ts
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
// (C)opyright 2021-07-20 Dirk Holtwick, holtwick.it. All rights reserved.
import { parse } from 'css-what'
import type { VElement } from './vdom'
function log(..._args: any) {}
// Alternative could be https://github.com/leaverou/parsel
const cache: Record<string, any> = {}
export function parseSelector(selector: string) {
let ast = cache[selector]
if (ast == null) {
ast = parse(selector)
cache[selector] = ast
}
return ast
}
// Just a very small subset for now: https://github.com/fb55/css-what#api
export function matchSelector(
selector: string,
element: VElement,
{ debug = false } = {},
) {
for (const rules of parseSelector(selector)) {
if (debug) {
log('Selector:', selector)
log('Rules:', rules)
log('Element:', element)
}
const handleRules = (element: VElement, rules: any[]) => {
// let pos = 0
let success = false
for (const part of rules) {
const { type, name, action, value, _ignoreCase = true, data } = part
if (type === 'attribute') {
if (action === 'equals') {
success = element.getAttribute(name) === value
if (debug)
log('Attribute equals', success)
}
else if (action === 'start') {
success = !!element.getAttribute(name)?.startsWith(value)
if (debug)
log('Attribute start', success)
}
else if (action === 'end') {
success = !!element.getAttribute(name)?.endsWith(value)
if (debug)
log('Attribute start', success)
}
else if (action === 'element') {
if (name === 'class') {
success = element.classList.contains(value)
if (debug)
log('Attribute class', success)
}
else {
success = !!element.getAttribute(name)?.includes(value)
if (debug)
log('Attribute element', success)
}
}
else if (action === 'exists') {
success = element.hasAttribute(name)
if (debug)
log('Attribute exists', success)
}
else if (action === 'any') {
success = !!element.getAttribute(name)?.includes(value)
if (debug)
log('Attribute any', success)
}
else {
console.warn('Unknown CSS selector action', action)
}
}
else if (type === 'tag') {
success = element.tagName === name.toUpperCase()
if (debug)
log('Is tag', success)
}
else if (type === 'universal') {
success = true
if (debug)
log('Is universal', success)
}
else if (type === 'pseudo') {
if (name === 'not') {
let ok = true
data.forEach((rules: any) => {
if (!handleRules(element, rules))
ok = false
})
success = !ok
}
if (debug)
log('Is :not', success)
// } else if (type === 'descendant') {
// element = element.
}
// else if (type === 'descendant') {
// for (const child of element.childNodes)
// handleRules(child, rules.slice(pos))
// }
else {
console.warn('Unknown CSS selector type', type, selector, rules)
}
// log(success, selector, part, element)
if (!success)
break
// pos += 1
}
return success
}
if (handleRules(element, rules))
return true
}
return false
}