@react-x11/components 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html/controls.d.ts +29 -2
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +70 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +336 -16
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +18 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +27 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +18 -4
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +66 -28
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +547 -144
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/transform.d.ts +85 -0
- package/dist/html/css/transform.d.ts.map +1 -0
- package/dist/html/css/transform.js +425 -0
- package/dist/html/css/transform.js.map +1 -0
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +14 -2
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +4 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/fonts.d.ts +55 -5
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +284 -23
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +104 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +410 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +72 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +40 -2
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +144 -24
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +585 -85
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +3 -0
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +144 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +97 -31
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +4 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +319 -47
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +83 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1474 -217
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +39 -9
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +482 -37
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.js +87 -7
- package/dist/html/widgets.js.map +1 -1
- package/dist/html/woff2.d.ts +14 -0
- package/dist/html/woff2.d.ts.map +1 -0
- package/dist/html/woff2.js +608 -0
- package/dist/html/woff2.js.map +1 -0
- package/dist/maps/controller.d.ts +17 -1
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +27 -7
- package/dist/maps/controller.js.map +1 -1
- package/dist/richtext/node.d.ts +8 -1
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +5 -1
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +25 -6
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +240 -24
- package/dist/richtext/runs.js.map +1 -1
- package/package.json +1 -1
- package/src/html/controls.ts +85 -2
- package/src/html/css/cascade.ts +366 -8
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +652 -147
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +15 -2
- package/src/html/css/values.ts +4 -0
- package/src/html/fonts.ts +312 -29
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +466 -37
- package/src/html/layout/boxes.ts +95 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +169 -32
- package/src/html/layout/flex.ts +686 -79
- package/src/html/layout/inline.ts +149 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +105 -35
- package/src/html/node.ts +354 -48
- package/src/html/paint.ts +1804 -315
- package/src/html/svg.ts +573 -36
- package/src/html/widgets.ts +95 -9
- package/src/html/woff2.ts +612 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
package/src/html/css/style.ts
CHANGED
|
@@ -40,7 +40,10 @@ import {
|
|
|
40
40
|
} from './content.js';
|
|
41
41
|
import type { ContentItem, CounterChange } from './content.js';
|
|
42
42
|
import type { CustomProps } from './vars.js';
|
|
43
|
+
import { parseRotate, parseScale, parseTransform } from './transform.js';
|
|
44
|
+
import type { TransformFunction } from './transform.js';
|
|
43
45
|
import { LIGHT_DARK, lightDark, usedColorScheme } from './color.js';
|
|
46
|
+
import { svgPaint } from './shapes.js';
|
|
44
47
|
|
|
45
48
|
export type Display =
|
|
46
49
|
| 'none'
|
|
@@ -108,15 +111,43 @@ export interface BoxShadow {
|
|
|
108
111
|
inset: boolean;
|
|
109
112
|
}
|
|
110
113
|
|
|
114
|
+
/** A gradient's colour stop: its colour, at a position along the gradient
|
|
115
|
+
* or, with none, where its neighbours put it. */
|
|
116
|
+
export interface GradientStop {
|
|
117
|
+
color: string;
|
|
118
|
+
at: Len | null;
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
/** A `linear-gradient()` (CSS Images 3, 3.1): its direction, an angle
|
|
112
122
|
* clockwise from up or a corner the box's shape turns into one, and its
|
|
113
|
-
* colour stops
|
|
123
|
+
* colour stops. */
|
|
114
124
|
export interface LinearGradient {
|
|
125
|
+
kind: 'linear';
|
|
115
126
|
angle: number;
|
|
116
127
|
corner: 'top left' | 'top right' | 'bottom left' | 'bottom right' | null;
|
|
117
|
-
stops:
|
|
128
|
+
stops: GradientStop[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** A `radial-gradient()` (CSS Images 3, 3.2): its ending shape, a circle or
|
|
132
|
+
* an ellipse; that shape's size, a keyword for the side or the corner of
|
|
133
|
+
* the box it reaches or the radii it was given, one for a circle and two
|
|
134
|
+
* for an ellipse; its centre, a position in the box; and its colour stops,
|
|
135
|
+
* along the ray from the centre to the shape's edge. */
|
|
136
|
+
export interface RadialGradient {
|
|
137
|
+
kind: 'radial';
|
|
138
|
+
circle: boolean;
|
|
139
|
+
extent: RadialExtent | null;
|
|
140
|
+
radii: [Len, Len] | null;
|
|
141
|
+
at: [Len, Len];
|
|
142
|
+
stops: GradientStop[];
|
|
118
143
|
}
|
|
119
144
|
|
|
145
|
+
export type RadialExtent =
|
|
146
|
+
'closest-side' | 'closest-corner' | 'farthest-side' | 'farthest-corner';
|
|
147
|
+
|
|
148
|
+
/** An image a gradient function draws. */
|
|
149
|
+
export type Gradient = LinearGradient | RadialGradient;
|
|
150
|
+
|
|
120
151
|
/** Where a grid item starts or ends (CSS Grid 1, 8.3): a line, counted
|
|
121
152
|
* among the lines of a name where it has one; a span, to a line of a name
|
|
122
153
|
* where it has one; a name alone, an area's edge or a line's; or auto. */
|
|
@@ -174,6 +205,121 @@ function parseClip(
|
|
|
174
205
|
return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
|
|
175
206
|
}
|
|
176
207
|
|
|
208
|
+
/** The box a `clip-path`'s shape is measured in, a `<geometry-box>` (CSS
|
|
209
|
+
* Masking 1, 5.1). */
|
|
210
|
+
export type ClipBox =
|
|
211
|
+
'margin-box' | 'border-box' | 'padding-box' | 'content-box';
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* `clip-path` (CSS Masking 1, 5.1): a rectangle in a box of the element's,
|
|
215
|
+
* its corners rounded or not — a `<basic-shape-rect>` (CSS Shapes 1, 3.1),
|
|
216
|
+
* kept as the function it was written as, since each measures its four
|
|
217
|
+
* lengths its own way and a percentage among them has no pixels before
|
|
218
|
+
* layout. The element shows through it, and everything in it does.
|
|
219
|
+
*/
|
|
220
|
+
export interface ClipPath {
|
|
221
|
+
box: ClipBox;
|
|
222
|
+
/** What the four lengths are: `inset()`'s, how far in from the box's
|
|
223
|
+
* top, right, bottom and left; `rect()`'s, those four edges from its top
|
|
224
|
+
* and its left, `auto` the box's own; `xywh()`'s, the rectangle's left
|
|
225
|
+
* and top from the box's, and its width and height. Null for a box
|
|
226
|
+
* named alone, which is the shape: the whole of it, rounded as the
|
|
227
|
+
* element's corners are. */
|
|
228
|
+
shape: 'inset' | 'rect' | 'xywh' | null;
|
|
229
|
+
lengths: readonly [Len, Len, Len, Len];
|
|
230
|
+
/** `round`: the corners' radii across, and down where they differ, as
|
|
231
|
+
* `border-radius` has them — a percentage is of the box, and not of the
|
|
232
|
+
* rectangle in it. Null for square corners. */
|
|
233
|
+
radii: readonly [Len, Len, Len, Len] | null;
|
|
234
|
+
radiiY: readonly [Len, Len, Len, Len] | null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** The boxes `clip-path` names; an SVG one is the CSS box that stands for
|
|
238
|
+
* it on an element with a CSS layout box (5.1). */
|
|
239
|
+
const CLIP_BOXES: Record<string, ClipBox> = {
|
|
240
|
+
'margin-box': 'margin-box',
|
|
241
|
+
'border-box': 'border-box',
|
|
242
|
+
'padding-box': 'padding-box',
|
|
243
|
+
'content-box': 'content-box',
|
|
244
|
+
'fill-box': 'content-box',
|
|
245
|
+
'stroke-box': 'border-box',
|
|
246
|
+
'view-box': 'border-box',
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/** The shapes that are no rectangle, and a reference to a `<clipPath>`
|
|
250
|
+
* element: a value, which sets the property, and none this draws. */
|
|
251
|
+
const UNDRAWN_CLIPS = /^(circle|ellipse|polygon|path|shape|url)\(/i;
|
|
252
|
+
|
|
253
|
+
const NO_LENGTHS = [0, 0, 0, 0] as const;
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* `none`; a rectangle's function, a box, or the two in either order; or a
|
|
257
|
+
* shape that is no rectangle, which is a value and clips nothing here.
|
|
258
|
+
* Undefined for anything else, which leaves the value as it was.
|
|
259
|
+
*/
|
|
260
|
+
function parseClipPath(
|
|
261
|
+
value: string,
|
|
262
|
+
ctx: UnitContext,
|
|
263
|
+
): ClipPath | null | undefined {
|
|
264
|
+
const v = value.trim();
|
|
265
|
+
if (/^none$/i.test(v)) return null;
|
|
266
|
+
let box: ClipBox | null = null;
|
|
267
|
+
let shape: Omit<ClipPath, 'box'> | null | undefined;
|
|
268
|
+
for (const part of splitValue(v)) {
|
|
269
|
+
const named = CLIP_BOXES[part.toLowerCase()];
|
|
270
|
+
if (named) {
|
|
271
|
+
if (box) return undefined;
|
|
272
|
+
box = named;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (shape !== undefined) return undefined;
|
|
276
|
+
if (UNDRAWN_CLIPS.test(part) && part.endsWith(')')) shape = null;
|
|
277
|
+
else shape = rectShape(part, ctx);
|
|
278
|
+
if (shape === undefined) return undefined;
|
|
279
|
+
}
|
|
280
|
+
if (shape === null) return null;
|
|
281
|
+
if (shape) return { box: box ?? 'border-box', ...shape };
|
|
282
|
+
if (!box) return undefined;
|
|
283
|
+
return { box, shape: null, lengths: NO_LENGTHS, radii: null, radiiY: null };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** `inset()`, `rect()` or `xywh()`, with the corners it rounds; undefined
|
|
287
|
+
* for anything else. */
|
|
288
|
+
function rectShape(
|
|
289
|
+
text: string,
|
|
290
|
+
ctx: UnitContext,
|
|
291
|
+
): Omit<ClipPath, 'box'> | undefined {
|
|
292
|
+
const m = /^(inset|rect|xywh)\((.*)\)$/is.exec(text);
|
|
293
|
+
if (!m) return undefined;
|
|
294
|
+
const shape = m[1].toLowerCase() as 'inset' | 'rect' | 'xywh';
|
|
295
|
+
const parts = splitValue(m[2].trim());
|
|
296
|
+
const round = parts.findIndex((p) => p.toLowerCase() === 'round');
|
|
297
|
+
const sides = round < 0 ? parts : parts.slice(0, round);
|
|
298
|
+
let radii: ClipPath['radii'] = null;
|
|
299
|
+
let radiiY: ClipPath['radiiY'] = null;
|
|
300
|
+
if (round >= 0) {
|
|
301
|
+
const corners = parseRadii(parts.slice(round + 1).join(' '), ctx);
|
|
302
|
+
if (!corners) return undefined;
|
|
303
|
+
[radii, radiiY] = corners;
|
|
304
|
+
}
|
|
305
|
+
// one to four insets, as a margin's sides are written; the other two
|
|
306
|
+
// take all four
|
|
307
|
+
if (
|
|
308
|
+
shape === 'inset' ? !sides.length || sides.length > 4 : sides.length !== 4
|
|
309
|
+
)
|
|
310
|
+
return undefined;
|
|
311
|
+
const lengths: Len[] = [];
|
|
312
|
+
for (let i = 0; i < sides.length; i += 1) {
|
|
313
|
+
const len = parseLength(sides[i], ctx);
|
|
314
|
+
if (len === null) return undefined;
|
|
315
|
+
// only `rect()` has an edge that is `auto`, and a width is not negative
|
|
316
|
+
if (len === AUTO && shape !== 'rect') return undefined;
|
|
317
|
+
if (shape === 'xywh' && i > 1 && !notNegative(len)) return undefined;
|
|
318
|
+
lengths.push(len);
|
|
319
|
+
}
|
|
320
|
+
return { shape, lengths: fourSides(lengths), radii, radiiY };
|
|
321
|
+
}
|
|
322
|
+
|
|
177
323
|
export type BorderStyle =
|
|
178
324
|
| 'none'
|
|
179
325
|
| 'hidden'
|
|
@@ -187,7 +333,7 @@ export type BorderStyle =
|
|
|
187
333
|
| 'outset';
|
|
188
334
|
|
|
189
335
|
/** A background layer's image: a url, a gradient, or none. */
|
|
190
|
-
export type BackgroundImage = string |
|
|
336
|
+
export type BackgroundImage = string | Gradient | null;
|
|
191
337
|
|
|
192
338
|
/** An intrinsic size: `min-content`, `max-content` or `fit-content`, and
|
|
193
339
|
* `fit-content()`, whose argument stands in for the room (`fit`). */
|
|
@@ -290,6 +436,15 @@ export interface ComputedStyle {
|
|
|
290
436
|
* in place of its `list-style-type`'s; null for `none`. */
|
|
291
437
|
listStyleImage: string | null;
|
|
292
438
|
cursor: string | null;
|
|
439
|
+
/** `pointer-events`: `none` for an element the pointer passes through,
|
|
440
|
+
* to what is under it (CSS UI 4, 5.2). Its SVG values are `auto`. */
|
|
441
|
+
pointerEvents: 'auto' | 'none';
|
|
442
|
+
/** `fill` and `stroke`, what an `<svg>` in the document is painted with
|
|
443
|
+
* (SVG 2, 13.2): a colour, `currentColor`, `none`, or a `url()` of a
|
|
444
|
+
* paint server in the drawing — and null where nothing has set one,
|
|
445
|
+
* which leaves the drawing its own. */
|
|
446
|
+
fill: string | null;
|
|
447
|
+
stroke: string | null;
|
|
293
448
|
borderCollapse: 'separate' | 'collapse';
|
|
294
449
|
/** `border-spacing`: between columns, and between rows. */
|
|
295
450
|
borderSpacing: number;
|
|
@@ -369,6 +524,26 @@ export interface ComputedStyle {
|
|
|
369
524
|
* multicol container, which this lays out as one column and which
|
|
370
525
|
* `line-clamp` does not clamp (CSS Overflow 4, 5.2). */
|
|
371
526
|
columns: number;
|
|
527
|
+
/** `column-count` where it is a number, and `column-width` where it is
|
|
528
|
+
* a length; null for `auto` (CSS Multi-column 1, 2 and 3). */
|
|
529
|
+
columnCount: number | null;
|
|
530
|
+
columnWidth: number | null;
|
|
531
|
+
/** Whether `column-gap` is `normal`: nothing in a flex box or a grid,
|
|
532
|
+
* and an em between the columns of a multicol container (4.1). */
|
|
533
|
+
columnGapNormal: boolean;
|
|
534
|
+
/** The fewest lines of a block a column may end on, and the fewest it
|
|
535
|
+
* may start with (CSS Fragmentation 3, 4.3). Inherited. */
|
|
536
|
+
orphans: number;
|
|
537
|
+
widows: number;
|
|
538
|
+
/** `break-inside`: whether a column break may fall inside the box;
|
|
539
|
+
* `avoid-column` and `avoid-page` are read as `avoid`. */
|
|
540
|
+
breakInside: 'auto' | 'avoid';
|
|
541
|
+
/** `column-fill`: whether the columns are balanced, or each filled
|
|
542
|
+
* before the next is started; `balance-all` is `balance`. */
|
|
543
|
+
columnFill: 'balance' | 'auto';
|
|
544
|
+
/** `column-span: all`: whether the box is set across all the columns of
|
|
545
|
+
* the multicol container it is in (CSS Multi-column 1, 6). */
|
|
546
|
+
columnSpan: boolean;
|
|
372
547
|
/** How a line cut by `overflow` ends: `clip`, or with an ellipsis. */
|
|
373
548
|
textOverflow: 'clip' | 'ellipsis';
|
|
374
549
|
/** `aspect-ratio`: a box's width over its height, where its height is
|
|
@@ -388,16 +563,28 @@ export interface ComputedStyle {
|
|
|
388
563
|
* its edges measured from the border box's top left, a null edge the
|
|
389
564
|
* border box's own (CSS 2.1 11.1.2). Null for `auto`. */
|
|
390
565
|
clip: ClipRect | null;
|
|
566
|
+
/** `clip-path`: the rectangle the element and all it holds show through
|
|
567
|
+
* (CSS Masking 1, 5.1); null for `none`, and for a shape this does not
|
|
568
|
+
* draw. */
|
|
569
|
+
clipPath: ClipPath | null;
|
|
391
570
|
opacity: number;
|
|
392
571
|
/** Where `translate` moves the box after layout (CSS Transforms 2): a
|
|
393
572
|
* length or a percentage of its own border box across and down; null
|
|
394
573
|
* for `none`. */
|
|
395
574
|
translate: [Len, Len] | null;
|
|
396
|
-
/**
|
|
397
|
-
*
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
575
|
+
/** `rotate`: a turn in the plane of the page, in degrees; null for
|
|
576
|
+
* `none`, and for a turn about an axis in the page. */
|
|
577
|
+
rotate: number | null;
|
|
578
|
+
/** `scale`: across and down; null for `none`. */
|
|
579
|
+
scale: [number, number] | null;
|
|
580
|
+
/** `transform`: its functions in the order written, the ones out of the
|
|
581
|
+
* plane left out (`css/transform.ts`); null for `none`. Any of the four
|
|
582
|
+
* makes the box a containing block and a stacking context, and paints it
|
|
583
|
+
* with the positioned boxes (CSS Transforms 1, 3; `transformed`). */
|
|
584
|
+
transform: TransformFunction[] | null;
|
|
585
|
+
/** `transform-origin`: the point a transform turns and scales about,
|
|
586
|
+
* from the border box's top left, a percentage of its width and height. */
|
|
587
|
+
transformOrigin: [Len, Len];
|
|
401
588
|
zIndex: number | 'auto';
|
|
402
589
|
/** A keyword, a length to raise the box by, or a percentage of its own
|
|
403
590
|
* line height. */
|
|
@@ -484,7 +671,7 @@ export interface ComputedStyle {
|
|
|
484
671
|
backgroundImage: string | null;
|
|
485
672
|
/** A background drawn rather than fetched: the first layer's linear
|
|
486
673
|
* gradient, where it is one. */
|
|
487
|
-
backgroundGradient:
|
|
674
|
+
backgroundGradient: Gradient | null;
|
|
488
675
|
/** How the first layer repeats across and down (CSS Backgrounds 3, 3.4):
|
|
489
676
|
* one of a few pairs made once, so a style copies no array. */
|
|
490
677
|
backgroundRepeat: BackgroundRepeat;
|
|
@@ -543,10 +730,17 @@ export interface ComputedStyle {
|
|
|
543
730
|
* set it (`decorate`). No property: the cascade works them out. */
|
|
544
731
|
underline: string | null;
|
|
545
732
|
underlineStyle: 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy';
|
|
546
|
-
/** And the underline's thickness and offset, the box's that set it
|
|
733
|
+
/** And the underline's thickness and offset, the box's that set it: the
|
|
734
|
+
* thickness the one used, which `auto` takes from that box's font size
|
|
735
|
+
* (`usedThickness`), and the offset null where it is `auto`. */
|
|
547
736
|
underlineThickness: number | null;
|
|
548
737
|
underlineOffset: number | null;
|
|
549
738
|
lineThrough: string | null;
|
|
739
|
+
/** And the line through's style and thickness, the box's that set it, as
|
|
740
|
+
* the underline's are. It has no offset to set: it is drawn where each
|
|
741
|
+
* font it crosses puts it (`paintRunRules`). */
|
|
742
|
+
lineThroughStyle: 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy';
|
|
743
|
+
lineThroughThickness: number | null;
|
|
550
744
|
|
|
551
745
|
// flex — handed to yoga rather than interpreted here
|
|
552
746
|
flexDirection: 'row' | 'row-reverse' | 'column' | 'column-reverse';
|
|
@@ -653,6 +847,8 @@ export const INHERITED = [
|
|
|
653
847
|
'fontFeatureSettings',
|
|
654
848
|
'tabSize',
|
|
655
849
|
'tabSizeIsLength',
|
|
850
|
+
'orphans',
|
|
851
|
+
'widows',
|
|
656
852
|
'whiteSpace',
|
|
657
853
|
'overflowWrap',
|
|
658
854
|
'wordBreak',
|
|
@@ -664,6 +860,9 @@ export const INHERITED = [
|
|
|
664
860
|
'listStylePosition',
|
|
665
861
|
'listStyleImage',
|
|
666
862
|
'cursor',
|
|
863
|
+
'pointerEvents',
|
|
864
|
+
'fill',
|
|
865
|
+
'stroke',
|
|
667
866
|
'borderCollapse',
|
|
668
867
|
'borderSpacing',
|
|
669
868
|
'borderSpacingY',
|
|
@@ -758,6 +957,9 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
758
957
|
listStylePosition: 'outside',
|
|
759
958
|
listStyleImage: null,
|
|
760
959
|
cursor: null,
|
|
960
|
+
pointerEvents: 'auto',
|
|
961
|
+
fill: null,
|
|
962
|
+
stroke: null,
|
|
761
963
|
borderCollapse: 'separate',
|
|
762
964
|
// CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
|
|
763
965
|
// an anonymous table, which no sheet names, has none
|
|
@@ -793,6 +995,14 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
793
995
|
webkitBoxAlign: null,
|
|
794
996
|
webkitBoxFlex: 0,
|
|
795
997
|
columns: 0,
|
|
998
|
+
columnCount: null,
|
|
999
|
+
columnWidth: null,
|
|
1000
|
+
columnGapNormal: true,
|
|
1001
|
+
orphans: 2,
|
|
1002
|
+
widows: 2,
|
|
1003
|
+
breakInside: 'auto',
|
|
1004
|
+
columnFill: 'balance',
|
|
1005
|
+
columnSpan: false,
|
|
796
1006
|
textOverflow: 'clip',
|
|
797
1007
|
aspectRatio: null,
|
|
798
1008
|
objectFit: 'fill',
|
|
@@ -802,9 +1012,13 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
802
1012
|
overflowX: 'visible',
|
|
803
1013
|
overflowY: 'visible',
|
|
804
1014
|
clip: null,
|
|
1015
|
+
clipPath: null,
|
|
805
1016
|
opacity: 1,
|
|
806
1017
|
translate: null,
|
|
807
|
-
|
|
1018
|
+
rotate: null,
|
|
1019
|
+
scale: null,
|
|
1020
|
+
transform: null,
|
|
1021
|
+
transformOrigin: [{ pct: 50 }, { pct: 50 }],
|
|
808
1022
|
zIndex: AUTO,
|
|
809
1023
|
verticalAlign: 'baseline',
|
|
810
1024
|
|
|
@@ -894,6 +1108,8 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
894
1108
|
underlineThickness: null,
|
|
895
1109
|
underlineOffset: null,
|
|
896
1110
|
lineThrough: null,
|
|
1111
|
+
lineThroughStyle: 'solid',
|
|
1112
|
+
lineThroughThickness: null,
|
|
897
1113
|
|
|
898
1114
|
flexDirection: 'row',
|
|
899
1115
|
flexWrap: 'nowrap',
|
|
@@ -1003,6 +1219,8 @@ export function inherit(
|
|
|
1003
1219
|
out.fontFeatureSettings = parent.fontFeatureSettings;
|
|
1004
1220
|
out.tabSize = parent.tabSize;
|
|
1005
1221
|
out.tabSizeIsLength = parent.tabSizeIsLength;
|
|
1222
|
+
out.orphans = parent.orphans;
|
|
1223
|
+
out.widows = parent.widows;
|
|
1006
1224
|
out.whiteSpace = parent.whiteSpace;
|
|
1007
1225
|
out.overflowWrap = parent.overflowWrap;
|
|
1008
1226
|
out.wordBreak = parent.wordBreak;
|
|
@@ -1014,6 +1232,9 @@ export function inherit(
|
|
|
1014
1232
|
out.listStylePosition = parent.listStylePosition;
|
|
1015
1233
|
out.listStyleImage = parent.listStyleImage;
|
|
1016
1234
|
out.cursor = parent.cursor;
|
|
1235
|
+
out.pointerEvents = parent.pointerEvents;
|
|
1236
|
+
out.fill = parent.fill;
|
|
1237
|
+
out.stroke = parent.stroke;
|
|
1017
1238
|
out.borderCollapse = parent.borderCollapse;
|
|
1018
1239
|
out.borderSpacing = parent.borderSpacing;
|
|
1019
1240
|
out.borderSpacingY = parent.borderSpacingY;
|
|
@@ -1033,6 +1254,8 @@ export function inherit(
|
|
|
1033
1254
|
out.underlineThickness = parent.underlineThickness;
|
|
1034
1255
|
out.underlineOffset = parent.underlineOffset;
|
|
1035
1256
|
out.lineThrough = parent.lineThrough;
|
|
1257
|
+
out.lineThroughStyle = parent.lineThroughStyle;
|
|
1258
|
+
out.lineThroughThickness = parent.lineThroughThickness;
|
|
1036
1259
|
return out;
|
|
1037
1260
|
}
|
|
1038
1261
|
|
|
@@ -1067,6 +1290,8 @@ export const FIRST_LINE_INHERITED = [
|
|
|
1067
1290
|
'underlineThickness',
|
|
1068
1291
|
'underlineOffset',
|
|
1069
1292
|
'lineThrough',
|
|
1293
|
+
'lineThroughStyle',
|
|
1294
|
+
'lineThroughThickness',
|
|
1070
1295
|
] as const satisfies readonly (keyof ComputedStyle)[];
|
|
1071
1296
|
|
|
1072
1297
|
/**
|
|
@@ -1142,6 +1367,15 @@ const BORDER_WIDTH_KEYWORDS: Record<string, number> = {
|
|
|
1142
1367
|
thick: 5,
|
|
1143
1368
|
};
|
|
1144
1369
|
|
|
1370
|
+
/** `-webkit-` names that are the multicol properties' own. */
|
|
1371
|
+
const MULTICOL_ALIASES: Record<string, string> = {
|
|
1372
|
+
'-webkit-columns': 'columns',
|
|
1373
|
+
'-webkit-column-count': 'column-count',
|
|
1374
|
+
'-webkit-column-width': 'column-width',
|
|
1375
|
+
'-webkit-column-gap': 'column-gap',
|
|
1376
|
+
'-webkit-column-break-inside': 'break-inside',
|
|
1377
|
+
};
|
|
1378
|
+
|
|
1145
1379
|
/**
|
|
1146
1380
|
* Apply one declaration to a style, in place.
|
|
1147
1381
|
*
|
|
@@ -1157,7 +1391,12 @@ export function applyDeclaration(
|
|
|
1157
1391
|
rawValue: string,
|
|
1158
1392
|
ctx: UnitContext,
|
|
1159
1393
|
): void {
|
|
1160
|
-
const
|
|
1394
|
+
const written = prop.toLowerCase();
|
|
1395
|
+
// the multicol properties under the names WebKit had them by, which
|
|
1396
|
+
// Blink still reads, each an alias of the property (`-webkit-column-
|
|
1397
|
+
// count` in its `css_properties.json5`): a page written for it alone
|
|
1398
|
+
// names no other
|
|
1399
|
+
const name = MULTICOL_ALIASES[written] ?? written;
|
|
1161
1400
|
let value = rawValue.trim();
|
|
1162
1401
|
if (!value) return;
|
|
1163
1402
|
// a logical property is the physical one it stands for, the CSS-wide
|
|
@@ -1285,17 +1524,63 @@ export function applyDeclaration(
|
|
|
1285
1524
|
case 'column-count':
|
|
1286
1525
|
case 'column-width':
|
|
1287
1526
|
case 'columns': {
|
|
1288
|
-
// `auto`, or a count, a width or both of them
|
|
1527
|
+
// `auto`, or a count, a width or both of them, in either order; what
|
|
1528
|
+
// the shorthand leaves out is `auto`
|
|
1289
1529
|
const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
|
|
1290
|
-
let
|
|
1530
|
+
let count: number | null = null;
|
|
1531
|
+
let width: number | null = null;
|
|
1291
1532
|
for (const part of splitValue(value)) {
|
|
1292
1533
|
const v = part.toLowerCase();
|
|
1293
1534
|
if (v === 'auto') continue;
|
|
1294
|
-
if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
1295
|
-
|
|
1296
|
-
|
|
1535
|
+
if (/^\d+$/.test(v) && Number(v) >= 1) {
|
|
1536
|
+
if (count !== null || !(bit & 1)) return;
|
|
1537
|
+
count = Number(v);
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
const len = parseLength(part, ctx);
|
|
1541
|
+
if (typeof len !== 'number' || !(len > 0)) return;
|
|
1542
|
+
if (width !== null || !(bit & 2)) return;
|
|
1543
|
+
width = len;
|
|
1544
|
+
}
|
|
1545
|
+
if (bit & 1) style.columnCount = count;
|
|
1546
|
+
if (bit & 2) style.columnWidth = width;
|
|
1547
|
+
style.columns =
|
|
1548
|
+
(style.columnCount !== null ? 1 : 0) |
|
|
1549
|
+
(style.columnWidth !== null ? 2 : 0);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
case 'orphans':
|
|
1553
|
+
case 'widows': {
|
|
1554
|
+
// a whole number of lines, one or more
|
|
1555
|
+
const v = value.trim();
|
|
1556
|
+
if (!/^\+?\d+$/.test(v) || Number(v) < 1) return;
|
|
1557
|
+
style[name] = Number(v);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
case '-webkit-column-span':
|
|
1561
|
+
case 'column-span': {
|
|
1562
|
+
const v = value.trim().toLowerCase();
|
|
1563
|
+
if (v === 'all') style.columnSpan = true;
|
|
1564
|
+
else if (v === 'none') style.columnSpan = false;
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
case 'column-fill': {
|
|
1568
|
+
const v = value.trim().toLowerCase();
|
|
1569
|
+
if (v === 'auto') style.columnFill = 'auto';
|
|
1570
|
+
else if (v === 'balance' || v === 'balance-all') {
|
|
1571
|
+
style.columnFill = 'balance';
|
|
1572
|
+
}
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
case 'break-inside':
|
|
1576
|
+
case 'page-break-inside': {
|
|
1577
|
+
const v = value.trim().toLowerCase();
|
|
1578
|
+
if (v === 'auto') style.breakInside = 'auto';
|
|
1579
|
+
else if (/^avoid(?:-page|-column|-region)?$/.test(v)) {
|
|
1580
|
+
// the legacy property knows `avoid` alone
|
|
1581
|
+
if (name === 'page-break-inside' && v !== 'avoid') return;
|
|
1582
|
+
style.breakInside = 'avoid';
|
|
1297
1583
|
}
|
|
1298
|
-
style.columns = (style.columns & ~bit) | (set & bit);
|
|
1299
1584
|
return;
|
|
1300
1585
|
}
|
|
1301
1586
|
case '-webkit-box-flex': {
|
|
@@ -1534,6 +1819,12 @@ export function applyDeclaration(
|
|
|
1534
1819
|
if (clip !== undefined) style.clip = clip;
|
|
1535
1820
|
return;
|
|
1536
1821
|
}
|
|
1822
|
+
case 'clip-path':
|
|
1823
|
+
case '-webkit-clip-path': {
|
|
1824
|
+
const path = parseClipPath(value, ctx);
|
|
1825
|
+
if (path !== undefined) style.clipPath = path;
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1537
1828
|
case 'opacity': {
|
|
1538
1829
|
const a = parseAlpha(value);
|
|
1539
1830
|
if (a !== null) style.opacity = a;
|
|
@@ -1553,9 +1844,31 @@ export function applyDeclaration(
|
|
|
1553
1844
|
style.translate = [x, y ?? 0];
|
|
1554
1845
|
return;
|
|
1555
1846
|
}
|
|
1556
|
-
case '
|
|
1557
|
-
const
|
|
1558
|
-
if (
|
|
1847
|
+
case 'rotate': {
|
|
1848
|
+
const turn = parseRotate(value);
|
|
1849
|
+
if (turn !== undefined) style.rotate = turn;
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
case 'scale': {
|
|
1853
|
+
const factors = parseScale(value);
|
|
1854
|
+
if (factors !== undefined) style.scale = factors;
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
case 'transform':
|
|
1858
|
+
case '-webkit-transform': {
|
|
1859
|
+
const list = parseTransform(value, ctx);
|
|
1860
|
+
if (list !== undefined) style.transform = list;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
case 'transform-origin':
|
|
1864
|
+
case '-webkit-transform-origin': {
|
|
1865
|
+
// across and down, as a position is, and a depth nothing here has
|
|
1866
|
+
const parts = splitValue(value);
|
|
1867
|
+
if (parts.length === 3) {
|
|
1868
|
+
if (typeof parseLength(parts.pop()!, ctx) !== 'number') return;
|
|
1869
|
+
} else if (parts.length > 2) return;
|
|
1870
|
+
const origin = positionPair(parts, ctx);
|
|
1871
|
+
if (origin) style.transformOrigin = origin;
|
|
1559
1872
|
return;
|
|
1560
1873
|
}
|
|
1561
1874
|
case 'visibility': {
|
|
@@ -1818,19 +2131,8 @@ export function applyDeclaration(
|
|
|
1818
2131
|
return;
|
|
1819
2132
|
}
|
|
1820
2133
|
case 'border-radius': {
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
const halves = value.split('/');
|
|
1824
|
-
if (halves.length > 2) return;
|
|
1825
|
-
const [horizontal, vertical] = halves.map((half) =>
|
|
1826
|
-
splitValue(half).map((p) => radiusOf(p, ctx)),
|
|
1827
|
-
);
|
|
1828
|
-
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
1829
|
-
if (!parts.length || parts.length > 4 || parts.includes(null)) return;
|
|
1830
|
-
}
|
|
1831
|
-
style.borderRadius = fourSides(horizontal as Len[]);
|
|
1832
|
-
const y = vertical ? fourSides(vertical as Len[]) : null;
|
|
1833
|
-
style.borderRadiusY = y && !sameRadii(style.borderRadius, y) ? y : null;
|
|
2134
|
+
const radii = parseRadii(value, ctx);
|
|
2135
|
+
if (radii) [style.borderRadius, style.borderRadiusY] = radii;
|
|
1834
2136
|
return;
|
|
1835
2137
|
}
|
|
1836
2138
|
|
|
@@ -2073,7 +2375,20 @@ export function applyDeclaration(
|
|
|
2073
2375
|
)
|
|
2074
2376
|
.filter(Boolean)
|
|
2075
2377
|
.join(', ');
|
|
2076
|
-
|
|
2378
|
+
// A list that ends in no generic family ends in the document's own
|
|
2379
|
+
// font: where no face in the list matches, the text is set in the
|
|
2380
|
+
// user agent's default (CSS Fonts 4, 5.1), which for a browser is its
|
|
2381
|
+
// standard font and here is the one the document is set in. The text
|
|
2382
|
+
// engine's own answer for a name nothing has is its platform's pick,
|
|
2383
|
+
// Verdana under fontconfig: the Zen Garden's 216 sets its summary in
|
|
2384
|
+
// Montserrat alone, which no machine here has, and Chrome sets it in
|
|
2385
|
+
// Times where ours was Verdana, a fifth wider
|
|
2386
|
+
const last = names[names.length - 1].trim();
|
|
2387
|
+
const generic =
|
|
2388
|
+
!/^['"]/.test(last) && GENERIC_FAMILY.has(last.toLowerCase());
|
|
2389
|
+
const fallback = generic ? null : (ctx.fallbackFamily?.() ?? null);
|
|
2390
|
+
const full = fallback ? `${list}, ${fallback}` : list;
|
|
2391
|
+
style.fontFamily = ctx.families ? ctx.families(full) : full;
|
|
2077
2392
|
return;
|
|
2078
2393
|
}
|
|
2079
2394
|
case 'font-size': {
|
|
@@ -2479,6 +2794,20 @@ export function applyDeclaration(
|
|
|
2479
2794
|
style.cursor = splitCommas(value)[0]?.trim().toLowerCase() || null;
|
|
2480
2795
|
return;
|
|
2481
2796
|
}
|
|
2797
|
+
case 'pointer-events': {
|
|
2798
|
+
// `none`, or one of the values SVG gives it, which are `auto` on
|
|
2799
|
+
// anything that is not SVG's to draw
|
|
2800
|
+
const v = value.trim().toLowerCase();
|
|
2801
|
+
if (v === 'none') style.pointerEvents = 'none';
|
|
2802
|
+
else if (POINTER_EVENTS.has(v)) style.pointerEvents = 'auto';
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
case 'fill':
|
|
2806
|
+
case 'stroke': {
|
|
2807
|
+
const paint = svgPaint(value);
|
|
2808
|
+
if (paint !== null) style[name] = paint;
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2482
2811
|
|
|
2483
2812
|
// --- lists --------------------------------------------------------------
|
|
2484
2813
|
case 'list-style': {
|
|
@@ -2651,7 +2980,11 @@ export function applyDeclaration(
|
|
|
2651
2980
|
const col = parts.length > 1 ? gap(parts[1]) : row;
|
|
2652
2981
|
if (row === null || col === null) return;
|
|
2653
2982
|
if (which !== 'column-gap') style.rowGap = row;
|
|
2654
|
-
if (which !== 'row-gap')
|
|
2983
|
+
if (which !== 'row-gap') {
|
|
2984
|
+
style.columnGap = col;
|
|
2985
|
+
const word = parts[parts.length - 1].toLowerCase();
|
|
2986
|
+
style.columnGapNormal = word === 'normal';
|
|
2987
|
+
}
|
|
2655
2988
|
return;
|
|
2656
2989
|
}
|
|
2657
2990
|
case 'grid-template-columns':
|
|
@@ -3128,6 +3461,12 @@ function alignKeyword(value: string): string | null {
|
|
|
3128
3461
|
return 'flex-end';
|
|
3129
3462
|
case 'first baseline':
|
|
3130
3463
|
return 'baseline';
|
|
3464
|
+
// By last baselines, which nothing here aligns: set where that falls
|
|
3465
|
+
// back to, the end (CSS Box Alignment 3, 4.2). Dropped, a row that asks
|
|
3466
|
+
// for it stretched its items, and a flex box among them was as tall as
|
|
3467
|
+
// the row, its lines spread down it.
|
|
3468
|
+
case 'last baseline':
|
|
3469
|
+
return 'flex-end';
|
|
3131
3470
|
case 'center':
|
|
3132
3471
|
return 'center';
|
|
3133
3472
|
case 'stretch':
|
|
@@ -3799,7 +4138,7 @@ function backgroundImageOf(
|
|
|
3799
4138
|
ctx: UnitContext,
|
|
3800
4139
|
): BackgroundImage | undefined {
|
|
3801
4140
|
const v = text.trim();
|
|
3802
|
-
if (IMAGE_FUNCTION.test(v.toLowerCase())) return
|
|
4141
|
+
if (IMAGE_FUNCTION.test(v.toLowerCase())) return parseGradient(v, ctx);
|
|
3803
4142
|
return parseUrl(v);
|
|
3804
4143
|
}
|
|
3805
4144
|
|
|
@@ -3821,7 +4160,7 @@ function layerValues<T>(
|
|
|
3821
4160
|
interface BackgroundLayer {
|
|
3822
4161
|
color: string | null;
|
|
3823
4162
|
image: string | null;
|
|
3824
|
-
gradient:
|
|
4163
|
+
gradient: Gradient | null;
|
|
3825
4164
|
repeat: ComputedStyle['backgroundRepeat'];
|
|
3826
4165
|
size: ComputedStyle['backgroundSize'];
|
|
3827
4166
|
attachment: ComputedStyle['backgroundAttachment'];
|
|
@@ -3837,7 +4176,7 @@ interface BackgroundLayer {
|
|
|
3837
4176
|
* is dropped rather than resetting the background it meant to replace
|
|
3838
4177
|
* (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
|
|
3839
4178
|
* browser keeps is kept: `/ cover` after the position, `space` and `round`,
|
|
3840
|
-
* a gradient
|
|
4179
|
+
* and a gradient, over the layer's colour.
|
|
3841
4180
|
*/
|
|
3842
4181
|
function readBackgroundLayer(
|
|
3843
4182
|
text: string,
|
|
@@ -3872,7 +4211,7 @@ function readBackgroundLayer(
|
|
|
3872
4211
|
const url = parseUrl(part);
|
|
3873
4212
|
if (url === undefined) return null;
|
|
3874
4213
|
layer.image = url;
|
|
3875
|
-
} else if (v !== 'none') layer.gradient =
|
|
4214
|
+
} else if (v !== 'none') layer.gradient = parseGradient(part, ctx);
|
|
3876
4215
|
i += 1;
|
|
3877
4216
|
} else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
|
|
3878
4217
|
if (!once(2)) return null;
|
|
@@ -3936,10 +4275,8 @@ function readBackgroundLayer(
|
|
|
3936
4275
|
|
|
3937
4276
|
/**
|
|
3938
4277
|
* A `linear-gradient()`, or null for any other image function and for one
|
|
3939
|
-
* that is no gradient: a direction by angle, by side or by corner,
|
|
3940
|
-
* colour
|
|
3941
|
-
* not honoured, and at least two stops, each with no position, one, or two;
|
|
3942
|
-
* a bare percentage between stops, a hint, is passed over.
|
|
4278
|
+
* that is no gradient: a direction by angle, by side or by corner, and its
|
|
4279
|
+
* colour stops.
|
|
3943
4280
|
*/
|
|
3944
4281
|
function parseLinearGradient(
|
|
3945
4282
|
text: string,
|
|
@@ -3948,14 +4285,13 @@ function parseLinearGradient(
|
|
|
3948
4285
|
const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
|
|
3949
4286
|
if (!m) return null;
|
|
3950
4287
|
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
3951
|
-
const out: LinearGradient = {
|
|
3952
|
-
|
|
3953
|
-
.
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
.trim();
|
|
4288
|
+
const out: LinearGradient = {
|
|
4289
|
+
kind: 'linear',
|
|
4290
|
+
angle: Math.PI,
|
|
4291
|
+
corner: null,
|
|
4292
|
+
stops: [],
|
|
4293
|
+
};
|
|
4294
|
+
const first = withoutInterpolation(args[0] ?? '');
|
|
3959
4295
|
let from = 0;
|
|
3960
4296
|
if (!first || first.startsWith('to ')) {
|
|
3961
4297
|
from = 1;
|
|
@@ -3991,7 +4327,120 @@ function parseLinearGradient(
|
|
|
3991
4327
|
from = 1;
|
|
3992
4328
|
}
|
|
3993
4329
|
}
|
|
3994
|
-
|
|
4330
|
+
const stops = parseStops(args.slice(from), ctx);
|
|
4331
|
+
if (!stops) return null;
|
|
4332
|
+
out.stops = stops;
|
|
4333
|
+
return out;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
/**
|
|
4337
|
+
* A `radial-gradient()` (CSS Images 3, 3.2.1), or null for anything else:
|
|
4338
|
+
* an ending shape, `circle` or `ellipse`, and its size, in either order —
|
|
4339
|
+
* a keyword for the side or corner of the box it reaches, one length for a
|
|
4340
|
+
* circle's radius, or two lengths or percentages for an ellipse's — then
|
|
4341
|
+
* `at` and its centre, a position as `background-position` writes one;
|
|
4342
|
+
* each of the three may be left out, for an ellipse through the farthest
|
|
4343
|
+
* corner from the middle of the box. A colour interpolation method is read
|
|
4344
|
+
* and not honoured, and the stops are a linear gradient's, spaced along
|
|
4345
|
+
* the ray from the centre to the shape's edge.
|
|
4346
|
+
*/
|
|
4347
|
+
function parseRadialGradient(
|
|
4348
|
+
text: string,
|
|
4349
|
+
ctx: UnitContext,
|
|
4350
|
+
): RadialGradient | null {
|
|
4351
|
+
const m = /^radial-gradient\((.*)\)$/is.exec(text.trim());
|
|
4352
|
+
if (!m) return null;
|
|
4353
|
+
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
4354
|
+
const out: RadialGradient = {
|
|
4355
|
+
kind: 'radial',
|
|
4356
|
+
circle: false,
|
|
4357
|
+
extent: null,
|
|
4358
|
+
radii: null,
|
|
4359
|
+
at: [{ pct: 50 }, { pct: 50 }],
|
|
4360
|
+
stops: [],
|
|
4361
|
+
};
|
|
4362
|
+
const first = withoutInterpolation(args[0] ?? '');
|
|
4363
|
+
let from = 0;
|
|
4364
|
+
if (!first) from = 1;
|
|
4365
|
+
else if (parseColor(splitValue(first)[0]) === null) {
|
|
4366
|
+
from = 1;
|
|
4367
|
+
const words = splitValue(first);
|
|
4368
|
+
const at = words.indexOf('at');
|
|
4369
|
+
if (at >= 0) {
|
|
4370
|
+
const position = positionPair(words.slice(at + 1), ctx);
|
|
4371
|
+
if (!position) return null;
|
|
4372
|
+
out.at = position;
|
|
4373
|
+
}
|
|
4374
|
+
let shape: string | null = null;
|
|
4375
|
+
const sizes: Len[] = [];
|
|
4376
|
+
// the size is one run of words, the shape's keyword before or after it
|
|
4377
|
+
let sized = false;
|
|
4378
|
+
for (const word of at >= 0 ? words.slice(0, at) : words) {
|
|
4379
|
+
if (word === 'circle' || word === 'ellipse') {
|
|
4380
|
+
if (shape) return null;
|
|
4381
|
+
shape = word;
|
|
4382
|
+
if (sizes.length || out.extent) sized = true;
|
|
4383
|
+
continue;
|
|
4384
|
+
}
|
|
4385
|
+
if (sized || out.extent) return null;
|
|
4386
|
+
if (RADIAL_EXTENTS.has(word)) {
|
|
4387
|
+
if (sizes.length) return null;
|
|
4388
|
+
out.extent = word as RadialExtent;
|
|
4389
|
+
continue;
|
|
4390
|
+
}
|
|
4391
|
+
const len = parseLength(word, ctx);
|
|
4392
|
+
if (len === null || len === AUTO || !notNegative(len)) return null;
|
|
4393
|
+
sizes.push(len);
|
|
4394
|
+
}
|
|
4395
|
+
if (sizes.length === 1) {
|
|
4396
|
+
// a circle's radius is a length: a percentage of what, the spec asks
|
|
4397
|
+
const [r] = sizes;
|
|
4398
|
+
if (shape === 'ellipse' || typeof r !== 'number') {
|
|
4399
|
+
return null;
|
|
4400
|
+
}
|
|
4401
|
+
out.circle = true;
|
|
4402
|
+
out.radii = [r, r];
|
|
4403
|
+
} else if (sizes.length === 2) {
|
|
4404
|
+
if (shape === 'circle') return null;
|
|
4405
|
+
out.radii = [sizes[0], sizes[1]];
|
|
4406
|
+
} else if (sizes.length) return null;
|
|
4407
|
+
else {
|
|
4408
|
+
out.circle = shape === 'circle';
|
|
4409
|
+
out.extent ??= 'farthest-corner';
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
const stops = parseStops(args.slice(from), ctx);
|
|
4413
|
+
if (!stops) return null;
|
|
4414
|
+
out.stops = stops;
|
|
4415
|
+
return out;
|
|
4416
|
+
}
|
|
4417
|
+
|
|
4418
|
+
const RADIAL_EXTENTS = new Set([
|
|
4419
|
+
'closest-side',
|
|
4420
|
+
'closest-corner',
|
|
4421
|
+
'farthest-side',
|
|
4422
|
+
'farthest-corner',
|
|
4423
|
+
]);
|
|
4424
|
+
|
|
4425
|
+
/** A gradient's first argument, lower-cased, without the colour
|
|
4426
|
+
* interpolation method CSS Images 4 adds to it (`in oklab`, which
|
|
4427
|
+
* Tailwind 4 writes): read, and not honoured. */
|
|
4428
|
+
function withoutInterpolation(arg: string): string {
|
|
4429
|
+
return arg
|
|
4430
|
+
.toLowerCase()
|
|
4431
|
+
.replace(
|
|
4432
|
+
/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/,
|
|
4433
|
+
'',
|
|
4434
|
+
)
|
|
4435
|
+
.trim();
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
/** A gradient's colour stops, at least two, each with no position, one or
|
|
4439
|
+
* two; a bare length between two stops, a hint, is passed over. Null
|
|
4440
|
+
* where an argument is none of those. */
|
|
4441
|
+
function parseStops(args: string[], ctx: UnitContext): GradientStop[] | null {
|
|
4442
|
+
const stops: GradientStop[] = [];
|
|
4443
|
+
for (const arg of args) {
|
|
3995
4444
|
const parts = splitValue(arg);
|
|
3996
4445
|
const color = parseColor(parts[0] ?? '');
|
|
3997
4446
|
if (color === null) {
|
|
@@ -4002,14 +4451,19 @@ function parseLinearGradient(
|
|
|
4002
4451
|
if (parts.length > 3) return null;
|
|
4003
4452
|
const at = parts.slice(1).map((p) => parseLength(p, ctx));
|
|
4004
4453
|
if (at.some((p) => p === null || p === AUTO)) return null;
|
|
4005
|
-
if (!at.length)
|
|
4006
|
-
for (const p of at)
|
|
4454
|
+
if (!at.length) stops.push({ color, at: null });
|
|
4455
|
+
for (const p of at) stops.push({ color, at: p as Len });
|
|
4007
4456
|
}
|
|
4008
|
-
return
|
|
4457
|
+
return stops.length >= 2 ? stops : null;
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
/** A gradient function's image, where it is one this draws. */
|
|
4461
|
+
function parseGradient(text: string, ctx: UnitContext): Gradient | null {
|
|
4462
|
+
return parseLinearGradient(text, ctx) ?? parseRadialGradient(text, ctx);
|
|
4009
4463
|
}
|
|
4010
4464
|
|
|
4011
4465
|
/** The images CSS3 has beyond `url()`, which a layer may name: a linear
|
|
4012
|
-
* gradient
|
|
4466
|
+
* and a radial gradient are drawn, and the rest draw as nothing. */
|
|
4013
4467
|
const IMAGE_FUNCTION =
|
|
4014
4468
|
/^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
|
|
4015
4469
|
|
|
@@ -4050,87 +4504,6 @@ function whiteSpaceOf(collapse: Collapse, wrap: boolean): WhiteSpace {
|
|
|
4050
4504
|
return 'pre-line';
|
|
4051
4505
|
}
|
|
4052
4506
|
|
|
4053
|
-
/**
|
|
4054
|
-
* The translation a `transform` makes: the sum of its translate functions'
|
|
4055
|
-
* and its matrices' — `translate(-50%, -50%)`, Tailwind 3's `translate(
|
|
4056
|
-
* var(--tw-translate-x), var(--tw-translate-y)) rotate(…) skewX(…) …` —
|
|
4057
|
-
* with the functions that rotate, scale, skew or add perspective read and
|
|
4058
|
-
* not drawn. Null for `none`, and undefined for what is no transform list,
|
|
4059
|
-
* which drops the declaration.
|
|
4060
|
-
*/
|
|
4061
|
-
function transformTranslation(
|
|
4062
|
-
value: string,
|
|
4063
|
-
ctx: UnitContext,
|
|
4064
|
-
): [Len, Len] | null | undefined {
|
|
4065
|
-
if (value.trim().toLowerCase() === 'none') return null;
|
|
4066
|
-
let x: Len = 0;
|
|
4067
|
-
let y: Len = 0;
|
|
4068
|
-
const parts = splitValue(value);
|
|
4069
|
-
if (!parts.length) return undefined;
|
|
4070
|
-
for (const part of parts) {
|
|
4071
|
-
const m = /^([a-z0-9]+)\((.*)\)$/is.exec(part.trim());
|
|
4072
|
-
if (!m) return undefined;
|
|
4073
|
-
const name = m[1].toLowerCase();
|
|
4074
|
-
const args = splitCommas(m[2]).map((a) => a.trim());
|
|
4075
|
-
let dx: Len | null = 0;
|
|
4076
|
-
let dy: Len | null = 0;
|
|
4077
|
-
if (name === 'translate' || name === 'translate3d') {
|
|
4078
|
-
dx = parseLength(args[0] ?? '', ctx);
|
|
4079
|
-
dy = args[1] === undefined ? 0 : parseLength(args[1], ctx);
|
|
4080
|
-
} else if (name === 'translatex') dx = parseLength(args[0] ?? '', ctx);
|
|
4081
|
-
else if (name === 'translatey') dy = parseLength(args[0] ?? '', ctx);
|
|
4082
|
-
else if (name === 'matrix' || name === 'matrix3d') {
|
|
4083
|
-
// the translation is the last column's
|
|
4084
|
-
const n = args.map(Number);
|
|
4085
|
-
if (n.length !== (name === 'matrix' ? 6 : 16) || n.some(isNaN)) {
|
|
4086
|
-
return undefined;
|
|
4087
|
-
}
|
|
4088
|
-
[dx, dy] = name === 'matrix' ? [n[4], n[5]] : [n[12], n[13]];
|
|
4089
|
-
} else if (!TRANSFORMS.has(name)) return undefined;
|
|
4090
|
-
if (dx === null || dx === AUTO || dy === null || dy === AUTO) {
|
|
4091
|
-
return undefined;
|
|
4092
|
-
}
|
|
4093
|
-
const sx = addLen(x, dx);
|
|
4094
|
-
const sy = addLen(y, dy);
|
|
4095
|
-
if (sx === null || sy === null) return undefined;
|
|
4096
|
-
x = sx;
|
|
4097
|
-
y = sy;
|
|
4098
|
-
}
|
|
4099
|
-
return [x, y];
|
|
4100
|
-
}
|
|
4101
|
-
|
|
4102
|
-
/** The transform functions read and not drawn. */
|
|
4103
|
-
const TRANSFORMS = new Set([
|
|
4104
|
-
'translatez',
|
|
4105
|
-
'rotate',
|
|
4106
|
-
'rotatex',
|
|
4107
|
-
'rotatey',
|
|
4108
|
-
'rotatez',
|
|
4109
|
-
'rotate3d',
|
|
4110
|
-
'scale',
|
|
4111
|
-
'scalex',
|
|
4112
|
-
'scaley',
|
|
4113
|
-
'scalez',
|
|
4114
|
-
'scale3d',
|
|
4115
|
-
'skew',
|
|
4116
|
-
'skewx',
|
|
4117
|
-
'skewy',
|
|
4118
|
-
'perspective',
|
|
4119
|
-
]);
|
|
4120
|
-
|
|
4121
|
-
/** Two lengths added, a percentage and a length kept apart; null where
|
|
4122
|
-
* one is a `min()` or a `max()`, which a sum cannot be kept of. */
|
|
4123
|
-
function addLen(a: Len, b: Len): Len | null {
|
|
4124
|
-
if (a === AUTO || b === AUTO) return null;
|
|
4125
|
-
if (typeof a === 'number' && typeof b === 'number') return a + b;
|
|
4126
|
-
if (a === 0) return b;
|
|
4127
|
-
if (b === 0) return a;
|
|
4128
|
-
const pa: Pct = typeof a === 'number' ? { pct: 0, px: a } : a;
|
|
4129
|
-
const pb: Pct = typeof b === 'number' ? { pct: 0, px: b } : b;
|
|
4130
|
-
if (pa.of || pb.of) return null;
|
|
4131
|
-
return { pct: pa.pct + pb.pct, px: (pa.px ?? 0) + (pb.px ?? 0) };
|
|
4132
|
-
}
|
|
4133
|
-
|
|
4134
4507
|
/** An intrinsic size keyword, with the prefixes browsers still read, or
|
|
4135
4508
|
* `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
|
|
4136
4509
|
* width's, where a height's is its content height as the keyword is;
|
|
@@ -4769,6 +5142,20 @@ function splitTopLevelSlash(value: string): string[] {
|
|
|
4769
5142
|
|
|
4770
5143
|
// --- logical properties ----------------------------------------------------
|
|
4771
5144
|
|
|
5145
|
+
/** `pointer-events`' values besides `none` (CSS UI 4, SVG 2 15.6). */
|
|
5146
|
+
const POINTER_EVENTS = new Set([
|
|
5147
|
+
'auto',
|
|
5148
|
+
'bounding-box',
|
|
5149
|
+
'visiblepainted',
|
|
5150
|
+
'visiblefill',
|
|
5151
|
+
'visiblestroke',
|
|
5152
|
+
'visible',
|
|
5153
|
+
'painted',
|
|
5154
|
+
'fill',
|
|
5155
|
+
'stroke',
|
|
5156
|
+
'all',
|
|
5157
|
+
]);
|
|
5158
|
+
|
|
4772
5159
|
const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
|
|
4773
5160
|
|
|
4774
5161
|
/**
|
|
@@ -4860,6 +5247,29 @@ function radiusOf(text: string, ctx: UnitContext): Len | null {
|
|
|
4860
5247
|
return len.pct < 0 && !len.px && !len.of ? null : len;
|
|
4861
5248
|
}
|
|
4862
5249
|
|
|
5250
|
+
/**
|
|
5251
|
+
* `border-radius`'s value: one to four radii, and after a `/` one to four
|
|
5252
|
+
* more for the vertical radii where they differ, elliptical corners — the
|
|
5253
|
+
* radii across, and the ones down, null where they are the same values.
|
|
5254
|
+
* Null for anything else.
|
|
5255
|
+
*/
|
|
5256
|
+
function parseRadii(
|
|
5257
|
+
value: string,
|
|
5258
|
+
ctx: UnitContext,
|
|
5259
|
+
): [[Len, Len, Len, Len], [Len, Len, Len, Len] | null] | null {
|
|
5260
|
+
const halves = value.split('/');
|
|
5261
|
+
if (halves.length > 2) return null;
|
|
5262
|
+
const [horizontal, vertical] = halves.map((half) =>
|
|
5263
|
+
splitValue(half).map((p) => radiusOf(p, ctx)),
|
|
5264
|
+
);
|
|
5265
|
+
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
5266
|
+
if (!parts.length || parts.length > 4 || parts.includes(null)) return null;
|
|
5267
|
+
}
|
|
5268
|
+
const across = fourSides(horizontal as Len[]);
|
|
5269
|
+
const down = vertical ? fourSides(vertical as Len[]) : null;
|
|
5270
|
+
return [across, down && !sameRadii(across, down) ? down : null];
|
|
5271
|
+
}
|
|
5272
|
+
|
|
4863
5273
|
/** Whether two corners' radii are the same values. */
|
|
4864
5274
|
function sameRadii(a: readonly Len[], b: readonly Len[]): boolean {
|
|
4865
5275
|
for (let i = 0; i < 4; i += 1) {
|
|
@@ -5102,10 +5512,15 @@ const INHERITED_NAMES = new Set<string>([
|
|
|
5102
5512
|
'list-style-position',
|
|
5103
5513
|
'list-style-image',
|
|
5104
5514
|
'cursor',
|
|
5515
|
+
'pointer-events',
|
|
5516
|
+
'fill',
|
|
5517
|
+
'stroke',
|
|
5105
5518
|
'border-collapse',
|
|
5106
5519
|
'border-spacing',
|
|
5107
5520
|
'empty-cells',
|
|
5108
5521
|
'quotes',
|
|
5522
|
+
'orphans',
|
|
5523
|
+
'widows',
|
|
5109
5524
|
]);
|
|
5110
5525
|
|
|
5111
5526
|
export function isInherited(name: string): boolean {
|
|
@@ -5260,6 +5675,9 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5260
5675
|
'list-style-position': ['listStylePosition'],
|
|
5261
5676
|
'list-style-image': ['listStyleImage'],
|
|
5262
5677
|
cursor: ['cursor'],
|
|
5678
|
+
'pointer-events': ['pointerEvents'],
|
|
5679
|
+
fill: ['fill'],
|
|
5680
|
+
stroke: ['stroke'],
|
|
5263
5681
|
'border-collapse': ['borderCollapse'],
|
|
5264
5682
|
'caption-side': ['captionSide'],
|
|
5265
5683
|
'empty-cells': ['emptyCells'],
|
|
@@ -5328,9 +5746,16 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5328
5746
|
'-webkit-box-pack': ['webkitBoxPack'],
|
|
5329
5747
|
'-webkit-box-align': ['webkitBoxAlign'],
|
|
5330
5748
|
'-webkit-box-flex': ['webkitBoxFlex'],
|
|
5331
|
-
columns: ['columns'],
|
|
5332
|
-
'column-count': ['columns'],
|
|
5333
|
-
'column-width': ['columns'],
|
|
5749
|
+
columns: ['columns', 'columnCount', 'columnWidth'],
|
|
5750
|
+
'column-count': ['columns', 'columnCount'],
|
|
5751
|
+
'column-width': ['columns', 'columnWidth'],
|
|
5752
|
+
orphans: ['orphans'],
|
|
5753
|
+
widows: ['widows'],
|
|
5754
|
+
'break-inside': ['breakInside'],
|
|
5755
|
+
'column-fill': ['columnFill'],
|
|
5756
|
+
'column-span': ['columnSpan'],
|
|
5757
|
+
'-webkit-column-span': ['columnSpan'],
|
|
5758
|
+
'page-break-inside': ['breakInside'],
|
|
5334
5759
|
'text-overflow': ['textOverflow'],
|
|
5335
5760
|
// a flex box's and its items', and a grid's
|
|
5336
5761
|
'flex-direction': ['flexDirection'],
|
|
@@ -5345,12 +5770,12 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5345
5770
|
'flex-shrink': ['flexShrink'],
|
|
5346
5771
|
'flex-basis': ['flexBasis'],
|
|
5347
5772
|
order: ['order'],
|
|
5348
|
-
gap: ['rowGap', 'columnGap'],
|
|
5773
|
+
gap: ['rowGap', 'columnGap', 'columnGapNormal'],
|
|
5349
5774
|
'row-gap': ['rowGap'],
|
|
5350
|
-
'column-gap': ['columnGap'],
|
|
5351
|
-
'grid-gap': ['rowGap', 'columnGap'],
|
|
5775
|
+
'column-gap': ['columnGap', 'columnGapNormal'],
|
|
5776
|
+
'grid-gap': ['rowGap', 'columnGap', 'columnGapNormal'],
|
|
5352
5777
|
'grid-row-gap': ['rowGap'],
|
|
5353
|
-
'grid-column-gap': ['columnGap'],
|
|
5778
|
+
'grid-column-gap': ['columnGap', 'columnGapNormal'],
|
|
5354
5779
|
'grid-template-columns': ['gridColumns'],
|
|
5355
5780
|
'grid-template-rows': ['gridRows'],
|
|
5356
5781
|
'grid-auto-rows': ['gridAutoRows'],
|
|
@@ -5445,11 +5870,18 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5445
5870
|
clear: ['clear'],
|
|
5446
5871
|
overflow: ['overflowX', 'overflowY'],
|
|
5447
5872
|
clip: ['clip'],
|
|
5873
|
+
'clip-path': ['clipPath'],
|
|
5874
|
+
'-webkit-clip-path': ['clipPath'],
|
|
5448
5875
|
'overflow-x': ['overflowX'],
|
|
5449
5876
|
'overflow-y': ['overflowY'],
|
|
5450
5877
|
opacity: ['opacity'],
|
|
5451
5878
|
translate: ['translate'],
|
|
5452
|
-
|
|
5879
|
+
rotate: ['rotate'],
|
|
5880
|
+
scale: ['scale'],
|
|
5881
|
+
transform: ['transform'],
|
|
5882
|
+
'-webkit-transform': ['transform'],
|
|
5883
|
+
'transform-origin': ['transformOrigin'],
|
|
5884
|
+
'-webkit-transform-origin': ['transformOrigin'],
|
|
5453
5885
|
'z-index': ['zIndex'],
|
|
5454
5886
|
'vertical-align': ['verticalAlign'],
|
|
5455
5887
|
'text-decoration': [
|
|
@@ -5512,7 +5944,7 @@ export function decorate(style: ComputedStyle): void {
|
|
|
5512
5944
|
style.color,
|
|
5513
5945
|
);
|
|
5514
5946
|
style.underlineStyle = style.textDecorationStyle;
|
|
5515
|
-
style.underlineThickness = style
|
|
5947
|
+
style.underlineThickness = usedThickness(style);
|
|
5516
5948
|
style.underlineOffset = style.textUnderlineOffset;
|
|
5517
5949
|
}
|
|
5518
5950
|
if (lines.includes('line-through')) {
|
|
@@ -5520,9 +5952,30 @@ export function decorate(style: ComputedStyle): void {
|
|
|
5520
5952
|
style.textDecorationColor ?? 'currentColor',
|
|
5521
5953
|
style.color,
|
|
5522
5954
|
);
|
|
5955
|
+
style.lineThroughStyle = style.textDecorationStyle;
|
|
5956
|
+
style.lineThroughThickness = usedThickness(style);
|
|
5523
5957
|
}
|
|
5524
5958
|
}
|
|
5525
5959
|
|
|
5960
|
+
/**
|
|
5961
|
+
* How thick the lines a box decorates its text with are. `auto` leaves it
|
|
5962
|
+
* to the user agent, which is to take it from the font where the font says
|
|
5963
|
+
* (CSS Text Decoration 4, 2.4.1); neither text engine here reports a face's
|
|
5964
|
+
* underline, so it is a part of the font size, Blink's tenth — of the size
|
|
5965
|
+
* of the box that set the decoration, which draws one line of one thickness
|
|
5966
|
+
* through everything inside it (2.9). A pixel for every size drew the dots
|
|
5967
|
+
* under the `<abbr>` of a 140px title as a hairline. `from-font` is `auto`
|
|
5968
|
+
* for the same want of a metric, a length is itself on whole pixels, and
|
|
5969
|
+
* no line is thinner than one.
|
|
5970
|
+
*/
|
|
5971
|
+
function usedThickness(style: ComputedStyle): number {
|
|
5972
|
+
const length = style.textDecorationThickness;
|
|
5973
|
+
return Math.max(
|
|
5974
|
+
1,
|
|
5975
|
+
length === null ? style.fontSize / 10 : Math.round(length),
|
|
5976
|
+
);
|
|
5977
|
+
}
|
|
5978
|
+
|
|
5526
5979
|
/**
|
|
5527
5980
|
* The absolutely positioned styles that were inline-level before they were
|
|
5528
5981
|
* made blocks: where such a box would have been in flow is on its line,
|
|
@@ -5638,3 +6091,55 @@ export function blockify(style: ComputedStyle, inFlexContainer: boolean): void {
|
|
|
5638
6091
|
return;
|
|
5639
6092
|
}
|
|
5640
6093
|
}
|
|
6094
|
+
|
|
6095
|
+
/**
|
|
6096
|
+
* The `display` a `<button>` is laid out with, which is not always the one
|
|
6097
|
+
* it was given (HTML's rendering section, button layout, 15.5.3): a flex
|
|
6098
|
+
* box or a grid is that, and of the rest, "a value such that the outer
|
|
6099
|
+
* display type is 'inline'" behaves as `inline-block` and anything else as
|
|
6100
|
+
* `flow-root`. Blink computes them so — `inline-block` for `inline` and
|
|
6101
|
+
* `inline-table`, `block` for `table` and `list-item` — and makes the one
|
|
6102
|
+
* block flow of each; the formatting context is the layout's to give every
|
|
6103
|
+
* button that is a block (`establishesBFC`).
|
|
6104
|
+
*
|
|
6105
|
+
* Left an inline box, a button around a block was broken in two around it,
|
|
6106
|
+
* as any inline box is: as wide as the line, its `height` applied to
|
|
6107
|
+
* nothing, and its border and padding drawn above and below the line it
|
|
6108
|
+
* opened. A table's part is left what it is, where the two disagree: a
|
|
6109
|
+
* block to the text, an inline-block in Blink. After `blockify`, since a
|
|
6110
|
+
* float, an absolute box and a flex item are blocks first.
|
|
6111
|
+
*/
|
|
6112
|
+
export function settleButton(style: ComputedStyle): void {
|
|
6113
|
+
switch (style.display) {
|
|
6114
|
+
case 'inline':
|
|
6115
|
+
case 'inline-table':
|
|
6116
|
+
style.display = 'inline-block';
|
|
6117
|
+
return;
|
|
6118
|
+
case 'list-item':
|
|
6119
|
+
case 'table':
|
|
6120
|
+
style.display = 'block';
|
|
6121
|
+
return;
|
|
6122
|
+
default:
|
|
6123
|
+
return;
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
|
|
6127
|
+
/** The generic font families, which a list ending in one falls back
|
|
6128
|
+
* through already, in the text engine (CSS Fonts 4, 4.2). */
|
|
6129
|
+
const GENERIC_FAMILY = new Set([
|
|
6130
|
+
'serif',
|
|
6131
|
+
'sans-serif',
|
|
6132
|
+
'monospace',
|
|
6133
|
+
'cursive',
|
|
6134
|
+
'fantasy',
|
|
6135
|
+
'system-ui',
|
|
6136
|
+
'ui-serif',
|
|
6137
|
+
'ui-sans-serif',
|
|
6138
|
+
'ui-monospace',
|
|
6139
|
+
'ui-rounded',
|
|
6140
|
+
'math',
|
|
6141
|
+
'emoji',
|
|
6142
|
+
'fangsong',
|
|
6143
|
+
'-apple-system',
|
|
6144
|
+
'blinkmacsystemfont',
|
|
6145
|
+
]);
|