@react-x11/components 0.14.0 → 0.14.1
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/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +310 -12
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +1 -15
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/layout/block.d.ts +16 -5
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +26 -5
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +13 -0
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +18 -0
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +58 -10
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.js +19 -6
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +38 -16
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +1 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +57 -12
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +4 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +269 -84
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +27 -10
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +259 -46
- package/dist/html/svg.js.map +1 -1
- package/package.json +1 -1
- package/src/html/css/cascade.ts +343 -8
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +1 -13
- package/src/html/layout/block.ts +27 -5
- package/src/html/layout/boxes.ts +30 -0
- package/src/html/layout/css-grid.ts +77 -13
- package/src/html/layout/flex.ts +22 -9
- package/src/html/layout/table.ts +45 -18
- package/src/html/node.ts +54 -11
- package/src/html/paint.ts +379 -90
- package/src/html/svg.ts +318 -39
package/src/html/css/cascade.ts
CHANGED
|
@@ -21,7 +21,6 @@ import { Element as DomElement, isTag } from 'domhandler';
|
|
|
21
21
|
import type { Element } from 'domhandler';
|
|
22
22
|
|
|
23
23
|
import { attr, tagOf } from '../dom.js';
|
|
24
|
-
import { svgSizeHint } from '../svg.js';
|
|
25
24
|
import {
|
|
26
25
|
asciiLower,
|
|
27
26
|
escapeEnd,
|
|
@@ -54,6 +53,13 @@ import type { UnitContext } from './values.js';
|
|
|
54
53
|
import { viewportUnit } from './values.js';
|
|
55
54
|
import { customProperties, substituteIn } from './vars.js';
|
|
56
55
|
import type { CustomProps } from './vars.js';
|
|
56
|
+
import {
|
|
57
|
+
ROOT_BOX_PROPS,
|
|
58
|
+
SHAPE_TAGS,
|
|
59
|
+
isShapeProp,
|
|
60
|
+
shapeValue,
|
|
61
|
+
} from './shapes.js';
|
|
62
|
+
import type { ShapeContext, ShapeStyle, ShapeStyles } from './shapes.js';
|
|
57
63
|
|
|
58
64
|
/** Where a declaration came from. Higher wins before specificity is asked. */
|
|
59
65
|
const enum Origin {
|
|
@@ -139,6 +145,14 @@ interface IndexedRule {
|
|
|
139
145
|
compiled: boolean;
|
|
140
146
|
/** Which rule this is, in a sharing key (`Cascade.sharedStyleFor`). */
|
|
141
147
|
id: number;
|
|
148
|
+
/** For a rule asked of the elements in a drawing by their type, or of
|
|
149
|
+
* them all (`Cascade.shapeStyles`): the ids and classes it asks of its
|
|
150
|
+
* subject's ancestors (`ancestorKeys`). Null until a drawing asks, and
|
|
151
|
+
* in every other rule. */
|
|
152
|
+
needs: readonly string[] | null;
|
|
153
|
+
/** For a rule kept for drawings: whether it declares something a
|
|
154
|
+
* drawing's root does not have from its box's style. Null until asked. */
|
|
155
|
+
root: boolean | null;
|
|
142
156
|
}
|
|
143
157
|
|
|
144
158
|
/**
|
|
@@ -194,6 +208,11 @@ class RuleIndex {
|
|
|
194
208
|
*/
|
|
195
209
|
reaches(el: Element): boolean {
|
|
196
210
|
if (this.universal.length || this.byTag.has(tagOf(el))) return true;
|
|
211
|
+
return this.names(el);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Whether a bucket here is for `el`'s id, or for a class of its. */
|
|
215
|
+
names(el: Element): boolean {
|
|
197
216
|
if (this.byId.size) {
|
|
198
217
|
const id = attr(el, 'id');
|
|
199
218
|
if (id && this.byId.has(id)) return true;
|
|
@@ -209,18 +228,19 @@ class RuleIndex {
|
|
|
209
228
|
return false;
|
|
210
229
|
}
|
|
211
230
|
|
|
212
|
-
add(rule: StyleRule): void {
|
|
231
|
+
add(rule: StyleRule, key = rightmostKey(rule.selector)): void {
|
|
213
232
|
this.size += 1;
|
|
214
233
|
const indexed: IndexedRule = {
|
|
215
234
|
rule,
|
|
216
235
|
match: null,
|
|
217
236
|
compiled: false,
|
|
218
237
|
id: this._nextId++,
|
|
238
|
+
needs: null,
|
|
239
|
+
root: null,
|
|
219
240
|
};
|
|
220
241
|
if (rule.selector.includes(':hover') || rule.selector.includes(':active')) {
|
|
221
242
|
this.hoverSensitive = true;
|
|
222
243
|
}
|
|
223
|
-
const key = rightmostKey(rule.selector);
|
|
224
244
|
if (UNSHAREABLE.test(rule.selector)) {
|
|
225
245
|
if (key.kind === 'id') this.ownStyleIds.add(key.name);
|
|
226
246
|
else if (key.kind === 'class') this.ownStyleClasses.add(key.name);
|
|
@@ -576,11 +596,12 @@ const POINTER_PSEUDO = /:(?:hover|active)(?![\w-])/i;
|
|
|
576
596
|
function compileSelector(
|
|
577
597
|
selector: string,
|
|
578
598
|
adapter: CssSelectAdapter,
|
|
599
|
+
pseudos: typeof PSEUDOS = PSEUDOS,
|
|
579
600
|
): (el: Element) => boolean {
|
|
580
601
|
return compile(noEmptyWords(selector), {
|
|
581
602
|
adapter,
|
|
582
603
|
xmlMode: false,
|
|
583
|
-
pseudos
|
|
604
|
+
pseudos,
|
|
584
605
|
cacheResults: !POINTER_PSEUDO.test(selector),
|
|
585
606
|
} as unknown as Parameters<typeof compile>[1]) as unknown as (
|
|
586
607
|
node: Element,
|
|
@@ -641,7 +662,14 @@ function rightmostKey(selector: string): {
|
|
|
641
662
|
start = i + 1;
|
|
642
663
|
}
|
|
643
664
|
}
|
|
644
|
-
|
|
665
|
+
return compoundKey(selector.slice(start));
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/** A compound selector's most selective key (`rightmostKey`). */
|
|
669
|
+
function compoundKey(compound: string): {
|
|
670
|
+
kind: 'id' | 'class' | 'tag' | 'any';
|
|
671
|
+
name: string;
|
|
672
|
+
} {
|
|
645
673
|
let id: string | null = null;
|
|
646
674
|
let cls: string | null = null;
|
|
647
675
|
let tag: string | null = null;
|
|
@@ -675,6 +703,39 @@ function rightmostKey(selector: string): {
|
|
|
675
703
|
return { kind: 'any', name: '' };
|
|
676
704
|
}
|
|
677
705
|
|
|
706
|
+
/**
|
|
707
|
+
* The ids and classes a selector asks of its subject's ancestors: for each
|
|
708
|
+
* compound a descendant or a child combinator follows, its id, or else its
|
|
709
|
+
* first class, as `#name` or `.name`. Such a compound is an ancestor of
|
|
710
|
+
* the subject — one a sibling combinator follows is a sibling of one — so
|
|
711
|
+
* an element with no ancestor of some such name is not the rule's, which
|
|
712
|
+
* is known without matching it. A compound with neither asks nothing.
|
|
713
|
+
*/
|
|
714
|
+
function ancestorKeys(selector: string): string[] {
|
|
715
|
+
const keys: string[] = [];
|
|
716
|
+
const compounds = compoundsOf(selector);
|
|
717
|
+
for (let i = 0; i < compounds.length - 1; i += 1) {
|
|
718
|
+
const { text, next } = compounds[i];
|
|
719
|
+
if (next !== ' ' && next !== '>') continue;
|
|
720
|
+
const key = compoundKey(text);
|
|
721
|
+
if (key.kind === 'id') keys.push(`#${key.name}`);
|
|
722
|
+
else if (key.kind === 'class') keys.push(`.${key.name}`);
|
|
723
|
+
}
|
|
724
|
+
return keys;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** What the rules asked of every element of a drawing, or of every one of
|
|
728
|
+
* a type, ask of its ancestors (`Cascade.shapeStyles`). */
|
|
729
|
+
interface ShapeNeeds {
|
|
730
|
+
/** The classes and the ids some such rule names in an ancestor. */
|
|
731
|
+
classes: Set<string>;
|
|
732
|
+
ids: Set<string>;
|
|
733
|
+
/** Whether a rule asked of every element asks nothing of its ancestors,
|
|
734
|
+
* and the types with such a rule. */
|
|
735
|
+
universal: boolean;
|
|
736
|
+
tags: Set<string>;
|
|
737
|
+
}
|
|
738
|
+
|
|
678
739
|
function balancedEnd(
|
|
679
740
|
text: string,
|
|
680
741
|
from: number,
|
|
@@ -763,6 +824,13 @@ export class Cascade {
|
|
|
763
824
|
marker: new RuleIndex(),
|
|
764
825
|
selection: new RuleIndex(),
|
|
765
826
|
};
|
|
827
|
+
/** The author's rules that could style a shape in a drawing: the ones
|
|
828
|
+
* that declare a property a shape has (`shapes.ts`) and whose subject
|
|
829
|
+
* is no element of HTML's alone. They are in `_index` too, for the
|
|
830
|
+
* elements that have boxes; these are asked of the ones in an inline
|
|
831
|
+
* `<svg>`, which have none (`shapeStyles`). */
|
|
832
|
+
private _shapes = new RuleIndex();
|
|
833
|
+
private _shapeNeeds: ShapeNeeds | null = null;
|
|
766
834
|
private _adapter: CssSelectAdapter;
|
|
767
835
|
/** The compounds of the selectors that test the pointer, each without
|
|
768
836
|
* its `:hover` or `:active` (`pointerCompounds`), and their matchers,
|
|
@@ -837,6 +905,8 @@ export class Cascade {
|
|
|
837
905
|
/** Whether any declaration has a length in `lh` or `rlh`: only then is
|
|
838
906
|
* the line height settled ahead of the declarations that read it. */
|
|
839
907
|
private _lh = false;
|
|
908
|
+
/** The pseudo-classes css-select is handed, `:root` this cascade's. */
|
|
909
|
+
private _pseudos: typeof PSEUDOS;
|
|
840
910
|
|
|
841
911
|
constructor(
|
|
842
912
|
sheets: Stylesheet[],
|
|
@@ -848,7 +918,13 @@ export class Cascade {
|
|
|
848
918
|
zeroWidth: FaceMetric | null = null,
|
|
849
919
|
normalLine: FaceMetric | null = null,
|
|
850
920
|
families: FontFamilies | null = null,
|
|
921
|
+
/** The document's element where it is not an `<html>`: an SVG image's
|
|
922
|
+
* `<svg>`, which is `:root` there (Selectors 4, 14.1). */
|
|
923
|
+
documentElement: Element | null = null,
|
|
851
924
|
) {
|
|
925
|
+
this._pseudos = documentElement
|
|
926
|
+
? { ...PSEUDOS, root: (el: Element) => el === documentElement }
|
|
927
|
+
: PSEUDOS;
|
|
852
928
|
this._xHeightOf = xHeight;
|
|
853
929
|
this._zeroWidthOf = zeroWidth;
|
|
854
930
|
this._normalLineOf = normalLine;
|
|
@@ -875,7 +951,17 @@ export class Cascade {
|
|
|
875
951
|
if (!this._lh && usesLh(rule.declarations)) this._lh = true;
|
|
876
952
|
const pseudo = splitPseudoElement(rule);
|
|
877
953
|
if (pseudo) this._pseudo[pseudo.which].add(pseudo.rule);
|
|
878
|
-
else
|
|
954
|
+
else {
|
|
955
|
+
const key = rightmostKey(rule.selector);
|
|
956
|
+
this._index.add(rule, key);
|
|
957
|
+
if (
|
|
958
|
+
rule.order >= 0 &&
|
|
959
|
+
(key.kind !== 'tag' || SHAPE_TAGS.has(key.name)) &&
|
|
960
|
+
declaresShape(rule.declarations)
|
|
961
|
+
) {
|
|
962
|
+
this._shapes.add(rule, key);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
879
965
|
this._noteHover((pseudo?.rule ?? rule).selector);
|
|
880
966
|
}
|
|
881
967
|
for (const bp of sheet.breakpoints) breakpoints.add(bp);
|
|
@@ -1052,7 +1138,7 @@ export class Cascade {
|
|
|
1052
1138
|
* it, as a rule it refuses drops out of the cascade. */
|
|
1053
1139
|
private _compile(selector: string): ((el: Element) => boolean) | null {
|
|
1054
1140
|
try {
|
|
1055
|
-
return compileSelector(selector, this._adapter);
|
|
1141
|
+
return compileSelector(selector, this._adapter, this._pseudos);
|
|
1056
1142
|
} catch {
|
|
1057
1143
|
return null;
|
|
1058
1144
|
}
|
|
@@ -1743,6 +1829,185 @@ export class Cascade {
|
|
|
1743
1829
|
return { style: asRoot(this.styleFor(synthetic, parent, false)), html };
|
|
1744
1830
|
}
|
|
1745
1831
|
|
|
1832
|
+
/** Whether any rule could style a shape in a drawing: a document with
|
|
1833
|
+
* none asks nothing of its drawings (`shapeStyles`). */
|
|
1834
|
+
get stylesShapes(): boolean {
|
|
1835
|
+
return this._shapes.size > 0;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/** Whether a pointer move can change what a rule gives a shape in a
|
|
1839
|
+
* drawing. */
|
|
1840
|
+
get shapesFollowPointer(): boolean {
|
|
1841
|
+
return this._shapes.hoverSensitive;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
/**
|
|
1845
|
+
* What the rules give the elements of an inline drawing, `root` and what
|
|
1846
|
+
* is in it, or null where none reaches one: the narrow cascade of
|
|
1847
|
+
* `shapes.ts`. `style` is the root's computed style — its `color`, its
|
|
1848
|
+
* custom properties and its font size are what a value in the drawing is
|
|
1849
|
+
* read against, as though every element in it had inherited them, which
|
|
1850
|
+
* all but one a rule gives its own has.
|
|
1851
|
+
*
|
|
1852
|
+
* An element's rules and its `style` attribute are put in cascade order,
|
|
1853
|
+
* and each property is the last valid declaration's. The presentation
|
|
1854
|
+
* attributes are below them all (SVG 2, 6.2), and `SvgView` reads those
|
|
1855
|
+
* itself. The root's box has its paint, its opacity and whether it is
|
|
1856
|
+
* shown from its computed style, so those are left out for it.
|
|
1857
|
+
*
|
|
1858
|
+
* Asked as a drawing is first painted (`BoxTree.shapeStyler`), of the
|
|
1859
|
+
* rules in `_shapes` alone: a page of hundreds of icons pays for the ones
|
|
1860
|
+
* it shows, a look in the buckets for each of their elements where no
|
|
1861
|
+
* rule reaches them, and a document with no such rule nothing.
|
|
1862
|
+
*/
|
|
1863
|
+
shapeStyles(root: Element, style: ComputedStyle): ShapeStyles | null {
|
|
1864
|
+
const index = this._shapes;
|
|
1865
|
+
if (!index.size) return null;
|
|
1866
|
+
// The rules kept by a class or an id are asked of the elements with
|
|
1867
|
+
// that name. The rest — `.dark .logo path`, `.menu [aria-hidden]` — are
|
|
1868
|
+
// asked of every `<path>`, or of every element, and nearly all of them
|
|
1869
|
+
// are for some other part of the page: each says so by the class or the
|
|
1870
|
+
// id it asks of an ancestor, which is looked for once for the drawing,
|
|
1871
|
+
// around it and in it, where matching the rule would look for it from
|
|
1872
|
+
// each of its elements. On a page with none of them around a drawing,
|
|
1873
|
+
// nothing is matched at all.
|
|
1874
|
+
const needs = (this._shapeNeeds ??= this._needsOfShapes());
|
|
1875
|
+
const found = new Set<string>();
|
|
1876
|
+
const asks = needs.classes.size > 0 || needs.ids.size > 0;
|
|
1877
|
+
const note = (el: Element): void => {
|
|
1878
|
+
if (needs.ids.size) {
|
|
1879
|
+
const id = attr(el, 'id');
|
|
1880
|
+
if (id && needs.ids.has(id)) found.add(`#${id}`);
|
|
1881
|
+
}
|
|
1882
|
+
if (needs.classes.size) {
|
|
1883
|
+
const className = attr(el, 'class');
|
|
1884
|
+
if (className) {
|
|
1885
|
+
for (const name of className.split(/\s+/)) {
|
|
1886
|
+
if (needs.classes.has(name)) found.add(`.${name}`);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
};
|
|
1891
|
+
if (asks) {
|
|
1892
|
+
let at = root.parent;
|
|
1893
|
+
while (at && at.type === 'tag') {
|
|
1894
|
+
note(at as Element);
|
|
1895
|
+
at = at.parent;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
const live = (indexed: IndexedRule): boolean => {
|
|
1899
|
+
const keys = indexed.needs;
|
|
1900
|
+
if (keys) for (const key of keys) if (!found.has(key)) return false;
|
|
1901
|
+
return true;
|
|
1902
|
+
};
|
|
1903
|
+
// The root's box has its paint from its computed style, so most of the
|
|
1904
|
+
// rules that name a drawing by its class — an icon set's — have
|
|
1905
|
+
// nothing else to say of it.
|
|
1906
|
+
const liveForRoot = (indexed: IndexedRule): boolean =>
|
|
1907
|
+
(indexed.root ??= declaresRootShape(indexed.rule.declarations)) &&
|
|
1908
|
+
live(indexed);
|
|
1909
|
+
|
|
1910
|
+
let of: Map<Element, ShapeStyle> | null = null;
|
|
1911
|
+
let key = '';
|
|
1912
|
+
let ctx: ShapeContext | null = null;
|
|
1913
|
+
// in document order, the root first: an element's place in it is what
|
|
1914
|
+
// the key names it by, and its ancestors in the drawing are noted
|
|
1915
|
+
// before it is asked of
|
|
1916
|
+
const stack: Element[] = [root];
|
|
1917
|
+
let place = -1;
|
|
1918
|
+
while (stack.length) {
|
|
1919
|
+
const el = stack.pop()!;
|
|
1920
|
+
place += 1;
|
|
1921
|
+
// what is in a `<foreignObject>` is not the drawing's to draw
|
|
1922
|
+
if (el.name !== 'foreignobject' && el.name !== 'foreignObject') {
|
|
1923
|
+
const children = el.children;
|
|
1924
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1925
|
+
const child = children[i];
|
|
1926
|
+
if (child.type === 'tag') stack.push(child as Element);
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
if (asks) note(el);
|
|
1930
|
+
const tag = tagOf(el);
|
|
1931
|
+
const asked = found.size
|
|
1932
|
+
? index.universal.length > 0 || index.byTag.has(tag)
|
|
1933
|
+
: needs.universal || needs.tags.has(tag);
|
|
1934
|
+
if (!asked && !index.names(el)) continue;
|
|
1935
|
+
const candidates: Candidate[] = [];
|
|
1936
|
+
this._matchInto(
|
|
1937
|
+
index,
|
|
1938
|
+
el,
|
|
1939
|
+
candidates,
|
|
1940
|
+
undefined,
|
|
1941
|
+
el === root ? liveForRoot : live,
|
|
1942
|
+
);
|
|
1943
|
+
if (!candidates.length) continue;
|
|
1944
|
+
const inline = attr(el, 'style');
|
|
1945
|
+
if (inline) pushInlineShapes(candidates, inline);
|
|
1946
|
+
candidates.sort(byCascade);
|
|
1947
|
+
ctx ??= {
|
|
1948
|
+
color: style.color,
|
|
1949
|
+
scheme: style.colorScheme,
|
|
1950
|
+
units: {
|
|
1951
|
+
em: style.fontSize,
|
|
1952
|
+
rem: (this._root ?? this.initial).fontSize,
|
|
1953
|
+
vw: this.viewportWidth,
|
|
1954
|
+
vh: this.viewportHeight,
|
|
1955
|
+
scale: this.scale,
|
|
1956
|
+
initial: this.initial,
|
|
1957
|
+
},
|
|
1958
|
+
};
|
|
1959
|
+
let own: Record<string, string> | null = null;
|
|
1960
|
+
for (const c of candidates) {
|
|
1961
|
+
for (const d of pick(c)) {
|
|
1962
|
+
if (!isShapeProp(d.prop)) continue;
|
|
1963
|
+
if (el === root && ROOT_BOX_PROPS.has(d.prop)) continue;
|
|
1964
|
+
// a `var()` with nothing to stand for it is invalid at
|
|
1965
|
+
// computed-value time, and the property as though `unset`
|
|
1966
|
+
const value = shapeValue(
|
|
1967
|
+
d.prop,
|
|
1968
|
+
d.vars ? (substituteIn(d.value, style.custom) ?? 'unset') : d.value,
|
|
1969
|
+
ctx,
|
|
1970
|
+
);
|
|
1971
|
+
if (value !== null) (own ??= {})[d.prop] = value;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
if (!own) continue;
|
|
1975
|
+
(of ??= new Map()).set(el, own);
|
|
1976
|
+
key += `${place}{`;
|
|
1977
|
+
for (const prop in own) key += `${prop}:${own[prop]};`;
|
|
1978
|
+
key += '}';
|
|
1979
|
+
}
|
|
1980
|
+
return of ? { of, key } : null;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
/** `ShapeNeeds`, and each such rule's own (`IndexedRule.needs`). */
|
|
1984
|
+
private _needsOfShapes(): ShapeNeeds {
|
|
1985
|
+
const index = this._shapes;
|
|
1986
|
+
const needs: ShapeNeeds = {
|
|
1987
|
+
classes: new Set(),
|
|
1988
|
+
ids: new Set(),
|
|
1989
|
+
universal: false,
|
|
1990
|
+
tags: new Set(),
|
|
1991
|
+
};
|
|
1992
|
+
const read = (bucket: IndexedRule[]): boolean => {
|
|
1993
|
+
let free = false;
|
|
1994
|
+
for (const indexed of bucket) {
|
|
1995
|
+
const keys = ancestorKeys(indexed.rule.selector);
|
|
1996
|
+
indexed.needs = keys;
|
|
1997
|
+
if (!keys.length) free = true;
|
|
1998
|
+
for (const key of keys) {
|
|
1999
|
+
(key[0] === '#' ? needs.ids : needs.classes).add(key.slice(1));
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return free;
|
|
2003
|
+
};
|
|
2004
|
+
needs.universal = read(index.universal);
|
|
2005
|
+
for (const [tag, bucket] of index.byTag) {
|
|
2006
|
+
if (read(bucket)) needs.tags.add(tag);
|
|
2007
|
+
}
|
|
2008
|
+
return needs;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1746
2011
|
/**
|
|
1747
2012
|
* Whether the pointer's move from `was` to where it is changed which
|
|
1748
2013
|
* rules match `el`, or its `::before`, `::after` or another of its
|
|
@@ -1785,6 +2050,8 @@ export class Cascade {
|
|
|
1785
2050
|
el: Element,
|
|
1786
2051
|
out: Candidate[] | null,
|
|
1787
2052
|
matched?: number[],
|
|
2053
|
+
/** The rules to try, where some are known not to match without it. */
|
|
2054
|
+
live?: (indexed: IndexedRule) => boolean,
|
|
1788
2055
|
): void {
|
|
1789
2056
|
// A media query's width is CSS pixels; the viewport is kept in device.
|
|
1790
2057
|
const width = this.viewportWidth / this.scale;
|
|
@@ -1793,6 +2060,7 @@ export class Cascade {
|
|
|
1793
2060
|
const consider = (bucket: IndexedRule[] | undefined): void => {
|
|
1794
2061
|
if (!bucket) return;
|
|
1795
2062
|
for (const indexed of bucket) {
|
|
2063
|
+
if (live !== undefined && !live(indexed)) continue;
|
|
1796
2064
|
const rule = indexed.rule;
|
|
1797
2065
|
if (
|
|
1798
2066
|
!mediaMatches(
|
|
@@ -1808,7 +2076,11 @@ export class Cascade {
|
|
|
1808
2076
|
if (!indexed.compiled) {
|
|
1809
2077
|
indexed.compiled = true;
|
|
1810
2078
|
try {
|
|
1811
|
-
indexed.match = compileSelector(
|
|
2079
|
+
indexed.match = compileSelector(
|
|
2080
|
+
rule.selector,
|
|
2081
|
+
this._adapter,
|
|
2082
|
+
this._pseudos,
|
|
2083
|
+
);
|
|
1812
2084
|
} catch {
|
|
1813
2085
|
// A selector this matcher does not know (`::-moz-…`, a CSS4 form
|
|
1814
2086
|
// it has not learnt) drops out of the cascade rather than out of
|
|
@@ -2088,6 +2360,51 @@ function setsContent(c: Candidate): boolean {
|
|
|
2088
2360
|
/** Whether a rule's declarations set a custom property, by the array. */
|
|
2089
2361
|
const CUSTOM_IN = new WeakMap<Declaration[], boolean>();
|
|
2090
2362
|
|
|
2363
|
+
/** Whether declarations set a property a shape in a drawing has. */
|
|
2364
|
+
function declaresShape(declarations: readonly Declaration[]): boolean {
|
|
2365
|
+
for (const d of declarations) if (isShapeProp(d.prop)) return true;
|
|
2366
|
+
return false;
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
/** Whether declarations set one a drawing's root does not have from its
|
|
2370
|
+
* box's style (`ROOT_BOX_PROPS`). */
|
|
2371
|
+
function declaresRootShape(declarations: readonly Declaration[]): boolean {
|
|
2372
|
+
for (const d of declarations) {
|
|
2373
|
+
if (isShapeProp(d.prop) && !ROOT_BOX_PROPS.has(d.prop)) return true;
|
|
2374
|
+
}
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
/** A `style` attribute's declarations of a shape's properties, as
|
|
2379
|
+
* candidates: over every rule, and its `!important` ones over theirs. */
|
|
2380
|
+
function pushInlineShapes(out: Candidate[], inline: string): void {
|
|
2381
|
+
const declarations = parseDeclarations(inline).filter((d) =>
|
|
2382
|
+
isShapeProp(d.prop),
|
|
2383
|
+
);
|
|
2384
|
+
const normal = declarations.filter((d) => !d.important);
|
|
2385
|
+
const important = declarations.filter((d) => d.important);
|
|
2386
|
+
if (normal.length) {
|
|
2387
|
+
out.push({
|
|
2388
|
+
origin: Origin.Inline,
|
|
2389
|
+
layer: null,
|
|
2390
|
+
specificity: 0,
|
|
2391
|
+
order: 0,
|
|
2392
|
+
declarations: normal,
|
|
2393
|
+
only: -1,
|
|
2394
|
+
});
|
|
2395
|
+
}
|
|
2396
|
+
if (important.length) {
|
|
2397
|
+
out.push({
|
|
2398
|
+
origin: Origin.InlineImportant,
|
|
2399
|
+
layer: null,
|
|
2400
|
+
specificity: 0,
|
|
2401
|
+
order: 0,
|
|
2402
|
+
declarations: important,
|
|
2403
|
+
only: -1,
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2091
2408
|
function pick(c: Candidate): Declaration[] {
|
|
2092
2409
|
return c.only < 0 ? c.declarations : [c.declarations[c.only]];
|
|
2093
2410
|
}
|
|
@@ -2473,6 +2790,24 @@ function lengthAttr(value: string): string {
|
|
|
2473
2790
|
return Number.isFinite(n) ? `${n}px` : v;
|
|
2474
2791
|
}
|
|
2475
2792
|
|
|
2793
|
+
/** A number and a unit, as an SVG's `width` and `height` are written. */
|
|
2794
|
+
const SVG_LENGTH =
|
|
2795
|
+
/^\s*([+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*([a-z]*)\s*$/i;
|
|
2796
|
+
|
|
2797
|
+
/**
|
|
2798
|
+
* The `width`/`height` of an inline `<svg>` as CSS: they are presentation
|
|
2799
|
+
* attributes for the properties of the same names (SVG 2, 5.1.1), so
|
|
2800
|
+
* `height="50%"` is a percentage of the containing block like a style's.
|
|
2801
|
+
* A bare number is pixels; null for what CSS would not parse.
|
|
2802
|
+
*/
|
|
2803
|
+
function svgSizeHint(value: string): string | null {
|
|
2804
|
+
const v = value.trim();
|
|
2805
|
+
if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?%$/i.test(v)) return v;
|
|
2806
|
+
const m = SVG_LENGTH.exec(v);
|
|
2807
|
+
if (!m) return null;
|
|
2808
|
+
return m[2] ? v : `${parseFloat(m[1])}px`;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2476
2811
|
function closestTable(el: Element): Element | null {
|
|
2477
2812
|
let node = el.parent;
|
|
2478
2813
|
while (node) {
|