@react-x11/components 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flow/node.d.ts +14 -6
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +82 -64
- package/dist/flow/node.js.map +1 -1
- package/dist/html/controls.d.ts +38 -10
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +43 -58
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +134 -20
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +538 -128
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +19 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +86 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +40 -2
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +391 -13
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +52 -1
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +328 -11
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts +11 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +73 -7
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +10 -1
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +27 -2
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +5 -0
- package/dist/html/css/vars.d.ts.map +1 -1
- package/dist/html/css/vars.js +2 -2
- package/dist/html/css/vars.js.map +1 -1
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +38 -3
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +25 -1
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +99 -6
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/form.d.ts +200 -0
- package/dist/html/form.d.ts.map +1 -0
- package/dist/html/form.js +801 -0
- package/dist/html/form.js.map +1 -0
- package/dist/html/index.d.ts +12 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +30 -264
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +11 -2
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +186 -19
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +65 -7
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +87 -22
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +2 -2
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +8 -3
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/flex.js +4 -1
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +4 -0
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +26 -0
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/inline.d.ts +26 -2
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +1232 -115
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +120 -15
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +993 -174
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +75 -2
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +836 -200
- package/dist/html/paint.js.map +1 -1
- package/dist/html/surfaces.d.ts +7 -2
- package/dist/html/surfaces.d.ts.map +1 -1
- package/dist/html/surfaces.js +16 -0
- package/dist/html/surfaces.js.map +1 -1
- package/dist/html/widgets.d.ts +29 -0
- package/dist/html/widgets.d.ts.map +1 -0
- package/dist/html/widgets.js +620 -0
- package/dist/html/widgets.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/window.d.ts +22 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +35 -8
- package/dist/internal/window.js.map +1 -1
- package/dist/richtext/node.d.ts +7 -0
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js.map +1 -1
- package/package.json +6 -4
- package/src/flow/node.ts +80 -64
- package/src/html/controls.ts +79 -58
- package/src/html/css/cascade.ts +610 -146
- package/src/html/css/color.ts +87 -0
- package/src/html/css/parse.ts +431 -13
- package/src/html/css/style.ts +367 -11
- package/src/html/css/ua.ts +76 -8
- package/src/html/css/values.ts +31 -3
- package/src/html/css/vars.ts +2 -2
- package/src/html/dom.ts +41 -3
- package/src/html/fonts.ts +116 -6
- package/src/html/form.ts +962 -0
- package/src/html/index.ts +49 -300
- package/src/html/layout/block.ts +231 -16
- package/src/html/layout/boxes.ts +133 -32
- package/src/html/layout/cache.ts +8 -3
- package/src/html/layout/flex.ts +4 -1
- package/src/html/layout/floats.ts +24 -0
- package/src/html/layout/inline.ts +1445 -108
- package/src/html/node.ts +1010 -166
- package/src/html/paint.ts +980 -270
- package/src/html/surfaces.ts +21 -1
- package/src/html/widgets.ts +735 -0
- package/src/index.ts +1 -0
- package/src/internal/window.ts +36 -8
- package/src/richtext/node.ts +7 -0
package/dist/html/css/cascade.js
CHANGED
|
@@ -24,6 +24,8 @@ import { asciiLower, escapeEnd, mediaMatches, readIdent, startsIdent, } from './
|
|
|
24
24
|
import { applyDeclaration, blockify, settleContentVisibility, settleOverflow, settleClamp, copyStyle, decorate, inherit, initialOne, initialStyle, isInherited, } from './style.js';
|
|
25
25
|
import { parseDeclarations } from './parse.js';
|
|
26
26
|
import { CounterStyles, counterStyleRule } from './counter-styles.js';
|
|
27
|
+
import { PALETTE_CHROME } from './ua.js';
|
|
28
|
+
import { viewportUnit } from './values.js';
|
|
27
29
|
import { customProperties, substituteIn } from './vars.js';
|
|
28
30
|
/** Where a declaration came from. Higher wins before specificity is asked. */
|
|
29
31
|
var Origin;
|
|
@@ -35,6 +37,22 @@ var Origin;
|
|
|
35
37
|
Origin[Origin["AuthorImportant"] = 4] = "AuthorImportant";
|
|
36
38
|
Origin[Origin["InlineImportant"] = 5] = "InlineImportant";
|
|
37
39
|
})(Origin || (Origin = {}));
|
|
40
|
+
export const HOVER_UNTOUCHED = 0;
|
|
41
|
+
export const HOVER_TOUCHED = 1;
|
|
42
|
+
export const HOVER_FOLLOWED = 2;
|
|
43
|
+
/** Which pseudo-elements' rules test the pointer (`Cascade.hoverPseudo`). */
|
|
44
|
+
export const HOVER_PSEUDO_NONE = 0;
|
|
45
|
+
export const HOVER_PSEUDO_GENERATED = 1;
|
|
46
|
+
export const HOVER_PSEUDO_OTHER = 2;
|
|
47
|
+
/** The pseudo-elements a pointer move can restyle: `::selection`'s style
|
|
48
|
+
* is worked out as a selection is painted. */
|
|
49
|
+
const POINTER_PSEUDO_ELEMENTS = [
|
|
50
|
+
'before',
|
|
51
|
+
'after',
|
|
52
|
+
'first-letter',
|
|
53
|
+
'first-line',
|
|
54
|
+
'marker',
|
|
55
|
+
];
|
|
38
56
|
/**
|
|
39
57
|
* What makes a selector's match depend on more than the element's own tag and
|
|
40
58
|
* attributes and its ancestors' — its siblings, where it sits among them,
|
|
@@ -61,6 +79,11 @@ class RuleIndex {
|
|
|
61
79
|
/** Whether any rule in here is pointer-sensitive, so the renderer knows
|
|
62
80
|
* whether a pointer move can change the cascade at all. */
|
|
63
81
|
hoverSensitive = false;
|
|
82
|
+
/** The rules that test `:hover`, in buckets of their own: the ones whose
|
|
83
|
+
* answers a pointer move can change, which is all that is asked to know
|
|
84
|
+
* whether an element's did (`Cascade.pointerChanged`). Null until there
|
|
85
|
+
* is one. */
|
|
86
|
+
pointer = null;
|
|
64
87
|
/** The buckets holding a rule an element cannot share its style under
|
|
65
88
|
* (`UNSHAREABLE`): the ids, classes and tags whose elements compute
|
|
66
89
|
* their own, and whether the universal bucket makes every element do so. */
|
|
@@ -119,20 +142,27 @@ class RuleIndex {
|
|
|
119
142
|
else
|
|
120
143
|
this.ownStyleEverywhere = true;
|
|
121
144
|
}
|
|
122
|
-
|
|
145
|
+
this._bucket(key).push(indexed);
|
|
146
|
+
if (HOVER.test(rule.selector)) {
|
|
147
|
+
this.pointer ??= new RuleIndex();
|
|
148
|
+
this.pointer.size += 1;
|
|
149
|
+
this.pointer._bucket(key).push(indexed);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
_bucket(key) {
|
|
153
|
+
return key.kind === 'id'
|
|
123
154
|
? mapBucket(this.byId, key.name)
|
|
124
155
|
: key.kind === 'class'
|
|
125
156
|
? mapBucket(this.byClass, key.name)
|
|
126
157
|
: key.kind === 'tag'
|
|
127
158
|
? mapBucket(this.byTag, key.name)
|
|
128
159
|
: this.universal;
|
|
129
|
-
bucket.push(indexed);
|
|
130
160
|
}
|
|
131
161
|
}
|
|
132
162
|
/** A selector's trailing `::before`, `::after`, `::first-letter`,
|
|
133
|
-
* `::first-line` or `::
|
|
134
|
-
* its four. */
|
|
135
|
-
const PSEUDO_ELEMENT = /::?(before|after|first-letter|first-line)$|::marker$/i;
|
|
163
|
+
* `::first-line`, `::marker` or `::selection`, or CSS 2's single-colon
|
|
164
|
+
* spelling of any of its four. */
|
|
165
|
+
const PSEUDO_ELEMENT = /::?(before|after|first-letter|first-line)$|::(marker|selection)$/i;
|
|
136
166
|
/**
|
|
137
167
|
* A rule for a pseudo-element, as the pseudo-element it styles and a rule
|
|
138
168
|
* for the element it hangs off, which is what gets matched. The specificity
|
|
@@ -152,22 +182,35 @@ function splitPseudoElement(rule) {
|
|
|
152
182
|
? `${trimmed} *`
|
|
153
183
|
: trimmed;
|
|
154
184
|
return {
|
|
155
|
-
which: (m[1] ??
|
|
185
|
+
which: (m[1] ?? m[2]).toLowerCase(),
|
|
156
186
|
rule: { ...rule, selector },
|
|
157
187
|
};
|
|
158
188
|
}
|
|
159
189
|
/**
|
|
160
190
|
* The compounds of a selector that test `:hover`, each without it (and
|
|
161
191
|
* without an `:active` beside it), and whether one is followed by a sibling
|
|
162
|
-
* combinator, which reaches the element's later siblings.
|
|
163
|
-
* `:hover` sits inside a functional pseudo-class — `:not(:hover)`,
|
|
164
|
-
* `:is(:hover)` — and reaches elements no compound names.
|
|
192
|
+
* combinator, which reaches the element's later siblings.
|
|
165
193
|
*
|
|
166
|
-
* A `:hover`
|
|
167
|
-
*
|
|
194
|
+
* A `:hover` inside `:is()`, `:where()` or `:not()` is found where it is:
|
|
195
|
+
* each takes a selector list and matches the element it is written on, so
|
|
196
|
+
* a list entry of one compound tests that element — `a:not(:hover)` is a
|
|
197
|
+
* compound of `a`'s that tests the pointer — and an entry with combinators
|
|
198
|
+
* in it names an ancestor or an earlier sibling, as it would outside.
|
|
199
|
+
* Tailwind 4 writes every `group-hover:` that way, `:is(:where(.group):hover
|
|
200
|
+
* *)`, and a page of them built its document again on every move. Whatever
|
|
201
|
+
* the nesting, the element whose hover changed is one a compound found here
|
|
202
|
+
* matches, and what changes with it is itself, what is in it, and what
|
|
203
|
+
* follows it where a sibling combinator is in play: a selector reads an
|
|
204
|
+
* element, its ancestors and the siblings before each of them, and nothing
|
|
205
|
+
* else. `nested` is left for a function this cannot read a selector list
|
|
206
|
+
* in — `:nth-child(… of :hover)`, `:host()` — which reaches elements no
|
|
207
|
+
* compound names.
|
|
208
|
+
*
|
|
209
|
+
* A `:hover` in a compound's `:has()` is the other way about, since what
|
|
210
|
+
* it tests is below: the compound, without its `:has()`, is an ancestor of
|
|
168
211
|
* the element the pointer is over — or an earlier sibling of it or of an
|
|
169
212
|
* ancestor, where the argument starts at a sibling (Selectors 4, 4.5). So
|
|
170
|
-
* it is given as an anchor (`has`)
|
|
213
|
+
* it is given as an anchor (`has`).
|
|
171
214
|
*
|
|
172
215
|
* `:active` is not the pointer's here: nothing sets it (`setPointer` is
|
|
173
216
|
* handed none), so a selector that tests only it never changes as the
|
|
@@ -180,8 +223,17 @@ export function pointerCompounds(selector) {
|
|
|
180
223
|
siblings: false,
|
|
181
224
|
nested: false,
|
|
182
225
|
has: [],
|
|
226
|
+
followed: [],
|
|
183
227
|
};
|
|
184
|
-
|
|
228
|
+
scanComplex(selector, out);
|
|
229
|
+
return out;
|
|
230
|
+
}
|
|
231
|
+
/** The functions that take a selector list and match the element they are
|
|
232
|
+
* written on (Selectors 4, 4.2 to 4.4), under the names css-select and
|
|
233
|
+
* older sheets know them by. */
|
|
234
|
+
const MATCHES_ANY = /^(?:is|where|not|matches|any|-webkit-any|-moz-any)$/i;
|
|
235
|
+
/** The compounds of a complex selector, each with the combinator after it. */
|
|
236
|
+
function compoundsOf(selector) {
|
|
185
237
|
const parts = [];
|
|
186
238
|
let start = 0;
|
|
187
239
|
let depth = 0;
|
|
@@ -214,94 +266,203 @@ export function pointerCompounds(selector) {
|
|
|
214
266
|
}
|
|
215
267
|
}
|
|
216
268
|
split(selector.length, '');
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
269
|
+
return parts;
|
|
270
|
+
}
|
|
271
|
+
/** A selector list's entries: split at its top-level commas. */
|
|
272
|
+
function selectorsOf(list) {
|
|
273
|
+
const entries = [];
|
|
274
|
+
let start = 0;
|
|
275
|
+
let depth = 0;
|
|
276
|
+
let quote = '';
|
|
277
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
278
|
+
const c = list[i];
|
|
279
|
+
if (quote) {
|
|
280
|
+
if (c === quote && list[i - 1] !== '\\')
|
|
281
|
+
quote = '';
|
|
282
|
+
}
|
|
283
|
+
else if (c === '\\')
|
|
284
|
+
i = escapeEnd(list, i) - 1;
|
|
285
|
+
else if (c === '"' || c === "'")
|
|
286
|
+
quote = c;
|
|
287
|
+
else if (c === '(' || c === '[')
|
|
288
|
+
depth += 1;
|
|
289
|
+
else if (c === ')' || c === ']')
|
|
290
|
+
depth = Math.max(0, depth - 1);
|
|
291
|
+
else if (c === ',' && depth === 0) {
|
|
292
|
+
entries.push(list.slice(start, i));
|
|
293
|
+
start = i + 1;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
entries.push(list.slice(start));
|
|
297
|
+
return entries;
|
|
298
|
+
}
|
|
299
|
+
/** Where the parenthesis opened at `open` closes, or the text's end. */
|
|
300
|
+
function closeOf(text, open) {
|
|
301
|
+
let depth = 0;
|
|
302
|
+
let quote = '';
|
|
303
|
+
for (let i = open; i < text.length; i += 1) {
|
|
304
|
+
const c = text[i];
|
|
305
|
+
if (quote) {
|
|
306
|
+
if (c === quote && text[i - 1] !== '\\')
|
|
307
|
+
quote = '';
|
|
308
|
+
}
|
|
309
|
+
else if (c === '\\')
|
|
310
|
+
i = escapeEnd(text, i) - 1;
|
|
311
|
+
else if (c === '"' || c === "'")
|
|
312
|
+
quote = c;
|
|
313
|
+
else if (c === '(' || c === '[')
|
|
314
|
+
depth += 1;
|
|
315
|
+
else if (c === ')' || c === ']') {
|
|
316
|
+
depth -= 1;
|
|
317
|
+
if (depth === 0)
|
|
318
|
+
return i;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return text.length;
|
|
322
|
+
}
|
|
323
|
+
/** `pointerCompounds` for one complex selector, into `out`. */
|
|
324
|
+
function scanComplex(selector, out) {
|
|
325
|
+
for (const { text, next } of compoundsOf(selector)) {
|
|
326
|
+
const compoundsFrom = out.compounds.length;
|
|
327
|
+
const hasFrom = out.has.length;
|
|
328
|
+
const found = scanCompound(text, out);
|
|
329
|
+
const sibling = next === '+' || next === '~';
|
|
330
|
+
if (found.args.length) {
|
|
331
|
+
out.has.push({
|
|
332
|
+
anchor: found.bare.trim() || '*',
|
|
333
|
+
siblings: found.args.some((arg) => selectorsOf(arg).some((one) => /^\s*[+~]/.test(one))),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (found.pointer)
|
|
337
|
+
out.compounds.push(found.bare.trim() || '*');
|
|
338
|
+
// what follows the element this compound matches follows one whose
|
|
339
|
+
// hover, or whose answer to a `:has()`, changed
|
|
340
|
+
// — the one this compound matches, or one a selector nested in it
|
|
341
|
+
// named, which is more than it reaches and never less
|
|
342
|
+
if (sibling && (found.pointer || found.below || found.args.length)) {
|
|
343
|
+
out.siblings = true;
|
|
344
|
+
for (let i = compoundsFrom; i < out.compounds.length; i += 1) {
|
|
345
|
+
out.followed.push(out.compounds[i]);
|
|
236
346
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
347
|
+
for (let i = hasFrom; i < out.has.length; i += 1) {
|
|
348
|
+
out.followed.push(out.has[i].anchor);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* What one compound says of the pointer: the compound without what tests
|
|
355
|
+
* it (`bare`), whether it tests the element's own hover (`pointer`), the
|
|
356
|
+
* arguments of each `:has()` of the element's that tests it below (`args`),
|
|
357
|
+
* and whether a selector nested in it named another element's (`below`),
|
|
358
|
+
* whose compounds went into `out`.
|
|
359
|
+
*/
|
|
360
|
+
function scanCompound(text, out) {
|
|
361
|
+
let bare = '';
|
|
362
|
+
let pointer = false;
|
|
363
|
+
let below = false;
|
|
364
|
+
const args = [];
|
|
365
|
+
let bracket = 0;
|
|
366
|
+
let quote = '';
|
|
367
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
368
|
+
const c = text[i];
|
|
369
|
+
if (quote) {
|
|
370
|
+
if (c === quote && text[i - 1] !== '\\')
|
|
371
|
+
quote = '';
|
|
372
|
+
bare += c;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (c === '\\') {
|
|
376
|
+
const end = escapeEnd(text, i);
|
|
377
|
+
bare += text.slice(i, end);
|
|
378
|
+
i = end - 1;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (c === '"' || c === "'")
|
|
382
|
+
quote = c;
|
|
383
|
+
else if (c === '[')
|
|
384
|
+
bracket += 1;
|
|
385
|
+
else if (c === ']')
|
|
386
|
+
bracket = Math.max(0, bracket - 1);
|
|
387
|
+
else if (c === ':' && bracket === 0 && text[i + 1] !== ':') {
|
|
388
|
+
const m = POINTER_PSEUDO_AT.exec(text.slice(i));
|
|
389
|
+
if (m) {
|
|
390
|
+
if (m[0].length === 6)
|
|
391
|
+
pointer = true;
|
|
392
|
+
i += m[0].length - 1;
|
|
241
393
|
continue;
|
|
242
394
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
395
|
+
const fn = FUNCTION_AT.exec(text.slice(i));
|
|
396
|
+
if (fn) {
|
|
397
|
+
const open = i + fn[0].length - 1;
|
|
398
|
+
const close = closeOf(text, open);
|
|
399
|
+
const arg = text.slice(open + 1, close);
|
|
400
|
+
const whole = text.slice(i, close + 1);
|
|
401
|
+
i = close;
|
|
402
|
+
if (!HOVER.test(arg)) {
|
|
403
|
+
bare += whole;
|
|
404
|
+
continue;
|
|
250
405
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
else if (c === ')' || c === ']') {
|
|
254
|
-
depth = Math.max(0, depth - 1);
|
|
255
|
-
if (depth === 0 && hasAt >= 0 && c === ')') {
|
|
406
|
+
const name = fn[1].toLowerCase();
|
|
407
|
+
if (name === 'has') {
|
|
256
408
|
// the compound is kept without it: its argument is what the
|
|
257
|
-
// pointer changes
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
bare = bare.slice(0, hasAt);
|
|
262
|
-
}
|
|
263
|
-
else
|
|
264
|
-
bare += c;
|
|
265
|
-
hasAt = -1;
|
|
266
|
-
continue;
|
|
409
|
+
// pointer changes, and whatever holds the `:hover` in there, the
|
|
410
|
+
// element it tests is in the anchor's subtree or a later
|
|
411
|
+
// sibling's
|
|
412
|
+
args.push(arg);
|
|
267
413
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
414
|
+
else if (MATCHES_ANY.test(name)) {
|
|
415
|
+
// Kept as what the entries that test this element's hover ask
|
|
416
|
+
// of it besides — `:where(a:hover)` is a compound of `a`'s, and
|
|
417
|
+
// so is `:not(a:hover)`, which only an `a` can change its answer
|
|
418
|
+
// to. Tailwind's typography writes its links `.prose
|
|
419
|
+
// :where(a:hover):not(:where(.not-prose, .not-prose *))`, and
|
|
420
|
+
// without the `a` that was a compound every element matched.
|
|
421
|
+
const own = [];
|
|
422
|
+
let any = false;
|
|
423
|
+
for (const entry of selectorsOf(arg)) {
|
|
424
|
+
if (!HOVER.test(entry))
|
|
425
|
+
continue;
|
|
426
|
+
const parts = compoundsOf(entry);
|
|
427
|
+
if (parts.length !== 1) {
|
|
428
|
+
scanComplex(entry, out);
|
|
429
|
+
below = true;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
// of this element itself
|
|
433
|
+
const one = scanCompound(parts[0].text, out);
|
|
434
|
+
if (one.below)
|
|
435
|
+
below = true;
|
|
436
|
+
if (!one.pointer && !one.args.length)
|
|
437
|
+
continue;
|
|
438
|
+
if (one.pointer)
|
|
283
439
|
pointer = true;
|
|
284
|
-
|
|
285
|
-
|
|
440
|
+
args.push(...one.args);
|
|
441
|
+
const rest = one.bare.trim();
|
|
442
|
+
if (rest && rest !== '*')
|
|
443
|
+
own.push(rest);
|
|
444
|
+
else
|
|
445
|
+
any = true;
|
|
286
446
|
}
|
|
447
|
+
if (own.length && !any)
|
|
448
|
+
bare += `:is(${own.join(',')})`;
|
|
287
449
|
}
|
|
450
|
+
else {
|
|
451
|
+
out.nested = true;
|
|
452
|
+
bare += whole;
|
|
453
|
+
}
|
|
454
|
+
continue;
|
|
288
455
|
}
|
|
289
|
-
bare += c;
|
|
290
|
-
}
|
|
291
|
-
if (anchored) {
|
|
292
|
-
out.has.push({
|
|
293
|
-
anchor: bare.trim() || '*',
|
|
294
|
-
siblings: args.some((arg) => arg.split(',').some((one) => /^\s*[+~]/.test(one))),
|
|
295
|
-
});
|
|
296
456
|
}
|
|
297
|
-
|
|
298
|
-
continue;
|
|
299
|
-
out.compounds.push(bare.trim() || '*');
|
|
300
|
-
if (next === '+' || next === '~')
|
|
301
|
-
out.siblings = true;
|
|
457
|
+
bare += c;
|
|
302
458
|
}
|
|
303
|
-
return
|
|
459
|
+
return { bare, pointer, below, args };
|
|
304
460
|
}
|
|
461
|
+
/** A functional pseudo-class's name and its opening parenthesis, at the
|
|
462
|
+
* start of a string. */
|
|
463
|
+
const FUNCTION_AT = /^:([\w-]+)\(/;
|
|
464
|
+
/** `:hover` anywhere in a selector. */
|
|
465
|
+
const HOVER = /:hover(?![\w-])/i;
|
|
305
466
|
/** `:hover` or `:active` at the start of a string, and nothing longer. */
|
|
306
467
|
const POINTER_PSEUDO_AT = /^:(?:hover|active)(?![\w-])/i;
|
|
307
468
|
/** `:hover` or `:active` anywhere in a selector. */
|
|
@@ -455,23 +616,22 @@ export class Cascade {
|
|
|
455
616
|
'first-letter': new RuleIndex(),
|
|
456
617
|
'first-line': new RuleIndex(),
|
|
457
618
|
marker: new RuleIndex(),
|
|
619
|
+
selection: new RuleIndex(),
|
|
458
620
|
};
|
|
459
621
|
_adapter;
|
|
460
622
|
/** The compounds of the selectors that test the pointer, each without
|
|
461
623
|
* its `:hover` or `:active` (`pointerCompounds`), and their matchers,
|
|
462
624
|
* compiled when a pointer move first asks (`hoverTouches`). */
|
|
463
|
-
_hoverCompounds = new
|
|
625
|
+
_hoverCompounds = new Map();
|
|
464
626
|
_hoverMatchers = null;
|
|
465
627
|
/** Whether a pointer move can be restyled where it happened
|
|
466
628
|
* (`HtmlViewNode._hoverInPlace`): false where a selector tests the
|
|
467
629
|
* pointer inside a functional pseudo-class, which reaches elements no
|
|
468
630
|
* compound names. */
|
|
469
631
|
hoverLocal = true;
|
|
470
|
-
/**
|
|
471
|
-
*
|
|
472
|
-
|
|
473
|
-
/** The compounds whose `:has()` tests the pointer, each without it, and
|
|
474
|
-
* whether its argument starts at a sibling (`hoverAnchors`). */
|
|
632
|
+
/** The compounds whose `:has()` tests the pointer, each without it,
|
|
633
|
+
* whether its argument starts at a sibling, and whether a sibling
|
|
634
|
+
* combinator follows it (`hoverAnchors`). */
|
|
475
635
|
_hoverAnchors = new Map();
|
|
476
636
|
_anchorMatchers = null;
|
|
477
637
|
_pointer = NO_POINTER;
|
|
@@ -511,6 +671,12 @@ export class Cascade {
|
|
|
511
671
|
/** The families the document loads itself (`fonts.ts`), or null for a
|
|
512
672
|
* document with no `@font-face`. */
|
|
513
673
|
_families;
|
|
674
|
+
/** The root element's computed style: its `font-size` is what a `rem`
|
|
675
|
+
* is and its line height what an `rlh` is (CSS Values 4, 6.1.1), in
|
|
676
|
+
* every element but the root, whose own declarations measure them from
|
|
677
|
+
* the initial values. Set as the root's style is computed, which a build
|
|
678
|
+
* does ahead of every element under it. */
|
|
679
|
+
_root = null;
|
|
514
680
|
_mapFamilies;
|
|
515
681
|
/** Whether any declaration has a length in `lh` or `rlh`: only then is
|
|
516
682
|
* the line height settled ahead of the declarations that read it. */
|
|
@@ -552,6 +718,11 @@ export class Cascade {
|
|
|
552
718
|
}
|
|
553
719
|
for (const bp of sheet.breakpoints)
|
|
554
720
|
breakpoints.add(bp);
|
|
721
|
+
// a query on the viewport's height reads it as a `vh` does
|
|
722
|
+
if (sheet.readsHeight)
|
|
723
|
+
this.readsViewportHeight = true;
|
|
724
|
+
if (sheet.readsWidth)
|
|
725
|
+
this.readsViewportWidth = true;
|
|
555
726
|
}
|
|
556
727
|
this.breakpoints = [...breakpoints].sort((a, b) => a - b);
|
|
557
728
|
this.counterStyles = new CounterStyles(counterStyles);
|
|
@@ -583,7 +754,7 @@ export class Cascade {
|
|
|
583
754
|
if (!VIEWPORT_UNIT.test(d.value))
|
|
584
755
|
continue;
|
|
585
756
|
for (const m of d.value.matchAll(VIEWPORT_UNITS)) {
|
|
586
|
-
const unit = m[1]
|
|
757
|
+
const unit = viewportUnit(m[1]);
|
|
587
758
|
if (unit !== 'vh')
|
|
588
759
|
this.readsViewportWidth = true;
|
|
589
760
|
if (unit !== 'vw')
|
|
@@ -603,42 +774,78 @@ export class Cascade {
|
|
|
603
774
|
setPointer(pointer) {
|
|
604
775
|
this._pointer = pointer;
|
|
605
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Which pseudo-elements' rules test the pointer: none's
|
|
779
|
+
* (`HOVER_PSEUDO_NONE`); a `::before`'s or an `::after`'s only
|
|
780
|
+
* (`HOVER_PSEUDO_GENERATED`), whose boxes a move restyles where they are
|
|
781
|
+
* as it does an element's; or a `::first-line`'s, a `::first-letter`'s
|
|
782
|
+
* or a `::marker`'s (`HOVER_PSEUDO_OTHER`), which layout styles and a
|
|
783
|
+
* move cannot.
|
|
784
|
+
*/
|
|
785
|
+
get hoverPseudo() {
|
|
786
|
+
if (this._pseudo['first-letter'].hoverSensitive ||
|
|
787
|
+
this._pseudo['first-line'].hoverSensitive ||
|
|
788
|
+
this._pseudo.marker.hoverSensitive) {
|
|
789
|
+
return HOVER_PSEUDO_OTHER;
|
|
790
|
+
}
|
|
791
|
+
return this._pseudo.before.hoverSensitive ||
|
|
792
|
+
this._pseudo.after.hoverSensitive
|
|
793
|
+
? HOVER_PSEUDO_GENERATED
|
|
794
|
+
: HOVER_PSEUDO_NONE;
|
|
795
|
+
}
|
|
606
796
|
/**
|
|
607
797
|
* Whether the pointer entering or leaving `el` can change a style: one of
|
|
608
798
|
* the compounds a selector tests the pointer in matches it, the pointer
|
|
609
799
|
* aside. A move between two paragraphs of a page whose only such rule is
|
|
610
|
-
* `a:hover` touches nothing, and restyles nothing.
|
|
800
|
+
* `a:hover` touches nothing, and restyles nothing. `HOVER_FOLLOWED`
|
|
801
|
+
* where a sibling combinator follows one that matches, so the element's
|
|
802
|
+
* later siblings can change with it — those rules' elements alone: a
|
|
803
|
+
* sheet with one `.peer:hover ~ *` in it does not make every row of a
|
|
804
|
+
* table restyle the rows after it.
|
|
611
805
|
*/
|
|
612
806
|
hoverTouches(el) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
807
|
+
// the followed ones first, so the first that matches is the answer
|
|
808
|
+
this._hoverMatchers ??= [...this._hoverCompounds]
|
|
809
|
+
.sort(([, a], [, b]) => Number(b) - Number(a))
|
|
810
|
+
.flatMap(([compound, followed]) => {
|
|
811
|
+
const match = this._compile(compound);
|
|
812
|
+
return match ? [{ match, followed }] : [];
|
|
616
813
|
});
|
|
617
|
-
for (const match of this._hoverMatchers)
|
|
814
|
+
for (const { match, followed } of this._hoverMatchers) {
|
|
618
815
|
if (match(el))
|
|
619
|
-
return
|
|
620
|
-
|
|
816
|
+
return followed ? HOVER_FOLLOWED : HOVER_TOUCHED;
|
|
817
|
+
}
|
|
818
|
+
return HOVER_UNTOUCHED;
|
|
621
819
|
}
|
|
622
820
|
/**
|
|
623
821
|
* The elements whose `:has()` may have changed its answer as `el` was
|
|
624
822
|
* hovered or left: the ancestors of it a compound whose `:has()` tests
|
|
625
823
|
* the pointer names, and their earlier siblings and its own where the
|
|
626
824
|
* argument starts at a sibling — a superset, which is restyled and found
|
|
627
|
-
* the same where it did not. Into `into
|
|
825
|
+
* the same where it did not. Into `into`, each with whether its later
|
|
826
|
+
* siblings can change with it (`hoverTouches`).
|
|
628
827
|
*/
|
|
629
828
|
hoverAnchors(el, into) {
|
|
630
829
|
if (!this._hoverAnchors.size)
|
|
631
830
|
return;
|
|
632
|
-
this._anchorMatchers ??= [...this._hoverAnchors]
|
|
831
|
+
this._anchorMatchers ??= [...this._hoverAnchors]
|
|
832
|
+
.sort(([, a], [, b]) => Number(b.followed) - Number(a.followed))
|
|
833
|
+
.flatMap(([anchor, { siblings, followed }]) => {
|
|
633
834
|
const match = this._compile(anchor);
|
|
634
|
-
return match ? [{ match, siblings }] : [];
|
|
835
|
+
return match ? [{ match, siblings, followed }] : [];
|
|
635
836
|
});
|
|
837
|
+
const note = (at, followed) => {
|
|
838
|
+
if (followed)
|
|
839
|
+
into.set(at, HOVER_FOLLOWED);
|
|
840
|
+
else if (!into.has(at))
|
|
841
|
+
into.set(at, HOVER_TOUCHED);
|
|
842
|
+
};
|
|
636
843
|
const siblings = this._anchorMatchers.some((m) => m.siblings);
|
|
637
844
|
for (let at = el; at;) {
|
|
638
845
|
if (at !== el) {
|
|
639
|
-
for (const { match } of this._anchorMatchers) {
|
|
846
|
+
for (const { match, followed } of this._anchorMatchers) {
|
|
640
847
|
if (match(at)) {
|
|
641
|
-
|
|
848
|
+
note(at, followed);
|
|
642
849
|
break;
|
|
643
850
|
}
|
|
644
851
|
}
|
|
@@ -647,9 +854,9 @@ export class Cascade {
|
|
|
647
854
|
for (let s = at.prev; s; s = s.prev) {
|
|
648
855
|
if (!isTag(s))
|
|
649
856
|
continue;
|
|
650
|
-
for (const
|
|
651
|
-
if (
|
|
652
|
-
|
|
857
|
+
for (const m of this._anchorMatchers) {
|
|
858
|
+
if (m.siblings && m.match(s)) {
|
|
859
|
+
note(s, m.followed);
|
|
653
860
|
break;
|
|
654
861
|
}
|
|
655
862
|
}
|
|
@@ -665,12 +872,16 @@ export class Cascade {
|
|
|
665
872
|
const found = pointerCompounds(selector);
|
|
666
873
|
if (found.nested)
|
|
667
874
|
this.hoverLocal = false;
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
875
|
+
const followed = new Set(found.followed);
|
|
876
|
+
for (const c of found.compounds) {
|
|
877
|
+
this._hoverCompounds.set(c, followed.has(c) || (this._hoverCompounds.get(c) ?? false));
|
|
878
|
+
}
|
|
672
879
|
for (const { anchor, siblings } of found.has) {
|
|
673
|
-
|
|
880
|
+
const was = this._hoverAnchors.get(anchor);
|
|
881
|
+
this._hoverAnchors.set(anchor, {
|
|
882
|
+
siblings: siblings || (was?.siblings ?? false),
|
|
883
|
+
followed: followed.has(anchor) || (was?.followed ?? false),
|
|
884
|
+
});
|
|
674
885
|
}
|
|
675
886
|
}
|
|
676
887
|
/** A selector compiled as a rule's is, or null where css-select refuses
|
|
@@ -707,12 +918,34 @@ export class Cascade {
|
|
|
707
918
|
_customs = new Map();
|
|
708
919
|
_ids = new WeakMap();
|
|
709
920
|
_nextId = 1;
|
|
710
|
-
/**
|
|
711
|
-
*
|
|
712
|
-
|
|
921
|
+
/** The styles kept from the build before, while a build that keeps them
|
|
922
|
+
* runs, and what each kept element shares under in this one. */
|
|
923
|
+
_kept = null;
|
|
924
|
+
_keptShared = new Map();
|
|
925
|
+
/**
|
|
926
|
+
* A box tree is about to be built: the styles shared in the last build
|
|
927
|
+
* were computed against a pointer and a viewport that may have moved.
|
|
928
|
+
*
|
|
929
|
+
* With `kept`, only its `restyle` elements are matched: a build the
|
|
930
|
+
* pointer asked for — a hover that moved something — changes the styles
|
|
931
|
+
* of the elements the move reached, and every other element's is what it
|
|
932
|
+
* was. Matching them all again was most of such a build: a frame of 550 ms
|
|
933
|
+
* on a page with a Tailwind sheet, and 150 without it.
|
|
934
|
+
*/
|
|
935
|
+
beginSharing(kept = null) {
|
|
713
936
|
this._shared.clear();
|
|
714
937
|
this._sharedByMatch.clear();
|
|
715
|
-
|
|
938
|
+
// the kept styles' custom properties are the sets in here, which the
|
|
939
|
+
// elements styled under them find again
|
|
940
|
+
if (!kept)
|
|
941
|
+
this._customs.clear();
|
|
942
|
+
this._kept = kept;
|
|
943
|
+
this._keptShared.clear();
|
|
944
|
+
}
|
|
945
|
+
/** The build is over: nothing later is answered from what it kept. */
|
|
946
|
+
endSharing() {
|
|
947
|
+
this._kept = null;
|
|
948
|
+
this._keptShared.clear();
|
|
716
949
|
}
|
|
717
950
|
/**
|
|
718
951
|
* An element's custom properties: its parent's, or its own over them —
|
|
@@ -794,6 +1027,23 @@ export class Cascade {
|
|
|
794
1027
|
* does after the cascade, and `rootStyle` does not go through here.
|
|
795
1028
|
*/
|
|
796
1029
|
sharedStyleFor(el, parentStyle, parentKey, inFlexContainer) {
|
|
1030
|
+
const kept = this._kept;
|
|
1031
|
+
if (kept !== null && !kept.restyle.has(el)) {
|
|
1032
|
+
// An element the change did not reach keeps its style, under a key
|
|
1033
|
+
// of its own: a key stands for an element's ancestors, which the
|
|
1034
|
+
// elements styled again under it have in common, and two kept
|
|
1035
|
+
// elements with one style need not — a hover restyled in place
|
|
1036
|
+
// leaves a hovered card's children the styles its neighbours' have.
|
|
1037
|
+
let shared = this._keptShared.get(el);
|
|
1038
|
+
if (shared !== undefined)
|
|
1039
|
+
return shared;
|
|
1040
|
+
const was = kept.styles.get(el);
|
|
1041
|
+
if (was !== undefined && was.inFlex === inFlexContainer) {
|
|
1042
|
+
shared = { style: was.style, key: this._nextShareKey++ };
|
|
1043
|
+
this._keptShared.set(el, shared);
|
|
1044
|
+
return shared;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
797
1047
|
let key = `${parentKey}\u0001${inFlexContainer ? 1 : 0}`;
|
|
798
1048
|
const tag = tagOf(el);
|
|
799
1049
|
key += `\u0001${tag.length}:${tag}`;
|
|
@@ -938,6 +1188,53 @@ export class Cascade {
|
|
|
938
1188
|
out.unicodeBidi = 'isolate';
|
|
939
1189
|
return out;
|
|
940
1190
|
}
|
|
1191
|
+
/** Whether any rule styles a `::selection`: a document with none draws
|
|
1192
|
+
* a selection in the palette's colour, and asks nothing. */
|
|
1193
|
+
get hasSelection() {
|
|
1194
|
+
return this._pseudo.selection.size > 0;
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* An element's `::selection`, over its parent's (`parent`, null for the
|
|
1198
|
+
* palette's own): the colour and the background the rules that reach it
|
|
1199
|
+
* set, and its parent's for either they leave alone. Both inherit along
|
|
1200
|
+
* the chain of highlights rather than the elements' (CSS Pseudo 4, 3.5,
|
|
1201
|
+
* "highlight inheritance"), which is what Chrome draws: a `<span>` in a
|
|
1202
|
+
* `div::selection { background: red }` is selected in red, and a `<p>`
|
|
1203
|
+
* whose own rule sets only a colour keeps the red under it.
|
|
1204
|
+
*
|
|
1205
|
+
* The palette's highlight is taken only where no rule has set either
|
|
1206
|
+
* (3.6, "paired defaults"): one that sets a colour and no background is
|
|
1207
|
+
* selected over none, as Chrome has it. A rule that sets neither — a
|
|
1208
|
+
* `text-shadow` only — leaves the element where its parent was.
|
|
1209
|
+
*/
|
|
1210
|
+
selectionStyle(el, style, parent) {
|
|
1211
|
+
const index = this._pseudo.selection;
|
|
1212
|
+
if (!index.size || !index.reaches(el))
|
|
1213
|
+
return parent;
|
|
1214
|
+
const candidates = [];
|
|
1215
|
+
this._matchInto(index, el, candidates);
|
|
1216
|
+
let color = false;
|
|
1217
|
+
let background = false;
|
|
1218
|
+
for (const candidate of candidates) {
|
|
1219
|
+
for (const d of candidate.declarations) {
|
|
1220
|
+
if (d.prop === 'color')
|
|
1221
|
+
color = true;
|
|
1222
|
+
else if (d.prop === 'background' || d.prop === 'background-color') {
|
|
1223
|
+
background = true;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (!color && !background)
|
|
1228
|
+
return parent;
|
|
1229
|
+
candidates.sort(byCascade);
|
|
1230
|
+
const own = this._computeStyle(el, style, false, candidates);
|
|
1231
|
+
return {
|
|
1232
|
+
color: color ? own.color : (parent?.color ?? null),
|
|
1233
|
+
background: background
|
|
1234
|
+
? own.backgroundColor
|
|
1235
|
+
: (parent?.background ?? null),
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
941
1238
|
/**
|
|
942
1239
|
* An element's `::first-line` style, inheriting from its own, or null
|
|
943
1240
|
* when no rule reaches it (CSS 2.1 5.12.1).
|
|
@@ -1018,9 +1315,11 @@ export class Cascade {
|
|
|
1018
1315
|
// size, so a `font-size: 1.2em` in the cascade resolves against the
|
|
1019
1316
|
// right em, and again after the font size is settled so every *other*
|
|
1020
1317
|
// em-relative length in the same rule resolves against this element's.
|
|
1318
|
+
const root = isRootElement(el);
|
|
1319
|
+
const rootStyle = root ? this.initial : (this._root ?? this.initial);
|
|
1021
1320
|
const ctxParent = {
|
|
1022
1321
|
em: parentStyle.fontSize,
|
|
1023
|
-
rem:
|
|
1322
|
+
rem: rootStyle.fontSize,
|
|
1024
1323
|
initial: this.initial,
|
|
1025
1324
|
vw: this.viewportWidth,
|
|
1026
1325
|
vh: this.viewportHeight,
|
|
@@ -1029,20 +1328,22 @@ export class Cascade {
|
|
|
1029
1328
|
ch: () => this._chOf(parentStyle),
|
|
1030
1329
|
families: this._mapFamilies,
|
|
1031
1330
|
lh: () => this._lineHeightOf(parentStyle),
|
|
1032
|
-
rlh: () => this._lineHeightOf(
|
|
1331
|
+
rlh: () => this._lineHeightOf(rootStyle),
|
|
1033
1332
|
};
|
|
1034
1333
|
// The family, the weight and the slant go with the size: together they
|
|
1035
1334
|
// pick the face an `ex`, a `ch` or an `lh` in any declaration is
|
|
1036
1335
|
// measured in, however the declarations are ordered. A `width: 10ex`
|
|
1037
1336
|
// in a sheet under an inline `font-weight: 900` was measured in the
|
|
1038
|
-
// family's regular face.
|
|
1337
|
+
// family's regular face. The colour scheme goes ahead of the rest for
|
|
1338
|
+
// the same reason: every `light-dark()` among them is read by it.
|
|
1039
1339
|
let sized = false;
|
|
1040
1340
|
let keyword = false;
|
|
1041
1341
|
for (const c of candidates) {
|
|
1042
1342
|
for (const d of pick(c)) {
|
|
1043
1343
|
if (d.prop === 'font-family' ||
|
|
1044
1344
|
d.prop === 'font-weight' ||
|
|
1045
|
-
d.prop === 'font-style'
|
|
1345
|
+
d.prop === 'font-style' ||
|
|
1346
|
+
d.prop === 'color-scheme') {
|
|
1046
1347
|
this._apply(style, parentStyle, d, ctxParent);
|
|
1047
1348
|
}
|
|
1048
1349
|
else if (d.prop === 'font-size' || d.prop === 'font') {
|
|
@@ -1083,9 +1384,13 @@ export class Cascade {
|
|
|
1083
1384
|
fontStyle: style.fontStyle,
|
|
1084
1385
|
};
|
|
1085
1386
|
let line = null;
|
|
1387
|
+
// the root's other declarations measure a `rem` from its own size
|
|
1388
|
+
if (root)
|
|
1389
|
+
this._root = style;
|
|
1086
1390
|
const ctx = {
|
|
1087
1391
|
...ctxParent,
|
|
1088
1392
|
em: style.fontSize,
|
|
1393
|
+
rem: root ? style.fontSize : ctxParent.rem,
|
|
1089
1394
|
ex: () => this._exOf(face),
|
|
1090
1395
|
ch: () => this._chOf(face),
|
|
1091
1396
|
lh: () => this._lineHeightOf(line ?? style),
|
|
@@ -1208,18 +1513,55 @@ export class Cascade {
|
|
|
1208
1513
|
// the root is still the initial containing block.
|
|
1209
1514
|
return { style: asRoot(this.styleFor(synthetic, parent, false)), html };
|
|
1210
1515
|
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Whether the pointer's move from `was` to where it is changed which
|
|
1518
|
+
* rules match `el`, or its `::before`, `::after` or another of its
|
|
1519
|
+
* pseudo-elements: the rules that test `:hover` asked under both, and
|
|
1520
|
+
* nothing else, since no other rule's answer is the pointer's. An element
|
|
1521
|
+
* they answer the same for, under a parent whose style is what it was, has
|
|
1522
|
+
* the style it had — which is most of what a move reaches: the rows of a
|
|
1523
|
+
* hovered table body, what is in a hovered card.
|
|
1524
|
+
*/
|
|
1525
|
+
pointerChanged(el, was) {
|
|
1526
|
+
const now = this._pointer;
|
|
1527
|
+
const after = this._pointerAnswers(el);
|
|
1528
|
+
this._pointer = was;
|
|
1529
|
+
const before = this._pointerAnswers(el);
|
|
1530
|
+
this._pointer = now;
|
|
1531
|
+
return before !== after;
|
|
1532
|
+
}
|
|
1533
|
+
/** The ids of the rules testing the pointer that match `el` as things
|
|
1534
|
+
* stand, the element's and each pseudo-element's. */
|
|
1535
|
+
_pointerAnswers(el) {
|
|
1536
|
+
let answers = '';
|
|
1537
|
+
const ask = (index, name) => {
|
|
1538
|
+
const pointer = index.pointer;
|
|
1539
|
+
if (!pointer || !pointer.reaches(el))
|
|
1540
|
+
return;
|
|
1541
|
+
const matched = [];
|
|
1542
|
+
this._matchInto(pointer, el, null, matched);
|
|
1543
|
+
if (matched.length)
|
|
1544
|
+
answers += `${name}${matched.join(',')};`;
|
|
1545
|
+
};
|
|
1546
|
+
ask(this._index, '');
|
|
1547
|
+
for (const which of POINTER_PSEUDO_ELEMENTS)
|
|
1548
|
+
ask(this._pseudo[which], which);
|
|
1549
|
+
return answers;
|
|
1550
|
+
}
|
|
1211
1551
|
/** The rules of `index` that match `el`, pushed onto `out` as candidates,
|
|
1212
1552
|
* and their ids onto `matched` in the order they were tried. */
|
|
1213
1553
|
_matchInto(index, el, out, matched) {
|
|
1214
1554
|
// A media query's width is CSS pixels; the viewport is kept in device.
|
|
1215
1555
|
const width = this.viewportWidth / this.scale;
|
|
1556
|
+
const height = this.viewportHeight / this.scale;
|
|
1216
1557
|
const consider = (bucket) => {
|
|
1217
1558
|
if (!bucket)
|
|
1218
1559
|
return;
|
|
1219
1560
|
for (const indexed of bucket) {
|
|
1220
1561
|
const rule = indexed.rule;
|
|
1221
|
-
if (!mediaMatches(rule.media, width, this.look.colorScheme))
|
|
1562
|
+
if (!mediaMatches(rule.media, width, this.look.colorScheme, height, this.scale)) {
|
|
1222
1563
|
continue;
|
|
1564
|
+
}
|
|
1223
1565
|
if (!indexed.compiled) {
|
|
1224
1566
|
indexed.compiled = true;
|
|
1225
1567
|
try {
|
|
@@ -1235,6 +1577,8 @@ export class Cascade {
|
|
|
1235
1577
|
if (!indexed.match || !indexed.match(el))
|
|
1236
1578
|
continue;
|
|
1237
1579
|
matched?.push(indexed.id);
|
|
1580
|
+
if (out === null)
|
|
1581
|
+
continue;
|
|
1238
1582
|
const origin = rule.order < 0 ? Origin.UserAgent : Origin.Author;
|
|
1239
1583
|
pushRule(out, rule, origin);
|
|
1240
1584
|
}
|
|
@@ -1301,9 +1645,57 @@ export class Cascade {
|
|
|
1301
1645
|
}
|
|
1302
1646
|
}
|
|
1303
1647
|
out.sort(byCascade);
|
|
1648
|
+
dropPaletteChrome(out);
|
|
1304
1649
|
return out;
|
|
1305
1650
|
}
|
|
1306
1651
|
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Takes the palette's control chrome (`PALETTE_CHROME`) out of an element's
|
|
1654
|
+
* candidates where the page styles its background or border, so what shows
|
|
1655
|
+
* through is the web's UA values the page was written against. The rule is
|
|
1656
|
+
* Blink's (`LayoutTheme::IsControlStyled`): a background or border longhand
|
|
1657
|
+
* set by the page, radius included, drops the control's native appearance;
|
|
1658
|
+
* so does `appearance: none`. `out` is in cascade order, so the UA's
|
|
1659
|
+
* candidates are the ones before the first of any other origin.
|
|
1660
|
+
*/
|
|
1661
|
+
function dropPaletteChrome(out) {
|
|
1662
|
+
let chrome = false;
|
|
1663
|
+
let i = 0;
|
|
1664
|
+
for (; i < out.length && out[i].origin === Origin.UserAgent; i += 1) {
|
|
1665
|
+
if (PALETTE_CHROME.has(out[i].declarations))
|
|
1666
|
+
chrome = true;
|
|
1667
|
+
}
|
|
1668
|
+
if (!chrome)
|
|
1669
|
+
return;
|
|
1670
|
+
let styled = false;
|
|
1671
|
+
for (let j = i; j < out.length && !styled; j += 1) {
|
|
1672
|
+
styled = pick(out[j]).some(stylesChrome);
|
|
1673
|
+
}
|
|
1674
|
+
if (!styled)
|
|
1675
|
+
return;
|
|
1676
|
+
let kept = 0;
|
|
1677
|
+
for (const c of out) {
|
|
1678
|
+
if (c.origin === Origin.UserAgent && PALETTE_CHROME.has(c.declarations)) {
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
out[kept++] = c;
|
|
1682
|
+
}
|
|
1683
|
+
out.length = kept;
|
|
1684
|
+
}
|
|
1685
|
+
/** Whether a declaration styles what a control's native look draws: Blink's
|
|
1686
|
+
* `is_background` and `is_border` properties and their shorthands, and an
|
|
1687
|
+
* `appearance` of `none`. */
|
|
1688
|
+
function stylesChrome(d) {
|
|
1689
|
+
const prop = d.prop;
|
|
1690
|
+
if (prop.startsWith('background')) {
|
|
1691
|
+
return prop !== 'background-repeat' && prop !== 'background-blend-mode';
|
|
1692
|
+
}
|
|
1693
|
+
if (prop.startsWith('border')) {
|
|
1694
|
+
return prop !== 'border-collapse' && prop !== 'border-spacing';
|
|
1695
|
+
}
|
|
1696
|
+
return ((prop === 'appearance' || prop === '-webkit-appearance') &&
|
|
1697
|
+
d.value.trim().toLowerCase() === 'none');
|
|
1698
|
+
}
|
|
1307
1699
|
/** An implied element's style as the root box's: its look, not its role —
|
|
1308
1700
|
* the root is still a block, in flow, and the initial containing block. */
|
|
1309
1701
|
function asRoot(style) {
|
|
@@ -1314,9 +1706,19 @@ function asRoot(style) {
|
|
|
1314
1706
|
}
|
|
1315
1707
|
/** `:root` is the `<html>` element: the one a browser implies around a
|
|
1316
1708
|
* fragment, whose style the root box takes (`rootStyle`), and never a
|
|
1317
|
-
* fragment's top-level elements, which css-select would take for it.
|
|
1709
|
+
* fragment's top-level elements, which css-select would take for it.
|
|
1710
|
+
*
|
|
1711
|
+
* `:focus` and its two kin match nothing: no element of the document takes
|
|
1712
|
+
* the focus — a control's widget does, beside it. css-select has none of
|
|
1713
|
+
* the three and throws on them, which dropped every rule that named one,
|
|
1714
|
+
* `:not(:focus)` among them: Wikipedia's skip link hides with
|
|
1715
|
+
* `.mw-jump-link:not(:focus)`, and without it stood in the flow at the top
|
|
1716
|
+
* of every page. */
|
|
1318
1717
|
const PSEUDOS = {
|
|
1319
1718
|
root: (el) => el.name === 'html' && !(el.parent && isTag(el.parent)),
|
|
1719
|
+
focus: () => false,
|
|
1720
|
+
'focus-visible': () => false,
|
|
1721
|
+
'focus-within': () => false,
|
|
1320
1722
|
// css-select's, but its ranges and tags lower-cased as ASCII has it, and
|
|
1321
1723
|
// no other script (CSS 2.1 4.1.3): Unicode's took `:lang(\u212Al)`, a
|
|
1322
1724
|
// Kelvin sign for the K, for `:lang(kl)`
|
|
@@ -1351,8 +1753,8 @@ const PSEUDOS = {
|
|
|
1351
1753
|
};
|
|
1352
1754
|
/** A length in a unit of the viewport: a number, then the unit, and no
|
|
1353
1755
|
* more of a name after it. */
|
|
1354
|
-
const VIEWPORT_UNIT = /\d(?:
|
|
1355
|
-
const VIEWPORT_UNITS = /\d(
|
|
1756
|
+
const VIEWPORT_UNIT = /\d[sld]?v(?:w|h|i|b|min|max)(?![\w-])/i;
|
|
1757
|
+
const VIEWPORT_UNITS = /\d([sld]?v(?:w|h|i|b|min|max))(?![\w-])/gi;
|
|
1356
1758
|
/**
|
|
1357
1759
|
* A selector with its `[attr~=""]` made one that matches nothing: an empty
|
|
1358
1760
|
* string is no word of a list, and such a selector represents nothing
|
|
@@ -1625,7 +2027,10 @@ function presentationHints(el) {
|
|
|
1625
2027
|
// `width`/`height` are lengths on the replaced and table elements and mean
|
|
1626
2028
|
// nothing anywhere else, which is what stops a `<input width>` from
|
|
1627
2029
|
// becoming a CSS width the widget then disagrees with.
|
|
1628
|
-
if (SIZED.has(tag)
|
|
2030
|
+
if (SIZED.has(tag) ||
|
|
2031
|
+
// an image button's are its image's, as an `<img>`'s are (HTML 15.4.3)
|
|
2032
|
+
(tag === 'input' &&
|
|
2033
|
+
(attr(el, 'type') ?? '').trim().toLowerCase() === 'image')) {
|
|
1629
2034
|
const width = attr(el, 'width');
|
|
1630
2035
|
if (width)
|
|
1631
2036
|
push('width', lengthAttr(width));
|
|
@@ -1865,4 +2270,9 @@ const ABSOLUTE_SIZES = new Set([
|
|
|
1865
2270
|
'xxx-large',
|
|
1866
2271
|
'initial',
|
|
1867
2272
|
]);
|
|
2273
|
+
/** Whether an element is the document's root: its parent the document, or
|
|
2274
|
+
* none, as the `<html>` the cascade supplies where the markup has none. */
|
|
2275
|
+
function isRootElement(el) {
|
|
2276
|
+
return !el.parent || el.parent.type === 'root';
|
|
2277
|
+
}
|
|
1868
2278
|
//# sourceMappingURL=cascade.js.map
|