@react-x11/components 0.13.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 +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.map +1 -1
- package/dist/html/css/cascade.js +26 -4
- 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/style.d.ts +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +561 -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 +93 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +389 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +59 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +22 -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 +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 +570 -83
- 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 +62 -18
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +3 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +263 -36
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +79 -4
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1213 -141
- 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.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 +23 -0
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/style.ts +664 -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 +444 -37
- package/src/html/layout/boxes.ts +65 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +95 -22
- package/src/html/layout/flex.ts +671 -77
- 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 +62 -19
- package/src/html/node.ts +300 -37
- package/src/html/paint.ts +1406 -206
- package/src/html/svg.ts +277 -19
- 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/layout/block.ts
CHANGED
|
@@ -32,13 +32,23 @@ import {
|
|
|
32
32
|
CONTAIN_SIZE,
|
|
33
33
|
scrolls,
|
|
34
34
|
} from '../css/style.js';
|
|
35
|
+
import { about, linearOf, matrixOf, transformed } from '../css/transform.js';
|
|
36
|
+
import type { Matrix } from '../css/transform.js';
|
|
35
37
|
import { lineOf, spanToName } from './grid-lines.js';
|
|
36
38
|
import type { GridLines } from './grid-lines.js';
|
|
39
|
+
import {
|
|
40
|
+
columnsOf,
|
|
41
|
+
forgetColumns,
|
|
42
|
+
layoutColumns,
|
|
43
|
+
spansColumns,
|
|
44
|
+
} from './multicol.js';
|
|
45
|
+
import type { Columns } from './multicol.js';
|
|
37
46
|
import {
|
|
38
47
|
BOX_RAISES,
|
|
39
48
|
Box,
|
|
40
49
|
CLAMPED,
|
|
41
50
|
CLEARED_FROM,
|
|
51
|
+
COLUMN_PIECES,
|
|
42
52
|
CUT_BLOCKS,
|
|
43
53
|
GRID_TRACKS,
|
|
44
54
|
FIRST_LINE,
|
|
@@ -47,6 +57,7 @@ import {
|
|
|
47
57
|
LINE_BOX_RAISES,
|
|
48
58
|
SHIFTED_LINES,
|
|
49
59
|
TEXT_SHIFTS,
|
|
60
|
+
columned,
|
|
50
61
|
isBlank,
|
|
51
62
|
} from './boxes.js';
|
|
52
63
|
import type {
|
|
@@ -61,6 +72,7 @@ import type {
|
|
|
61
72
|
import { FloatContext } from './floats.js';
|
|
62
73
|
import {
|
|
63
74
|
faceExtentOf,
|
|
75
|
+
isButton,
|
|
64
76
|
joinsBefore,
|
|
65
77
|
layoutInline,
|
|
66
78
|
lineHeightMultiplier,
|
|
@@ -114,8 +126,15 @@ export interface LayoutContext {
|
|
|
114
126
|
* package's tree-shaking contract forbids exactly that (AGENTS.md, "no side
|
|
115
127
|
* effects at import time anywhere else"), and a field is cheaper than the
|
|
116
128
|
* exception would be.
|
|
129
|
+
*
|
|
130
|
+
* `containing` is the width of the box's containing block where `width`
|
|
131
|
+
* is the box's own, as a flex layout sized it: what the percentages in
|
|
132
|
+
* its padding, its margins and its width limits are of (CSS 2.1 8.3,
|
|
133
|
+
* 8.4, 10.4). Left out, `width` is both — a box probed at the room there
|
|
134
|
+
* is for it. For a table cell it is the width of its row, which its
|
|
135
|
+
* padding's percentages are of.
|
|
117
136
|
*/
|
|
118
|
-
layoutSubtree(box: Box, width: number): void;
|
|
137
|
+
layoutSubtree(box: Box, width: number, containing?: number): void;
|
|
119
138
|
/** How many flex boxes' Yoga passes are running, one inside the last's
|
|
120
139
|
* measure (`layoutFlex`). */
|
|
121
140
|
flexDepth?: number;
|
|
@@ -172,7 +191,8 @@ export function layoutDocument(
|
|
|
172
191
|
viewportWidth,
|
|
173
192
|
viewportHeight,
|
|
174
193
|
positioned: [],
|
|
175
|
-
layoutSubtree: (box, width) =>
|
|
194
|
+
layoutSubtree: (box, width, containing) =>
|
|
195
|
+
layoutSubtree(box, ctx, width, containing),
|
|
176
196
|
firstLine: tree.firstLine,
|
|
177
197
|
firstLineStyler: tree.firstLineStyler,
|
|
178
198
|
nestedOutOfLine: tree.nestedOutOfLine,
|
|
@@ -1664,16 +1684,28 @@ export function measureIntrinsicWidth(
|
|
|
1664
1684
|
* the shrink-to-fit probe uses. A replaced box is sized rather than laid out,
|
|
1665
1685
|
* because there is nothing inside it to lay out.
|
|
1666
1686
|
*/
|
|
1667
|
-
function layoutSubtree(
|
|
1668
|
-
|
|
1687
|
+
function layoutSubtree(
|
|
1688
|
+
box: Box,
|
|
1689
|
+
ctx: LayoutContext,
|
|
1690
|
+
width: number,
|
|
1691
|
+
/** Its containing block's width, where `width` is the box's own. */
|
|
1692
|
+
containing = width,
|
|
1693
|
+
): void {
|
|
1694
|
+
resolveEdges(box, Number.isFinite(containing) ? containing : 0);
|
|
1669
1695
|
if (box.kind === 'replaced') {
|
|
1670
1696
|
sizeReplaced(box, width);
|
|
1671
1697
|
box.x = 0;
|
|
1672
1698
|
box.y = 0;
|
|
1673
1699
|
return;
|
|
1674
1700
|
}
|
|
1701
|
+
// within its limits, a percentage among them of its containing block's
|
|
1702
|
+
// width and not of its own: a flex item `max-width: 50%` that the flex
|
|
1703
|
+
// layout made half its row was laid out at a quarter of it. A table
|
|
1704
|
+
// cell's is of nothing, and ignored: what a percentage limit does to a
|
|
1705
|
+
// cell it does where its column is sized (CSS Tables 3, 3.8.2), and a
|
|
1706
|
+
// cell is laid out in the width its columns came to.
|
|
1675
1707
|
const borderBox = Number.isFinite(width)
|
|
1676
|
-
? clampWidth(box, width,
|
|
1708
|
+
? clampWidth(box, width, box.kind === 'table-cell' ? NaN : containing)
|
|
1677
1709
|
: Infinity;
|
|
1678
1710
|
layoutInternals(box, ctx, borderBox, 0, 0);
|
|
1679
1711
|
}
|
|
@@ -1787,9 +1819,13 @@ function layoutBox(
|
|
|
1787
1819
|
return;
|
|
1788
1820
|
}
|
|
1789
1821
|
|
|
1822
|
+
// A multicol container's content is laid out as one column, all of it,
|
|
1823
|
+
// and then set in the columns there are (`layoutColumns`)
|
|
1824
|
+
const columns = columnsOf(box, contentWidth);
|
|
1825
|
+
const flowWidth = columns ? columns.width : contentWidth;
|
|
1790
1826
|
const ownFloats = establishesBFC(box) || !outerFloats;
|
|
1791
1827
|
const floats = ownFloats
|
|
1792
|
-
? new FloatContext(box.contentX, box.contentX +
|
|
1828
|
+
? new FloatContext(box.contentX, box.contentX + flowWidth)
|
|
1793
1829
|
: outerFloats;
|
|
1794
1830
|
// the line a pass before gave a marker, which this one may not
|
|
1795
1831
|
if (box.marker) box.lines = null;
|
|
@@ -1800,7 +1836,19 @@ function layoutBox(
|
|
|
1800
1836
|
if (box.style.listStylePosition === 'inside') MARKER_ASCENT.delete(box);
|
|
1801
1837
|
else MARKER_ASCENT.set(box, markerAscent(box, box.marker, ctx.fonts));
|
|
1802
1838
|
}
|
|
1803
|
-
|
|
1839
|
+
// a column wide while its content is laid out, for what reads the box
|
|
1840
|
+
// it is in rather than the width it is handed
|
|
1841
|
+
// and about a box that spans them, in rows of columns (`layoutRows`)
|
|
1842
|
+
const rows =
|
|
1843
|
+
columns && columns.count > 1 && box.children.some(spansColumns)
|
|
1844
|
+
? layoutRows(box, ctx, columns, contentWidth, borderBoxWidth)
|
|
1845
|
+
: null;
|
|
1846
|
+
if (columns) box.width = columns.width + box.horizontalExtra;
|
|
1847
|
+
const flow =
|
|
1848
|
+
rows === null
|
|
1849
|
+
? layoutChildren(box, ctx, floats, box.contentY, flowWidth)
|
|
1850
|
+
: { height: rows, hanging: NO_MARGIN };
|
|
1851
|
+
box.width = borderBoxWidth;
|
|
1804
1852
|
// A list item with a marker and no line holds one, the marker's, a line
|
|
1805
1853
|
// of its own face tall: an empty `<li>` is a line tall in a browser, and
|
|
1806
1854
|
// an inline-block around one sits on its marker's baseline.
|
|
@@ -1844,7 +1892,16 @@ function layoutBox(
|
|
|
1844
1892
|
floats.bottom === -Infinity ? 0 : floats.bottom - box.contentY,
|
|
1845
1893
|
)
|
|
1846
1894
|
: height;
|
|
1847
|
-
finishHeight(box,
|
|
1895
|
+
if (rows !== null) finishHeight(box, rows);
|
|
1896
|
+
else if (columns) {
|
|
1897
|
+
finishHeight(
|
|
1898
|
+
box,
|
|
1899
|
+
layoutColumns(box, columns, withFloats, columnLimit(box)),
|
|
1900
|
+
);
|
|
1901
|
+
} else {
|
|
1902
|
+
if (box.style.columns) forgetColumns(box);
|
|
1903
|
+
finishHeight(box, withFloats);
|
|
1904
|
+
}
|
|
1848
1905
|
// The margin that escaped through this box's bottom edge becomes part of
|
|
1849
1906
|
// its own: the parent's flow loop reads `child.marginBottom` for the next
|
|
1850
1907
|
// sibling's collapse, which is exactly where an escaped margin goes.
|
|
@@ -1856,6 +1913,103 @@ function layoutBox(
|
|
|
1856
1913
|
box.marginBottom = marginOf(bottom);
|
|
1857
1914
|
} else box.bottomStrut = null;
|
|
1858
1915
|
if (box.marker) layoutMarker(box, box.marker, ctx);
|
|
1916
|
+
if (isButtonBlock(box)) {
|
|
1917
|
+
BUTTON_CONTENT.set(box, { height: withFloats, down: 0 });
|
|
1918
|
+
centreButton(box);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/**
|
|
1923
|
+
* A multicol container's content about the boxes that span its columns
|
|
1924
|
+
* (`column-span: all`, CSS Multi-column 1, 6): what comes before one is set
|
|
1925
|
+
* in columns of its own, balanced, the spanner under them across the whole
|
|
1926
|
+
* container, and what comes after in columns under that. A spanner is a
|
|
1927
|
+
* formatting context of its own and its margins collapse with no column
|
|
1928
|
+
* content's, only with the spanner's next to it. Answers the content's
|
|
1929
|
+
* height.
|
|
1930
|
+
*/
|
|
1931
|
+
function layoutRows(
|
|
1932
|
+
box: Box,
|
|
1933
|
+
ctx: LayoutContext,
|
|
1934
|
+
columns: Columns,
|
|
1935
|
+
contentWidth: number,
|
|
1936
|
+
borderBoxWidth: number,
|
|
1937
|
+
): number {
|
|
1938
|
+
const all = box.children;
|
|
1939
|
+
const limit = columnLimit(box);
|
|
1940
|
+
const top = box.contentY;
|
|
1941
|
+
let y = top;
|
|
1942
|
+
/** The bottom margin of the spanner just before, which the next thing
|
|
1943
|
+
* comes under: all of it, or what the next spanner's does not cover. */
|
|
1944
|
+
let margin: number | null = null;
|
|
1945
|
+
let from = 0;
|
|
1946
|
+
try {
|
|
1947
|
+
for (let i = 0; i <= all.length; i += 1) {
|
|
1948
|
+
const child = all[i];
|
|
1949
|
+
if (child && !spansColumns(child)) continue;
|
|
1950
|
+
const row = all.slice(from, i);
|
|
1951
|
+
from = i + 1;
|
|
1952
|
+
if (row.length) {
|
|
1953
|
+
const set = row.some(
|
|
1954
|
+
(in_) =>
|
|
1955
|
+
in_.kind !== 'text' && in_.kind !== 'break' && !in_.outOfFlow,
|
|
1956
|
+
);
|
|
1957
|
+
if (set && margin !== null) {
|
|
1958
|
+
y += margin;
|
|
1959
|
+
margin = null;
|
|
1960
|
+
}
|
|
1961
|
+
// the row's content as a strip a column wide, and then in columns,
|
|
1962
|
+
// no taller than the container has left
|
|
1963
|
+
box.children = row;
|
|
1964
|
+
box.width = columns.width + box.horizontalExtra;
|
|
1965
|
+
const floats = new FloatContext(
|
|
1966
|
+
box.contentX,
|
|
1967
|
+
box.contentX + columns.width,
|
|
1968
|
+
);
|
|
1969
|
+
const flow = layoutChildren(box, ctx, floats, y, columns.width);
|
|
1970
|
+
box.width = borderBoxWidth;
|
|
1971
|
+
const strip = Math.max(
|
|
1972
|
+
flow.height,
|
|
1973
|
+
floats.bottom === -Infinity ? 0 : floats.bottom - y,
|
|
1974
|
+
);
|
|
1975
|
+
y += layoutColumns(
|
|
1976
|
+
box,
|
|
1977
|
+
columns,
|
|
1978
|
+
strip,
|
|
1979
|
+
Math.max(0, limit - (y - top)),
|
|
1980
|
+
y,
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
if (!child) break;
|
|
1984
|
+
resolveEdges(child, contentWidth);
|
|
1985
|
+
y +=
|
|
1986
|
+
margin === null ? child.marginTop : Math.max(margin, child.marginTop);
|
|
1987
|
+
layoutBlockLevel(
|
|
1988
|
+
child,
|
|
1989
|
+
ctx,
|
|
1990
|
+
new FloatContext(box.contentX, box.contentX + contentWidth),
|
|
1991
|
+
box.contentX,
|
|
1992
|
+
y,
|
|
1993
|
+
contentWidth,
|
|
1994
|
+
);
|
|
1995
|
+
y = child.y + child.height;
|
|
1996
|
+
margin = child.marginBottom;
|
|
1997
|
+
}
|
|
1998
|
+
} finally {
|
|
1999
|
+
box.children = all;
|
|
2000
|
+
box.width = borderBoxWidth;
|
|
2001
|
+
}
|
|
2002
|
+
return y + (margin ?? 0) - top;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
/** The tallest a multicol container's columns may be: its own height where
|
|
2006
|
+
* it has one, and its `max-height`; no limit where it has neither. */
|
|
2007
|
+
function columnLimit(box: Box): number {
|
|
2008
|
+
const height = box.height;
|
|
2009
|
+
finishHeight(box, specifiedHeight(box) === null ? Infinity : 0);
|
|
2010
|
+
const limit = box.height - box.verticalExtra;
|
|
2011
|
+
box.height = height;
|
|
2012
|
+
return Math.max(0, limit);
|
|
1859
2013
|
}
|
|
1860
2014
|
|
|
1861
2015
|
/**
|
|
@@ -2166,13 +2320,25 @@ function stretchHeight(box: Box): number | null {
|
|
|
2166
2320
|
}
|
|
2167
2321
|
|
|
2168
2322
|
/**
|
|
2169
|
-
* The content height a flex layout made definite for an item — a
|
|
2170
|
-
* item's, a flexed one's in a container of a definite height (CSS
|
|
2171
|
-
* 9.8)
|
|
2172
|
-
*
|
|
2323
|
+
* The content height a flex or grid layout made definite for an item — a
|
|
2324
|
+
* stretched item's, a flexed one's in a container of a definite height (CSS
|
|
2325
|
+
* Flexbox 9.8), a grid item's stretched down its area (CSS Grid 1, 11.1) —
|
|
2326
|
+
* which what is in it takes its percentages of, and which an item that is
|
|
2327
|
+
* a flex or grid container itself lays its own items out in. Set by
|
|
2328
|
+
* `flex.ts` and `css-grid.ts` for the item's own layout alone.
|
|
2173
2329
|
*/
|
|
2174
2330
|
export const FLEXED_HEIGHT = new WeakMap<Box, number>();
|
|
2175
2331
|
|
|
2332
|
+
/**
|
|
2333
|
+
* The content height a flex layout gave an item without making it definite:
|
|
2334
|
+
* flexed along a column that has no height of its own, which 9.8 leaves
|
|
2335
|
+
* indefinite. An item that is a flex or grid container lays its own items
|
|
2336
|
+
* out in it all the same — it is its used size (9.7) — and nothing in it
|
|
2337
|
+
* takes a percentage of it. Set by `flex.ts` for the item's own layout
|
|
2338
|
+
* alone.
|
|
2339
|
+
*/
|
|
2340
|
+
export const USED_HEIGHT = new WeakMap<Box, number>();
|
|
2341
|
+
|
|
2176
2342
|
/**
|
|
2177
2343
|
* Hand a box's children the height their percentages resolve against —
|
|
2178
2344
|
* through inline boxes, which are no containing block, to the inline-block
|
|
@@ -2488,7 +2654,7 @@ function blockWidth(
|
|
|
2488
2654
|
): number {
|
|
2489
2655
|
const style = box.style;
|
|
2490
2656
|
const available = containingWidth - box.marginLeft - box.marginRight;
|
|
2491
|
-
if (style.width === AUTO) {
|
|
2657
|
+
if (style.width === AUTO || cyclicWidth(style, percentBase)) {
|
|
2492
2658
|
// a width its height gives it through its ratio
|
|
2493
2659
|
const fromRatio = ratioWidth(box);
|
|
2494
2660
|
if (fromRatio !== null) {
|
|
@@ -2496,9 +2662,11 @@ function blockWidth(
|
|
|
2496
2662
|
return ratioMinimum(box, clamped, percentBase, ctx);
|
|
2497
2663
|
}
|
|
2498
2664
|
const room = Math.max(0, available);
|
|
2665
|
+
const keyword =
|
|
2666
|
+
style.widthKeyword ?? (fitsContent(box) ? 'fit-content' : null);
|
|
2499
2667
|
const width =
|
|
2500
|
-
|
|
2501
|
-
? contentSizedWidth(box, ctx,
|
|
2668
|
+
keyword && ctx
|
|
2669
|
+
? contentSizedWidth(box, ctx, keyword, room, percentBase)
|
|
2502
2670
|
: room;
|
|
2503
2671
|
return clampWidth(
|
|
2504
2672
|
box,
|
|
@@ -2517,6 +2685,43 @@ function blockWidth(
|
|
|
2517
2685
|
return clampWidth(box, borderBox, percentBase, ctx, containingWidth);
|
|
2518
2686
|
}
|
|
2519
2687
|
|
|
2688
|
+
/**
|
|
2689
|
+
* Whether a block-level box's `width: auto` is `fit-content` and not the
|
|
2690
|
+
* room its margins leave: a `<button>`'s, of which HTML's rendering section
|
|
2691
|
+
* says "If the computed value of 'inline-size' is 'auto', then the used
|
|
2692
|
+
* value is the fit-content inline size" (button layout, 15.5.3) — whatever
|
|
2693
|
+
* it is laid out as inside, a block, a flex box or a grid, as Blink leaves
|
|
2694
|
+
* it out of the boxes whose `auto` stretches
|
|
2695
|
+
* (`ShouldBlockContainerChildStretchAutoInlineSize`). Set `display: block`
|
|
2696
|
+
* to stand on a line of its own, a button was as wide as the line, where it
|
|
2697
|
+
* is as wide as its label. Not one a flex box, a grid or a pair of offsets
|
|
2698
|
+
* sizes, which stretch it still.
|
|
2699
|
+
*
|
|
2700
|
+
* Asked of every block: most are told from a button by the length of their
|
|
2701
|
+
* name.
|
|
2702
|
+
*/
|
|
2703
|
+
function fitsContent(box: Box): boolean {
|
|
2704
|
+
return box.el !== null && box.el.name.length === 6 && isButton(box);
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
/**
|
|
2708
|
+
* Whether a box's `width` is a percentage of a width that is not there to
|
|
2709
|
+
* take it of: its containing block's, while that is being found from what
|
|
2710
|
+
* it holds — a float's, an inline-block's, a flex item's or a table cell's
|
|
2711
|
+
* content measured at its widest. The percentage is cyclic, and the box is
|
|
2712
|
+
* measured as though its width were `auto` (CSS Sizing 3, 5.2.1); it is a
|
|
2713
|
+
* share of the width that comes to once there is one.
|
|
2714
|
+
*
|
|
2715
|
+
* Taken as a share of nothing, the box was no width at all, and its content
|
|
2716
|
+
* wrapped at every word in it: a float around a `width: 100%` block was as
|
|
2717
|
+
* wide as the block's longest word, and one around a `width: 100%` flex box
|
|
2718
|
+
* of a label that clips — the branch button of a GitHub repository — as
|
|
2719
|
+
* wide as nothing, its label gone.
|
|
2720
|
+
*/
|
|
2721
|
+
export function cyclicWidth(style: ComputedStyle, base: number): boolean {
|
|
2722
|
+
return isPct(style.width) && !Number.isFinite(base);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2520
2725
|
/**
|
|
2521
2726
|
* A width a box with a ratio and no height of its own works out for itself
|
|
2522
2727
|
* within the least and greatest widths its least and greatest heights make
|
|
@@ -2716,7 +2921,7 @@ function shrinkToFitWidth(
|
|
|
2716
2921
|
offset = 0,
|
|
2717
2922
|
): number {
|
|
2718
2923
|
const style = box.style;
|
|
2719
|
-
if (style.width !== AUTO) {
|
|
2924
|
+
if (style.width !== AUTO && !cyclicWidth(style, available)) {
|
|
2720
2925
|
const specified = Math.max(0, resolve(style.width, available, 0));
|
|
2721
2926
|
const borderBox =
|
|
2722
2927
|
style.boxSizing === 'border-box'
|
|
@@ -2951,8 +3156,12 @@ function wordBound(box: Box, fonts: FontsLike, measured: boolean): number {
|
|
|
2951
3156
|
* was `Infinity`), so its own width says nothing and only what it drew does.
|
|
2952
3157
|
* Skipping the non-finite ones is what stops the probe from answering
|
|
2953
3158
|
* `Infinity` for every box that contains a paragraph.
|
|
3159
|
+
*
|
|
3160
|
+
* `seen.cut` is set where a line it measured was cut short by
|
|
3161
|
+
* `text-overflow`, in a box with a width to cut it at: the line is wider
|
|
3162
|
+
* than it was drawn, and the answer is of what was drawn.
|
|
2954
3163
|
*/
|
|
2955
|
-
export function intrinsicWidth(box: Box): number {
|
|
3164
|
+
export function intrinsicWidth(box: Box, seen?: { cut: boolean }): number {
|
|
2956
3165
|
// under size containment, as though it held nothing: the size
|
|
2957
3166
|
// `contain-intrinsic-size` gives it, or none (CSS Containment 2, 3.2)
|
|
2958
3167
|
if (contained(box, CONTAIN_WIDTH)) {
|
|
@@ -2991,6 +3200,12 @@ export function intrinsicWidth(box: Box): number {
|
|
|
2991
3200
|
widest = Math.max(widest, line.width + (line.floats ?? 0));
|
|
2992
3201
|
}
|
|
2993
3202
|
widest = Math.max(widest, box.floatRow);
|
|
3203
|
+
// a line `text-overflow` cut short is wider than this finds it
|
|
3204
|
+
if (seen && !seen.cut && box.style.textOverflow === 'ellipsis') {
|
|
3205
|
+
seen.cut = lines.some((line) =>
|
|
3206
|
+
line.texts.some((text) => text.layout.truncated),
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
2994
3209
|
}
|
|
2995
3210
|
// Floats among blocks stand side by side too, as many as come together,
|
|
2996
3211
|
// where each block in flow is a line of its own; one with a formatting
|
|
@@ -3001,6 +3216,9 @@ export function intrinsicWidth(box: Box): number {
|
|
|
3001
3216
|
const sideBySide = !Number.isFinite(box.width);
|
|
3002
3217
|
let left = 0;
|
|
3003
3218
|
let right = 0;
|
|
3219
|
+
/** In a multicol container, the widest box that spans its columns. */
|
|
3220
|
+
const spanning = box.style.columns !== 0 && box.kind === 'block';
|
|
3221
|
+
let across = 0;
|
|
3004
3222
|
for (const child of box.children) {
|
|
3005
3223
|
if (child.kind === 'text' || child.kind === 'break') continue;
|
|
3006
3224
|
if (child.outOfFlow) continue;
|
|
@@ -3022,14 +3240,35 @@ export function intrinsicWidth(box: Box): number {
|
|
|
3022
3240
|
// counts as `auto`, so its content decides.
|
|
3023
3241
|
if (typeof style.width === 'number') {
|
|
3024
3242
|
contribution = Math.max(own, min + margins);
|
|
3243
|
+
// And it is that width where the box was laid out at less. In a row
|
|
3244
|
+
// of flex items it was shrunk to fit the width this is measured at,
|
|
3245
|
+
// and what an item gives its flex box's own size is its width (CSS
|
|
3246
|
+
// Flexbox 9.9.3); under a percentage `max-width` it was cut short,
|
|
3247
|
+
// by a percentage of the size being worked out, which is none to
|
|
3248
|
+
// what a box contributes (5.2.1). But a replaced box's is of nothing
|
|
3249
|
+
// at its least and none at its most: as it was laid out.
|
|
3250
|
+
const cyclic = typeof style.maxWidth === 'object';
|
|
3251
|
+
if (cyclic ? child.kind !== 'replaced' : row) {
|
|
3252
|
+
let stated = style.width + contentExtra(child);
|
|
3253
|
+
if (typeof style.maxWidth === 'number') {
|
|
3254
|
+
stated = Math.min(stated, style.maxWidth + contentExtra(child));
|
|
3255
|
+
}
|
|
3256
|
+
stated = Math.max(stated, child.horizontalExtra, min);
|
|
3257
|
+
contribution = Math.max(contribution, stated + margins);
|
|
3258
|
+
}
|
|
3025
3259
|
} else {
|
|
3026
|
-
let inner = intrinsicWidth(child) + child.horizontalExtra;
|
|
3260
|
+
let inner = intrinsicWidth(child, seen) + child.horizontalExtra;
|
|
3027
3261
|
if (typeof style.maxWidth === 'number') {
|
|
3028
3262
|
inner = Math.min(inner, style.maxWidth + contentExtra(child));
|
|
3029
3263
|
}
|
|
3030
3264
|
if (min > inner) inner = min;
|
|
3031
3265
|
contribution = Math.max(inner + margins, own);
|
|
3032
3266
|
}
|
|
3267
|
+
if (spanning && spansColumns(child)) {
|
|
3268
|
+
// across the columns, and no part of what a column is as wide as
|
|
3269
|
+
across = Math.max(across, contribution);
|
|
3270
|
+
continue;
|
|
3271
|
+
}
|
|
3033
3272
|
if (row) {
|
|
3034
3273
|
total += contribution;
|
|
3035
3274
|
items += 1;
|
|
@@ -3055,6 +3294,19 @@ export function intrinsicWidth(box: Box): number {
|
|
|
3055
3294
|
const gap = gapOf(box.style.columnGap, NaN);
|
|
3056
3295
|
widest = Math.max(widest, total + gap * (items - 1));
|
|
3057
3296
|
}
|
|
3297
|
+
// A multicol container is as wide as its columns side by side, each as
|
|
3298
|
+
// wide as its content or as `column-width` where that is wider, and the
|
|
3299
|
+
// gaps between them (as Blink measures one,
|
|
3300
|
+
// `NGColumnLayoutAlgorithm::ComputeMinMaxSizes`)
|
|
3301
|
+
if (box.style.columns && box.kind === 'block') {
|
|
3302
|
+
const count = box.style.columnCount ?? 1;
|
|
3303
|
+
const gap = box.style.columnGapNormal
|
|
3304
|
+
? box.style.fontSize
|
|
3305
|
+
: gapOf(box.style.columnGap, NaN);
|
|
3306
|
+
widest = Math.max(widest, box.style.columnWidth ?? 0);
|
|
3307
|
+
// and no narrower than a box that spans them
|
|
3308
|
+
widest = Math.max(widest * count + gap * (count - 1), across);
|
|
3309
|
+
}
|
|
3058
3310
|
return widest;
|
|
3059
3311
|
}
|
|
3060
3312
|
|
|
@@ -3319,6 +3571,22 @@ function placeFloat(
|
|
|
3319
3571
|
});
|
|
3320
3572
|
}
|
|
3321
3573
|
|
|
3574
|
+
/** Whether a box is a form control's: a replaced box for what draws it,
|
|
3575
|
+
* a widget, and an inline block for how it is sized. */
|
|
3576
|
+
function isControl(box: Box): boolean {
|
|
3577
|
+
switch (box.replaced) {
|
|
3578
|
+
case 'input':
|
|
3579
|
+
case 'textarea':
|
|
3580
|
+
case 'select':
|
|
3581
|
+
case 'button':
|
|
3582
|
+
case 'checkbox':
|
|
3583
|
+
case 'radio':
|
|
3584
|
+
return true;
|
|
3585
|
+
default:
|
|
3586
|
+
return false;
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3322
3590
|
/**
|
|
3323
3591
|
* An absolutely positioned box, against its containing block: a box's
|
|
3324
3592
|
* padding box, or an inline box's fragments (`inlineContainingBlock`). An
|
|
@@ -3440,11 +3708,25 @@ function layoutPositioned(box: Box, containing: Box, ctx: LayoutContext): void {
|
|
|
3440
3708
|
top ?? (bottom === null && at ? at.from.y + at.y - cbY : 0),
|
|
3441
3709
|
);
|
|
3442
3710
|
}
|
|
3711
|
+
// A form control is an inline block to CSS and no replaced element,
|
|
3712
|
+
// whatever draws it (HTML's rendering section): with both offsets and no
|
|
3713
|
+
// size of its own it fills what they leave, as any box does, where an
|
|
3714
|
+
// image keeps its own (10.3.8, 10.6.5). A page lays an invisible
|
|
3715
|
+
// `<select>` over a picker it draws, `position: absolute; inset: 0`, to
|
|
3716
|
+
// take the press, and it has to cover it.
|
|
3717
|
+
const control = isControl(box);
|
|
3443
3718
|
if (box.kind === 'replaced') {
|
|
3444
3719
|
sizeReplaced(box, cbWidth);
|
|
3445
3720
|
stretchReplaced(
|
|
3446
3721
|
box,
|
|
3447
|
-
style.widthKeyword === 'stretch'
|
|
3722
|
+
style.widthKeyword === 'stretch' ||
|
|
3723
|
+
(control &&
|
|
3724
|
+
style.width === AUTO &&
|
|
3725
|
+
style.widthKeyword === null &&
|
|
3726
|
+
left !== null &&
|
|
3727
|
+
right !== null)
|
|
3728
|
+
? width
|
|
3729
|
+
: null,
|
|
3448
3730
|
cbWidth,
|
|
3449
3731
|
);
|
|
3450
3732
|
} else layoutOwn(box, ctx, width);
|
|
@@ -3513,12 +3795,17 @@ function layoutPositioned(box: Box, containing: Box, ctx: LayoutContext): void {
|
|
|
3513
3795
|
);
|
|
3514
3796
|
// — a box with a ratio has its height from its width
|
|
3515
3797
|
const stretches =
|
|
3516
|
-
style.height === AUTO &&
|
|
3798
|
+
style.height === AUTO &&
|
|
3799
|
+
(box.kind !== 'replaced' || control) &&
|
|
3800
|
+
!boxRatio(box);
|
|
3517
3801
|
// both offsets and no height: the box fills what they leave, its
|
|
3518
3802
|
// `auto` margins nothing (10.6.4, rule 5) — unless `min-height` or
|
|
3519
3803
|
// `max-height` moves that, which makes it a height like one set, and
|
|
3520
3804
|
// the rules run again with it (10.7)
|
|
3521
|
-
if (stretches)
|
|
3805
|
+
if (stretches) {
|
|
3806
|
+
box.height = clampHeight(box, fill);
|
|
3807
|
+
centreButton(box);
|
|
3808
|
+
}
|
|
3522
3809
|
if (!stretches || box.height !== fill) {
|
|
3523
3810
|
// and a height: the `auto` margins share the rest, and where none
|
|
3524
3811
|
// is `auto`, `bottom` gives way (10.6.4, 10.6.5)
|
|
@@ -4026,11 +4313,84 @@ export function moveTo(box: Box, x: number, y: number): void {
|
|
|
4026
4313
|
translate(box, x - box.x, y - box.y);
|
|
4027
4314
|
}
|
|
4028
4315
|
|
|
4029
|
-
/**
|
|
4030
|
-
*
|
|
4316
|
+
/**
|
|
4317
|
+
* Move what a box holds down, and not the box: a table cell's content
|
|
4318
|
+
* sits where its `vertical-align` puts it, in a box that fills the row, and
|
|
4319
|
+
* a button's in the middle of its own (`centreButton`).
|
|
4320
|
+
*
|
|
4321
|
+
* The static positions the box gave the absolutely positioned boxes in its
|
|
4322
|
+
* flow go down with the content: each is where its box "would have been in
|
|
4323
|
+
* the normal flow" (CSS 2.1 10.6.4), and the flow is what moved. They are
|
|
4324
|
+
* kept from the box's own corner, which stays, so that moving the boxes
|
|
4325
|
+
* alone left them behind: a badge with no offsets in a `vertical-align:
|
|
4326
|
+
* middle` cell stood at the cell's top, over an icon 22px further down.
|
|
4327
|
+
*
|
|
4328
|
+
* It adds to where the content stands, so it is asked for once for each
|
|
4329
|
+
* layout of the box, which puts the content and the static positions back
|
|
4330
|
+
* at the top. A table lays a cell out and moves its content in one pass
|
|
4331
|
+
* over its cells, however many times a pass lays the table out, and one
|
|
4332
|
+
* whose layout is kept (`kept`) is moved whole, the cell with it; a button
|
|
4333
|
+
* counts how far down its content stands (`centreButton`).
|
|
4334
|
+
*/
|
|
4031
4335
|
export function moveContent(box: Box, dy: number): void {
|
|
4336
|
+
if (!dy) return;
|
|
4032
4337
|
translate(box, 0, dy);
|
|
4033
4338
|
box.y -= dy;
|
|
4339
|
+
moveStatics(box, box, dy);
|
|
4340
|
+
}
|
|
4341
|
+
|
|
4342
|
+
/** Whether a box is a `<button>`'s own, laid out as a block container: the
|
|
4343
|
+
* one HTML's button layout sets an anonymous button content box in. Not
|
|
4344
|
+
* the text in it, whose element is the button too; not a button that is a
|
|
4345
|
+
* flex box or a grid, which has no such box; nor an `<input type=button>`,
|
|
4346
|
+
* which is a widget of its label's size. */
|
|
4347
|
+
function isButtonBlock(box: Box): boolean {
|
|
4348
|
+
return box.kind === 'block' && box.el !== null && box.el.name === 'button';
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
/** How tall a `<button>`'s content came to, and how far down its content
|
|
4352
|
+
* box it stands for being centred in it (`centreButton`). */
|
|
4353
|
+
const BUTTON_CONTENT = new WeakMap<Box, { height: number; down: number }>();
|
|
4354
|
+
|
|
4355
|
+
/**
|
|
4356
|
+
* Centre a `<button>`'s content down its content box, where the box is the
|
|
4357
|
+
* taller: a button's content is in an anonymous box of its own that is
|
|
4358
|
+
* "centered vertically" where it "does not overflow in the vertical axis"
|
|
4359
|
+
* (HTML 15.5.5, button layout), and is at the top where it does. Blink
|
|
4360
|
+
* moves a button's children down by half the room left, clamped at none
|
|
4361
|
+
* (`AlignBlockContent`); set at the top as any block's is, the two lines of
|
|
4362
|
+
* a 60px sidebar picker on nextjs.org sat 4px high in it.
|
|
4363
|
+
*
|
|
4364
|
+
* Asked for when the button is laid out, and again wherever its height is
|
|
4365
|
+
* given to it afterwards — a flex or a grid item stretched to its line, or
|
|
4366
|
+
* put back to the height it was measured at, and an absolute box between
|
|
4367
|
+
* two offsets: the content moves by the difference from where it was last
|
|
4368
|
+
* put. A button that is a flex box or a grid has no such box, and its
|
|
4369
|
+
* content is where its own alignment puts it.
|
|
4370
|
+
*/
|
|
4371
|
+
export function centreButton(box: Box): void {
|
|
4372
|
+
if (!isButtonBlock(box)) return;
|
|
4373
|
+
const content = BUTTON_CONTENT.get(box);
|
|
4374
|
+
if (content === undefined) return;
|
|
4375
|
+
const down = Math.max(0, (box.contentHeight - content.height) / 2);
|
|
4376
|
+
const by = down - content.down;
|
|
4377
|
+
if (by === 0 || !Number.isFinite(by)) return;
|
|
4378
|
+
content.down = down;
|
|
4379
|
+
// and the static positions kept from its corner with it: a menu under a
|
|
4380
|
+
// button's label opens under the label, where the label went
|
|
4381
|
+
moveContent(box, by);
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4384
|
+
/** Move the static positions a block gave the absolutely positioned boxes
|
|
4385
|
+
* in its flow, at any depth of inline box, which are kept from its own
|
|
4386
|
+
* corner and not from its content's (`moveContent`). One from a box
|
|
4387
|
+
* inside it is kept from that box's corner, which moved with the rest. */
|
|
4388
|
+
function moveStatics(parent: Box, block: Box, dy: number): void {
|
|
4389
|
+
for (const child of parent.children) {
|
|
4390
|
+
if (child.outOfFlow) {
|
|
4391
|
+
if (child.staticPosition?.from === block) child.staticPosition.y += dy;
|
|
4392
|
+
} else if (child.kind === 'inline') moveStatics(child, block, dy);
|
|
4393
|
+
}
|
|
4034
4394
|
}
|
|
4035
4395
|
|
|
4036
4396
|
function translate(box: Box, dx: number, dy: number): void {
|
|
@@ -4042,6 +4402,17 @@ function translate(box: Box, dx: number, dy: number): void {
|
|
|
4042
4402
|
box.marker.x += dx;
|
|
4043
4403
|
box.marker.y += dy;
|
|
4044
4404
|
}
|
|
4405
|
+
// the pieces columns broke it in, which are kept beside it
|
|
4406
|
+
if (columned.any) {
|
|
4407
|
+
const pieces = COLUMN_PIECES.get(box);
|
|
4408
|
+
if (pieces) {
|
|
4409
|
+
for (const piece of pieces) {
|
|
4410
|
+
piece.x += dx;
|
|
4411
|
+
piece.y += dy;
|
|
4412
|
+
piece.wholeY += dy;
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4045
4416
|
if (box.lines) {
|
|
4046
4417
|
for (const line of box.lines) {
|
|
4047
4418
|
line.x += dx;
|
|
@@ -4166,6 +4537,10 @@ export function establishesBFC(box: Box): boolean {
|
|
|
4166
4537
|
if (style.lineClamp !== null) return true;
|
|
4167
4538
|
if (style.float !== 'none') return true;
|
|
4168
4539
|
if (style.position === 'absolute' || style.position === 'fixed') return true;
|
|
4540
|
+
// a multicol container (CSS Multi-column 1, 2), and a box that spans
|
|
4541
|
+
// one's columns (6)
|
|
4542
|
+
if (style.columns && box.kind === 'block') return true;
|
|
4543
|
+
if (style.columnSpan && box.parent?.style.columns) return true;
|
|
4169
4544
|
if (
|
|
4170
4545
|
style.display === 'inline-block' ||
|
|
4171
4546
|
style.display === 'flex' ||
|
|
@@ -4180,10 +4555,17 @@ export function establishesBFC(box: Box): boolean {
|
|
|
4180
4555
|
) {
|
|
4181
4556
|
return true;
|
|
4182
4557
|
}
|
|
4558
|
+
// a flex item, and a grid's: each an independent formatting context, which
|
|
4559
|
+
// the margins of what it holds stay inside (CSS Flexbox 1, 4; CSS Grid 1,
|
|
4560
|
+
// 6.1)
|
|
4561
|
+
if (box.parent?.kind === 'flex' && !box.outOfFlow) return true;
|
|
4183
4562
|
// The root element establishes the document's formatting context. Here it
|
|
4184
4563
|
// is a box below the synthetic initial containing block, so it is named:
|
|
4185
4564
|
// without it, <html>'s own margin collapsed with <body>'s first block's.
|
|
4186
4565
|
if (box.el?.name === 'html') return true;
|
|
4566
|
+
// a button's content is in a formatting context of its own, the
|
|
4567
|
+
// anonymous button content box's (HTML 15.5.5), whatever its `display`
|
|
4568
|
+
if (isButtonBlock(box)) return true;
|
|
4187
4569
|
return box.parent === null;
|
|
4188
4570
|
}
|
|
4189
4571
|
|
|
@@ -4224,23 +4606,48 @@ export function applyRelativeOffsets(
|
|
|
4224
4606
|
if (blocks) for (const block of blocks) translate(block, dx, dy);
|
|
4225
4607
|
}
|
|
4226
4608
|
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
export
|
|
4230
|
-
return style.translate !== null || style.transformTranslate !== null;
|
|
4231
|
-
}
|
|
4609
|
+
// what makes a box a containing block and a layer, as a positioned box is,
|
|
4610
|
+
// is asked here by the layouts beside this one
|
|
4611
|
+
export { transformed };
|
|
4232
4612
|
|
|
4233
|
-
/** How far
|
|
4234
|
-
* its
|
|
4613
|
+
/** How far a box's transform moves it — `translate`, and the translation
|
|
4614
|
+
* its `transform` comes to with its turns and scales: a percentage is of
|
|
4615
|
+
* its own border box (CSS Transforms 1, 7). The rest of the transform is
|
|
4616
|
+
* paint's (`placedMatrix`). */
|
|
4235
4617
|
function translationOf(box: Box, style = box.style): [number, number] {
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4618
|
+
if (!transformed(style)) return [0, 0];
|
|
4619
|
+
const m = matrixOf(style, box.width, box.height);
|
|
4620
|
+
return [m[4], m[5]];
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
/**
|
|
4624
|
+
* The matrix a box is painted through, in the document's coordinates: what
|
|
4625
|
+
* its transform does besides move it — a turn, a scale, a skew — about its
|
|
4626
|
+
* `transform-origin`, where layout has put it (`applyRelativeOffsets`). Null
|
|
4627
|
+
* for a box that only moves, which is every box but a few, and for an
|
|
4628
|
+
* inline box, which no transform applies to (CSS Transforms 1, 2: a
|
|
4629
|
+
* transformable element is no inline box).
|
|
4630
|
+
*/
|
|
4631
|
+
export function placedMatrix(box: Box): Matrix | null {
|
|
4632
|
+
const style = box.style;
|
|
4633
|
+
if (
|
|
4634
|
+
style.transform === null &&
|
|
4635
|
+
style.rotate === null &&
|
|
4636
|
+
style.scale === null
|
|
4637
|
+
) {
|
|
4638
|
+
return null;
|
|
4242
4639
|
}
|
|
4243
|
-
|
|
4640
|
+
if (box.kind === 'inline' || box.kind === 'text' || box.kind === 'break') {
|
|
4641
|
+
return null;
|
|
4642
|
+
}
|
|
4643
|
+
const linear = linearOf(style);
|
|
4644
|
+
if (!linear) return null;
|
|
4645
|
+
const origin = style.transformOrigin;
|
|
4646
|
+
return about(
|
|
4647
|
+
linear,
|
|
4648
|
+
box.x + resolve(origin[0], box.width, 0),
|
|
4649
|
+
box.y + resolve(origin[1], box.height, 0),
|
|
4650
|
+
);
|
|
4244
4651
|
}
|
|
4245
4652
|
|
|
4246
4653
|
/**
|