@react-x11/components 0.13.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/controls.d.ts +29 -2
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +70 -2
- package/dist/html/controls.js.map +1 -1
- 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 +336 -16
- 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 +18 -4
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +66 -28
- package/dist/html/css/parse.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 +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +547 -144
- 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.map +1 -1
- package/dist/html/css/ua.js +14 -2
- 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/fonts.d.ts +55 -5
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +284 -23
- package/dist/html/fonts.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 +104 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +410 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +72 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +40 -2
- 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 +144 -24
- 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 +585 -85
- 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 +144 -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 +97 -31
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +4 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +319 -47
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +83 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1474 -217
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +39 -9
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +482 -37
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.js +87 -7
- package/dist/html/widgets.js.map +1 -1
- 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/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 +1 -1
- package/src/html/controls.ts +85 -2
- package/src/html/css/cascade.ts +366 -8
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +652 -147
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +15 -2
- package/src/html/css/values.ts +4 -0
- package/src/html/fonts.ts +312 -29
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +466 -37
- package/src/html/layout/boxes.ts +95 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +169 -32
- package/src/html/layout/flex.ts +686 -79
- package/src/html/layout/inline.ts +149 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +105 -35
- package/src/html/node.ts +354 -48
- package/src/html/paint.ts +1804 -315
- package/src/html/svg.ts +573 -36
- package/src/html/widgets.ts +95 -9
- package/src/html/woff2.ts +612 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
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,
|
|
@@ -33,6 +32,7 @@ import type { Declaration, StyleRule, Stylesheet } from './parse.js';
|
|
|
33
32
|
import {
|
|
34
33
|
applyDeclaration,
|
|
35
34
|
blockify,
|
|
35
|
+
settleButton,
|
|
36
36
|
settleContentVisibility,
|
|
37
37
|
settleOverflow,
|
|
38
38
|
settleClamp,
|
|
@@ -53,6 +53,13 @@ import type { UnitContext } from './values.js';
|
|
|
53
53
|
import { viewportUnit } from './values.js';
|
|
54
54
|
import { customProperties, substituteIn } from './vars.js';
|
|
55
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';
|
|
56
63
|
|
|
57
64
|
/** Where a declaration came from. Higher wins before specificity is asked. */
|
|
58
65
|
const enum Origin {
|
|
@@ -138,6 +145,14 @@ interface IndexedRule {
|
|
|
138
145
|
compiled: boolean;
|
|
139
146
|
/** Which rule this is, in a sharing key (`Cascade.sharedStyleFor`). */
|
|
140
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;
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
/**
|
|
@@ -193,6 +208,11 @@ class RuleIndex {
|
|
|
193
208
|
*/
|
|
194
209
|
reaches(el: Element): boolean {
|
|
195
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 {
|
|
196
216
|
if (this.byId.size) {
|
|
197
217
|
const id = attr(el, 'id');
|
|
198
218
|
if (id && this.byId.has(id)) return true;
|
|
@@ -208,18 +228,19 @@ class RuleIndex {
|
|
|
208
228
|
return false;
|
|
209
229
|
}
|
|
210
230
|
|
|
211
|
-
add(rule: StyleRule): void {
|
|
231
|
+
add(rule: StyleRule, key = rightmostKey(rule.selector)): void {
|
|
212
232
|
this.size += 1;
|
|
213
233
|
const indexed: IndexedRule = {
|
|
214
234
|
rule,
|
|
215
235
|
match: null,
|
|
216
236
|
compiled: false,
|
|
217
237
|
id: this._nextId++,
|
|
238
|
+
needs: null,
|
|
239
|
+
root: null,
|
|
218
240
|
};
|
|
219
241
|
if (rule.selector.includes(':hover') || rule.selector.includes(':active')) {
|
|
220
242
|
this.hoverSensitive = true;
|
|
221
243
|
}
|
|
222
|
-
const key = rightmostKey(rule.selector);
|
|
223
244
|
if (UNSHAREABLE.test(rule.selector)) {
|
|
224
245
|
if (key.kind === 'id') this.ownStyleIds.add(key.name);
|
|
225
246
|
else if (key.kind === 'class') this.ownStyleClasses.add(key.name);
|
|
@@ -575,11 +596,12 @@ const POINTER_PSEUDO = /:(?:hover|active)(?![\w-])/i;
|
|
|
575
596
|
function compileSelector(
|
|
576
597
|
selector: string,
|
|
577
598
|
adapter: CssSelectAdapter,
|
|
599
|
+
pseudos: typeof PSEUDOS = PSEUDOS,
|
|
578
600
|
): (el: Element) => boolean {
|
|
579
601
|
return compile(noEmptyWords(selector), {
|
|
580
602
|
adapter,
|
|
581
603
|
xmlMode: false,
|
|
582
|
-
pseudos
|
|
604
|
+
pseudos,
|
|
583
605
|
cacheResults: !POINTER_PSEUDO.test(selector),
|
|
584
606
|
} as unknown as Parameters<typeof compile>[1]) as unknown as (
|
|
585
607
|
node: Element,
|
|
@@ -640,7 +662,14 @@ function rightmostKey(selector: string): {
|
|
|
640
662
|
start = i + 1;
|
|
641
663
|
}
|
|
642
664
|
}
|
|
643
|
-
|
|
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
|
+
} {
|
|
644
673
|
let id: string | null = null;
|
|
645
674
|
let cls: string | null = null;
|
|
646
675
|
let tag: string | null = null;
|
|
@@ -674,6 +703,39 @@ function rightmostKey(selector: string): {
|
|
|
674
703
|
return { kind: 'any', name: '' };
|
|
675
704
|
}
|
|
676
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
|
+
|
|
677
739
|
function balancedEnd(
|
|
678
740
|
text: string,
|
|
679
741
|
from: number,
|
|
@@ -762,6 +824,13 @@ export class Cascade {
|
|
|
762
824
|
marker: new RuleIndex(),
|
|
763
825
|
selection: new RuleIndex(),
|
|
764
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;
|
|
765
834
|
private _adapter: CssSelectAdapter;
|
|
766
835
|
/** The compounds of the selectors that test the pointer, each without
|
|
767
836
|
* its `:hover` or `:active` (`pointerCompounds`), and their matchers,
|
|
@@ -836,6 +905,8 @@ export class Cascade {
|
|
|
836
905
|
/** Whether any declaration has a length in `lh` or `rlh`: only then is
|
|
837
906
|
* the line height settled ahead of the declarations that read it. */
|
|
838
907
|
private _lh = false;
|
|
908
|
+
/** The pseudo-classes css-select is handed, `:root` this cascade's. */
|
|
909
|
+
private _pseudos: typeof PSEUDOS;
|
|
839
910
|
|
|
840
911
|
constructor(
|
|
841
912
|
sheets: Stylesheet[],
|
|
@@ -847,7 +918,13 @@ export class Cascade {
|
|
|
847
918
|
zeroWidth: FaceMetric | null = null,
|
|
848
919
|
normalLine: FaceMetric | null = null,
|
|
849
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,
|
|
850
924
|
) {
|
|
925
|
+
this._pseudos = documentElement
|
|
926
|
+
? { ...PSEUDOS, root: (el: Element) => el === documentElement }
|
|
927
|
+
: PSEUDOS;
|
|
851
928
|
this._xHeightOf = xHeight;
|
|
852
929
|
this._zeroWidthOf = zeroWidth;
|
|
853
930
|
this._normalLineOf = normalLine;
|
|
@@ -874,7 +951,17 @@ export class Cascade {
|
|
|
874
951
|
if (!this._lh && usesLh(rule.declarations)) this._lh = true;
|
|
875
952
|
const pseudo = splitPseudoElement(rule);
|
|
876
953
|
if (pseudo) this._pseudo[pseudo.which].add(pseudo.rule);
|
|
877
|
-
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
|
+
}
|
|
878
965
|
this._noteHover((pseudo?.rule ?? rule).selector);
|
|
879
966
|
}
|
|
880
967
|
for (const bp of sheet.breakpoints) breakpoints.add(bp);
|
|
@@ -1051,7 +1138,7 @@ export class Cascade {
|
|
|
1051
1138
|
* it, as a rule it refuses drops out of the cascade. */
|
|
1052
1139
|
private _compile(selector: string): ((el: Element) => boolean) | null {
|
|
1053
1140
|
try {
|
|
1054
|
-
return compileSelector(selector, this._adapter);
|
|
1141
|
+
return compileSelector(selector, this._adapter, this._pseudos);
|
|
1055
1142
|
} catch {
|
|
1056
1143
|
return null;
|
|
1057
1144
|
}
|
|
@@ -1237,6 +1324,7 @@ export class Cascade {
|
|
|
1237
1324
|
parentStyle,
|
|
1238
1325
|
inFlexContainer,
|
|
1239
1326
|
candidates,
|
|
1327
|
+
true,
|
|
1240
1328
|
),
|
|
1241
1329
|
key: this._nextShareKey++,
|
|
1242
1330
|
};
|
|
@@ -1293,6 +1381,7 @@ export class Cascade {
|
|
|
1293
1381
|
parentStyle,
|
|
1294
1382
|
inFlexContainer,
|
|
1295
1383
|
this._candidates(el),
|
|
1384
|
+
true,
|
|
1296
1385
|
);
|
|
1297
1386
|
}
|
|
1298
1387
|
|
|
@@ -1509,6 +1598,9 @@ export class Cascade {
|
|
|
1509
1598
|
parentStyle: ComputedStyle,
|
|
1510
1599
|
inFlexContainer: boolean,
|
|
1511
1600
|
candidates: Candidate[],
|
|
1601
|
+
/** Whether the style is the element's own, and not that of one of its
|
|
1602
|
+
* pseudo-elements. */
|
|
1603
|
+
own = false,
|
|
1512
1604
|
): ComputedStyle {
|
|
1513
1605
|
const style = inherit(parentStyle, this.initial);
|
|
1514
1606
|
// custom properties first, in cascade order, so every `var()` in the
|
|
@@ -1521,10 +1613,14 @@ export class Cascade {
|
|
|
1521
1613
|
// em-relative length in the same rule resolves against this element's.
|
|
1522
1614
|
const root = isRootElement(el);
|
|
1523
1615
|
const rootStyle = root ? this.initial : (this._root ?? this.initial);
|
|
1616
|
+
// whose declaration is being applied: the UA sheet's families are the
|
|
1617
|
+
// host's own faces, and its lists end where they end
|
|
1618
|
+
let authored = true;
|
|
1524
1619
|
const ctxParent: UnitContext = {
|
|
1525
1620
|
em: parentStyle.fontSize,
|
|
1526
1621
|
rem: rootStyle.fontSize,
|
|
1527
1622
|
initial: this.initial,
|
|
1623
|
+
fallbackFamily: () => (authored ? this.initial.fontFamily : null),
|
|
1528
1624
|
vw: this.viewportWidth,
|
|
1529
1625
|
vh: this.viewportHeight,
|
|
1530
1626
|
scale: this.scale,
|
|
@@ -1543,6 +1639,7 @@ export class Cascade {
|
|
|
1543
1639
|
let sized = false;
|
|
1544
1640
|
let keyword = false;
|
|
1545
1641
|
for (const c of candidates) {
|
|
1642
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1546
1643
|
for (const d of pick(c)) {
|
|
1547
1644
|
if (
|
|
1548
1645
|
d.prop === 'font-family' ||
|
|
@@ -1606,6 +1703,7 @@ export class Cascade {
|
|
|
1606
1703
|
// them. A `font` sets the size as well, which the first pass settled.
|
|
1607
1704
|
if (this._lh) {
|
|
1608
1705
|
for (const c of candidates) {
|
|
1706
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1609
1707
|
for (const d of pick(c)) {
|
|
1610
1708
|
if (d.prop === 'line-height' || d.prop === 'font') {
|
|
1611
1709
|
this._apply(style, parentStyle, d, ctx);
|
|
@@ -1621,6 +1719,7 @@ export class Cascade {
|
|
|
1621
1719
|
}
|
|
1622
1720
|
const settled = style.fontSize;
|
|
1623
1721
|
for (const c of candidates) {
|
|
1722
|
+
authored = c.origin !== Origin.UserAgent;
|
|
1624
1723
|
for (const d of pick(c)) {
|
|
1625
1724
|
if (d.prop === 'font-size' || d.custom) continue;
|
|
1626
1725
|
this._apply(style, parentStyle, d, ctx);
|
|
@@ -1648,6 +1747,10 @@ export class Cascade {
|
|
|
1648
1747
|
settleOverflow(style);
|
|
1649
1748
|
settleContentVisibility(style);
|
|
1650
1749
|
blockify(style, inFlexContainer);
|
|
1750
|
+
// a button is laid out as one, whatever `display` it was given
|
|
1751
|
+
if (own && el.name.length === 6 && tagOf(el) === 'button') {
|
|
1752
|
+
settleButton(style);
|
|
1753
|
+
}
|
|
1651
1754
|
decorate(style);
|
|
1652
1755
|
// which faces of the document's own families this family, weight and
|
|
1653
1756
|
// slant ask for — known only now, with all three computed
|
|
@@ -1726,6 +1829,185 @@ export class Cascade {
|
|
|
1726
1829
|
return { style: asRoot(this.styleFor(synthetic, parent, false)), html };
|
|
1727
1830
|
}
|
|
1728
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
|
+
|
|
1729
2011
|
/**
|
|
1730
2012
|
* Whether the pointer's move from `was` to where it is changed which
|
|
1731
2013
|
* rules match `el`, or its `::before`, `::after` or another of its
|
|
@@ -1768,6 +2050,8 @@ export class Cascade {
|
|
|
1768
2050
|
el: Element,
|
|
1769
2051
|
out: Candidate[] | null,
|
|
1770
2052
|
matched?: number[],
|
|
2053
|
+
/** The rules to try, where some are known not to match without it. */
|
|
2054
|
+
live?: (indexed: IndexedRule) => boolean,
|
|
1771
2055
|
): void {
|
|
1772
2056
|
// A media query's width is CSS pixels; the viewport is kept in device.
|
|
1773
2057
|
const width = this.viewportWidth / this.scale;
|
|
@@ -1776,6 +2060,7 @@ export class Cascade {
|
|
|
1776
2060
|
const consider = (bucket: IndexedRule[] | undefined): void => {
|
|
1777
2061
|
if (!bucket) return;
|
|
1778
2062
|
for (const indexed of bucket) {
|
|
2063
|
+
if (live !== undefined && !live(indexed)) continue;
|
|
1779
2064
|
const rule = indexed.rule;
|
|
1780
2065
|
if (
|
|
1781
2066
|
!mediaMatches(
|
|
@@ -1791,7 +2076,11 @@ export class Cascade {
|
|
|
1791
2076
|
if (!indexed.compiled) {
|
|
1792
2077
|
indexed.compiled = true;
|
|
1793
2078
|
try {
|
|
1794
|
-
indexed.match = compileSelector(
|
|
2079
|
+
indexed.match = compileSelector(
|
|
2080
|
+
rule.selector,
|
|
2081
|
+
this._adapter,
|
|
2082
|
+
this._pseudos,
|
|
2083
|
+
);
|
|
1795
2084
|
} catch {
|
|
1796
2085
|
// A selector this matcher does not know (`::-moz-…`, a CSS4 form
|
|
1797
2086
|
// it has not learnt) drops out of the cascade rather than out of
|
|
@@ -2071,6 +2360,51 @@ function setsContent(c: Candidate): boolean {
|
|
|
2071
2360
|
/** Whether a rule's declarations set a custom property, by the array. */
|
|
2072
2361
|
const CUSTOM_IN = new WeakMap<Declaration[], boolean>();
|
|
2073
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
|
+
|
|
2074
2408
|
function pick(c: Candidate): Declaration[] {
|
|
2075
2409
|
return c.only < 0 ? c.declarations : [c.declarations[c.only]];
|
|
2076
2410
|
}
|
|
@@ -2275,6 +2609,12 @@ function presentationHints(el: Element): Declaration[] {
|
|
|
2275
2609
|
const height = attr(el, 'height');
|
|
2276
2610
|
const h = height ? svgSizeHint(height) : null;
|
|
2277
2611
|
if (h) push('height', h);
|
|
2612
|
+
// and its paint is its `fill` and `stroke` properties' (13.2), which a
|
|
2613
|
+
// rule of the document's then sets over the attribute
|
|
2614
|
+
const fill = attr(el, 'fill');
|
|
2615
|
+
if (fill) push('fill', fill);
|
|
2616
|
+
const stroke = attr(el, 'stroke');
|
|
2617
|
+
if (stroke) push('stroke', stroke);
|
|
2278
2618
|
}
|
|
2279
2619
|
|
|
2280
2620
|
if (tag === 'table') {
|
|
@@ -2450,6 +2790,24 @@ function lengthAttr(value: string): string {
|
|
|
2450
2790
|
return Number.isFinite(n) ? `${n}px` : v;
|
|
2451
2791
|
}
|
|
2452
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
|
+
|
|
2453
2811
|
function closestTable(el: Element): Element | null {
|
|
2454
2812
|
let node = el.parent;
|
|
2455
2813
|
while (node) {
|
package/src/html/css/color.ts
CHANGED
|
@@ -773,6 +773,24 @@ export function lightDark(
|
|
|
773
773
|
value: string,
|
|
774
774
|
scheme: 'light' | 'dark',
|
|
775
775
|
): string | null {
|
|
776
|
+
// kept by the value: the UA sheet's link colour is one, so every link of
|
|
777
|
+
// every document asks, and a theme's are a few asked by every element
|
|
778
|
+
const kept = PICKED[scheme];
|
|
779
|
+
let picked = kept.get(value);
|
|
780
|
+
if (picked === undefined) {
|
|
781
|
+
picked = pickScheme(value, scheme);
|
|
782
|
+
if (kept.size >= 512) kept.clear();
|
|
783
|
+
kept.set(value, picked);
|
|
784
|
+
}
|
|
785
|
+
return picked;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const PICKED = {
|
|
789
|
+
light: new Map<string, string | null>(),
|
|
790
|
+
dark: new Map<string, string | null>(),
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
function pickScheme(value: string, scheme: 'light' | 'dark'): string | null {
|
|
776
794
|
let out = value;
|
|
777
795
|
// a branch can hold another, which the next round replaces
|
|
778
796
|
for (let round = 0; round < 32; round += 1) {
|
|
@@ -827,6 +845,17 @@ export function usedColorScheme(
|
|
|
827
845
|
return first ?? preferred;
|
|
828
846
|
}
|
|
829
847
|
|
|
848
|
+
/**
|
|
849
|
+
* A browser's own colours in each scheme — the system colours `Canvas`,
|
|
850
|
+
* `CanvasText` and `LinkText` (CSS Color 4, 6.2), as Chrome has them: what
|
|
851
|
+
* a document is drawn in where its root's scheme is not the palette's,
|
|
852
|
+
* which has the colours of its own scheme and of no other.
|
|
853
|
+
*/
|
|
854
|
+
export const SCHEME_COLORS = {
|
|
855
|
+
light: { canvas: '#ffffff', text: '#000000', link: '#0000ee' },
|
|
856
|
+
dark: { canvas: '#121212', text: '#ffffff', link: '#9e9eff' },
|
|
857
|
+
} as const;
|
|
858
|
+
|
|
830
859
|
const SCHEME_IDENT = /^-?(?:[a-z_]|--)[\w-]*$/;
|
|
831
860
|
/** What a scheme's name cannot be: the keywords the grammar has, and the
|
|
832
861
|
* CSS-wide ones, which reach here only in a list. */
|