@react-x11/components 0.14.1 → 0.16.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/code-editor/node.d.ts +13 -0
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +23 -5
- package/dist/code-editor/node.js.map +1 -1
- package/dist/html/css/cascade.d.ts +171 -26
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +616 -216
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +25 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +307 -19
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/content.d.ts +11 -6
- package/dist/html/css/content.d.ts.map +1 -1
- package/dist/html/css/content.js +171 -12
- package/dist/html/css/content.js.map +1 -1
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +62 -9
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/shapes.d.ts +18 -0
- package/dist/html/css/shapes.d.ts.map +1 -1
- package/dist/html/css/shapes.js +21 -1
- package/dist/html/css/shapes.js.map +1 -1
- package/dist/html/css/style.d.ts +42 -5
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +74 -12
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.js +27 -8
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +5 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +18 -9
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +7 -0
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +29 -9
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +141 -52
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +29 -2
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +172 -23
- 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 +11 -9
- 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 +1759 -238
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +15 -3
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +64 -19
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/table.d.ts +27 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +886 -324
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +35 -4
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +122 -26
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +558 -178
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.js +14 -1
- package/dist/html/resources.js.map +1 -1
- package/dist/html/svg.d.ts +23 -7
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +264 -59
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.d.ts.map +1 -1
- package/dist/html/widgets.js +57 -4
- package/dist/html/widgets.js.map +1 -1
- package/package.json +3 -3
- package/src/code-editor/node.ts +24 -5
- package/src/html/css/cascade.ts +697 -221
- package/src/html/css/color.ts +345 -18
- package/src/html/css/content.ts +173 -15
- package/src/html/css/parse.ts +60 -8
- package/src/html/css/shapes.ts +35 -1
- package/src/html/css/style.ts +122 -17
- package/src/html/css/ua.ts +27 -8
- package/src/html/css/values.ts +23 -9
- package/src/html/index.ts +7 -0
- package/src/html/layout/block.ts +149 -58
- package/src/html/layout/boxes.ts +211 -27
- package/src/html/layout/css-grid.ts +12 -9
- package/src/html/layout/flex.ts +1975 -231
- package/src/html/layout/inline.ts +70 -16
- package/src/html/layout/table.ts +961 -332
- package/src/html/node.ts +155 -27
- package/src/html/paint.ts +692 -234
- package/src/html/resources.ts +13 -1
- package/src/html/svg.ts +299 -56
- package/src/html/widgets.ts +61 -4
package/src/html/layout/boxes.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// reason richtext's node gives for answering from what it draws: an index
|
|
17
17
|
// built by a second traversal can disagree with the boxes, and a selection
|
|
18
18
|
// that disagrees with the glyphs is worse than no selection.
|
|
19
|
-
import type { Element } from 'domhandler';
|
|
19
|
+
import type { ChildNode, Element } from 'domhandler';
|
|
20
20
|
|
|
21
21
|
import type { TextRun } from '../../richtext/index.js';
|
|
22
22
|
import {
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
NON_RENDERED,
|
|
29
29
|
tagOf,
|
|
30
30
|
} from '../dom.js';
|
|
31
|
+
import { ShapeCopies, languageOf, metaColorScheme } from '../css/cascade.js';
|
|
31
32
|
import type {
|
|
32
33
|
Cascade,
|
|
33
34
|
FirstLetterRules,
|
|
@@ -48,7 +49,7 @@ import {
|
|
|
48
49
|
inherit,
|
|
49
50
|
} from '../css/style.js';
|
|
50
51
|
import { AUTO } from '../css/values.js';
|
|
51
|
-
import { svgIntrinsics } from '../svg.js';
|
|
52
|
+
import { concreteSize, svgIntrinsics } from '../svg.js';
|
|
52
53
|
import type { IntrinsicSize } from '../svg.js';
|
|
53
54
|
import type { ComputedStyle } from '../css/style.js';
|
|
54
55
|
import type { GridLines } from './grid-lines.js';
|
|
@@ -82,15 +83,32 @@ export interface Marker {
|
|
|
82
83
|
/** Its own style where a `::marker` rule gives it one; the item's where
|
|
83
84
|
* none does. */
|
|
84
85
|
style: ComputedStyle | null;
|
|
85
|
-
/** The image it is, `list-style-image`'s,
|
|
86
|
-
*
|
|
87
|
-
|
|
86
|
+
/** The image it is, `list-style-image`'s, where that has arrived, at its
|
|
87
|
+
* size in device pixels — its own, or the one a marker image with none
|
|
88
|
+
* is given (`Builder._markerImageSize`); its text is then not drawn.
|
|
89
|
+
* `gap` is the room between it and the content, in device pixels
|
|
90
|
+
* (`MARKER_IMAGE_GAP`). */
|
|
91
|
+
image?: { url: string; width: number; height: number; gap: number };
|
|
88
92
|
/** Its text is its own — a string `list-style-type`, a `::marker`'s
|
|
89
93
|
* `content` — and ends at the content's edge, with no gap a number
|
|
90
94
|
* or a bullet is set apart by. */
|
|
91
95
|
flush?: true;
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
/**
|
|
99
|
+
* The room between a list marker's image and the item's content, in CSS
|
|
100
|
+
* pixels: between the image and the content's edge outside the item, and
|
|
101
|
+
* after the image inside it. CSS 2.1 (12.5.1) leaves where a marker goes
|
|
102
|
+
* to the user agent, and this is Blink's `kCMarkerPaddingPx`
|
|
103
|
+
* (`ListMarker::InlineMarginsForOutside` and `…ForInside`), which WebKit
|
|
104
|
+
* has too. Outside, ours was the gap a bullet has, 0.4em — 16 pixels at
|
|
105
|
+
* 40px text where Chrome has 7 — and inside the space a marker's text ends
|
|
106
|
+
* in, a space's width. Blink writes it into the marker's margins unzoomed,
|
|
107
|
+
* so at a device scale of 2 Chrome's is 7 device pixels; here it is 7 CSS
|
|
108
|
+
* pixels at any scale, so that a page at 2x is the page at 1x doubled.
|
|
109
|
+
*/
|
|
110
|
+
const MARKER_IMAGE_GAP = 7;
|
|
111
|
+
|
|
94
112
|
/** An out-of-flow box's static position, as an offset from the box whose
|
|
95
113
|
* flow it was taken from, which may yet move. */
|
|
96
114
|
export interface StaticPosition {
|
|
@@ -544,6 +562,40 @@ export type ReplacedKind =
|
|
|
544
562
|
/** An inline `<svg>`: a drawing, sized by what it says of its size. */
|
|
545
563
|
| 'svg';
|
|
546
564
|
|
|
565
|
+
/**
|
|
566
|
+
* Where a control sits on the line, as Chrome sets it: a field, a select
|
|
567
|
+
* or an input button on the baseline of the text it shows, its line in its
|
|
568
|
+
* face centred in its content box — that face's ascent and descent — and a
|
|
569
|
+
* checkbox, a radio button or a range on its border box's bottom, its
|
|
570
|
+
* bottom margin under the line. A `<textarea>` is none of these, and sits
|
|
571
|
+
* on its bottom margin edge as a box that scrolls does.
|
|
572
|
+
*/
|
|
573
|
+
export const CONTROL_BASELINES = new WeakMap<
|
|
574
|
+
Box,
|
|
575
|
+
InlineDecoration | 'border'
|
|
576
|
+
>();
|
|
577
|
+
|
|
578
|
+
function controlBaseline(
|
|
579
|
+
el: Element,
|
|
580
|
+
kind: ReplacedKind,
|
|
581
|
+
style: ComputedStyle,
|
|
582
|
+
options: BuildOptions,
|
|
583
|
+
): InlineDecoration | 'border' | null {
|
|
584
|
+
if (kind === 'checkbox' || kind === 'radio') return 'border';
|
|
585
|
+
if (kind === 'input' && (attr(el, 'type') ?? '').toLowerCase() === 'range') {
|
|
586
|
+
return 'border';
|
|
587
|
+
}
|
|
588
|
+
if (kind !== 'input' && kind !== 'select' && kind !== 'button') return null;
|
|
589
|
+
// a list box shows rows, and sits on its bottom edge
|
|
590
|
+
if (kind === 'select' && Number(attr(el, 'size') ?? 1) > 1) return null;
|
|
591
|
+
return (
|
|
592
|
+
options.faceExtent?.(style) ?? {
|
|
593
|
+
ascent: style.fontSize * 0.8,
|
|
594
|
+
descent: style.fontSize * 0.2,
|
|
595
|
+
}
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
547
599
|
/** A replaced box's intrinsic dimensions, in device pixels. */
|
|
548
600
|
export interface Intrinsic {
|
|
549
601
|
/** Its size — or, on an axis `missing` names, the default object size of
|
|
@@ -789,6 +841,11 @@ export interface BoxTree {
|
|
|
789
841
|
* could reach one: worked out as a drawing is first painted, since a
|
|
790
842
|
* page of icons shows few of them at once. */
|
|
791
843
|
shapeStyler: ShapeStyler | null;
|
|
844
|
+
/** What the drawings of this build share in `Cascade.shapeStyles`: the
|
|
845
|
+
* document's elements by their ids, indexed when first asked, where a
|
|
846
|
+
* `<use>` finds the icon a sprite has for it; and what the rules matched
|
|
847
|
+
* in each copy a `<use>` makes. Null where `shapeStyler` is. */
|
|
848
|
+
shapeCopies: ShapeCopies | null;
|
|
792
849
|
/** Whether a float or an out-of-flow box sits in an inline box: where
|
|
793
850
|
* none does, layout looks for them among a block's own children and
|
|
794
851
|
* goes through no inline box to find them. */
|
|
@@ -826,6 +883,14 @@ export interface BuildOptions {
|
|
|
826
883
|
imageSize(el: Element): IntrinsicSize | null;
|
|
827
884
|
/** The same for an image named by url — generated content's. */
|
|
828
885
|
urlSize?(url: string): IntrinsicSize | null;
|
|
886
|
+
/** How far a style's first face reaches above its baseline, in device
|
|
887
|
+
* pixels: what a list marker's image with no size of its own is sized
|
|
888
|
+
* by (`_markerImageSize`). Where it is not known, 0.8em. */
|
|
889
|
+
faceAscent?(style: ComputedStyle): number | undefined;
|
|
890
|
+
/** How far a style's first face reaches above and below its baseline,
|
|
891
|
+
* in device pixels: where a control sets its text, which is where its
|
|
892
|
+
* baseline is (`CONTROL_BASELINES`). */
|
|
893
|
+
faceExtent?(style: ComputedStyle): InlineDecoration | undefined;
|
|
829
894
|
/** The size a real widget wants, so the box in the flow is the size the
|
|
830
895
|
* control will be drawn at. */
|
|
831
896
|
controlSize(
|
|
@@ -847,6 +912,28 @@ export function buildBoxes(
|
|
|
847
912
|
return builder.run(root as Element);
|
|
848
913
|
}
|
|
849
914
|
|
|
915
|
+
/** A document's first element with an id: one walk of it, the first time
|
|
916
|
+
* an id is asked for (`ShapeCopies`). */
|
|
917
|
+
function idIndex(root: Element): (id: string) => Element | null {
|
|
918
|
+
let index: Map<string, Element> | null = null;
|
|
919
|
+
return (id) => {
|
|
920
|
+
if (!index) {
|
|
921
|
+
index = new Map();
|
|
922
|
+
const stack: ChildNode[] = [...root.children].reverse();
|
|
923
|
+
for (let node = stack.pop(); node; node = stack.pop()) {
|
|
924
|
+
if (!isElement(node)) continue;
|
|
925
|
+
const own = node.attribs.id;
|
|
926
|
+
if (own && !index.has(own)) index.set(own, node);
|
|
927
|
+
const children = node.children;
|
|
928
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
929
|
+
stack.push(children[i]);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return index.get(id) ?? null;
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
850
937
|
/**
|
|
851
938
|
* How deep the box tree may go. Everything downstream of the builder — the
|
|
852
939
|
* fix-up pass, layout, paint, the accessor walks — recurses on box depth, so
|
|
@@ -854,8 +941,17 @@ export function buildBoxes(
|
|
|
854
941
|
* output, a runaway template) from a stack overflow five phases later.
|
|
855
942
|
* Blink's parser flattens at 512 for the same reason; content past the cap
|
|
856
943
|
* is dropped, which beats the alternative of crashing the application.
|
|
944
|
+
*
|
|
857
945
|
* The parser keeps a document to 256 elements (`dom.ts`), so what reaches
|
|
858
|
-
* this
|
|
946
|
+
* this is the anonymous boxes a table builds round them, which the builder
|
|
947
|
+
* counts as it goes (`wrappersAround`), since the fix-up makes them after
|
|
948
|
+
* it. Counted as elements, a `display: table-cell` box in another was one
|
|
949
|
+
* level where it is four boxes — a table, a row group, a row and the cell
|
|
950
|
+
* — and every pass pays for a box: painting one takes some 800 bytes of
|
|
951
|
+
* the stack. Two hundred and fifty cells deep, a thousand boxes, layout
|
|
952
|
+
* and paint each ran out of the 984 KB V8 gives the main thread, and the
|
|
953
|
+
* document came out blank. 512 boxes paint in under half of it, and are a
|
|
954
|
+
* cell in a cell 128 deep.
|
|
859
955
|
*/
|
|
860
956
|
const MAX_DEPTH = 512;
|
|
861
957
|
|
|
@@ -916,11 +1012,15 @@ class Builder {
|
|
|
916
1012
|
run(root: Element): BoxTree {
|
|
917
1013
|
const cascade = this._options.cascade;
|
|
918
1014
|
cascade.beginSharing(this._options.kept ?? null);
|
|
1015
|
+
cascade.pageColorScheme = metaColorScheme(root);
|
|
919
1016
|
const { style: rootStyle, html: impliedHtml } = cascade.rootStyle(
|
|
920
1017
|
hasBody(root),
|
|
921
1018
|
hasHtml(root),
|
|
922
1019
|
);
|
|
923
1020
|
const rootBox = new Box('block', null, rootStyle);
|
|
1021
|
+
// the root standing in for a `<body>` has its background clipped to
|
|
1022
|
+
// the document's text as the body's would be
|
|
1023
|
+
if (rootStyle.backgroundClipText) this._clipText = true;
|
|
924
1024
|
// a fragment's root stands in for a `<body>`, counters and all
|
|
925
1025
|
this._scopes.open();
|
|
926
1026
|
this._counterChanges(rootStyle, {
|
|
@@ -938,6 +1038,7 @@ class Builder {
|
|
|
938
1038
|
const anonymous = anonymousStyles(cascade.initial);
|
|
939
1039
|
fixUp(rootBox, anonymous);
|
|
940
1040
|
assignSubtreeRanges(rootBox);
|
|
1041
|
+
const copies = cascade.stylesShapes ? new ShapeCopies(idIndex(root)) : null;
|
|
941
1042
|
return {
|
|
942
1043
|
root: rootBox,
|
|
943
1044
|
anonymous,
|
|
@@ -954,7 +1055,8 @@ class Builder {
|
|
|
954
1055
|
firstLine: this._firstLine,
|
|
955
1056
|
firstLineStyler: this._firstLine ? this._firstLineStyler() : null,
|
|
956
1057
|
selectionStyler: cascade.hasSelection ? this._selectionStyler() : null,
|
|
957
|
-
shapeStyler:
|
|
1058
|
+
shapeStyler: copies ? this._shapeStyler(copies) : null,
|
|
1059
|
+
shapeCopies: copies,
|
|
958
1060
|
nestedOutOfLine: this._nestedOutOfLine,
|
|
959
1061
|
movedInline: this._movedInline,
|
|
960
1062
|
clipText: this._clipText,
|
|
@@ -989,12 +1091,12 @@ class Builder {
|
|
|
989
1091
|
|
|
990
1092
|
/** `BoxTree.shapeStyler`, over this build's cascade: each drawing's
|
|
991
1093
|
* once, from its box's style as it is when first asked. */
|
|
992
|
-
private _shapeStyler(): ShapeStyler {
|
|
1094
|
+
private _shapeStyler(copies: ShapeCopies): ShapeStyler {
|
|
993
1095
|
const cascade = this._options.cascade;
|
|
994
1096
|
return (box) => {
|
|
995
1097
|
let shapes = SHAPE_STYLES.get(box);
|
|
996
1098
|
if (shapes === undefined) {
|
|
997
|
-
shapes = box.el ? cascade.shapeStyles(box.el, box.style) : null;
|
|
1099
|
+
shapes = box.el ? cascade.shapeStyles(box.el, box.style, copies) : null;
|
|
998
1100
|
SHAPE_STYLES.set(box, shapes);
|
|
999
1101
|
}
|
|
1000
1102
|
return shapes;
|
|
@@ -1190,8 +1292,10 @@ class Builder {
|
|
|
1190
1292
|
return;
|
|
1191
1293
|
}
|
|
1192
1294
|
|
|
1193
|
-
if (this._depth >= MAX_DEPTH) return;
|
|
1194
1295
|
const kind = boxKindFor(style.display);
|
|
1296
|
+
// the box, and the anonymous boxes the fix-up will put round it
|
|
1297
|
+
const depth = 1 + wrappersAround(into.kind, kind, style);
|
|
1298
|
+
if (this._depth + depth > MAX_DEPTH) return;
|
|
1195
1299
|
const box = new Box(kind, el, style);
|
|
1196
1300
|
into.append(box);
|
|
1197
1301
|
if (
|
|
@@ -1221,7 +1325,7 @@ class Builder {
|
|
|
1221
1325
|
let markerStyle: ComputedStyle | null = null;
|
|
1222
1326
|
let ownMarker = false;
|
|
1223
1327
|
let markerImage: { url: string; size: IntrinsicSize } | null = null;
|
|
1224
|
-
let insideImage:
|
|
1328
|
+
let insideImage: NonNullable<Marker['image']> | null = null;
|
|
1225
1329
|
if (style.display === 'list-item' && style.listStyleImage) {
|
|
1226
1330
|
// asked for as generated content's images are, and the tree built
|
|
1227
1331
|
// again when it arrives; until it has, and where it never does, the
|
|
@@ -1256,20 +1360,21 @@ class Builder {
|
|
|
1256
1360
|
flush = true;
|
|
1257
1361
|
}
|
|
1258
1362
|
if (markerImage && !ownMarker) {
|
|
1259
|
-
|
|
1363
|
+
const [width, height] = this._markerImageSize(
|
|
1364
|
+
markerImage.size,
|
|
1365
|
+
markerStyle ?? style,
|
|
1366
|
+
);
|
|
1367
|
+
const gap = MARKER_IMAGE_GAP * (this._options.scale ?? 1);
|
|
1368
|
+
const image = { url: markerImage.url, width, height, gap };
|
|
1369
|
+
if (style.listStylePosition === 'inside') insideImage = image;
|
|
1260
1370
|
else {
|
|
1261
|
-
const scale = this._options.scale ?? 1;
|
|
1262
1371
|
box.marker = {
|
|
1263
1372
|
text: '',
|
|
1264
1373
|
layout: null,
|
|
1265
1374
|
x: 0,
|
|
1266
1375
|
y: 0,
|
|
1267
1376
|
style: markerStyle,
|
|
1268
|
-
image
|
|
1269
|
-
url: markerImage.url,
|
|
1270
|
-
width: (markerImage.size.width ?? 0) * scale,
|
|
1271
|
-
height: (markerImage.size.height ?? 0) * scale,
|
|
1272
|
-
},
|
|
1377
|
+
image,
|
|
1273
1378
|
};
|
|
1274
1379
|
}
|
|
1275
1380
|
} else if (text && style.listStylePosition === 'inside') {
|
|
@@ -1302,19 +1407,23 @@ class Builder {
|
|
|
1302
1407
|
: null;
|
|
1303
1408
|
const ownLetter = ownRules ? { rules: ownRules, punctuation: [] } : null;
|
|
1304
1409
|
if (ownLetter) this._firstLetter = ownLetter;
|
|
1305
|
-
this._depth +=
|
|
1410
|
+
this._depth += depth;
|
|
1306
1411
|
if (insideImage) {
|
|
1307
1412
|
// an inline image at the start of the first line, as a generated
|
|
1308
|
-
// image is, and the
|
|
1309
|
-
|
|
1310
|
-
|
|
1413
|
+
// image is, and the room after it Blink gives it rather than a
|
|
1414
|
+
// space: the content's own white space after it is kept, as it is
|
|
1415
|
+
// after an image (`MARKER_IMAGE_GAP`)
|
|
1416
|
+
const { url, gap } = insideImage;
|
|
1417
|
+
const image = this._contentImage(url, box, style, el, insideImage);
|
|
1418
|
+
if (style.direction === 'rtl') image.style.marginLeft = gap;
|
|
1419
|
+
else image.style.marginRight = gap;
|
|
1311
1420
|
} else if (insideMarker) {
|
|
1312
1421
|
this._insideMarker(insideMarker, style, markerStyle, box, el);
|
|
1313
1422
|
}
|
|
1314
1423
|
this._pseudo(el, 'before', style, box);
|
|
1315
1424
|
this._children(el, box, style, childInFlex, el, key);
|
|
1316
1425
|
this._pseudo(el, 'after', style, box);
|
|
1317
|
-
this._depth -=
|
|
1426
|
+
this._depth -= depth;
|
|
1318
1427
|
this._letterAfter(flow, skipped, outerLetter, ownLetter);
|
|
1319
1428
|
if (flow !== 'inline') this._endLine();
|
|
1320
1429
|
this._ws = after(flow, this._ws, around);
|
|
@@ -1415,6 +1524,8 @@ class Builder {
|
|
|
1415
1524
|
ratio: 0,
|
|
1416
1525
|
};
|
|
1417
1526
|
this._controls.push(box);
|
|
1527
|
+
const baseline = controlBaseline(el, replaced, style, this._options);
|
|
1528
|
+
if (baseline) CONTROL_BASELINES.set(box, baseline);
|
|
1418
1529
|
// A control's value is the widget's, not the document's: putting it in
|
|
1419
1530
|
// the selection index would make Ctrl+A copy the contents of every text
|
|
1420
1531
|
// field, which no document viewer does.
|
|
@@ -1754,14 +1865,14 @@ class Builder {
|
|
|
1754
1865
|
.join(item.separator);
|
|
1755
1866
|
break;
|
|
1756
1867
|
case 'open-quote':
|
|
1757
|
-
text += quoteAt(style.quotes, this._quoteDepth, 0);
|
|
1868
|
+
text += quoteAt(style.quotes, this._quoteDepth, 0, languageOf(el));
|
|
1758
1869
|
this._quoteDepth += 1;
|
|
1759
1870
|
break;
|
|
1760
1871
|
case 'close-quote':
|
|
1761
1872
|
// a close with nothing open writes nothing and closes nothing
|
|
1762
1873
|
if (this._quoteDepth > 0) {
|
|
1763
1874
|
this._quoteDepth -= 1;
|
|
1764
|
-
text += quoteAt(style.quotes, this._quoteDepth, 1);
|
|
1875
|
+
text += quoteAt(style.quotes, this._quoteDepth, 1, languageOf(el));
|
|
1765
1876
|
}
|
|
1766
1877
|
break;
|
|
1767
1878
|
case 'no-open-quote':
|
|
@@ -1787,7 +1898,10 @@ class Builder {
|
|
|
1787
1898
|
into: Box,
|
|
1788
1899
|
style: ComputedStyle,
|
|
1789
1900
|
owner: Element,
|
|
1790
|
-
|
|
1901
|
+
/** Its size where it is a list marker's, which has one whatever the
|
|
1902
|
+
* image says (`_markerImageSize`). */
|
|
1903
|
+
marker?: { width: number; height: number },
|
|
1904
|
+
): Box {
|
|
1791
1905
|
const box = new Box('replaced', null, {
|
|
1792
1906
|
...inherit(style, this._options.cascade.initial),
|
|
1793
1907
|
display: 'inline',
|
|
@@ -1797,11 +1911,40 @@ class Builder {
|
|
|
1797
1911
|
CONTENT_IMAGES.set(box, url);
|
|
1798
1912
|
const size = this._options.urlSize?.(url) ?? null;
|
|
1799
1913
|
this._contentImages.push({ url, element: owner, sized: !!size });
|
|
1800
|
-
if (
|
|
1914
|
+
if (marker) {
|
|
1915
|
+
const { width, height } = marker;
|
|
1916
|
+
const ratio = width > 0 && height > 0 ? width / height : 0;
|
|
1917
|
+
box.intrinsic = { width, height, missing: 0, ratio };
|
|
1918
|
+
} else if (size) setIntrinsics(box, size, this._options.scale ?? 1);
|
|
1801
1919
|
else box.intrinsic = { width: 0, height: 0, missing: 0, ratio: 0 };
|
|
1802
1920
|
this._ws = after('atomic', this._ws, this._ws);
|
|
1803
1921
|
this._lettered = false;
|
|
1804
1922
|
this._abandonLetter();
|
|
1923
|
+
return box;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
/**
|
|
1927
|
+
* A list marker image's size, in device pixels: its own where it has one,
|
|
1928
|
+
* and where it lacks some — an SVG with only a `viewBox`, or with no size
|
|
1929
|
+
* and no ratio — CSS Images' default sizing in a square half its face's
|
|
1930
|
+
* ascent across. CSS Lists 3 (3.3) makes the square 1em, which both
|
|
1931
|
+
* engines find too large (w3c/csswg-drafts#4207): Blink's is half the
|
|
1932
|
+
* ascent, rounded to a whole pixel first
|
|
1933
|
+
* (`LayoutListMarkerImage::DefaultSize`), Gecko's 0.4em, and this is
|
|
1934
|
+
* Blink's. A `viewBox` alone came to 0 by 0 and drew no marker at all,
|
|
1935
|
+
* and inside the item to the 300 by 150 a generated image defaults to.
|
|
1936
|
+
*/
|
|
1937
|
+
private _markerImageSize(
|
|
1938
|
+
size: IntrinsicSize,
|
|
1939
|
+
style: ComputedStyle,
|
|
1940
|
+
): [number, number] {
|
|
1941
|
+
let side = 0;
|
|
1942
|
+
// CoreText reads a size of 0 as its default twelve points
|
|
1943
|
+
if ((size.width === null || size.height === null) && style.fontSize > 0) {
|
|
1944
|
+
const ascent = this._options.faceAscent?.(style) ?? style.fontSize * 0.8;
|
|
1945
|
+
side = Math.round(ascent) / 2;
|
|
1946
|
+
}
|
|
1947
|
+
return concreteSize(size, side, side, this._options.scale ?? 1);
|
|
1805
1948
|
}
|
|
1806
1949
|
|
|
1807
1950
|
/** A text node, whitespace-processed per the inherited `white-space`. */
|
|
@@ -2467,6 +2610,47 @@ function after(
|
|
|
2467
2610
|
}
|
|
2468
2611
|
}
|
|
2469
2612
|
|
|
2613
|
+
/**
|
|
2614
|
+
* How many anonymous boxes the fix-up will put between a box of `kind` and
|
|
2615
|
+
* the box it is appended to, `parent` (`fixUpTable`, `wrapOrphans`,
|
|
2616
|
+
* `wrapTableParts`): a table part outside a table is given what it is
|
|
2617
|
+
* missing of one, and what is in a table and no part of it is given a
|
|
2618
|
+
* cell, a row and a row group. A cell in a block is in a table, a row group
|
|
2619
|
+
* and a row of its own, and a block in a row is in a cell. Counted against
|
|
2620
|
+
* `MAX_DEPTH`, which bounds the boxes and not the elements.
|
|
2621
|
+
*/
|
|
2622
|
+
function wrappersAround(
|
|
2623
|
+
parent: BoxKind,
|
|
2624
|
+
kind: BoxKind,
|
|
2625
|
+
style: ComputedStyle,
|
|
2626
|
+
): number {
|
|
2627
|
+
const column =
|
|
2628
|
+
style.display === 'table-column' || style.display === 'table-column-group';
|
|
2629
|
+
switch (parent) {
|
|
2630
|
+
case 'table':
|
|
2631
|
+
if (kind === 'table-row-group' || kind === 'table-caption' || column) {
|
|
2632
|
+
return 0;
|
|
2633
|
+
}
|
|
2634
|
+
return kind === 'table-row' ? 1 : kind === 'table-cell' ? 2 : 3;
|
|
2635
|
+
case 'table-row-group':
|
|
2636
|
+
return kind === 'table-row' ? 0 : kind === 'table-cell' ? 1 : 2;
|
|
2637
|
+
case 'table-row':
|
|
2638
|
+
return kind === 'table-cell' ? 0 : 1;
|
|
2639
|
+
}
|
|
2640
|
+
// a part out of flow is no part (`isTablePart`)
|
|
2641
|
+
if (
|
|
2642
|
+
style.position === 'absolute' ||
|
|
2643
|
+
style.position === 'fixed' ||
|
|
2644
|
+
style.float !== 'none'
|
|
2645
|
+
) {
|
|
2646
|
+
return 0;
|
|
2647
|
+
}
|
|
2648
|
+
if (kind === 'table-row-group' || kind === 'table-caption' || column) {
|
|
2649
|
+
return 1;
|
|
2650
|
+
}
|
|
2651
|
+
return kind === 'table-row' ? 2 : kind === 'table-cell' ? 3 : 0;
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2470
2654
|
function boxKindFor(display: ComputedStyle['display']): BoxKind {
|
|
2471
2655
|
switch (display) {
|
|
2472
2656
|
case 'inline':
|
|
@@ -374,18 +374,19 @@ export function layoutGrid(
|
|
|
374
374
|
} else if (align === 'center') dy = (tall - outerHeight) / 2;
|
|
375
375
|
else if (align === 'flex-end') dy = tall - outerHeight;
|
|
376
376
|
else if (
|
|
377
|
-
(align === 'stretch' || align === 'baseline') &&
|
|
377
|
+
(align === 'stretch' || align === 'normal' || align === 'baseline') &&
|
|
378
378
|
child.style.height === AUTO &&
|
|
379
379
|
!child.style.heightKeyword &&
|
|
380
|
-
(
|
|
380
|
+
(align === 'stretch' ||
|
|
381
381
|
(child.kind !== 'replaced' && !child.style.aspectRatio))
|
|
382
382
|
) {
|
|
383
383
|
// a stretched item is its area's height, within its own least and
|
|
384
384
|
// greatest (10.3): taller than what it holds, or shorter where its
|
|
385
385
|
// tracks let it be, what it holds then running past it. An image, a
|
|
386
|
-
// control or a box with an `aspect-ratio` keeps its own height
|
|
387
|
-
// `normal
|
|
388
|
-
//
|
|
386
|
+
// control or a box with an `aspect-ratio` keeps its own height under
|
|
387
|
+
// `normal`, which stretches only an item with neither a natural size
|
|
388
|
+
// nor a ratio, and is stretched by `stretch`, the grid's or its own
|
|
389
|
+
// (6.2, and CSS Box Alignment 3, 6.1).
|
|
389
390
|
const stretched = tall - child.marginTop - child.marginBottom;
|
|
390
391
|
const height = clampHeight(
|
|
391
392
|
child,
|
|
@@ -939,9 +940,9 @@ function percentOwn(box: Box): boolean {
|
|
|
939
940
|
/**
|
|
940
941
|
* An item's border-box height before the rows are sized, where it has one
|
|
941
942
|
* then: a height of its own — a percentage of the rows it spans where they
|
|
942
|
-
* have a length for a size — or the rows' where
|
|
943
|
-
*
|
|
944
|
-
* height is its content's.
|
|
943
|
+
* have a length for a size — or the rows' where `stretch` stretches it
|
|
944
|
+
* down them, its own `align-self` or the grid's `align-items`. Null where
|
|
945
|
+
* it has none, which is where its height is its content's.
|
|
945
946
|
*/
|
|
946
947
|
function heightBeforeRows(box: Box, rows: number): number | null {
|
|
947
948
|
const style = box.style;
|
|
@@ -954,10 +955,12 @@ function heightBeforeRows(box: Box, rows: number): number | null {
|
|
|
954
955
|
: own + box.verticalExtra,
|
|
955
956
|
);
|
|
956
957
|
}
|
|
958
|
+
const align =
|
|
959
|
+
style.alignSelf === AUTO ? box.parent?.style.alignItems : style.alignSelf;
|
|
957
960
|
if (
|
|
958
961
|
style.height === AUTO &&
|
|
959
962
|
!style.heightKeyword &&
|
|
960
|
-
|
|
963
|
+
align === 'stretch' &&
|
|
961
964
|
Number.isFinite(rows) &&
|
|
962
965
|
!autoMargins(box, 'y').some(Boolean)
|
|
963
966
|
) {
|