@react-x11/components 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html/controls.d.ts +41 -5
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +85 -57
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +134 -20
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +545 -126
- 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 +47 -6
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +336 -30
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +142 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +563 -147
- 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 +11 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +73 -9
- 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/dom.d.ts.map +1 -1
- package/dist/html/dom.js +3 -1
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +78 -4
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +373 -19
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/form.d.ts +200 -0
- package/dist/html/form.d.ts.map +1 -0
- package/dist/html/form.js +801 -0
- package/dist/html/form.js.map +1 -0
- package/dist/html/index.d.ts +12 -0
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +30 -301
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +93 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +393 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +90 -2
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +74 -10
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +90 -18
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +574 -84
- 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 +154 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +62 -18
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +90 -15
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +771 -186
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +101 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1502 -160
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +15 -2
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +249 -17
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.d.ts +29 -0
- package/dist/html/widgets.d.ts.map +1 -0
- package/dist/html/widgets.js +700 -0
- package/dist/html/widgets.js.map +1 -0
- 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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- 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 +2 -2
- package/src/html/controls.ts +114 -51
- package/src/html/css/cascade.ts +614 -140
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +388 -33
- package/src/html/css/style.ts +688 -150
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +77 -10
- package/src/html/css/values.ts +4 -0
- package/src/html/dom.ts +3 -1
- package/src/html/fonts.ts +417 -24
- package/src/html/form.ts +962 -0
- package/src/html/index.ts +49 -338
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +447 -37
- package/src/html/layout/boxes.ts +143 -14
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +95 -22
- package/src/html/layout/flex.ts +675 -78
- package/src/html/layout/inline.ts +159 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +62 -19
- package/src/html/node.ts +798 -172
- package/src/html/paint.ts +1742 -241
- package/src/html/svg.ts +277 -19
- package/src/html/widgets.ts +821 -0
- package/src/html/woff2.ts +612 -0
- package/src/index.ts +1 -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,6 +40,8 @@ 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';
|
|
44
46
|
|
|
45
47
|
export type Display =
|
|
@@ -108,15 +110,43 @@ export interface BoxShadow {
|
|
|
108
110
|
inset: boolean;
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
/** A gradient's colour stop: its colour, at a position along the gradient
|
|
114
|
+
* or, with none, where its neighbours put it. */
|
|
115
|
+
export interface GradientStop {
|
|
116
|
+
color: string;
|
|
117
|
+
at: Len | null;
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
/** A `linear-gradient()` (CSS Images 3, 3.1): its direction, an angle
|
|
112
121
|
* clockwise from up or a corner the box's shape turns into one, and its
|
|
113
|
-
* colour stops
|
|
122
|
+
* colour stops. */
|
|
114
123
|
export interface LinearGradient {
|
|
124
|
+
kind: 'linear';
|
|
115
125
|
angle: number;
|
|
116
126
|
corner: 'top left' | 'top right' | 'bottom left' | 'bottom right' | null;
|
|
117
|
-
stops:
|
|
127
|
+
stops: GradientStop[];
|
|
118
128
|
}
|
|
119
129
|
|
|
130
|
+
/** A `radial-gradient()` (CSS Images 3, 3.2): its ending shape, a circle or
|
|
131
|
+
* an ellipse; that shape's size, a keyword for the side or the corner of
|
|
132
|
+
* the box it reaches or the radii it was given, one for a circle and two
|
|
133
|
+
* for an ellipse; its centre, a position in the box; and its colour stops,
|
|
134
|
+
* along the ray from the centre to the shape's edge. */
|
|
135
|
+
export interface RadialGradient {
|
|
136
|
+
kind: 'radial';
|
|
137
|
+
circle: boolean;
|
|
138
|
+
extent: RadialExtent | null;
|
|
139
|
+
radii: [Len, Len] | null;
|
|
140
|
+
at: [Len, Len];
|
|
141
|
+
stops: GradientStop[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type RadialExtent =
|
|
145
|
+
'closest-side' | 'closest-corner' | 'farthest-side' | 'farthest-corner';
|
|
146
|
+
|
|
147
|
+
/** An image a gradient function draws. */
|
|
148
|
+
export type Gradient = LinearGradient | RadialGradient;
|
|
149
|
+
|
|
120
150
|
/** Where a grid item starts or ends (CSS Grid 1, 8.3): a line, counted
|
|
121
151
|
* among the lines of a name where it has one; a span, to a line of a name
|
|
122
152
|
* where it has one; a name alone, an area's edge or a line's; or auto. */
|
|
@@ -174,6 +204,121 @@ function parseClip(
|
|
|
174
204
|
return { top: edges[0], right: edges[1], bottom: edges[2], left: edges[3] };
|
|
175
205
|
}
|
|
176
206
|
|
|
207
|
+
/** The box a `clip-path`'s shape is measured in, a `<geometry-box>` (CSS
|
|
208
|
+
* Masking 1, 5.1). */
|
|
209
|
+
export type ClipBox =
|
|
210
|
+
'margin-box' | 'border-box' | 'padding-box' | 'content-box';
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* `clip-path` (CSS Masking 1, 5.1): a rectangle in a box of the element's,
|
|
214
|
+
* its corners rounded or not — a `<basic-shape-rect>` (CSS Shapes 1, 3.1),
|
|
215
|
+
* kept as the function it was written as, since each measures its four
|
|
216
|
+
* lengths its own way and a percentage among them has no pixels before
|
|
217
|
+
* layout. The element shows through it, and everything in it does.
|
|
218
|
+
*/
|
|
219
|
+
export interface ClipPath {
|
|
220
|
+
box: ClipBox;
|
|
221
|
+
/** What the four lengths are: `inset()`'s, how far in from the box's
|
|
222
|
+
* top, right, bottom and left; `rect()`'s, those four edges from its top
|
|
223
|
+
* and its left, `auto` the box's own; `xywh()`'s, the rectangle's left
|
|
224
|
+
* and top from the box's, and its width and height. Null for a box
|
|
225
|
+
* named alone, which is the shape: the whole of it, rounded as the
|
|
226
|
+
* element's corners are. */
|
|
227
|
+
shape: 'inset' | 'rect' | 'xywh' | null;
|
|
228
|
+
lengths: readonly [Len, Len, Len, Len];
|
|
229
|
+
/** `round`: the corners' radii across, and down where they differ, as
|
|
230
|
+
* `border-radius` has them — a percentage is of the box, and not of the
|
|
231
|
+
* rectangle in it. Null for square corners. */
|
|
232
|
+
radii: readonly [Len, Len, Len, Len] | null;
|
|
233
|
+
radiiY: readonly [Len, Len, Len, Len] | null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The boxes `clip-path` names; an SVG one is the CSS box that stands for
|
|
237
|
+
* it on an element with a CSS layout box (5.1). */
|
|
238
|
+
const CLIP_BOXES: Record<string, ClipBox> = {
|
|
239
|
+
'margin-box': 'margin-box',
|
|
240
|
+
'border-box': 'border-box',
|
|
241
|
+
'padding-box': 'padding-box',
|
|
242
|
+
'content-box': 'content-box',
|
|
243
|
+
'fill-box': 'content-box',
|
|
244
|
+
'stroke-box': 'border-box',
|
|
245
|
+
'view-box': 'border-box',
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/** The shapes that are no rectangle, and a reference to a `<clipPath>`
|
|
249
|
+
* element: a value, which sets the property, and none this draws. */
|
|
250
|
+
const UNDRAWN_CLIPS = /^(circle|ellipse|polygon|path|shape|url)\(/i;
|
|
251
|
+
|
|
252
|
+
const NO_LENGTHS = [0, 0, 0, 0] as const;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* `none`; a rectangle's function, a box, or the two in either order; or a
|
|
256
|
+
* shape that is no rectangle, which is a value and clips nothing here.
|
|
257
|
+
* Undefined for anything else, which leaves the value as it was.
|
|
258
|
+
*/
|
|
259
|
+
function parseClipPath(
|
|
260
|
+
value: string,
|
|
261
|
+
ctx: UnitContext,
|
|
262
|
+
): ClipPath | null | undefined {
|
|
263
|
+
const v = value.trim();
|
|
264
|
+
if (/^none$/i.test(v)) return null;
|
|
265
|
+
let box: ClipBox | null = null;
|
|
266
|
+
let shape: Omit<ClipPath, 'box'> | null | undefined;
|
|
267
|
+
for (const part of splitValue(v)) {
|
|
268
|
+
const named = CLIP_BOXES[part.toLowerCase()];
|
|
269
|
+
if (named) {
|
|
270
|
+
if (box) return undefined;
|
|
271
|
+
box = named;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (shape !== undefined) return undefined;
|
|
275
|
+
if (UNDRAWN_CLIPS.test(part) && part.endsWith(')')) shape = null;
|
|
276
|
+
else shape = rectShape(part, ctx);
|
|
277
|
+
if (shape === undefined) return undefined;
|
|
278
|
+
}
|
|
279
|
+
if (shape === null) return null;
|
|
280
|
+
if (shape) return { box: box ?? 'border-box', ...shape };
|
|
281
|
+
if (!box) return undefined;
|
|
282
|
+
return { box, shape: null, lengths: NO_LENGTHS, radii: null, radiiY: null };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** `inset()`, `rect()` or `xywh()`, with the corners it rounds; undefined
|
|
286
|
+
* for anything else. */
|
|
287
|
+
function rectShape(
|
|
288
|
+
text: string,
|
|
289
|
+
ctx: UnitContext,
|
|
290
|
+
): Omit<ClipPath, 'box'> | undefined {
|
|
291
|
+
const m = /^(inset|rect|xywh)\((.*)\)$/is.exec(text);
|
|
292
|
+
if (!m) return undefined;
|
|
293
|
+
const shape = m[1].toLowerCase() as 'inset' | 'rect' | 'xywh';
|
|
294
|
+
const parts = splitValue(m[2].trim());
|
|
295
|
+
const round = parts.findIndex((p) => p.toLowerCase() === 'round');
|
|
296
|
+
const sides = round < 0 ? parts : parts.slice(0, round);
|
|
297
|
+
let radii: ClipPath['radii'] = null;
|
|
298
|
+
let radiiY: ClipPath['radiiY'] = null;
|
|
299
|
+
if (round >= 0) {
|
|
300
|
+
const corners = parseRadii(parts.slice(round + 1).join(' '), ctx);
|
|
301
|
+
if (!corners) return undefined;
|
|
302
|
+
[radii, radiiY] = corners;
|
|
303
|
+
}
|
|
304
|
+
// one to four insets, as a margin's sides are written; the other two
|
|
305
|
+
// take all four
|
|
306
|
+
if (
|
|
307
|
+
shape === 'inset' ? !sides.length || sides.length > 4 : sides.length !== 4
|
|
308
|
+
)
|
|
309
|
+
return undefined;
|
|
310
|
+
const lengths: Len[] = [];
|
|
311
|
+
for (let i = 0; i < sides.length; i += 1) {
|
|
312
|
+
const len = parseLength(sides[i], ctx);
|
|
313
|
+
if (len === null) return undefined;
|
|
314
|
+
// only `rect()` has an edge that is `auto`, and a width is not negative
|
|
315
|
+
if (len === AUTO && shape !== 'rect') return undefined;
|
|
316
|
+
if (shape === 'xywh' && i > 1 && !notNegative(len)) return undefined;
|
|
317
|
+
lengths.push(len);
|
|
318
|
+
}
|
|
319
|
+
return { shape, lengths: fourSides(lengths), radii, radiiY };
|
|
320
|
+
}
|
|
321
|
+
|
|
177
322
|
export type BorderStyle =
|
|
178
323
|
| 'none'
|
|
179
324
|
| 'hidden'
|
|
@@ -187,7 +332,7 @@ export type BorderStyle =
|
|
|
187
332
|
| 'outset';
|
|
188
333
|
|
|
189
334
|
/** A background layer's image: a url, a gradient, or none. */
|
|
190
|
-
export type BackgroundImage = string |
|
|
335
|
+
export type BackgroundImage = string | Gradient | null;
|
|
191
336
|
|
|
192
337
|
/** An intrinsic size: `min-content`, `max-content` or `fit-content`, and
|
|
193
338
|
* `fit-content()`, whose argument stands in for the room (`fit`). */
|
|
@@ -290,6 +435,15 @@ export interface ComputedStyle {
|
|
|
290
435
|
* in place of its `list-style-type`'s; null for `none`. */
|
|
291
436
|
listStyleImage: string | null;
|
|
292
437
|
cursor: string | null;
|
|
438
|
+
/** `pointer-events`: `none` for an element the pointer passes through,
|
|
439
|
+
* to what is under it (CSS UI 4, 5.2). Its SVG values are `auto`. */
|
|
440
|
+
pointerEvents: 'auto' | 'none';
|
|
441
|
+
/** `fill` and `stroke`, what an `<svg>` in the document is painted with
|
|
442
|
+
* (SVG 2, 13.2): a colour, `currentColor`, `none`, or a `url()` of a
|
|
443
|
+
* paint server in the drawing — and null where nothing has set one,
|
|
444
|
+
* which leaves the drawing its own. */
|
|
445
|
+
fill: string | null;
|
|
446
|
+
stroke: string | null;
|
|
293
447
|
borderCollapse: 'separate' | 'collapse';
|
|
294
448
|
/** `border-spacing`: between columns, and between rows. */
|
|
295
449
|
borderSpacing: number;
|
|
@@ -369,6 +523,26 @@ export interface ComputedStyle {
|
|
|
369
523
|
* multicol container, which this lays out as one column and which
|
|
370
524
|
* `line-clamp` does not clamp (CSS Overflow 4, 5.2). */
|
|
371
525
|
columns: number;
|
|
526
|
+
/** `column-count` where it is a number, and `column-width` where it is
|
|
527
|
+
* a length; null for `auto` (CSS Multi-column 1, 2 and 3). */
|
|
528
|
+
columnCount: number | null;
|
|
529
|
+
columnWidth: number | null;
|
|
530
|
+
/** Whether `column-gap` is `normal`: nothing in a flex box or a grid,
|
|
531
|
+
* and an em between the columns of a multicol container (4.1). */
|
|
532
|
+
columnGapNormal: boolean;
|
|
533
|
+
/** The fewest lines of a block a column may end on, and the fewest it
|
|
534
|
+
* may start with (CSS Fragmentation 3, 4.3). Inherited. */
|
|
535
|
+
orphans: number;
|
|
536
|
+
widows: number;
|
|
537
|
+
/** `break-inside`: whether a column break may fall inside the box;
|
|
538
|
+
* `avoid-column` and `avoid-page` are read as `avoid`. */
|
|
539
|
+
breakInside: 'auto' | 'avoid';
|
|
540
|
+
/** `column-fill`: whether the columns are balanced, or each filled
|
|
541
|
+
* before the next is started; `balance-all` is `balance`. */
|
|
542
|
+
columnFill: 'balance' | 'auto';
|
|
543
|
+
/** `column-span: all`: whether the box is set across all the columns of
|
|
544
|
+
* the multicol container it is in (CSS Multi-column 1, 6). */
|
|
545
|
+
columnSpan: boolean;
|
|
372
546
|
/** How a line cut by `overflow` ends: `clip`, or with an ellipsis. */
|
|
373
547
|
textOverflow: 'clip' | 'ellipsis';
|
|
374
548
|
/** `aspect-ratio`: a box's width over its height, where its height is
|
|
@@ -388,16 +562,28 @@ export interface ComputedStyle {
|
|
|
388
562
|
* its edges measured from the border box's top left, a null edge the
|
|
389
563
|
* border box's own (CSS 2.1 11.1.2). Null for `auto`. */
|
|
390
564
|
clip: ClipRect | null;
|
|
565
|
+
/** `clip-path`: the rectangle the element and all it holds show through
|
|
566
|
+
* (CSS Masking 1, 5.1); null for `none`, and for a shape this does not
|
|
567
|
+
* draw. */
|
|
568
|
+
clipPath: ClipPath | null;
|
|
391
569
|
opacity: number;
|
|
392
570
|
/** Where `translate` moves the box after layout (CSS Transforms 2): a
|
|
393
571
|
* length or a percentage of its own border box across and down; null
|
|
394
572
|
* for `none`. */
|
|
395
573
|
translate: [Len, Len] | null;
|
|
396
|
-
/**
|
|
397
|
-
*
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
574
|
+
/** `rotate`: a turn in the plane of the page, in degrees; null for
|
|
575
|
+
* `none`, and for a turn about an axis in the page. */
|
|
576
|
+
rotate: number | null;
|
|
577
|
+
/** `scale`: across and down; null for `none`. */
|
|
578
|
+
scale: [number, number] | null;
|
|
579
|
+
/** `transform`: its functions in the order written, the ones out of the
|
|
580
|
+
* plane left out (`css/transform.ts`); null for `none`. Any of the four
|
|
581
|
+
* makes the box a containing block and a stacking context, and paints it
|
|
582
|
+
* with the positioned boxes (CSS Transforms 1, 3; `transformed`). */
|
|
583
|
+
transform: TransformFunction[] | null;
|
|
584
|
+
/** `transform-origin`: the point a transform turns and scales about,
|
|
585
|
+
* from the border box's top left, a percentage of its width and height. */
|
|
586
|
+
transformOrigin: [Len, Len];
|
|
401
587
|
zIndex: number | 'auto';
|
|
402
588
|
/** A keyword, a length to raise the box by, or a percentage of its own
|
|
403
589
|
* line height. */
|
|
@@ -484,7 +670,7 @@ export interface ComputedStyle {
|
|
|
484
670
|
backgroundImage: string | null;
|
|
485
671
|
/** A background drawn rather than fetched: the first layer's linear
|
|
486
672
|
* gradient, where it is one. */
|
|
487
|
-
backgroundGradient:
|
|
673
|
+
backgroundGradient: Gradient | null;
|
|
488
674
|
/** How the first layer repeats across and down (CSS Backgrounds 3, 3.4):
|
|
489
675
|
* one of a few pairs made once, so a style copies no array. */
|
|
490
676
|
backgroundRepeat: BackgroundRepeat;
|
|
@@ -543,10 +729,17 @@ export interface ComputedStyle {
|
|
|
543
729
|
* set it (`decorate`). No property: the cascade works them out. */
|
|
544
730
|
underline: string | null;
|
|
545
731
|
underlineStyle: 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy';
|
|
546
|
-
/** And the underline's thickness and offset, the box's that set it
|
|
732
|
+
/** And the underline's thickness and offset, the box's that set it: the
|
|
733
|
+
* thickness the one used, which `auto` takes from that box's font size
|
|
734
|
+
* (`usedThickness`), and the offset null where it is `auto`. */
|
|
547
735
|
underlineThickness: number | null;
|
|
548
736
|
underlineOffset: number | null;
|
|
549
737
|
lineThrough: string | null;
|
|
738
|
+
/** And the line through's style and thickness, the box's that set it, as
|
|
739
|
+
* the underline's are. It has no offset to set: it is drawn where each
|
|
740
|
+
* font it crosses puts it (`paintRunRules`). */
|
|
741
|
+
lineThroughStyle: 'solid' | 'double' | 'dotted' | 'dashed' | 'wavy';
|
|
742
|
+
lineThroughThickness: number | null;
|
|
550
743
|
|
|
551
744
|
// flex — handed to yoga rather than interpreted here
|
|
552
745
|
flexDirection: 'row' | 'row-reverse' | 'column' | 'column-reverse';
|
|
@@ -653,6 +846,8 @@ export const INHERITED = [
|
|
|
653
846
|
'fontFeatureSettings',
|
|
654
847
|
'tabSize',
|
|
655
848
|
'tabSizeIsLength',
|
|
849
|
+
'orphans',
|
|
850
|
+
'widows',
|
|
656
851
|
'whiteSpace',
|
|
657
852
|
'overflowWrap',
|
|
658
853
|
'wordBreak',
|
|
@@ -664,6 +859,9 @@ export const INHERITED = [
|
|
|
664
859
|
'listStylePosition',
|
|
665
860
|
'listStyleImage',
|
|
666
861
|
'cursor',
|
|
862
|
+
'pointerEvents',
|
|
863
|
+
'fill',
|
|
864
|
+
'stroke',
|
|
667
865
|
'borderCollapse',
|
|
668
866
|
'borderSpacing',
|
|
669
867
|
'borderSpacingY',
|
|
@@ -700,6 +898,20 @@ export interface RootLook {
|
|
|
700
898
|
controlPadY: number;
|
|
701
899
|
controlBorder: number;
|
|
702
900
|
controlRadius: number;
|
|
901
|
+
/**
|
|
902
|
+
* The size the palette's widgets set their text at, `theme.fontSize` — and
|
|
903
|
+
* so the size of a control's text, whatever the text around it is: a
|
|
904
|
+
* browser's controls take a system font rather than their parent's, and
|
|
905
|
+
* this is the system's. Without it, `fontSize`.
|
|
906
|
+
*/
|
|
907
|
+
controlFontSize?: number;
|
|
908
|
+
/**
|
|
909
|
+
* The family they set it in, `theme.fontFamily`, for the same reason: a
|
|
910
|
+
* browser's system font is its own, Arial in Chrome whatever the page is
|
|
911
|
+
* set in. A `<textarea>` is `monoFamily`, a browser's `monospace`, as it
|
|
912
|
+
* is in Chrome. Without it, `fontFamily`.
|
|
913
|
+
*/
|
|
914
|
+
controlFontFamily?: string;
|
|
703
915
|
}
|
|
704
916
|
|
|
705
917
|
export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
@@ -744,6 +956,9 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
744
956
|
listStylePosition: 'outside',
|
|
745
957
|
listStyleImage: null,
|
|
746
958
|
cursor: null,
|
|
959
|
+
pointerEvents: 'auto',
|
|
960
|
+
fill: null,
|
|
961
|
+
stroke: null,
|
|
747
962
|
borderCollapse: 'separate',
|
|
748
963
|
// CSS's initial value; a `<table>` gets its 2px from the UA sheet, and
|
|
749
964
|
// an anonymous table, which no sheet names, has none
|
|
@@ -779,6 +994,14 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
779
994
|
webkitBoxAlign: null,
|
|
780
995
|
webkitBoxFlex: 0,
|
|
781
996
|
columns: 0,
|
|
997
|
+
columnCount: null,
|
|
998
|
+
columnWidth: null,
|
|
999
|
+
columnGapNormal: true,
|
|
1000
|
+
orphans: 2,
|
|
1001
|
+
widows: 2,
|
|
1002
|
+
breakInside: 'auto',
|
|
1003
|
+
columnFill: 'balance',
|
|
1004
|
+
columnSpan: false,
|
|
782
1005
|
textOverflow: 'clip',
|
|
783
1006
|
aspectRatio: null,
|
|
784
1007
|
objectFit: 'fill',
|
|
@@ -788,9 +1011,13 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
788
1011
|
overflowX: 'visible',
|
|
789
1012
|
overflowY: 'visible',
|
|
790
1013
|
clip: null,
|
|
1014
|
+
clipPath: null,
|
|
791
1015
|
opacity: 1,
|
|
792
1016
|
translate: null,
|
|
793
|
-
|
|
1017
|
+
rotate: null,
|
|
1018
|
+
scale: null,
|
|
1019
|
+
transform: null,
|
|
1020
|
+
transformOrigin: [{ pct: 50 }, { pct: 50 }],
|
|
794
1021
|
zIndex: AUTO,
|
|
795
1022
|
verticalAlign: 'baseline',
|
|
796
1023
|
|
|
@@ -880,6 +1107,8 @@ export function initialStyle(look: RootLook, scale = 1): ComputedStyle {
|
|
|
880
1107
|
underlineThickness: null,
|
|
881
1108
|
underlineOffset: null,
|
|
882
1109
|
lineThrough: null,
|
|
1110
|
+
lineThroughStyle: 'solid',
|
|
1111
|
+
lineThroughThickness: null,
|
|
883
1112
|
|
|
884
1113
|
flexDirection: 'row',
|
|
885
1114
|
flexWrap: 'nowrap',
|
|
@@ -989,6 +1218,8 @@ export function inherit(
|
|
|
989
1218
|
out.fontFeatureSettings = parent.fontFeatureSettings;
|
|
990
1219
|
out.tabSize = parent.tabSize;
|
|
991
1220
|
out.tabSizeIsLength = parent.tabSizeIsLength;
|
|
1221
|
+
out.orphans = parent.orphans;
|
|
1222
|
+
out.widows = parent.widows;
|
|
992
1223
|
out.whiteSpace = parent.whiteSpace;
|
|
993
1224
|
out.overflowWrap = parent.overflowWrap;
|
|
994
1225
|
out.wordBreak = parent.wordBreak;
|
|
@@ -1000,6 +1231,9 @@ export function inherit(
|
|
|
1000
1231
|
out.listStylePosition = parent.listStylePosition;
|
|
1001
1232
|
out.listStyleImage = parent.listStyleImage;
|
|
1002
1233
|
out.cursor = parent.cursor;
|
|
1234
|
+
out.pointerEvents = parent.pointerEvents;
|
|
1235
|
+
out.fill = parent.fill;
|
|
1236
|
+
out.stroke = parent.stroke;
|
|
1003
1237
|
out.borderCollapse = parent.borderCollapse;
|
|
1004
1238
|
out.borderSpacing = parent.borderSpacing;
|
|
1005
1239
|
out.borderSpacingY = parent.borderSpacingY;
|
|
@@ -1019,6 +1253,8 @@ export function inherit(
|
|
|
1019
1253
|
out.underlineThickness = parent.underlineThickness;
|
|
1020
1254
|
out.underlineOffset = parent.underlineOffset;
|
|
1021
1255
|
out.lineThrough = parent.lineThrough;
|
|
1256
|
+
out.lineThroughStyle = parent.lineThroughStyle;
|
|
1257
|
+
out.lineThroughThickness = parent.lineThroughThickness;
|
|
1022
1258
|
return out;
|
|
1023
1259
|
}
|
|
1024
1260
|
|
|
@@ -1053,6 +1289,8 @@ export const FIRST_LINE_INHERITED = [
|
|
|
1053
1289
|
'underlineThickness',
|
|
1054
1290
|
'underlineOffset',
|
|
1055
1291
|
'lineThrough',
|
|
1292
|
+
'lineThroughStyle',
|
|
1293
|
+
'lineThroughThickness',
|
|
1056
1294
|
] as const satisfies readonly (keyof ComputedStyle)[];
|
|
1057
1295
|
|
|
1058
1296
|
/**
|
|
@@ -1128,6 +1366,15 @@ const BORDER_WIDTH_KEYWORDS: Record<string, number> = {
|
|
|
1128
1366
|
thick: 5,
|
|
1129
1367
|
};
|
|
1130
1368
|
|
|
1369
|
+
/** `-webkit-` names that are the multicol properties' own. */
|
|
1370
|
+
const MULTICOL_ALIASES: Record<string, string> = {
|
|
1371
|
+
'-webkit-columns': 'columns',
|
|
1372
|
+
'-webkit-column-count': 'column-count',
|
|
1373
|
+
'-webkit-column-width': 'column-width',
|
|
1374
|
+
'-webkit-column-gap': 'column-gap',
|
|
1375
|
+
'-webkit-column-break-inside': 'break-inside',
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1131
1378
|
/**
|
|
1132
1379
|
* Apply one declaration to a style, in place.
|
|
1133
1380
|
*
|
|
@@ -1143,7 +1390,12 @@ export function applyDeclaration(
|
|
|
1143
1390
|
rawValue: string,
|
|
1144
1391
|
ctx: UnitContext,
|
|
1145
1392
|
): void {
|
|
1146
|
-
const
|
|
1393
|
+
const written = prop.toLowerCase();
|
|
1394
|
+
// the multicol properties under the names WebKit had them by, which
|
|
1395
|
+
// Blink still reads, each an alias of the property (`-webkit-column-
|
|
1396
|
+
// count` in its `css_properties.json5`): a page written for it alone
|
|
1397
|
+
// names no other
|
|
1398
|
+
const name = MULTICOL_ALIASES[written] ?? written;
|
|
1147
1399
|
let value = rawValue.trim();
|
|
1148
1400
|
if (!value) return;
|
|
1149
1401
|
// a logical property is the physical one it stands for, the CSS-wide
|
|
@@ -1271,17 +1523,63 @@ export function applyDeclaration(
|
|
|
1271
1523
|
case 'column-count':
|
|
1272
1524
|
case 'column-width':
|
|
1273
1525
|
case 'columns': {
|
|
1274
|
-
// `auto`, or a count, a width or both of them
|
|
1526
|
+
// `auto`, or a count, a width or both of them, in either order; what
|
|
1527
|
+
// the shorthand leaves out is `auto`
|
|
1275
1528
|
const bit = name === 'column-count' ? 1 : name === 'column-width' ? 2 : 3;
|
|
1276
|
-
let
|
|
1529
|
+
let count: number | null = null;
|
|
1530
|
+
let width: number | null = null;
|
|
1277
1531
|
for (const part of splitValue(value)) {
|
|
1278
1532
|
const v = part.toLowerCase();
|
|
1279
1533
|
if (v === 'auto') continue;
|
|
1280
|
-
if (/^\d+$/.test(v) && Number(v) >= 1)
|
|
1281
|
-
|
|
1282
|
-
|
|
1534
|
+
if (/^\d+$/.test(v) && Number(v) >= 1) {
|
|
1535
|
+
if (count !== null || !(bit & 1)) return;
|
|
1536
|
+
count = Number(v);
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const len = parseLength(part, ctx);
|
|
1540
|
+
if (typeof len !== 'number' || !(len > 0)) return;
|
|
1541
|
+
if (width !== null || !(bit & 2)) return;
|
|
1542
|
+
width = len;
|
|
1543
|
+
}
|
|
1544
|
+
if (bit & 1) style.columnCount = count;
|
|
1545
|
+
if (bit & 2) style.columnWidth = width;
|
|
1546
|
+
style.columns =
|
|
1547
|
+
(style.columnCount !== null ? 1 : 0) |
|
|
1548
|
+
(style.columnWidth !== null ? 2 : 0);
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
case 'orphans':
|
|
1552
|
+
case 'widows': {
|
|
1553
|
+
// a whole number of lines, one or more
|
|
1554
|
+
const v = value.trim();
|
|
1555
|
+
if (!/^\+?\d+$/.test(v) || Number(v) < 1) return;
|
|
1556
|
+
style[name] = Number(v);
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
case '-webkit-column-span':
|
|
1560
|
+
case 'column-span': {
|
|
1561
|
+
const v = value.trim().toLowerCase();
|
|
1562
|
+
if (v === 'all') style.columnSpan = true;
|
|
1563
|
+
else if (v === 'none') style.columnSpan = false;
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
case 'column-fill': {
|
|
1567
|
+
const v = value.trim().toLowerCase();
|
|
1568
|
+
if (v === 'auto') style.columnFill = 'auto';
|
|
1569
|
+
else if (v === 'balance' || v === 'balance-all') {
|
|
1570
|
+
style.columnFill = 'balance';
|
|
1571
|
+
}
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
case 'break-inside':
|
|
1575
|
+
case 'page-break-inside': {
|
|
1576
|
+
const v = value.trim().toLowerCase();
|
|
1577
|
+
if (v === 'auto') style.breakInside = 'auto';
|
|
1578
|
+
else if (/^avoid(?:-page|-column|-region)?$/.test(v)) {
|
|
1579
|
+
// the legacy property knows `avoid` alone
|
|
1580
|
+
if (name === 'page-break-inside' && v !== 'avoid') return;
|
|
1581
|
+
style.breakInside = 'avoid';
|
|
1283
1582
|
}
|
|
1284
|
-
style.columns = (style.columns & ~bit) | (set & bit);
|
|
1285
1583
|
return;
|
|
1286
1584
|
}
|
|
1287
1585
|
case '-webkit-box-flex': {
|
|
@@ -1520,6 +1818,12 @@ export function applyDeclaration(
|
|
|
1520
1818
|
if (clip !== undefined) style.clip = clip;
|
|
1521
1819
|
return;
|
|
1522
1820
|
}
|
|
1821
|
+
case 'clip-path':
|
|
1822
|
+
case '-webkit-clip-path': {
|
|
1823
|
+
const path = parseClipPath(value, ctx);
|
|
1824
|
+
if (path !== undefined) style.clipPath = path;
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1523
1827
|
case 'opacity': {
|
|
1524
1828
|
const a = parseAlpha(value);
|
|
1525
1829
|
if (a !== null) style.opacity = a;
|
|
@@ -1539,9 +1843,31 @@ export function applyDeclaration(
|
|
|
1539
1843
|
style.translate = [x, y ?? 0];
|
|
1540
1844
|
return;
|
|
1541
1845
|
}
|
|
1542
|
-
case '
|
|
1543
|
-
const
|
|
1544
|
-
if (
|
|
1846
|
+
case 'rotate': {
|
|
1847
|
+
const turn = parseRotate(value);
|
|
1848
|
+
if (turn !== undefined) style.rotate = turn;
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
case 'scale': {
|
|
1852
|
+
const factors = parseScale(value);
|
|
1853
|
+
if (factors !== undefined) style.scale = factors;
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
case 'transform':
|
|
1857
|
+
case '-webkit-transform': {
|
|
1858
|
+
const list = parseTransform(value, ctx);
|
|
1859
|
+
if (list !== undefined) style.transform = list;
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
case 'transform-origin':
|
|
1863
|
+
case '-webkit-transform-origin': {
|
|
1864
|
+
// across and down, as a position is, and a depth nothing here has
|
|
1865
|
+
const parts = splitValue(value);
|
|
1866
|
+
if (parts.length === 3) {
|
|
1867
|
+
if (typeof parseLength(parts.pop()!, ctx) !== 'number') return;
|
|
1868
|
+
} else if (parts.length > 2) return;
|
|
1869
|
+
const origin = positionPair(parts, ctx);
|
|
1870
|
+
if (origin) style.transformOrigin = origin;
|
|
1545
1871
|
return;
|
|
1546
1872
|
}
|
|
1547
1873
|
case 'visibility': {
|
|
@@ -1804,19 +2130,8 @@ export function applyDeclaration(
|
|
|
1804
2130
|
return;
|
|
1805
2131
|
}
|
|
1806
2132
|
case 'border-radius': {
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
const halves = value.split('/');
|
|
1810
|
-
if (halves.length > 2) return;
|
|
1811
|
-
const [horizontal, vertical] = halves.map((half) =>
|
|
1812
|
-
splitValue(half).map((p) => radiusOf(p, ctx)),
|
|
1813
|
-
);
|
|
1814
|
-
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
1815
|
-
if (!parts.length || parts.length > 4 || parts.includes(null)) return;
|
|
1816
|
-
}
|
|
1817
|
-
style.borderRadius = fourSides(horizontal as Len[]);
|
|
1818
|
-
const y = vertical ? fourSides(vertical as Len[]) : null;
|
|
1819
|
-
style.borderRadiusY = y && !sameRadii(style.borderRadius, y) ? y : null;
|
|
2133
|
+
const radii = parseRadii(value, ctx);
|
|
2134
|
+
if (radii) [style.borderRadius, style.borderRadiusY] = radii;
|
|
1820
2135
|
return;
|
|
1821
2136
|
}
|
|
1822
2137
|
|
|
@@ -2059,7 +2374,20 @@ export function applyDeclaration(
|
|
|
2059
2374
|
)
|
|
2060
2375
|
.filter(Boolean)
|
|
2061
2376
|
.join(', ');
|
|
2062
|
-
|
|
2377
|
+
// A list that ends in no generic family ends in the document's own
|
|
2378
|
+
// font: where no face in the list matches, the text is set in the
|
|
2379
|
+
// user agent's default (CSS Fonts 4, 5.1), which for a browser is its
|
|
2380
|
+
// standard font and here is the one the document is set in. The text
|
|
2381
|
+
// engine's own answer for a name nothing has is its platform's pick,
|
|
2382
|
+
// Verdana under fontconfig: the Zen Garden's 216 sets its summary in
|
|
2383
|
+
// Montserrat alone, which no machine here has, and Chrome sets it in
|
|
2384
|
+
// Times where ours was Verdana, a fifth wider
|
|
2385
|
+
const last = names[names.length - 1].trim();
|
|
2386
|
+
const generic =
|
|
2387
|
+
!/^['"]/.test(last) && GENERIC_FAMILY.has(last.toLowerCase());
|
|
2388
|
+
const fallback = generic ? null : (ctx.fallbackFamily?.() ?? null);
|
|
2389
|
+
const full = fallback ? `${list}, ${fallback}` : list;
|
|
2390
|
+
style.fontFamily = ctx.families ? ctx.families(full) : full;
|
|
2063
2391
|
return;
|
|
2064
2392
|
}
|
|
2065
2393
|
case 'font-size': {
|
|
@@ -2067,7 +2395,11 @@ export function applyDeclaration(
|
|
|
2067
2395
|
// handed a face at a size of millions shapes and caches glyphs that
|
|
2068
2396
|
// size, and a nest of `larger`s gets there on its own
|
|
2069
2397
|
const most = MAX_FONT_SIZE * ctx.scale;
|
|
2070
|
-
const kw = keywordFontSize(
|
|
2398
|
+
const kw = keywordFontSize(
|
|
2399
|
+
value,
|
|
2400
|
+
parent.fontSize,
|
|
2401
|
+
ctx.initial?.fontSize ?? ctx.rem,
|
|
2402
|
+
);
|
|
2071
2403
|
if (kw !== null) {
|
|
2072
2404
|
style.fontSize = Math.min(kw, most);
|
|
2073
2405
|
return;
|
|
@@ -2461,6 +2793,20 @@ export function applyDeclaration(
|
|
|
2461
2793
|
style.cursor = splitCommas(value)[0]?.trim().toLowerCase() || null;
|
|
2462
2794
|
return;
|
|
2463
2795
|
}
|
|
2796
|
+
case 'pointer-events': {
|
|
2797
|
+
// `none`, or one of the values SVG gives it, which are `auto` on
|
|
2798
|
+
// anything that is not SVG's to draw
|
|
2799
|
+
const v = value.trim().toLowerCase();
|
|
2800
|
+
if (v === 'none') style.pointerEvents = 'none';
|
|
2801
|
+
else if (POINTER_EVENTS.has(v)) style.pointerEvents = 'auto';
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2804
|
+
case 'fill':
|
|
2805
|
+
case 'stroke': {
|
|
2806
|
+
const paint = svgPaint(value);
|
|
2807
|
+
if (paint !== null) style[name] = paint;
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2464
2810
|
|
|
2465
2811
|
// --- lists --------------------------------------------------------------
|
|
2466
2812
|
case 'list-style': {
|
|
@@ -2633,7 +2979,11 @@ export function applyDeclaration(
|
|
|
2633
2979
|
const col = parts.length > 1 ? gap(parts[1]) : row;
|
|
2634
2980
|
if (row === null || col === null) return;
|
|
2635
2981
|
if (which !== 'column-gap') style.rowGap = row;
|
|
2636
|
-
if (which !== 'row-gap')
|
|
2982
|
+
if (which !== 'row-gap') {
|
|
2983
|
+
style.columnGap = col;
|
|
2984
|
+
const word = parts[parts.length - 1].toLowerCase();
|
|
2985
|
+
style.columnGapNormal = word === 'normal';
|
|
2986
|
+
}
|
|
2637
2987
|
return;
|
|
2638
2988
|
}
|
|
2639
2989
|
case 'grid-template-columns':
|
|
@@ -3110,6 +3460,12 @@ function alignKeyword(value: string): string | null {
|
|
|
3110
3460
|
return 'flex-end';
|
|
3111
3461
|
case 'first baseline':
|
|
3112
3462
|
return 'baseline';
|
|
3463
|
+
// By last baselines, which nothing here aligns: set where that falls
|
|
3464
|
+
// back to, the end (CSS Box Alignment 3, 4.2). Dropped, a row that asks
|
|
3465
|
+
// for it stretched its items, and a flex box among them was as tall as
|
|
3466
|
+
// the row, its lines spread down it.
|
|
3467
|
+
case 'last baseline':
|
|
3468
|
+
return 'flex-end';
|
|
3113
3469
|
case 'center':
|
|
3114
3470
|
return 'center';
|
|
3115
3471
|
case 'stretch':
|
|
@@ -3781,7 +4137,7 @@ function backgroundImageOf(
|
|
|
3781
4137
|
ctx: UnitContext,
|
|
3782
4138
|
): BackgroundImage | undefined {
|
|
3783
4139
|
const v = text.trim();
|
|
3784
|
-
if (IMAGE_FUNCTION.test(v.toLowerCase())) return
|
|
4140
|
+
if (IMAGE_FUNCTION.test(v.toLowerCase())) return parseGradient(v, ctx);
|
|
3785
4141
|
return parseUrl(v);
|
|
3786
4142
|
}
|
|
3787
4143
|
|
|
@@ -3803,7 +4159,7 @@ function layerValues<T>(
|
|
|
3803
4159
|
interface BackgroundLayer {
|
|
3804
4160
|
color: string | null;
|
|
3805
4161
|
image: string | null;
|
|
3806
|
-
gradient:
|
|
4162
|
+
gradient: Gradient | null;
|
|
3807
4163
|
repeat: ComputedStyle['backgroundRepeat'];
|
|
3808
4164
|
size: ComputedStyle['backgroundSize'];
|
|
3809
4165
|
attachment: ComputedStyle['backgroundAttachment'];
|
|
@@ -3819,7 +4175,7 @@ interface BackgroundLayer {
|
|
|
3819
4175
|
* is dropped rather than resetting the background it meant to replace
|
|
3820
4176
|
* (CSS 2.1 4.2). What CSS3 adds is read too, so that a declaration a
|
|
3821
4177
|
* browser keeps is kept: `/ cover` after the position, `space` and `round`,
|
|
3822
|
-
* a gradient
|
|
4178
|
+
* and a gradient, over the layer's colour.
|
|
3823
4179
|
*/
|
|
3824
4180
|
function readBackgroundLayer(
|
|
3825
4181
|
text: string,
|
|
@@ -3854,7 +4210,7 @@ function readBackgroundLayer(
|
|
|
3854
4210
|
const url = parseUrl(part);
|
|
3855
4211
|
if (url === undefined) return null;
|
|
3856
4212
|
layer.image = url;
|
|
3857
|
-
} else if (v !== 'none') layer.gradient =
|
|
4213
|
+
} else if (v !== 'none') layer.gradient = parseGradient(part, ctx);
|
|
3858
4214
|
i += 1;
|
|
3859
4215
|
} else if (v === 'repeat-x' || v === 'repeat-y' || REPEATS.has(v)) {
|
|
3860
4216
|
if (!once(2)) return null;
|
|
@@ -3918,10 +4274,8 @@ function readBackgroundLayer(
|
|
|
3918
4274
|
|
|
3919
4275
|
/**
|
|
3920
4276
|
* A `linear-gradient()`, or null for any other image function and for one
|
|
3921
|
-
* that is no gradient: a direction by angle, by side or by corner,
|
|
3922
|
-
* colour
|
|
3923
|
-
* not honoured, and at least two stops, each with no position, one, or two;
|
|
3924
|
-
* a bare percentage between stops, a hint, is passed over.
|
|
4277
|
+
* that is no gradient: a direction by angle, by side or by corner, and its
|
|
4278
|
+
* colour stops.
|
|
3925
4279
|
*/
|
|
3926
4280
|
function parseLinearGradient(
|
|
3927
4281
|
text: string,
|
|
@@ -3930,14 +4284,13 @@ function parseLinearGradient(
|
|
|
3930
4284
|
const m = /^linear-gradient\((.*)\)$/is.exec(text.trim());
|
|
3931
4285
|
if (!m) return null;
|
|
3932
4286
|
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
3933
|
-
const out: LinearGradient = {
|
|
3934
|
-
|
|
3935
|
-
.
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
.trim();
|
|
4287
|
+
const out: LinearGradient = {
|
|
4288
|
+
kind: 'linear',
|
|
4289
|
+
angle: Math.PI,
|
|
4290
|
+
corner: null,
|
|
4291
|
+
stops: [],
|
|
4292
|
+
};
|
|
4293
|
+
const first = withoutInterpolation(args[0] ?? '');
|
|
3941
4294
|
let from = 0;
|
|
3942
4295
|
if (!first || first.startsWith('to ')) {
|
|
3943
4296
|
from = 1;
|
|
@@ -3973,7 +4326,120 @@ function parseLinearGradient(
|
|
|
3973
4326
|
from = 1;
|
|
3974
4327
|
}
|
|
3975
4328
|
}
|
|
3976
|
-
|
|
4329
|
+
const stops = parseStops(args.slice(from), ctx);
|
|
4330
|
+
if (!stops) return null;
|
|
4331
|
+
out.stops = stops;
|
|
4332
|
+
return out;
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4335
|
+
/**
|
|
4336
|
+
* A `radial-gradient()` (CSS Images 3, 3.2.1), or null for anything else:
|
|
4337
|
+
* an ending shape, `circle` or `ellipse`, and its size, in either order —
|
|
4338
|
+
* a keyword for the side or corner of the box it reaches, one length for a
|
|
4339
|
+
* circle's radius, or two lengths or percentages for an ellipse's — then
|
|
4340
|
+
* `at` and its centre, a position as `background-position` writes one;
|
|
4341
|
+
* each of the three may be left out, for an ellipse through the farthest
|
|
4342
|
+
* corner from the middle of the box. A colour interpolation method is read
|
|
4343
|
+
* and not honoured, and the stops are a linear gradient's, spaced along
|
|
4344
|
+
* the ray from the centre to the shape's edge.
|
|
4345
|
+
*/
|
|
4346
|
+
function parseRadialGradient(
|
|
4347
|
+
text: string,
|
|
4348
|
+
ctx: UnitContext,
|
|
4349
|
+
): RadialGradient | null {
|
|
4350
|
+
const m = /^radial-gradient\((.*)\)$/is.exec(text.trim());
|
|
4351
|
+
if (!m) return null;
|
|
4352
|
+
const args = splitCommas(m[1]).map((a) => a.trim());
|
|
4353
|
+
const out: RadialGradient = {
|
|
4354
|
+
kind: 'radial',
|
|
4355
|
+
circle: false,
|
|
4356
|
+
extent: null,
|
|
4357
|
+
radii: null,
|
|
4358
|
+
at: [{ pct: 50 }, { pct: 50 }],
|
|
4359
|
+
stops: [],
|
|
4360
|
+
};
|
|
4361
|
+
const first = withoutInterpolation(args[0] ?? '');
|
|
4362
|
+
let from = 0;
|
|
4363
|
+
if (!first) from = 1;
|
|
4364
|
+
else if (parseColor(splitValue(first)[0]) === null) {
|
|
4365
|
+
from = 1;
|
|
4366
|
+
const words = splitValue(first);
|
|
4367
|
+
const at = words.indexOf('at');
|
|
4368
|
+
if (at >= 0) {
|
|
4369
|
+
const position = positionPair(words.slice(at + 1), ctx);
|
|
4370
|
+
if (!position) return null;
|
|
4371
|
+
out.at = position;
|
|
4372
|
+
}
|
|
4373
|
+
let shape: string | null = null;
|
|
4374
|
+
const sizes: Len[] = [];
|
|
4375
|
+
// the size is one run of words, the shape's keyword before or after it
|
|
4376
|
+
let sized = false;
|
|
4377
|
+
for (const word of at >= 0 ? words.slice(0, at) : words) {
|
|
4378
|
+
if (word === 'circle' || word === 'ellipse') {
|
|
4379
|
+
if (shape) return null;
|
|
4380
|
+
shape = word;
|
|
4381
|
+
if (sizes.length || out.extent) sized = true;
|
|
4382
|
+
continue;
|
|
4383
|
+
}
|
|
4384
|
+
if (sized || out.extent) return null;
|
|
4385
|
+
if (RADIAL_EXTENTS.has(word)) {
|
|
4386
|
+
if (sizes.length) return null;
|
|
4387
|
+
out.extent = word as RadialExtent;
|
|
4388
|
+
continue;
|
|
4389
|
+
}
|
|
4390
|
+
const len = parseLength(word, ctx);
|
|
4391
|
+
if (len === null || len === AUTO || !notNegative(len)) return null;
|
|
4392
|
+
sizes.push(len);
|
|
4393
|
+
}
|
|
4394
|
+
if (sizes.length === 1) {
|
|
4395
|
+
// a circle's radius is a length: a percentage of what, the spec asks
|
|
4396
|
+
const [r] = sizes;
|
|
4397
|
+
if (shape === 'ellipse' || typeof r !== 'number') {
|
|
4398
|
+
return null;
|
|
4399
|
+
}
|
|
4400
|
+
out.circle = true;
|
|
4401
|
+
out.radii = [r, r];
|
|
4402
|
+
} else if (sizes.length === 2) {
|
|
4403
|
+
if (shape === 'circle') return null;
|
|
4404
|
+
out.radii = [sizes[0], sizes[1]];
|
|
4405
|
+
} else if (sizes.length) return null;
|
|
4406
|
+
else {
|
|
4407
|
+
out.circle = shape === 'circle';
|
|
4408
|
+
out.extent ??= 'farthest-corner';
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
const stops = parseStops(args.slice(from), ctx);
|
|
4412
|
+
if (!stops) return null;
|
|
4413
|
+
out.stops = stops;
|
|
4414
|
+
return out;
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
const RADIAL_EXTENTS = new Set([
|
|
4418
|
+
'closest-side',
|
|
4419
|
+
'closest-corner',
|
|
4420
|
+
'farthest-side',
|
|
4421
|
+
'farthest-corner',
|
|
4422
|
+
]);
|
|
4423
|
+
|
|
4424
|
+
/** A gradient's first argument, lower-cased, without the colour
|
|
4425
|
+
* interpolation method CSS Images 4 adds to it (`in oklab`, which
|
|
4426
|
+
* Tailwind 4 writes): read, and not honoured. */
|
|
4427
|
+
function withoutInterpolation(arg: string): string {
|
|
4428
|
+
return arg
|
|
4429
|
+
.toLowerCase()
|
|
4430
|
+
.replace(
|
|
4431
|
+
/\bin\s+[a-z-]+(?:\s+(?:shorter|longer|increasing|decreasing)\s+hue)?/,
|
|
4432
|
+
'',
|
|
4433
|
+
)
|
|
4434
|
+
.trim();
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
/** A gradient's colour stops, at least two, each with no position, one or
|
|
4438
|
+
* two; a bare length between two stops, a hint, is passed over. Null
|
|
4439
|
+
* where an argument is none of those. */
|
|
4440
|
+
function parseStops(args: string[], ctx: UnitContext): GradientStop[] | null {
|
|
4441
|
+
const stops: GradientStop[] = [];
|
|
4442
|
+
for (const arg of args) {
|
|
3977
4443
|
const parts = splitValue(arg);
|
|
3978
4444
|
const color = parseColor(parts[0] ?? '');
|
|
3979
4445
|
if (color === null) {
|
|
@@ -3984,14 +4450,19 @@ function parseLinearGradient(
|
|
|
3984
4450
|
if (parts.length > 3) return null;
|
|
3985
4451
|
const at = parts.slice(1).map((p) => parseLength(p, ctx));
|
|
3986
4452
|
if (at.some((p) => p === null || p === AUTO)) return null;
|
|
3987
|
-
if (!at.length)
|
|
3988
|
-
for (const p of at)
|
|
4453
|
+
if (!at.length) stops.push({ color, at: null });
|
|
4454
|
+
for (const p of at) stops.push({ color, at: p as Len });
|
|
3989
4455
|
}
|
|
3990
|
-
return
|
|
4456
|
+
return stops.length >= 2 ? stops : null;
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4459
|
+
/** A gradient function's image, where it is one this draws. */
|
|
4460
|
+
function parseGradient(text: string, ctx: UnitContext): Gradient | null {
|
|
4461
|
+
return parseLinearGradient(text, ctx) ?? parseRadialGradient(text, ctx);
|
|
3991
4462
|
}
|
|
3992
4463
|
|
|
3993
4464
|
/** The images CSS3 has beyond `url()`, which a layer may name: a linear
|
|
3994
|
-
* gradient
|
|
4465
|
+
* and a radial gradient are drawn, and the rest draw as nothing. */
|
|
3995
4466
|
const IMAGE_FUNCTION =
|
|
3996
4467
|
/^(?:-(?:webkit|moz|o|ms)-)?(?:(?:repeating-)?(?:linear|radial|conic)-gradient|gradient|image-set|cross-fade|element|paint)\(/;
|
|
3997
4468
|
|
|
@@ -4032,87 +4503,6 @@ function whiteSpaceOf(collapse: Collapse, wrap: boolean): WhiteSpace {
|
|
|
4032
4503
|
return 'pre-line';
|
|
4033
4504
|
}
|
|
4034
4505
|
|
|
4035
|
-
/**
|
|
4036
|
-
* The translation a `transform` makes: the sum of its translate functions'
|
|
4037
|
-
* and its matrices' — `translate(-50%, -50%)`, Tailwind 3's `translate(
|
|
4038
|
-
* var(--tw-translate-x), var(--tw-translate-y)) rotate(…) skewX(…) …` —
|
|
4039
|
-
* with the functions that rotate, scale, skew or add perspective read and
|
|
4040
|
-
* not drawn. Null for `none`, and undefined for what is no transform list,
|
|
4041
|
-
* which drops the declaration.
|
|
4042
|
-
*/
|
|
4043
|
-
function transformTranslation(
|
|
4044
|
-
value: string,
|
|
4045
|
-
ctx: UnitContext,
|
|
4046
|
-
): [Len, Len] | null | undefined {
|
|
4047
|
-
if (value.trim().toLowerCase() === 'none') return null;
|
|
4048
|
-
let x: Len = 0;
|
|
4049
|
-
let y: Len = 0;
|
|
4050
|
-
const parts = splitValue(value);
|
|
4051
|
-
if (!parts.length) return undefined;
|
|
4052
|
-
for (const part of parts) {
|
|
4053
|
-
const m = /^([a-z0-9]+)\((.*)\)$/is.exec(part.trim());
|
|
4054
|
-
if (!m) return undefined;
|
|
4055
|
-
const name = m[1].toLowerCase();
|
|
4056
|
-
const args = splitCommas(m[2]).map((a) => a.trim());
|
|
4057
|
-
let dx: Len | null = 0;
|
|
4058
|
-
let dy: Len | null = 0;
|
|
4059
|
-
if (name === 'translate' || name === 'translate3d') {
|
|
4060
|
-
dx = parseLength(args[0] ?? '', ctx);
|
|
4061
|
-
dy = args[1] === undefined ? 0 : parseLength(args[1], ctx);
|
|
4062
|
-
} else if (name === 'translatex') dx = parseLength(args[0] ?? '', ctx);
|
|
4063
|
-
else if (name === 'translatey') dy = parseLength(args[0] ?? '', ctx);
|
|
4064
|
-
else if (name === 'matrix' || name === 'matrix3d') {
|
|
4065
|
-
// the translation is the last column's
|
|
4066
|
-
const n = args.map(Number);
|
|
4067
|
-
if (n.length !== (name === 'matrix' ? 6 : 16) || n.some(isNaN)) {
|
|
4068
|
-
return undefined;
|
|
4069
|
-
}
|
|
4070
|
-
[dx, dy] = name === 'matrix' ? [n[4], n[5]] : [n[12], n[13]];
|
|
4071
|
-
} else if (!TRANSFORMS.has(name)) return undefined;
|
|
4072
|
-
if (dx === null || dx === AUTO || dy === null || dy === AUTO) {
|
|
4073
|
-
return undefined;
|
|
4074
|
-
}
|
|
4075
|
-
const sx = addLen(x, dx);
|
|
4076
|
-
const sy = addLen(y, dy);
|
|
4077
|
-
if (sx === null || sy === null) return undefined;
|
|
4078
|
-
x = sx;
|
|
4079
|
-
y = sy;
|
|
4080
|
-
}
|
|
4081
|
-
return [x, y];
|
|
4082
|
-
}
|
|
4083
|
-
|
|
4084
|
-
/** The transform functions read and not drawn. */
|
|
4085
|
-
const TRANSFORMS = new Set([
|
|
4086
|
-
'translatez',
|
|
4087
|
-
'rotate',
|
|
4088
|
-
'rotatex',
|
|
4089
|
-
'rotatey',
|
|
4090
|
-
'rotatez',
|
|
4091
|
-
'rotate3d',
|
|
4092
|
-
'scale',
|
|
4093
|
-
'scalex',
|
|
4094
|
-
'scaley',
|
|
4095
|
-
'scalez',
|
|
4096
|
-
'scale3d',
|
|
4097
|
-
'skew',
|
|
4098
|
-
'skewx',
|
|
4099
|
-
'skewy',
|
|
4100
|
-
'perspective',
|
|
4101
|
-
]);
|
|
4102
|
-
|
|
4103
|
-
/** Two lengths added, a percentage and a length kept apart; null where
|
|
4104
|
-
* one is a `min()` or a `max()`, which a sum cannot be kept of. */
|
|
4105
|
-
function addLen(a: Len, b: Len): Len | null {
|
|
4106
|
-
if (a === AUTO || b === AUTO) return null;
|
|
4107
|
-
if (typeof a === 'number' && typeof b === 'number') return a + b;
|
|
4108
|
-
if (a === 0) return b;
|
|
4109
|
-
if (b === 0) return a;
|
|
4110
|
-
const pa: Pct = typeof a === 'number' ? { pct: 0, px: a } : a;
|
|
4111
|
-
const pb: Pct = typeof b === 'number' ? { pct: 0, px: b } : b;
|
|
4112
|
-
if (pa.of || pb.of) return null;
|
|
4113
|
-
return { pct: pa.pct + pb.pct, px: (pa.px ?? 0) + (pb.px ?? 0) };
|
|
4114
|
-
}
|
|
4115
|
-
|
|
4116
4506
|
/** An intrinsic size keyword, with the prefixes browsers still read, or
|
|
4117
4507
|
* `fit-content()` of a length or a percentage (CSS Sizing 3, 3.1) — a
|
|
4118
4508
|
* width's, where a height's is its content height as the keyword is;
|
|
@@ -4307,8 +4697,11 @@ function applyFontShorthand(
|
|
|
4307
4697
|
// dropped whole, as CSS drops any value it cannot read — a line height
|
|
4308
4698
|
// below nought included: `font: 4em/-2em serif` set the text at 4em.
|
|
4309
4699
|
const size =
|
|
4310
|
-
keywordFontSize(
|
|
4311
|
-
|
|
4700
|
+
keywordFontSize(
|
|
4701
|
+
sizeText ?? '',
|
|
4702
|
+
parent.fontSize,
|
|
4703
|
+
ctx.initial?.fontSize ?? ctx.rem,
|
|
4704
|
+
) ?? parseLength(sizeText ?? '', { ...ctx, em: parent.fontSize });
|
|
4312
4705
|
if (!family || size === null || size === AUTO) return;
|
|
4313
4706
|
if (lineText && negativeLength(lineText, ctx)) return;
|
|
4314
4707
|
// What the shorthand does not name goes back to its initial value rather
|
|
@@ -4748,6 +5141,33 @@ function splitTopLevelSlash(value: string): string[] {
|
|
|
4748
5141
|
|
|
4749
5142
|
// --- logical properties ----------------------------------------------------
|
|
4750
5143
|
|
|
5144
|
+
/** `pointer-events`' values besides `none` (CSS UI 4, SVG 2 15.6). */
|
|
5145
|
+
const POINTER_EVENTS = new Set([
|
|
5146
|
+
'auto',
|
|
5147
|
+
'bounding-box',
|
|
5148
|
+
'visiblepainted',
|
|
5149
|
+
'visiblefill',
|
|
5150
|
+
'visiblestroke',
|
|
5151
|
+
'visible',
|
|
5152
|
+
'painted',
|
|
5153
|
+
'fill',
|
|
5154
|
+
'stroke',
|
|
5155
|
+
'all',
|
|
5156
|
+
]);
|
|
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
|
+
|
|
4751
5171
|
const OBJECT_FITS = new Set(['fill', 'contain', 'cover', 'none', 'scale-down']);
|
|
4752
5172
|
|
|
4753
5173
|
/**
|
|
@@ -4839,6 +5259,29 @@ function radiusOf(text: string, ctx: UnitContext): Len | null {
|
|
|
4839
5259
|
return len.pct < 0 && !len.px && !len.of ? null : len;
|
|
4840
5260
|
}
|
|
4841
5261
|
|
|
5262
|
+
/**
|
|
5263
|
+
* `border-radius`'s value: one to four radii, and after a `/` one to four
|
|
5264
|
+
* more for the vertical radii where they differ, elliptical corners — the
|
|
5265
|
+
* radii across, and the ones down, null where they are the same values.
|
|
5266
|
+
* Null for anything else.
|
|
5267
|
+
*/
|
|
5268
|
+
function parseRadii(
|
|
5269
|
+
value: string,
|
|
5270
|
+
ctx: UnitContext,
|
|
5271
|
+
): [[Len, Len, Len, Len], [Len, Len, Len, Len] | null] | null {
|
|
5272
|
+
const halves = value.split('/');
|
|
5273
|
+
if (halves.length > 2) return null;
|
|
5274
|
+
const [horizontal, vertical] = halves.map((half) =>
|
|
5275
|
+
splitValue(half).map((p) => radiusOf(p, ctx)),
|
|
5276
|
+
);
|
|
5277
|
+
for (const parts of vertical ? [horizontal, vertical] : [horizontal]) {
|
|
5278
|
+
if (!parts.length || parts.length > 4 || parts.includes(null)) return null;
|
|
5279
|
+
}
|
|
5280
|
+
const across = fourSides(horizontal as Len[]);
|
|
5281
|
+
const down = vertical ? fourSides(vertical as Len[]) : null;
|
|
5282
|
+
return [across, down && !sameRadii(across, down) ? down : null];
|
|
5283
|
+
}
|
|
5284
|
+
|
|
4842
5285
|
/** Whether two corners' radii are the same values. */
|
|
4843
5286
|
function sameRadii(a: readonly Len[], b: readonly Len[]): boolean {
|
|
4844
5287
|
for (let i = 0; i < 4; i += 1) {
|
|
@@ -5081,10 +5524,15 @@ const INHERITED_NAMES = new Set<string>([
|
|
|
5081
5524
|
'list-style-position',
|
|
5082
5525
|
'list-style-image',
|
|
5083
5526
|
'cursor',
|
|
5527
|
+
'pointer-events',
|
|
5528
|
+
'fill',
|
|
5529
|
+
'stroke',
|
|
5084
5530
|
'border-collapse',
|
|
5085
5531
|
'border-spacing',
|
|
5086
5532
|
'empty-cells',
|
|
5087
5533
|
'quotes',
|
|
5534
|
+
'orphans',
|
|
5535
|
+
'widows',
|
|
5088
5536
|
]);
|
|
5089
5537
|
|
|
5090
5538
|
export function isInherited(name: string): boolean {
|
|
@@ -5239,6 +5687,9 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5239
5687
|
'list-style-position': ['listStylePosition'],
|
|
5240
5688
|
'list-style-image': ['listStyleImage'],
|
|
5241
5689
|
cursor: ['cursor'],
|
|
5690
|
+
'pointer-events': ['pointerEvents'],
|
|
5691
|
+
fill: ['fill'],
|
|
5692
|
+
stroke: ['stroke'],
|
|
5242
5693
|
'border-collapse': ['borderCollapse'],
|
|
5243
5694
|
'caption-side': ['captionSide'],
|
|
5244
5695
|
'empty-cells': ['emptyCells'],
|
|
@@ -5307,9 +5758,16 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5307
5758
|
'-webkit-box-pack': ['webkitBoxPack'],
|
|
5308
5759
|
'-webkit-box-align': ['webkitBoxAlign'],
|
|
5309
5760
|
'-webkit-box-flex': ['webkitBoxFlex'],
|
|
5310
|
-
columns: ['columns'],
|
|
5311
|
-
'column-count': ['columns'],
|
|
5312
|
-
'column-width': ['columns'],
|
|
5761
|
+
columns: ['columns', 'columnCount', 'columnWidth'],
|
|
5762
|
+
'column-count': ['columns', 'columnCount'],
|
|
5763
|
+
'column-width': ['columns', 'columnWidth'],
|
|
5764
|
+
orphans: ['orphans'],
|
|
5765
|
+
widows: ['widows'],
|
|
5766
|
+
'break-inside': ['breakInside'],
|
|
5767
|
+
'column-fill': ['columnFill'],
|
|
5768
|
+
'column-span': ['columnSpan'],
|
|
5769
|
+
'-webkit-column-span': ['columnSpan'],
|
|
5770
|
+
'page-break-inside': ['breakInside'],
|
|
5313
5771
|
'text-overflow': ['textOverflow'],
|
|
5314
5772
|
// a flex box's and its items', and a grid's
|
|
5315
5773
|
'flex-direction': ['flexDirection'],
|
|
@@ -5324,12 +5782,12 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5324
5782
|
'flex-shrink': ['flexShrink'],
|
|
5325
5783
|
'flex-basis': ['flexBasis'],
|
|
5326
5784
|
order: ['order'],
|
|
5327
|
-
gap: ['rowGap', 'columnGap'],
|
|
5785
|
+
gap: ['rowGap', 'columnGap', 'columnGapNormal'],
|
|
5328
5786
|
'row-gap': ['rowGap'],
|
|
5329
|
-
'column-gap': ['columnGap'],
|
|
5330
|
-
'grid-gap': ['rowGap', 'columnGap'],
|
|
5787
|
+
'column-gap': ['columnGap', 'columnGapNormal'],
|
|
5788
|
+
'grid-gap': ['rowGap', 'columnGap', 'columnGapNormal'],
|
|
5331
5789
|
'grid-row-gap': ['rowGap'],
|
|
5332
|
-
'grid-column-gap': ['columnGap'],
|
|
5790
|
+
'grid-column-gap': ['columnGap', 'columnGapNormal'],
|
|
5333
5791
|
'grid-template-columns': ['gridColumns'],
|
|
5334
5792
|
'grid-template-rows': ['gridRows'],
|
|
5335
5793
|
'grid-auto-rows': ['gridAutoRows'],
|
|
@@ -5424,11 +5882,18 @@ const INHERIT_TARGETS: Record<string, readonly (keyof ComputedStyle)[]> = {
|
|
|
5424
5882
|
clear: ['clear'],
|
|
5425
5883
|
overflow: ['overflowX', 'overflowY'],
|
|
5426
5884
|
clip: ['clip'],
|
|
5885
|
+
'clip-path': ['clipPath'],
|
|
5886
|
+
'-webkit-clip-path': ['clipPath'],
|
|
5427
5887
|
'overflow-x': ['overflowX'],
|
|
5428
5888
|
'overflow-y': ['overflowY'],
|
|
5429
5889
|
opacity: ['opacity'],
|
|
5430
5890
|
translate: ['translate'],
|
|
5431
|
-
|
|
5891
|
+
rotate: ['rotate'],
|
|
5892
|
+
scale: ['scale'],
|
|
5893
|
+
transform: ['transform'],
|
|
5894
|
+
'-webkit-transform': ['transform'],
|
|
5895
|
+
'transform-origin': ['transformOrigin'],
|
|
5896
|
+
'-webkit-transform-origin': ['transformOrigin'],
|
|
5432
5897
|
'z-index': ['zIndex'],
|
|
5433
5898
|
'vertical-align': ['verticalAlign'],
|
|
5434
5899
|
'text-decoration': [
|
|
@@ -5491,7 +5956,7 @@ export function decorate(style: ComputedStyle): void {
|
|
|
5491
5956
|
style.color,
|
|
5492
5957
|
);
|
|
5493
5958
|
style.underlineStyle = style.textDecorationStyle;
|
|
5494
|
-
style.underlineThickness = style
|
|
5959
|
+
style.underlineThickness = usedThickness(style);
|
|
5495
5960
|
style.underlineOffset = style.textUnderlineOffset;
|
|
5496
5961
|
}
|
|
5497
5962
|
if (lines.includes('line-through')) {
|
|
@@ -5499,9 +5964,30 @@ export function decorate(style: ComputedStyle): void {
|
|
|
5499
5964
|
style.textDecorationColor ?? 'currentColor',
|
|
5500
5965
|
style.color,
|
|
5501
5966
|
);
|
|
5967
|
+
style.lineThroughStyle = style.textDecorationStyle;
|
|
5968
|
+
style.lineThroughThickness = usedThickness(style);
|
|
5502
5969
|
}
|
|
5503
5970
|
}
|
|
5504
5971
|
|
|
5972
|
+
/**
|
|
5973
|
+
* How thick the lines a box decorates its text with are. `auto` leaves it
|
|
5974
|
+
* to the user agent, which is to take it from the font where the font says
|
|
5975
|
+
* (CSS Text Decoration 4, 2.4.1); neither text engine here reports a face's
|
|
5976
|
+
* underline, so it is a part of the font size, Blink's tenth — of the size
|
|
5977
|
+
* of the box that set the decoration, which draws one line of one thickness
|
|
5978
|
+
* through everything inside it (2.9). A pixel for every size drew the dots
|
|
5979
|
+
* under the `<abbr>` of a 140px title as a hairline. `from-font` is `auto`
|
|
5980
|
+
* for the same want of a metric, a length is itself on whole pixels, and
|
|
5981
|
+
* no line is thinner than one.
|
|
5982
|
+
*/
|
|
5983
|
+
function usedThickness(style: ComputedStyle): number {
|
|
5984
|
+
const length = style.textDecorationThickness;
|
|
5985
|
+
return Math.max(
|
|
5986
|
+
1,
|
|
5987
|
+
length === null ? style.fontSize / 10 : Math.round(length),
|
|
5988
|
+
);
|
|
5989
|
+
}
|
|
5990
|
+
|
|
5505
5991
|
/**
|
|
5506
5992
|
* The absolutely positioned styles that were inline-level before they were
|
|
5507
5993
|
* made blocks: where such a box would have been in flow is on its line,
|
|
@@ -5617,3 +6103,55 @@ export function blockify(style: ComputedStyle, inFlexContainer: boolean): void {
|
|
|
5617
6103
|
return;
|
|
5618
6104
|
}
|
|
5619
6105
|
}
|
|
6106
|
+
|
|
6107
|
+
/**
|
|
6108
|
+
* The `display` a `<button>` is laid out with, which is not always the one
|
|
6109
|
+
* it was given (HTML's rendering section, button layout, 15.5.3): a flex
|
|
6110
|
+
* box or a grid is that, and of the rest, "a value such that the outer
|
|
6111
|
+
* display type is 'inline'" behaves as `inline-block` and anything else as
|
|
6112
|
+
* `flow-root`. Blink computes them so — `inline-block` for `inline` and
|
|
6113
|
+
* `inline-table`, `block` for `table` and `list-item` — and makes the one
|
|
6114
|
+
* block flow of each; the formatting context is the layout's to give every
|
|
6115
|
+
* button that is a block (`establishesBFC`).
|
|
6116
|
+
*
|
|
6117
|
+
* Left an inline box, a button around a block was broken in two around it,
|
|
6118
|
+
* as any inline box is: as wide as the line, its `height` applied to
|
|
6119
|
+
* nothing, and its border and padding drawn above and below the line it
|
|
6120
|
+
* opened. A table's part is left what it is, where the two disagree: a
|
|
6121
|
+
* block to the text, an inline-block in Blink. After `blockify`, since a
|
|
6122
|
+
* float, an absolute box and a flex item are blocks first.
|
|
6123
|
+
*/
|
|
6124
|
+
export function settleButton(style: ComputedStyle): void {
|
|
6125
|
+
switch (style.display) {
|
|
6126
|
+
case 'inline':
|
|
6127
|
+
case 'inline-table':
|
|
6128
|
+
style.display = 'inline-block';
|
|
6129
|
+
return;
|
|
6130
|
+
case 'list-item':
|
|
6131
|
+
case 'table':
|
|
6132
|
+
style.display = 'block';
|
|
6133
|
+
return;
|
|
6134
|
+
default:
|
|
6135
|
+
return;
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
/** The generic font families, which a list ending in one falls back
|
|
6140
|
+
* through already, in the text engine (CSS Fonts 4, 4.2). */
|
|
6141
|
+
const GENERIC_FAMILY = new Set([
|
|
6142
|
+
'serif',
|
|
6143
|
+
'sans-serif',
|
|
6144
|
+
'monospace',
|
|
6145
|
+
'cursive',
|
|
6146
|
+
'fantasy',
|
|
6147
|
+
'system-ui',
|
|
6148
|
+
'ui-serif',
|
|
6149
|
+
'ui-sans-serif',
|
|
6150
|
+
'ui-monospace',
|
|
6151
|
+
'ui-rounded',
|
|
6152
|
+
'math',
|
|
6153
|
+
'emoji',
|
|
6154
|
+
'fangsong',
|
|
6155
|
+
'-apple-system',
|
|
6156
|
+
'blinkmacsystemfont',
|
|
6157
|
+
]);
|