@react-x11/components 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +310 -12
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +1 -15
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/layout/block.d.ts +16 -5
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +26 -5
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +13 -0
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +18 -0
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +58 -10
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.js +19 -6
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +38 -16
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +1 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +57 -12
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +4 -1
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +269 -84
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +27 -10
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +259 -46
- package/dist/html/svg.js.map +1 -1
- package/package.json +1 -1
- package/src/html/css/cascade.ts +343 -8
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +1 -13
- package/src/html/layout/block.ts +27 -5
- package/src/html/layout/boxes.ts +30 -0
- package/src/html/layout/css-grid.ts +77 -13
- package/src/html/layout/flex.ts +22 -9
- package/src/html/layout/table.ts +45 -18
- package/src/html/node.ts +54 -11
- package/src/html/paint.ts +379 -90
- package/src/html/svg.ts +318 -39
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// What a style sheet says of the shapes in a drawing.
|
|
2
|
+
//
|
|
3
|
+
// An inline `<svg>` is a replaced box: the box builder stops at it, and no
|
|
4
|
+
// computed style is made for what is inside. `fill`, `stroke` and their kin
|
|
5
|
+
// are properties all the same (SVG 2, 6.2 and 13.2), which a rule of the
|
|
6
|
+
// document's sets on a `<path>` as it would on a paragraph — a logo's
|
|
7
|
+
// `.dark .logo path { fill: #fff }` — and a drawing exported with a style
|
|
8
|
+
// sheet of its own, `.st0 { fill: #fff }` on `<path class="st0">`, has all
|
|
9
|
+
// its colours in one. So the cascade is run for them narrowly
|
|
10
|
+
// (`Cascade.shapeStyles`): over the rules that declare one of the
|
|
11
|
+
// properties here, and down to values `SvgView` reads from a `style`
|
|
12
|
+
// attribute, which is where the drawing's copy of an element carries them
|
|
13
|
+
// (`svg.ts`). A `ComputedStyle` per `<path>` would be a few hundred
|
|
14
|
+
// properties for the dozen a shape has, on a page of hundreds of icons.
|
|
15
|
+
import type { Element } from 'domhandler';
|
|
16
|
+
import { LIGHT_DARK, lightDark } from './color.js';
|
|
17
|
+
import { inkColor, parseAlpha, parseColor, parseLength } from './values.js';
|
|
18
|
+
import type { UnitContext } from './values.js';
|
|
19
|
+
|
|
20
|
+
/** What the rules give one element of a drawing: each property they set,
|
|
21
|
+
* as `SvgView` reads it — `inherit` for one they put back to what the
|
|
22
|
+
* element inherits, over its own attribute. */
|
|
23
|
+
export type ShapeStyle = Readonly<Record<string, string>>;
|
|
24
|
+
|
|
25
|
+
/** What the rules give the elements of one drawing, and all of it as a
|
|
26
|
+
* string: what a drawing's tree was last made with (`SvgDrawing`). */
|
|
27
|
+
export interface ShapeStyles {
|
|
28
|
+
readonly of: ReadonlyMap<Element, ShapeStyle>;
|
|
29
|
+
readonly key: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Each property read, and whether it is inherited. */
|
|
33
|
+
const PROPS: Record<string, boolean> = {
|
|
34
|
+
fill: true,
|
|
35
|
+
stroke: true,
|
|
36
|
+
'stroke-width': true,
|
|
37
|
+
'stroke-linecap': true,
|
|
38
|
+
'stroke-linejoin': true,
|
|
39
|
+
'stroke-miterlimit': true,
|
|
40
|
+
'fill-rule': true,
|
|
41
|
+
'fill-opacity': true,
|
|
42
|
+
'stroke-opacity': true,
|
|
43
|
+
color: true,
|
|
44
|
+
visibility: true,
|
|
45
|
+
opacity: false,
|
|
46
|
+
display: false,
|
|
47
|
+
'stop-color': false,
|
|
48
|
+
'stop-opacity': false,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Whether a rule with this declaration is one to keep for drawings. */
|
|
52
|
+
export function isShapeProp(prop: string): boolean {
|
|
53
|
+
return PROPS[prop] !== undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The properties the drawing's box has from its computed style already:
|
|
57
|
+
* its paint, which `paintSvg` hands the drawing, and what makes it a box,
|
|
58
|
+
* shown and see-through as one. */
|
|
59
|
+
export const ROOT_BOX_PROPS: ReadonlySet<string> = new Set([
|
|
60
|
+
'fill',
|
|
61
|
+
'stroke',
|
|
62
|
+
'color',
|
|
63
|
+
'display',
|
|
64
|
+
'visibility',
|
|
65
|
+
'opacity',
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
/** Each at its initial value (SVG 2, 13; CSS Color 4; CSS Display 3). */
|
|
69
|
+
const INITIAL: Record<string, string> = {
|
|
70
|
+
fill: '#000000',
|
|
71
|
+
stroke: 'none',
|
|
72
|
+
'stroke-width': '1',
|
|
73
|
+
'stroke-linecap': 'butt',
|
|
74
|
+
'stroke-linejoin': 'miter',
|
|
75
|
+
'stroke-miterlimit': '4',
|
|
76
|
+
'fill-rule': 'nonzero',
|
|
77
|
+
'fill-opacity': '1',
|
|
78
|
+
'stroke-opacity': '1',
|
|
79
|
+
visibility: 'visible',
|
|
80
|
+
opacity: '1',
|
|
81
|
+
display: 'inline',
|
|
82
|
+
'stop-color': '#000000',
|
|
83
|
+
'stop-opacity': '1',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** The elements a rule's subject can be, where it names one by its type,
|
|
87
|
+
* for the rule to reach into a drawing: the ones `SvgView` draws, or
|
|
88
|
+
* reads a paint from. `a` is both languages'. */
|
|
89
|
+
export const SHAPE_TAGS: ReadonlySet<string> = new Set([
|
|
90
|
+
'svg',
|
|
91
|
+
'g',
|
|
92
|
+
'a',
|
|
93
|
+
'path',
|
|
94
|
+
'rect',
|
|
95
|
+
'circle',
|
|
96
|
+
'ellipse',
|
|
97
|
+
'line',
|
|
98
|
+
'polyline',
|
|
99
|
+
'polygon',
|
|
100
|
+
'text',
|
|
101
|
+
'tspan',
|
|
102
|
+
'use',
|
|
103
|
+
'symbol',
|
|
104
|
+
'switch',
|
|
105
|
+
'image',
|
|
106
|
+
'stop',
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
/** What a shape's values are read against: the drawing's own `color` and
|
|
110
|
+
* colour scheme, and its font size for a length in `em`. */
|
|
111
|
+
export interface ShapeContext {
|
|
112
|
+
color: string;
|
|
113
|
+
scheme: 'light' | 'dark';
|
|
114
|
+
units: UnitContext;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A `fill` or a `stroke` (SVG 2, 13.2): `none`, a colour, or a `url()`
|
|
119
|
+
* naming a gradient in the drawing, with what follows it, its fallback,
|
|
120
|
+
* left off. Null for anything else, `context-fill` and `context-stroke`
|
|
121
|
+
* among it, which only a marker or a `<use>` has a context for.
|
|
122
|
+
*/
|
|
123
|
+
export function svgPaint(value: string): string | null {
|
|
124
|
+
const v = value.trim();
|
|
125
|
+
if (v.toLowerCase() === 'none') return 'none';
|
|
126
|
+
const url = /^url\(\s*(['"]?)(#[^'")\s]+)\1\s*\)/i.exec(v);
|
|
127
|
+
if (url) return `url(${url[2]})`;
|
|
128
|
+
return parseColor(v);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const NUMBER = /^[+-]?(?:\d*\.\d+|\d+)(?:e[+-]?\d+)?$/i;
|
|
132
|
+
|
|
133
|
+
/** A word of a `display` that makes a box (CSS Display 3, 2). */
|
|
134
|
+
const DISPLAY =
|
|
135
|
+
/^(?:block|inline|run-in|flow|flow-root|table|flex|grid|ruby|list-item|contents|inline-[a-z]+|table-[a-z-]+|ruby-[a-z-]+|-webkit-[a-z-]+)$/;
|
|
136
|
+
|
|
137
|
+
const LINE_JOINS: Record<string, string> = {
|
|
138
|
+
miter: 'miter',
|
|
139
|
+
round: 'round',
|
|
140
|
+
bevel: 'bevel',
|
|
141
|
+
// the two a canvas has no join for end in a miter, clipped or not
|
|
142
|
+
'miter-clip': 'miter',
|
|
143
|
+
arcs: 'miter',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* One declaration's value as a drawing reads it, or null for a value the
|
|
148
|
+
* property does not take, which leaves the declaration out of the cascade
|
|
149
|
+
* as any invalid one is left. `inherit` for a CSS-wide keyword that puts an
|
|
150
|
+
* inherited property back to what the element inherits.
|
|
151
|
+
*/
|
|
152
|
+
export function shapeValue(
|
|
153
|
+
prop: string,
|
|
154
|
+
raw: string,
|
|
155
|
+
ctx: ShapeContext,
|
|
156
|
+
): string | null {
|
|
157
|
+
let value = raw.trim();
|
|
158
|
+
const wide = value.toLowerCase();
|
|
159
|
+
if (wide === 'inherit') return PROPS[prop] ? 'inherit' : null;
|
|
160
|
+
if (wide === 'initial') {
|
|
161
|
+
return prop === 'color'
|
|
162
|
+
? (ctx.units.initial?.color ?? '#000000')
|
|
163
|
+
: INITIAL[prop];
|
|
164
|
+
}
|
|
165
|
+
if (wide === 'unset' || wide === 'revert' || wide === 'revert-layer') {
|
|
166
|
+
return PROPS[prop] ? 'inherit' : INITIAL[prop];
|
|
167
|
+
}
|
|
168
|
+
if (LIGHT_DARK.test(value)) {
|
|
169
|
+
const picked = lightDark(value, ctx.scheme);
|
|
170
|
+
if (picked === null) return null;
|
|
171
|
+
value = picked;
|
|
172
|
+
}
|
|
173
|
+
switch (prop) {
|
|
174
|
+
case 'fill':
|
|
175
|
+
case 'stroke': {
|
|
176
|
+
const paint = svgPaint(value);
|
|
177
|
+
// `currentColor` is the shape's own `color`, which the drawing knows
|
|
178
|
+
return paint === null || paint === 'currentColor'
|
|
179
|
+
? paint
|
|
180
|
+
: inkColor(paint, ctx.color);
|
|
181
|
+
}
|
|
182
|
+
case 'color':
|
|
183
|
+
case 'stop-color': {
|
|
184
|
+
const color = parseColor(value);
|
|
185
|
+
return color === null ? null : inkColor(color, ctx.color);
|
|
186
|
+
}
|
|
187
|
+
case 'stroke-width': {
|
|
188
|
+
// a number is user units, as a length in pixels is; a percentage is
|
|
189
|
+
// of the viewport's diagonal, which only the drawing has
|
|
190
|
+
const px = NUMBER.test(value)
|
|
191
|
+
? Number(value)
|
|
192
|
+
: lengthIn(value, ctx.units);
|
|
193
|
+
return px !== null && px >= 0 ? String(px) : null;
|
|
194
|
+
}
|
|
195
|
+
case 'stroke-miterlimit': {
|
|
196
|
+
const n = NUMBER.test(value) ? Number(value) : NaN;
|
|
197
|
+
return n >= 1 ? String(n) : null;
|
|
198
|
+
}
|
|
199
|
+
case 'fill-opacity':
|
|
200
|
+
case 'stroke-opacity':
|
|
201
|
+
case 'stop-opacity':
|
|
202
|
+
case 'opacity': {
|
|
203
|
+
const a = parseAlpha(value);
|
|
204
|
+
return a === null ? null : String(a);
|
|
205
|
+
}
|
|
206
|
+
case 'stroke-linecap': {
|
|
207
|
+
const v = value.toLowerCase();
|
|
208
|
+
return v === 'butt' || v === 'round' || v === 'square' ? v : null;
|
|
209
|
+
}
|
|
210
|
+
case 'stroke-linejoin':
|
|
211
|
+
return LINE_JOINS[value.toLowerCase()] ?? null;
|
|
212
|
+
case 'fill-rule': {
|
|
213
|
+
const v = value.toLowerCase();
|
|
214
|
+
return v === 'nonzero' || v === 'evenodd' ? v : null;
|
|
215
|
+
}
|
|
216
|
+
case 'visibility': {
|
|
217
|
+
const v = value.toLowerCase();
|
|
218
|
+
if (v === 'visible') return v;
|
|
219
|
+
return v === 'hidden' || v === 'collapse' ? 'hidden' : null;
|
|
220
|
+
}
|
|
221
|
+
case 'display': {
|
|
222
|
+
// `none`, or some box: which one is nothing to a shape
|
|
223
|
+
const v = value.toLowerCase();
|
|
224
|
+
if (v === 'none') return v;
|
|
225
|
+
return v.split(/\s+/).every((word) => DISPLAY.test(word))
|
|
226
|
+
? 'inline'
|
|
227
|
+
: null;
|
|
228
|
+
}
|
|
229
|
+
default:
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** A length in user units, which are CSS pixels; null for none, and for a
|
|
235
|
+
* percentage. */
|
|
236
|
+
function lengthIn(value: string, units: UnitContext): number | null {
|
|
237
|
+
const length = parseLength(value, units);
|
|
238
|
+
return typeof length === 'number' ? length / units.scale : null;
|
|
239
|
+
}
|
package/src/html/css/style.ts
CHANGED
|
@@ -43,6 +43,7 @@ import type { CustomProps } from './vars.js';
|
|
|
43
43
|
import { parseRotate, parseScale, parseTransform } from './transform.js';
|
|
44
44
|
import type { TransformFunction } from './transform.js';
|
|
45
45
|
import { LIGHT_DARK, lightDark, usedColorScheme } from './color.js';
|
|
46
|
+
import { svgPaint } from './shapes.js';
|
|
46
47
|
|
|
47
48
|
export type Display =
|
|
48
49
|
| 'none'
|
|
@@ -5154,19 +5155,6 @@ const POINTER_EVENTS = new Set([
|
|
|
5154
5155
|
'stroke',
|
|
5155
5156
|
'all',
|
|
5156
5157
|
]);
|
|
5157
|
-
/**
|
|
5158
|
-
* A `fill` or a `stroke` (SVG 2, 13.2): `none`, a colour, or a `url()`
|
|
5159
|
-
* naming a gradient in the drawing, with what follows it, its fallback,
|
|
5160
|
-
* left off. Null for anything else, `context-fill` and `context-stroke`
|
|
5161
|
-
* among it, which only a marker or a `<use>` has a context for.
|
|
5162
|
-
*/
|
|
5163
|
-
function svgPaint(value: string): string | null {
|
|
5164
|
-
const v = value.trim();
|
|
5165
|
-
if (v.toLowerCase() === 'none') return 'none';
|
|
5166
|
-
const url = /^url\(\s*(['"]?)(#[^'")\s]+)\1\s*\)/i.exec(v);
|
|
5167
|
-
if (url) return `url(${url[2]})`;
|
|
5168
|
-
return parseColor(v);
|
|
5169
|
-
}
|
|
5170
5158
|
|
|
5171
5159
|
const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
|
|
5172
5160
|
|
package/src/html/layout/block.ts
CHANGED
|
@@ -2324,8 +2324,9 @@ function stretchHeight(box: Box): number | null {
|
|
|
2324
2324
|
* stretched item's, a flexed one's in a container of a definite height (CSS
|
|
2325
2325
|
* Flexbox 9.8), a grid item's stretched down its area (CSS Grid 1, 11.1) —
|
|
2326
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
|
|
2328
|
-
* `flex.ts` and `css-grid.ts` for the
|
|
2327
|
+
* a flex or grid container itself lays its own items out in, and a table
|
|
2328
|
+
* shares out among its rows. Set by `flex.ts` and `css-grid.ts` for the
|
|
2329
|
+
* item's own layout alone.
|
|
2329
2330
|
*/
|
|
2330
2331
|
export const FLEXED_HEIGHT = new WeakMap<Box, number>();
|
|
2331
2332
|
|
|
@@ -2333,12 +2334,23 @@ export const FLEXED_HEIGHT = new WeakMap<Box, number>();
|
|
|
2333
2334
|
* The content height a flex layout gave an item without making it definite:
|
|
2334
2335
|
* flexed along a column that has no height of its own, which 9.8 leaves
|
|
2335
2336
|
* 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) —
|
|
2337
|
-
* takes a percentage of it. Set by
|
|
2338
|
-
* alone.
|
|
2337
|
+
* out in it all the same — it is its used size (9.7) — as a table shares
|
|
2338
|
+
* it among its rows, and nothing in it takes a percentage of it. Set by
|
|
2339
|
+
* `flex.ts` for the item's own layout alone.
|
|
2339
2340
|
*/
|
|
2340
2341
|
export const USED_HEIGHT = new WeakMap<Box, number>();
|
|
2341
2342
|
|
|
2343
|
+
/**
|
|
2344
|
+
* A table a grid stretches across its area, whose `auto` width is then the
|
|
2345
|
+
* one it is laid out at. `normal` stretches any grid item but a replaced
|
|
2346
|
+
* one (CSS Grid 1, 6.2), a table among them, where in a block's flow a
|
|
2347
|
+
* table's `auto` width is its columns' (CSS 2.1 17.5.2; Blink leaves tables
|
|
2348
|
+
* out of the boxes a block stretches, `space_utils.cc`, and stretches them
|
|
2349
|
+
* in a grid, `grid_item.cc`). Set by `css-grid.ts` for the item's own
|
|
2350
|
+
* layout alone, and never while its widths are measured.
|
|
2351
|
+
*/
|
|
2352
|
+
export const STRETCHED_ACROSS = new WeakSet<Box>();
|
|
2353
|
+
|
|
2342
2354
|
/**
|
|
2343
2355
|
* Hand a box's children the height their percentages resolve against —
|
|
2344
2356
|
* through inline boxes, which are no containing block, to the inline-block
|
|
@@ -3167,6 +3179,16 @@ export function intrinsicWidth(box: Box, seen?: { cut: boolean }): number {
|
|
|
3167
3179
|
if (contained(box, CONTAIN_WIDTH)) {
|
|
3168
3180
|
return box.style.containIntrinsicWidth ?? 0;
|
|
3169
3181
|
}
|
|
3182
|
+
// a table is as wide as its layout made it: its columns, the spacing
|
|
3183
|
+
// either side of them and between, and its captions' least (`layoutTable`)
|
|
3184
|
+
// — at no width limit its widest, and at none its narrowest. What it
|
|
3185
|
+
// holds says less: its rows run from the first column to the last, the
|
|
3186
|
+
// spacing either side outside them, and measured by them a table in a
|
|
3187
|
+
// flex row, a grid, a float or an `inline-table` came to that much too
|
|
3188
|
+
// narrow, and its columns gave it up
|
|
3189
|
+
if (box.kind === 'table' && Number.isFinite(box.width)) {
|
|
3190
|
+
return Math.max(0, box.width - box.horizontalExtra);
|
|
3191
|
+
}
|
|
3170
3192
|
// a grid laid out at no width limit has its columns at their widest, and
|
|
3171
3193
|
// is as wide as they are — an item that runs past its column makes it no
|
|
3172
3194
|
// wider — or, where it has none, as where its items end
|
package/src/html/layout/boxes.ts
CHANGED
|
@@ -38,6 +38,7 @@ import type { CollapsedTable } from './collapse.js';
|
|
|
38
38
|
import { quoteAt } from '../css/content.js';
|
|
39
39
|
import type { CounterStyles } from '../css/counter-styles.js';
|
|
40
40
|
import type { ContentItem } from '../css/content.js';
|
|
41
|
+
import type { ShapeStyles } from '../css/shapes.js';
|
|
41
42
|
import {
|
|
42
43
|
CONTAIN_STYLE,
|
|
43
44
|
FIRST_LINE_INHERITED,
|
|
@@ -713,6 +714,15 @@ export const COLUMN_ROWS = new WeakMap<
|
|
|
713
714
|
* that would ask the three maps above of every box ask first. */
|
|
714
715
|
export const columned = { any: false };
|
|
715
716
|
|
|
717
|
+
/** What the document's rules give the elements inside an inline `<svg>`
|
|
718
|
+
* (`Cascade.shapeStyles`), null where they give none. */
|
|
719
|
+
export type ShapeStyler = (box: Box) => ShapeStyles | null;
|
|
720
|
+
|
|
721
|
+
/** What `BoxTree.shapeStyler` answered for each drawing's box it has been
|
|
722
|
+
* asked of, which are the ones painted: a box restyled where it is has
|
|
723
|
+
* its answer put here again (`HtmlViewNode._hoverInPlace`). */
|
|
724
|
+
export const SHAPE_STYLES = new WeakMap<Box, ShapeStyles | null>();
|
|
725
|
+
|
|
716
726
|
/** The element a pseudo-element's box is generated from, which has no
|
|
717
727
|
* element of its own (`Box.pseudo`): whose the images its styles name are,
|
|
718
728
|
* when the host is asked for them. */
|
|
@@ -774,6 +784,11 @@ export interface BoxTree {
|
|
|
774
784
|
* one: worked out as a selection is first painted over it, not as the
|
|
775
785
|
* tree is built, since most builds are never selected in. */
|
|
776
786
|
selectionStyler: SelectionStyler | null;
|
|
787
|
+
/** What the rules give the elements in each inline `<svg>`
|
|
788
|
+
* (`ShapeStyler`), which have no boxes and no styles, where any rule
|
|
789
|
+
* could reach one: worked out as a drawing is first painted, since a
|
|
790
|
+
* page of icons shows few of them at once. */
|
|
791
|
+
shapeStyler: ShapeStyler | null;
|
|
777
792
|
/** Whether a float or an out-of-flow box sits in an inline box: where
|
|
778
793
|
* none does, layout looks for them among a block's own children and
|
|
779
794
|
* goes through no inline box to find them. */
|
|
@@ -939,6 +954,7 @@ class Builder {
|
|
|
939
954
|
firstLine: this._firstLine,
|
|
940
955
|
firstLineStyler: this._firstLine ? this._firstLineStyler() : null,
|
|
941
956
|
selectionStyler: cascade.hasSelection ? this._selectionStyler() : null,
|
|
957
|
+
shapeStyler: cascade.stylesShapes ? this._shapeStyler() : null,
|
|
942
958
|
nestedOutOfLine: this._nestedOutOfLine,
|
|
943
959
|
movedInline: this._movedInline,
|
|
944
960
|
clipText: this._clipText,
|
|
@@ -971,6 +987,20 @@ class Builder {
|
|
|
971
987
|
};
|
|
972
988
|
}
|
|
973
989
|
|
|
990
|
+
/** `BoxTree.shapeStyler`, over this build's cascade: each drawing's
|
|
991
|
+
* once, from its box's style as it is when first asked. */
|
|
992
|
+
private _shapeStyler(): ShapeStyler {
|
|
993
|
+
const cascade = this._options.cascade;
|
|
994
|
+
return (box) => {
|
|
995
|
+
let shapes = SHAPE_STYLES.get(box);
|
|
996
|
+
if (shapes === undefined) {
|
|
997
|
+
shapes = box.el ? cascade.shapeStyles(box.el, box.style) : null;
|
|
998
|
+
SHAPE_STYLES.set(box, shapes);
|
|
999
|
+
}
|
|
1000
|
+
return shapes;
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
|
|
974
1004
|
/** `BoxTree.firstLineStyler`, over this build's cascade. */
|
|
975
1005
|
private _firstLineStyler(): FirstLineStyler {
|
|
976
1006
|
const cascade = this._options.cascade;
|
|
@@ -31,6 +31,7 @@ import { Box, GRID_TRACKS, isBlank } from './boxes.js';
|
|
|
31
31
|
import {
|
|
32
32
|
FLEXED_HEIGHT,
|
|
33
33
|
MIN_CONTENT_PROBE,
|
|
34
|
+
STRETCHED_ACROSS,
|
|
34
35
|
USED_HEIGHT,
|
|
35
36
|
centreButton,
|
|
36
37
|
clampHeight,
|
|
@@ -60,6 +61,9 @@ interface Item {
|
|
|
60
61
|
/** Min-content and max-content widths, margin box, taken when asked. */
|
|
61
62
|
min: number;
|
|
62
63
|
max: number;
|
|
64
|
+
/** Whether it is a table stretched across its area (`STRETCHED_ACROSS`),
|
|
65
|
+
* which `layoutItem` decides. */
|
|
66
|
+
across: boolean;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/**
|
|
@@ -203,6 +207,11 @@ export function layoutGrid(
|
|
|
203
207
|
const extra = style.boxSizing === 'border-box' ? 0 : box.horizontalExtra;
|
|
204
208
|
widest = narrowest = Math.max(style.width + extra, box.horizontalExtra);
|
|
205
209
|
} else {
|
|
210
|
+
// A percentage is of an area the columns are still being sized for,
|
|
211
|
+
// and what it comes to depends on what the item makes them: it is
|
|
212
|
+
// `auto` for that (CSS Sizing 3, 5.2.1), and the item's content says
|
|
213
|
+
// how wide it is. Measured at the width a percentage made of the
|
|
214
|
+
// probe's, an item `width: 100%` made its `auto` column nothing.
|
|
206
215
|
const fresh = box.intrinsicMaxContent < 0;
|
|
207
216
|
widest = maxContentOf(box, ctx);
|
|
208
217
|
if (box.intrinsicMinContent < 0) {
|
|
@@ -210,8 +219,7 @@ export function layoutGrid(
|
|
|
210
219
|
// which is the max-content one only if it was made just now
|
|
211
220
|
const exact =
|
|
212
221
|
fresh && ctx.fonts ? exactMinContent(box, ctx.fonts) : null;
|
|
213
|
-
box.intrinsicMinContent =
|
|
214
|
-
exact ?? measureIntrinsicWidth(box, ctx, MIN_CONTENT_PROBE);
|
|
222
|
+
box.intrinsicMinContent = exact ?? minContentOf(box, ctx);
|
|
215
223
|
}
|
|
216
224
|
narrowest = box.intrinsicMinContent;
|
|
217
225
|
}
|
|
@@ -401,21 +409,27 @@ export function layoutGrid(
|
|
|
401
409
|
// purpose"): a card's `margin-top: auto` takes what its row is taller
|
|
402
410
|
// than the card by, its `flex: 1` grows into it (CSS Flexbox 8.1,
|
|
403
411
|
// 9.7). Its content's own height is the same layout, and kept.
|
|
412
|
+
// A table shares out among its rows what the area is taller than
|
|
413
|
+
// they are (`layoutTable`), and is no shorter than they are however
|
|
414
|
+
// short its area: its rows are the least it can be, as they are
|
|
415
|
+
// where it has a height of its own (CSS 2.1 17.5.3).
|
|
404
416
|
if (
|
|
405
417
|
child.kind !== 'replaced' &&
|
|
406
418
|
(wide !== null ||
|
|
407
419
|
percentHeightsIn(child) ||
|
|
408
|
-
(child.kind === 'flex' && height !== child.height)
|
|
420
|
+
(child.kind === 'flex' && height !== child.height) ||
|
|
421
|
+
(child.kind === 'table' && height > child.height + 0.01))
|
|
409
422
|
) {
|
|
410
423
|
FLEXED_HEIGHT.set(child, Math.max(0, height - child.verticalExtra));
|
|
411
424
|
try {
|
|
412
|
-
|
|
425
|
+
layoutAcross(item, width, area, ctx);
|
|
413
426
|
} finally {
|
|
414
427
|
FLEXED_HEIGHT.delete(child);
|
|
415
428
|
}
|
|
416
429
|
}
|
|
417
430
|
child.width = width;
|
|
418
|
-
child.height =
|
|
431
|
+
child.height =
|
|
432
|
+
child.kind === 'table' ? Math.max(height, child.height) : height;
|
|
419
433
|
centreButton(child);
|
|
420
434
|
}
|
|
421
435
|
moveTo(
|
|
@@ -596,6 +610,7 @@ function place(
|
|
|
596
610
|
cols: p.bs,
|
|
597
611
|
min: -1,
|
|
598
612
|
max: -1,
|
|
613
|
+
across: false,
|
|
599
614
|
}
|
|
600
615
|
: {
|
|
601
616
|
box: p.box,
|
|
@@ -605,6 +620,7 @@ function place(
|
|
|
605
620
|
cols: p.as,
|
|
606
621
|
min: -1,
|
|
607
622
|
max: -1,
|
|
623
|
+
across: false,
|
|
608
624
|
},
|
|
609
625
|
);
|
|
610
626
|
}
|
|
@@ -641,14 +657,37 @@ function extent(
|
|
|
641
657
|
}
|
|
642
658
|
|
|
643
659
|
/** An item's max-content width, its border box's: measured once in the
|
|
644
|
-
* box's life, as a table cell's is (`Box.intrinsicMaxContent`).
|
|
660
|
+
* box's life, as a table cell's is (`Box.intrinsicMaxContent`). Its
|
|
661
|
+
* content's, whatever width it has of its own (`minContentOf`). */
|
|
645
662
|
function maxContentOf(box: Box, ctx: LayoutContext): number {
|
|
646
663
|
if (box.intrinsicMaxContent < 0) {
|
|
647
|
-
box.intrinsicMaxContent = measureIntrinsicWidth(
|
|
664
|
+
box.intrinsicMaxContent = measureIntrinsicWidth(
|
|
665
|
+
box,
|
|
666
|
+
ctx,
|
|
667
|
+
Infinity,
|
|
668
|
+
box.kind !== 'replaced',
|
|
669
|
+
);
|
|
648
670
|
}
|
|
649
671
|
return box.intrinsicMaxContent;
|
|
650
672
|
}
|
|
651
673
|
|
|
674
|
+
/**
|
|
675
|
+
* An item's min-content width, its border box's. What it is measured for is
|
|
676
|
+
* what it contributes to its columns, and the width a percentage makes of
|
|
677
|
+
* them is not known then: a box that is not replaced is as wide as its
|
|
678
|
+
* content for that, whatever its own `width` says, and a replaced element
|
|
679
|
+
* takes its percentage of nothing (CSS Sizing 3, 5.2.1). A width that is a
|
|
680
|
+
* length is weighed apart (`measure` in `layoutGrid`).
|
|
681
|
+
*/
|
|
682
|
+
function minContentOf(box: Box, ctx: LayoutContext): number {
|
|
683
|
+
return measureIntrinsicWidth(
|
|
684
|
+
box,
|
|
685
|
+
ctx,
|
|
686
|
+
MIN_CONTENT_PROBE,
|
|
687
|
+
box.kind !== 'replaced',
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
652
691
|
/**
|
|
653
692
|
* Lay an item out in its area: stretched across it where its width is
|
|
654
693
|
* `auto` and it is stretched, or at its content's width, no wider than
|
|
@@ -693,11 +732,7 @@ function layoutItem(
|
|
|
693
732
|
probed = true;
|
|
694
733
|
}
|
|
695
734
|
if (child.intrinsicMinContent < 0) {
|
|
696
|
-
child.intrinsicMinContent =
|
|
697
|
-
child,
|
|
698
|
-
ctx,
|
|
699
|
-
MIN_CONTENT_PROBE,
|
|
700
|
-
);
|
|
735
|
+
child.intrinsicMinContent = minContentOf(child, ctx);
|
|
701
736
|
probed = true;
|
|
702
737
|
}
|
|
703
738
|
if (probed) resolveEdges(child, area);
|
|
@@ -757,7 +792,15 @@ function layoutItem(
|
|
|
757
792
|
width = Math.min(widest, Math.max(narrowest, room));
|
|
758
793
|
}
|
|
759
794
|
if (child.kind !== 'replaced') {
|
|
760
|
-
|
|
795
|
+
// a table stretched across is as wide as its area, which in a block's
|
|
796
|
+
// flow it would shrink from to its columns (`STRETCHED_ACROSS`)
|
|
797
|
+
item.across =
|
|
798
|
+
across &&
|
|
799
|
+
child.kind === 'table' &&
|
|
800
|
+
style.width === AUTO &&
|
|
801
|
+
!keyword &&
|
|
802
|
+
through === null;
|
|
803
|
+
layoutAcross(item, width, area, ctx);
|
|
761
804
|
return;
|
|
762
805
|
}
|
|
763
806
|
// a replaced element sizes itself from its style: then it is given the
|
|
@@ -776,6 +819,27 @@ function layoutItem(
|
|
|
776
819
|
}
|
|
777
820
|
}
|
|
778
821
|
|
|
822
|
+
/** Lay an item out at a width: a table stretched across its area at that
|
|
823
|
+
* width as its own (`STRETCHED_ACROSS`), for this layout alone. */
|
|
824
|
+
function layoutAcross(
|
|
825
|
+
item: Item,
|
|
826
|
+
width: number,
|
|
827
|
+
area: number,
|
|
828
|
+
ctx: LayoutContext,
|
|
829
|
+
): void {
|
|
830
|
+
const child = item.box;
|
|
831
|
+
if (!item.across) {
|
|
832
|
+
ctx.layoutSubtree(child, width, area);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
STRETCHED_ACROSS.add(child);
|
|
836
|
+
try {
|
|
837
|
+
ctx.layoutSubtree(child, width, area);
|
|
838
|
+
} finally {
|
|
839
|
+
STRETCHED_ACROSS.delete(child);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
779
843
|
/** What the percentages in a box's padding come to across it, its edges
|
|
780
844
|
* resolved: what a width of its content's, measured with them of nothing,
|
|
781
845
|
* is short of its border box by. */
|
package/src/html/layout/flex.ts
CHANGED
|
@@ -590,14 +590,19 @@ function layoutItemAt(
|
|
|
590
590
|
// size all the same (9.7), and `min-h-screen flex flex-col` around a
|
|
591
591
|
// `flex-1 flex items-center` centres what is in that. At its content's
|
|
592
592
|
// own height there is nothing to share, and the layout it has is that
|
|
593
|
-
// one: the tallest card of a row is laid out the once.
|
|
593
|
+
// one: the tallest card of a row is laid out the once. A table shares
|
|
594
|
+
// what it is given past its rows among them (`layoutTable`), and a
|
|
595
|
+
// height short of them is none it can have: it is laid out again where
|
|
596
|
+
// it was given more.
|
|
594
597
|
const given = definite ?? (column ? height : null);
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
598
|
+
let shares = false;
|
|
599
|
+
if (given !== null && (box.kind === 'flex' || box.kind === 'table')) {
|
|
600
|
+
const natural = naturalHeight(box, ctx, width, containing, laid);
|
|
601
|
+
shares =
|
|
602
|
+
box.kind === 'flex'
|
|
603
|
+
? !(Math.abs(natural - given) <= 0.01)
|
|
604
|
+
: !(natural >= given - 0.01);
|
|
605
|
+
}
|
|
601
606
|
if (given !== null) {
|
|
602
607
|
const inner = Math.max(0, given - box.verticalExtra);
|
|
603
608
|
if (Object.is(inner, percentBaseInside(box))) {
|
|
@@ -630,8 +635,16 @@ function layoutItemAt(
|
|
|
630
635
|
// A stretched item is taller than its content, and the box has to say so
|
|
631
636
|
// or its background stops short of the row — and it is no taller than its
|
|
632
637
|
// line where its content is, or than what its ratio makes of its width:
|
|
633
|
-
// its height is the line's (CSS Flexbox 9.4, step 11).
|
|
634
|
-
|
|
638
|
+
// its height is the line's (CSS Flexbox 9.4, step 11). But for a table,
|
|
639
|
+
// whose rows are the least it can be, however short its line (CSS 2.1
|
|
640
|
+
// 17.5.3, as a height of its own is; Blink: "Tables can't shrink below
|
|
641
|
+
// their min-intrinsic size").
|
|
642
|
+
if (
|
|
643
|
+
height > box.height ||
|
|
644
|
+
((column || definite !== null) && box.kind !== 'table')
|
|
645
|
+
) {
|
|
646
|
+
box.height = height;
|
|
647
|
+
}
|
|
635
648
|
// and a button's content is centred in the height it came to
|
|
636
649
|
centreButton(box);
|
|
637
650
|
moveTo(box, x, y);
|