@react-x11/components 0.12.0 → 0.14.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/html/controls.d.ts +41 -5
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +85 -57
- 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 +545 -126
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +18 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +27 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +47 -6
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +336 -30
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +142 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +563 -147
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/transform.d.ts +85 -0
- package/dist/html/css/transform.d.ts.map +1 -0
- package/dist/html/css/transform.js +425 -0
- package/dist/html/css/transform.js.map +1 -0
- 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 -9
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +4 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +3 -1
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +78 -4
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +373 -19
- 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 -301
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +93 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +393 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +90 -2
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +74 -10
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +90 -18
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +574 -84
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +3 -0
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +154 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +62 -18
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +90 -15
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +771 -186
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +101 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1502 -160
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +15 -2
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +249 -17
- package/dist/html/svg.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 +700 -0
- package/dist/html/widgets.js.map +1 -0
- package/dist/html/woff2.d.ts +14 -0
- package/dist/html/woff2.d.ts.map +1 -0
- package/dist/html/woff2.js +608 -0
- package/dist/html/woff2.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/maps/controller.d.ts +17 -1
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +27 -7
- package/dist/maps/controller.js.map +1 -1
- package/dist/richtext/node.d.ts +8 -1
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +5 -1
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +25 -6
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +240 -24
- package/dist/richtext/runs.js.map +1 -1
- package/package.json +2 -2
- package/src/html/controls.ts +114 -51
- package/src/html/css/cascade.ts +614 -140
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +388 -33
- package/src/html/css/style.ts +688 -150
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +77 -10
- package/src/html/css/values.ts +4 -0
- package/src/html/dom.ts +3 -1
- package/src/html/fonts.ts +417 -24
- package/src/html/form.ts +962 -0
- package/src/html/index.ts +49 -338
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +447 -37
- package/src/html/layout/boxes.ts +143 -14
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +95 -22
- package/src/html/layout/flex.ts +675 -78
- package/src/html/layout/inline.ts +159 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +62 -19
- package/src/html/node.ts +798 -172
- package/src/html/paint.ts +1742 -241
- package/src/html/svg.ts +277 -19
- package/src/html/widgets.ts +821 -0
- package/src/html/woff2.ts +612 -0
- package/src/index.ts +1 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
package/dist/html/css/cascade.js
CHANGED
|
@@ -21,9 +21,10 @@ import { Element as DomElement, isTag } from 'domhandler';
|
|
|
21
21
|
import { attr, tagOf } from '../dom.js';
|
|
22
22
|
import { svgSizeHint } from '../svg.js';
|
|
23
23
|
import { asciiLower, escapeEnd, mediaMatches, readIdent, startsIdent, } from './parse.js';
|
|
24
|
-
import { applyDeclaration, blockify, settleContentVisibility, settleOverflow, settleClamp, copyStyle, decorate, inherit, initialOne, initialStyle, isInherited, } from './style.js';
|
|
24
|
+
import { applyDeclaration, blockify, settleButton, 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';
|
|
27
28
|
import { viewportUnit } from './values.js';
|
|
28
29
|
import { customProperties, substituteIn } from './vars.js';
|
|
29
30
|
/** Where a declaration came from. Higher wins before specificity is asked. */
|
|
@@ -36,6 +37,22 @@ var Origin;
|
|
|
36
37
|
Origin[Origin["AuthorImportant"] = 4] = "AuthorImportant";
|
|
37
38
|
Origin[Origin["InlineImportant"] = 5] = "InlineImportant";
|
|
38
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
|
+
];
|
|
39
56
|
/**
|
|
40
57
|
* What makes a selector's match depend on more than the element's own tag and
|
|
41
58
|
* attributes and its ancestors' — its siblings, where it sits among them,
|
|
@@ -62,6 +79,11 @@ class RuleIndex {
|
|
|
62
79
|
/** Whether any rule in here is pointer-sensitive, so the renderer knows
|
|
63
80
|
* whether a pointer move can change the cascade at all. */
|
|
64
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;
|
|
65
87
|
/** The buckets holding a rule an element cannot share its style under
|
|
66
88
|
* (`UNSHAREABLE`): the ids, classes and tags whose elements compute
|
|
67
89
|
* their own, and whether the universal bucket makes every element do so. */
|
|
@@ -120,20 +142,27 @@ class RuleIndex {
|
|
|
120
142
|
else
|
|
121
143
|
this.ownStyleEverywhere = true;
|
|
122
144
|
}
|
|
123
|
-
|
|
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'
|
|
124
154
|
? mapBucket(this.byId, key.name)
|
|
125
155
|
: key.kind === 'class'
|
|
126
156
|
? mapBucket(this.byClass, key.name)
|
|
127
157
|
: key.kind === 'tag'
|
|
128
158
|
? mapBucket(this.byTag, key.name)
|
|
129
159
|
: this.universal;
|
|
130
|
-
bucket.push(indexed);
|
|
131
160
|
}
|
|
132
161
|
}
|
|
133
162
|
/** A selector's trailing `::before`, `::after`, `::first-letter`,
|
|
134
|
-
* `::first-line` or `::
|
|
135
|
-
* its four. */
|
|
136
|
-
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;
|
|
137
166
|
/**
|
|
138
167
|
* A rule for a pseudo-element, as the pseudo-element it styles and a rule
|
|
139
168
|
* for the element it hangs off, which is what gets matched. The specificity
|
|
@@ -153,22 +182,35 @@ function splitPseudoElement(rule) {
|
|
|
153
182
|
? `${trimmed} *`
|
|
154
183
|
: trimmed;
|
|
155
184
|
return {
|
|
156
|
-
which: (m[1] ??
|
|
185
|
+
which: (m[1] ?? m[2]).toLowerCase(),
|
|
157
186
|
rule: { ...rule, selector },
|
|
158
187
|
};
|
|
159
188
|
}
|
|
160
189
|
/**
|
|
161
190
|
* The compounds of a selector that test `:hover`, each without it (and
|
|
162
191
|
* without an `:active` beside it), and whether one is followed by a sibling
|
|
163
|
-
* combinator, which reaches the element's later siblings.
|
|
164
|
-
* `:hover` sits inside a functional pseudo-class — `:not(:hover)`,
|
|
165
|
-
* `:is(:hover)` — and reaches elements no compound names.
|
|
192
|
+
* combinator, which reaches the element's later siblings.
|
|
166
193
|
*
|
|
167
|
-
* A `:hover`
|
|
168
|
-
*
|
|
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
|
|
169
211
|
* the element the pointer is over — or an earlier sibling of it or of an
|
|
170
212
|
* ancestor, where the argument starts at a sibling (Selectors 4, 4.5). So
|
|
171
|
-
* it is given as an anchor (`has`)
|
|
213
|
+
* it is given as an anchor (`has`).
|
|
172
214
|
*
|
|
173
215
|
* `:active` is not the pointer's here: nothing sets it (`setPointer` is
|
|
174
216
|
* handed none), so a selector that tests only it never changes as the
|
|
@@ -181,8 +223,17 @@ export function pointerCompounds(selector) {
|
|
|
181
223
|
siblings: false,
|
|
182
224
|
nested: false,
|
|
183
225
|
has: [],
|
|
226
|
+
followed: [],
|
|
184
227
|
};
|
|
185
|
-
|
|
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) {
|
|
186
237
|
const parts = [];
|
|
187
238
|
let start = 0;
|
|
188
239
|
let depth = 0;
|
|
@@ -215,94 +266,203 @@ export function pointerCompounds(selector) {
|
|
|
215
266
|
}
|
|
216
267
|
}
|
|
217
268
|
split(selector.length, '');
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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]);
|
|
237
346
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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;
|
|
242
393
|
continue;
|
|
243
394
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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;
|
|
251
405
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
else if (c === ')' || c === ']') {
|
|
255
|
-
depth = Math.max(0, depth - 1);
|
|
256
|
-
if (depth === 0 && hasAt >= 0 && c === ')') {
|
|
406
|
+
const name = fn[1].toLowerCase();
|
|
407
|
+
if (name === 'has') {
|
|
257
408
|
// the compound is kept without it: its argument is what the
|
|
258
|
-
// pointer changes
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
bare = bare.slice(0, hasAt);
|
|
263
|
-
}
|
|
264
|
-
else
|
|
265
|
-
bare += c;
|
|
266
|
-
hasAt = -1;
|
|
267
|
-
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);
|
|
268
413
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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)
|
|
284
439
|
pointer = true;
|
|
285
|
-
|
|
286
|
-
|
|
440
|
+
args.push(...one.args);
|
|
441
|
+
const rest = one.bare.trim();
|
|
442
|
+
if (rest && rest !== '*')
|
|
443
|
+
own.push(rest);
|
|
444
|
+
else
|
|
445
|
+
any = true;
|
|
287
446
|
}
|
|
447
|
+
if (own.length && !any)
|
|
448
|
+
bare += `:is(${own.join(',')})`;
|
|
288
449
|
}
|
|
450
|
+
else {
|
|
451
|
+
out.nested = true;
|
|
452
|
+
bare += whole;
|
|
453
|
+
}
|
|
454
|
+
continue;
|
|
289
455
|
}
|
|
290
|
-
bare += c;
|
|
291
|
-
}
|
|
292
|
-
if (anchored) {
|
|
293
|
-
out.has.push({
|
|
294
|
-
anchor: bare.trim() || '*',
|
|
295
|
-
siblings: args.some((arg) => arg.split(',').some((one) => /^\s*[+~]/.test(one))),
|
|
296
|
-
});
|
|
297
456
|
}
|
|
298
|
-
|
|
299
|
-
continue;
|
|
300
|
-
out.compounds.push(bare.trim() || '*');
|
|
301
|
-
if (next === '+' || next === '~')
|
|
302
|
-
out.siblings = true;
|
|
457
|
+
bare += c;
|
|
303
458
|
}
|
|
304
|
-
return
|
|
459
|
+
return { bare, pointer, below, args };
|
|
305
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;
|
|
306
466
|
/** `:hover` or `:active` at the start of a string, and nothing longer. */
|
|
307
467
|
const POINTER_PSEUDO_AT = /^:(?:hover|active)(?![\w-])/i;
|
|
308
468
|
/** `:hover` or `:active` anywhere in a selector. */
|
|
@@ -456,23 +616,22 @@ export class Cascade {
|
|
|
456
616
|
'first-letter': new RuleIndex(),
|
|
457
617
|
'first-line': new RuleIndex(),
|
|
458
618
|
marker: new RuleIndex(),
|
|
619
|
+
selection: new RuleIndex(),
|
|
459
620
|
};
|
|
460
621
|
_adapter;
|
|
461
622
|
/** The compounds of the selectors that test the pointer, each without
|
|
462
623
|
* its `:hover` or `:active` (`pointerCompounds`), and their matchers,
|
|
463
624
|
* compiled when a pointer move first asks (`hoverTouches`). */
|
|
464
|
-
_hoverCompounds = new
|
|
625
|
+
_hoverCompounds = new Map();
|
|
465
626
|
_hoverMatchers = null;
|
|
466
627
|
/** Whether a pointer move can be restyled where it happened
|
|
467
628
|
* (`HtmlViewNode._hoverInPlace`): false where a selector tests the
|
|
468
629
|
* pointer inside a functional pseudo-class, which reaches elements no
|
|
469
630
|
* compound names. */
|
|
470
631
|
hoverLocal = true;
|
|
471
|
-
/**
|
|
472
|
-
*
|
|
473
|
-
|
|
474
|
-
/** The compounds whose `:has()` tests the pointer, each without it, and
|
|
475
|
-
* 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`). */
|
|
476
635
|
_hoverAnchors = new Map();
|
|
477
636
|
_anchorMatchers = null;
|
|
478
637
|
_pointer = NO_POINTER;
|
|
@@ -512,6 +671,12 @@ export class Cascade {
|
|
|
512
671
|
/** The families the document loads itself (`fonts.ts`), or null for a
|
|
513
672
|
* document with no `@font-face`. */
|
|
514
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;
|
|
515
680
|
_mapFamilies;
|
|
516
681
|
/** Whether any declaration has a length in `lh` or `rlh`: only then is
|
|
517
682
|
* the line height settled ahead of the declarations that read it. */
|
|
@@ -553,6 +718,11 @@ export class Cascade {
|
|
|
553
718
|
}
|
|
554
719
|
for (const bp of sheet.breakpoints)
|
|
555
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;
|
|
556
726
|
}
|
|
557
727
|
this.breakpoints = [...breakpoints].sort((a, b) => a - b);
|
|
558
728
|
this.counterStyles = new CounterStyles(counterStyles);
|
|
@@ -604,42 +774,78 @@ export class Cascade {
|
|
|
604
774
|
setPointer(pointer) {
|
|
605
775
|
this._pointer = pointer;
|
|
606
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
|
+
}
|
|
607
796
|
/**
|
|
608
797
|
* Whether the pointer entering or leaving `el` can change a style: one of
|
|
609
798
|
* the compounds a selector tests the pointer in matches it, the pointer
|
|
610
799
|
* aside. A move between two paragraphs of a page whose only such rule is
|
|
611
|
-
* `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.
|
|
612
805
|
*/
|
|
613
806
|
hoverTouches(el) {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
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 }] : [];
|
|
617
813
|
});
|
|
618
|
-
for (const match of this._hoverMatchers)
|
|
814
|
+
for (const { match, followed } of this._hoverMatchers) {
|
|
619
815
|
if (match(el))
|
|
620
|
-
return
|
|
621
|
-
|
|
816
|
+
return followed ? HOVER_FOLLOWED : HOVER_TOUCHED;
|
|
817
|
+
}
|
|
818
|
+
return HOVER_UNTOUCHED;
|
|
622
819
|
}
|
|
623
820
|
/**
|
|
624
821
|
* The elements whose `:has()` may have changed its answer as `el` was
|
|
625
822
|
* hovered or left: the ancestors of it a compound whose `:has()` tests
|
|
626
823
|
* the pointer names, and their earlier siblings and its own where the
|
|
627
824
|
* argument starts at a sibling — a superset, which is restyled and found
|
|
628
|
-
* 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`).
|
|
629
827
|
*/
|
|
630
828
|
hoverAnchors(el, into) {
|
|
631
829
|
if (!this._hoverAnchors.size)
|
|
632
830
|
return;
|
|
633
|
-
this._anchorMatchers ??= [...this._hoverAnchors]
|
|
831
|
+
this._anchorMatchers ??= [...this._hoverAnchors]
|
|
832
|
+
.sort(([, a], [, b]) => Number(b.followed) - Number(a.followed))
|
|
833
|
+
.flatMap(([anchor, { siblings, followed }]) => {
|
|
634
834
|
const match = this._compile(anchor);
|
|
635
|
-
return match ? [{ match, siblings }] : [];
|
|
835
|
+
return match ? [{ match, siblings, followed }] : [];
|
|
636
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
|
+
};
|
|
637
843
|
const siblings = this._anchorMatchers.some((m) => m.siblings);
|
|
638
844
|
for (let at = el; at;) {
|
|
639
845
|
if (at !== el) {
|
|
640
|
-
for (const { match } of this._anchorMatchers) {
|
|
846
|
+
for (const { match, followed } of this._anchorMatchers) {
|
|
641
847
|
if (match(at)) {
|
|
642
|
-
|
|
848
|
+
note(at, followed);
|
|
643
849
|
break;
|
|
644
850
|
}
|
|
645
851
|
}
|
|
@@ -648,9 +854,9 @@ export class Cascade {
|
|
|
648
854
|
for (let s = at.prev; s; s = s.prev) {
|
|
649
855
|
if (!isTag(s))
|
|
650
856
|
continue;
|
|
651
|
-
for (const
|
|
652
|
-
if (
|
|
653
|
-
|
|
857
|
+
for (const m of this._anchorMatchers) {
|
|
858
|
+
if (m.siblings && m.match(s)) {
|
|
859
|
+
note(s, m.followed);
|
|
654
860
|
break;
|
|
655
861
|
}
|
|
656
862
|
}
|
|
@@ -666,12 +872,16 @@ export class Cascade {
|
|
|
666
872
|
const found = pointerCompounds(selector);
|
|
667
873
|
if (found.nested)
|
|
668
874
|
this.hoverLocal = false;
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
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
|
+
}
|
|
673
879
|
for (const { anchor, siblings } of found.has) {
|
|
674
|
-
|
|
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
|
+
});
|
|
675
885
|
}
|
|
676
886
|
}
|
|
677
887
|
/** A selector compiled as a rule's is, or null where css-select refuses
|
|
@@ -708,12 +918,34 @@ export class Cascade {
|
|
|
708
918
|
_customs = new Map();
|
|
709
919
|
_ids = new WeakMap();
|
|
710
920
|
_nextId = 1;
|
|
711
|
-
/**
|
|
712
|
-
*
|
|
713
|
-
|
|
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) {
|
|
714
936
|
this._shared.clear();
|
|
715
937
|
this._sharedByMatch.clear();
|
|
716
|
-
|
|
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();
|
|
717
949
|
}
|
|
718
950
|
/**
|
|
719
951
|
* An element's custom properties: its parent's, or its own over them —
|
|
@@ -795,6 +1027,23 @@ export class Cascade {
|
|
|
795
1027
|
* does after the cascade, and `rootStyle` does not go through here.
|
|
796
1028
|
*/
|
|
797
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
|
+
}
|
|
798
1047
|
let key = `${parentKey}\u0001${inFlexContainer ? 1 : 0}`;
|
|
799
1048
|
const tag = tagOf(el);
|
|
800
1049
|
key += `\u0001${tag.length}:${tag}`;
|
|
@@ -816,7 +1065,7 @@ export class Cascade {
|
|
|
816
1065
|
let shared = this._sharedByMatch.get(key);
|
|
817
1066
|
if (shared === undefined) {
|
|
818
1067
|
shared = {
|
|
819
|
-
style: this._computeStyle(el, parentStyle, inFlexContainer, candidates),
|
|
1068
|
+
style: this._computeStyle(el, parentStyle, inFlexContainer, candidates, true),
|
|
820
1069
|
key: this._nextShareKey++,
|
|
821
1070
|
};
|
|
822
1071
|
this._sharedByMatch.set(key, shared);
|
|
@@ -864,7 +1113,7 @@ export class Cascade {
|
|
|
864
1113
|
* a style is computed only for the elements with none to share.
|
|
865
1114
|
*/
|
|
866
1115
|
styleFor(el, parentStyle, inFlexContainer) {
|
|
867
|
-
return this._computeStyle(el, parentStyle, inFlexContainer, this._candidates(el));
|
|
1116
|
+
return this._computeStyle(el, parentStyle, inFlexContainer, this._candidates(el), true);
|
|
868
1117
|
}
|
|
869
1118
|
/**
|
|
870
1119
|
* The style of an element's `::before` or `::after`, or null when it has
|
|
@@ -939,6 +1188,53 @@ export class Cascade {
|
|
|
939
1188
|
out.unicodeBidi = 'isolate';
|
|
940
1189
|
return out;
|
|
941
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
|
+
}
|
|
942
1238
|
/**
|
|
943
1239
|
* An element's `::first-line` style, inheriting from its own, or null
|
|
944
1240
|
* when no rule reaches it (CSS 2.1 5.12.1).
|
|
@@ -1009,7 +1305,10 @@ export class Cascade {
|
|
|
1009
1305
|
return line;
|
|
1010
1306
|
}
|
|
1011
1307
|
/** `styleFor`, from the rules and hints already gathered for `el`. */
|
|
1012
|
-
_computeStyle(el, parentStyle, inFlexContainer, candidates
|
|
1308
|
+
_computeStyle(el, parentStyle, inFlexContainer, candidates,
|
|
1309
|
+
/** Whether the style is the element's own, and not that of one of its
|
|
1310
|
+
* pseudo-elements. */
|
|
1311
|
+
own = false) {
|
|
1013
1312
|
const style = inherit(parentStyle, this.initial);
|
|
1014
1313
|
// custom properties first, in cascade order, so every `var()` in the
|
|
1015
1314
|
// declarations below finds the one that wins
|
|
@@ -1019,10 +1318,16 @@ export class Cascade {
|
|
|
1019
1318
|
// size, so a `font-size: 1.2em` in the cascade resolves against the
|
|
1020
1319
|
// right em, and again after the font size is settled so every *other*
|
|
1021
1320
|
// em-relative length in the same rule resolves against this element's.
|
|
1321
|
+
const root = isRootElement(el);
|
|
1322
|
+
const rootStyle = root ? this.initial : (this._root ?? this.initial);
|
|
1323
|
+
// whose declaration is being applied: the UA sheet's families are the
|
|
1324
|
+
// host's own faces, and its lists end where they end
|
|
1325
|
+
let authored = true;
|
|
1022
1326
|
const ctxParent = {
|
|
1023
1327
|
em: parentStyle.fontSize,
|
|
1024
|
-
rem:
|
|
1328
|
+
rem: rootStyle.fontSize,
|
|
1025
1329
|
initial: this.initial,
|
|
1330
|
+
fallbackFamily: () => (authored ? this.initial.fontFamily : null),
|
|
1026
1331
|
vw: this.viewportWidth,
|
|
1027
1332
|
vh: this.viewportHeight,
|
|
1028
1333
|
scale: this.scale,
|
|
@@ -1030,7 +1335,7 @@ export class Cascade {
|
|
|
1030
1335
|
ch: () => this._chOf(parentStyle),
|
|
1031
1336
|
families: this._mapFamilies,
|
|
1032
1337
|
lh: () => this._lineHeightOf(parentStyle),
|
|
1033
|
-
rlh: () => this._lineHeightOf(
|
|
1338
|
+
rlh: () => this._lineHeightOf(rootStyle),
|
|
1034
1339
|
};
|
|
1035
1340
|
// The family, the weight and the slant go with the size: together they
|
|
1036
1341
|
// pick the face an `ex`, a `ch` or an `lh` in any declaration is
|
|
@@ -1041,6 +1346,7 @@ export class Cascade {
|
|
|
1041
1346
|
let sized = false;
|
|
1042
1347
|
let keyword = false;
|
|
1043
1348
|
for (const c of candidates) {
|
|
1349
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1044
1350
|
for (const d of pick(c)) {
|
|
1045
1351
|
if (d.prop === 'font-family' ||
|
|
1046
1352
|
d.prop === 'font-weight' ||
|
|
@@ -1086,9 +1392,13 @@ export class Cascade {
|
|
|
1086
1392
|
fontStyle: style.fontStyle,
|
|
1087
1393
|
};
|
|
1088
1394
|
let line = null;
|
|
1395
|
+
// the root's other declarations measure a `rem` from its own size
|
|
1396
|
+
if (root)
|
|
1397
|
+
this._root = style;
|
|
1089
1398
|
const ctx = {
|
|
1090
1399
|
...ctxParent,
|
|
1091
1400
|
em: style.fontSize,
|
|
1401
|
+
rem: root ? style.fontSize : ctxParent.rem,
|
|
1092
1402
|
ex: () => this._exOf(face),
|
|
1093
1403
|
ch: () => this._chOf(face),
|
|
1094
1404
|
lh: () => this._lineHeightOf(line ?? style),
|
|
@@ -1099,6 +1409,7 @@ export class Cascade {
|
|
|
1099
1409
|
// them. A `font` sets the size as well, which the first pass settled.
|
|
1100
1410
|
if (this._lh) {
|
|
1101
1411
|
for (const c of candidates) {
|
|
1412
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1102
1413
|
for (const d of pick(c)) {
|
|
1103
1414
|
if (d.prop === 'line-height' || d.prop === 'font') {
|
|
1104
1415
|
this._apply(style, parentStyle, d, ctx);
|
|
@@ -1114,6 +1425,7 @@ export class Cascade {
|
|
|
1114
1425
|
}
|
|
1115
1426
|
const settled = style.fontSize;
|
|
1116
1427
|
for (const c of candidates) {
|
|
1428
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1117
1429
|
for (const d of pick(c)) {
|
|
1118
1430
|
if (d.prop === 'font-size' || d.custom)
|
|
1119
1431
|
continue;
|
|
@@ -1141,6 +1453,10 @@ export class Cascade {
|
|
|
1141
1453
|
settleOverflow(style);
|
|
1142
1454
|
settleContentVisibility(style);
|
|
1143
1455
|
blockify(style, inFlexContainer);
|
|
1456
|
+
// a button is laid out as one, whatever `display` it was given
|
|
1457
|
+
if (own && el.name.length === 6 && tagOf(el) === 'button') {
|
|
1458
|
+
settleButton(style);
|
|
1459
|
+
}
|
|
1144
1460
|
decorate(style);
|
|
1145
1461
|
// which faces of the document's own families this family, weight and
|
|
1146
1462
|
// slant ask for — known only now, with all three computed
|
|
@@ -1211,18 +1527,55 @@ export class Cascade {
|
|
|
1211
1527
|
// the root is still the initial containing block.
|
|
1212
1528
|
return { style: asRoot(this.styleFor(synthetic, parent, false)), html };
|
|
1213
1529
|
}
|
|
1530
|
+
/**
|
|
1531
|
+
* Whether the pointer's move from `was` to where it is changed which
|
|
1532
|
+
* rules match `el`, or its `::before`, `::after` or another of its
|
|
1533
|
+
* pseudo-elements: the rules that test `:hover` asked under both, and
|
|
1534
|
+
* nothing else, since no other rule's answer is the pointer's. An element
|
|
1535
|
+
* they answer the same for, under a parent whose style is what it was, has
|
|
1536
|
+
* the style it had — which is most of what a move reaches: the rows of a
|
|
1537
|
+
* hovered table body, what is in a hovered card.
|
|
1538
|
+
*/
|
|
1539
|
+
pointerChanged(el, was) {
|
|
1540
|
+
const now = this._pointer;
|
|
1541
|
+
const after = this._pointerAnswers(el);
|
|
1542
|
+
this._pointer = was;
|
|
1543
|
+
const before = this._pointerAnswers(el);
|
|
1544
|
+
this._pointer = now;
|
|
1545
|
+
return before !== after;
|
|
1546
|
+
}
|
|
1547
|
+
/** The ids of the rules testing the pointer that match `el` as things
|
|
1548
|
+
* stand, the element's and each pseudo-element's. */
|
|
1549
|
+
_pointerAnswers(el) {
|
|
1550
|
+
let answers = '';
|
|
1551
|
+
const ask = (index, name) => {
|
|
1552
|
+
const pointer = index.pointer;
|
|
1553
|
+
if (!pointer || !pointer.reaches(el))
|
|
1554
|
+
return;
|
|
1555
|
+
const matched = [];
|
|
1556
|
+
this._matchInto(pointer, el, null, matched);
|
|
1557
|
+
if (matched.length)
|
|
1558
|
+
answers += `${name}${matched.join(',')};`;
|
|
1559
|
+
};
|
|
1560
|
+
ask(this._index, '');
|
|
1561
|
+
for (const which of POINTER_PSEUDO_ELEMENTS)
|
|
1562
|
+
ask(this._pseudo[which], which);
|
|
1563
|
+
return answers;
|
|
1564
|
+
}
|
|
1214
1565
|
/** The rules of `index` that match `el`, pushed onto `out` as candidates,
|
|
1215
1566
|
* and their ids onto `matched` in the order they were tried. */
|
|
1216
1567
|
_matchInto(index, el, out, matched) {
|
|
1217
1568
|
// A media query's width is CSS pixels; the viewport is kept in device.
|
|
1218
1569
|
const width = this.viewportWidth / this.scale;
|
|
1570
|
+
const height = this.viewportHeight / this.scale;
|
|
1219
1571
|
const consider = (bucket) => {
|
|
1220
1572
|
if (!bucket)
|
|
1221
1573
|
return;
|
|
1222
1574
|
for (const indexed of bucket) {
|
|
1223
1575
|
const rule = indexed.rule;
|
|
1224
|
-
if (!mediaMatches(rule.media, width, this.look.colorScheme))
|
|
1576
|
+
if (!mediaMatches(rule.media, width, this.look.colorScheme, height, this.scale)) {
|
|
1225
1577
|
continue;
|
|
1578
|
+
}
|
|
1226
1579
|
if (!indexed.compiled) {
|
|
1227
1580
|
indexed.compiled = true;
|
|
1228
1581
|
try {
|
|
@@ -1238,6 +1591,8 @@ export class Cascade {
|
|
|
1238
1591
|
if (!indexed.match || !indexed.match(el))
|
|
1239
1592
|
continue;
|
|
1240
1593
|
matched?.push(indexed.id);
|
|
1594
|
+
if (out === null)
|
|
1595
|
+
continue;
|
|
1241
1596
|
const origin = rule.order < 0 ? Origin.UserAgent : Origin.Author;
|
|
1242
1597
|
pushRule(out, rule, origin);
|
|
1243
1598
|
}
|
|
@@ -1304,9 +1659,57 @@ export class Cascade {
|
|
|
1304
1659
|
}
|
|
1305
1660
|
}
|
|
1306
1661
|
out.sort(byCascade);
|
|
1662
|
+
dropPaletteChrome(out);
|
|
1307
1663
|
return out;
|
|
1308
1664
|
}
|
|
1309
1665
|
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Takes the palette's control chrome (`PALETTE_CHROME`) out of an element's
|
|
1668
|
+
* candidates where the page styles its background or border, so what shows
|
|
1669
|
+
* through is the web's UA values the page was written against. The rule is
|
|
1670
|
+
* Blink's (`LayoutTheme::IsControlStyled`): a background or border longhand
|
|
1671
|
+
* set by the page, radius included, drops the control's native appearance;
|
|
1672
|
+
* so does `appearance: none`. `out` is in cascade order, so the UA's
|
|
1673
|
+
* candidates are the ones before the first of any other origin.
|
|
1674
|
+
*/
|
|
1675
|
+
function dropPaletteChrome(out) {
|
|
1676
|
+
let chrome = false;
|
|
1677
|
+
let i = 0;
|
|
1678
|
+
for (; i < out.length && out[i].origin === Origin.UserAgent; i += 1) {
|
|
1679
|
+
if (PALETTE_CHROME.has(out[i].declarations))
|
|
1680
|
+
chrome = true;
|
|
1681
|
+
}
|
|
1682
|
+
if (!chrome)
|
|
1683
|
+
return;
|
|
1684
|
+
let styled = false;
|
|
1685
|
+
for (let j = i; j < out.length && !styled; j += 1) {
|
|
1686
|
+
styled = pick(out[j]).some(stylesChrome);
|
|
1687
|
+
}
|
|
1688
|
+
if (!styled)
|
|
1689
|
+
return;
|
|
1690
|
+
let kept = 0;
|
|
1691
|
+
for (const c of out) {
|
|
1692
|
+
if (c.origin === Origin.UserAgent && PALETTE_CHROME.has(c.declarations)) {
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
out[kept++] = c;
|
|
1696
|
+
}
|
|
1697
|
+
out.length = kept;
|
|
1698
|
+
}
|
|
1699
|
+
/** Whether a declaration styles what a control's native look draws: Blink's
|
|
1700
|
+
* `is_background` and `is_border` properties and their shorthands, and an
|
|
1701
|
+
* `appearance` of `none`. */
|
|
1702
|
+
function stylesChrome(d) {
|
|
1703
|
+
const prop = d.prop;
|
|
1704
|
+
if (prop.startsWith('background')) {
|
|
1705
|
+
return prop !== 'background-repeat' && prop !== 'background-blend-mode';
|
|
1706
|
+
}
|
|
1707
|
+
if (prop.startsWith('border')) {
|
|
1708
|
+
return prop !== 'border-collapse' && prop !== 'border-spacing';
|
|
1709
|
+
}
|
|
1710
|
+
return ((prop === 'appearance' || prop === '-webkit-appearance') &&
|
|
1711
|
+
d.value.trim().toLowerCase() === 'none');
|
|
1712
|
+
}
|
|
1310
1713
|
/** An implied element's style as the root box's: its look, not its role —
|
|
1311
1714
|
* the root is still a block, in flow, and the initial containing block. */
|
|
1312
1715
|
function asRoot(style) {
|
|
@@ -1638,7 +2041,10 @@ function presentationHints(el) {
|
|
|
1638
2041
|
// `width`/`height` are lengths on the replaced and table elements and mean
|
|
1639
2042
|
// nothing anywhere else, which is what stops a `<input width>` from
|
|
1640
2043
|
// becoming a CSS width the widget then disagrees with.
|
|
1641
|
-
if (SIZED.has(tag)
|
|
2044
|
+
if (SIZED.has(tag) ||
|
|
2045
|
+
// an image button's are its image's, as an `<img>`'s are (HTML 15.4.3)
|
|
2046
|
+
(tag === 'input' &&
|
|
2047
|
+
(attr(el, 'type') ?? '').trim().toLowerCase() === 'image')) {
|
|
1642
2048
|
const width = attr(el, 'width');
|
|
1643
2049
|
if (width)
|
|
1644
2050
|
push('width', lengthAttr(width));
|
|
@@ -1666,6 +2072,14 @@ function presentationHints(el) {
|
|
|
1666
2072
|
const h = height ? svgSizeHint(height) : null;
|
|
1667
2073
|
if (h)
|
|
1668
2074
|
push('height', h);
|
|
2075
|
+
// and its paint is its `fill` and `stroke` properties' (13.2), which a
|
|
2076
|
+
// rule of the document's then sets over the attribute
|
|
2077
|
+
const fill = attr(el, 'fill');
|
|
2078
|
+
if (fill)
|
|
2079
|
+
push('fill', fill);
|
|
2080
|
+
const stroke = attr(el, 'stroke');
|
|
2081
|
+
if (stroke)
|
|
2082
|
+
push('stroke', stroke);
|
|
1669
2083
|
}
|
|
1670
2084
|
if (tag === 'table') {
|
|
1671
2085
|
const border = attr(el, 'border');
|
|
@@ -1878,4 +2292,9 @@ const ABSOLUTE_SIZES = new Set([
|
|
|
1878
2292
|
'xxx-large',
|
|
1879
2293
|
'initial',
|
|
1880
2294
|
]);
|
|
2295
|
+
/** Whether an element is the document's root: its parent the document, or
|
|
2296
|
+
* none, as the `<html>` the cascade supplies where the markup has none. */
|
|
2297
|
+
function isRootElement(el) {
|
|
2298
|
+
return !el.parent || el.parent.type === 'root';
|
|
2299
|
+
}
|
|
1881
2300
|
//# sourceMappingURL=cascade.js.map
|