@react-x11/components 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html/controls.d.ts +29 -2
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +70 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +26 -4
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +18 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +27 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +18 -4
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +66 -28
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +561 -144
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/transform.d.ts +85 -0
- package/dist/html/css/transform.d.ts.map +1 -0
- package/dist/html/css/transform.js +425 -0
- package/dist/html/css/transform.js.map +1 -0
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +14 -2
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +4 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/fonts.d.ts +55 -5
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +284 -23
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +93 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +389 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +59 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +22 -2
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +90 -18
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +570 -83
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +3 -0
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +144 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +62 -18
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +3 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +263 -36
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +79 -4
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1213 -141
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +15 -2
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +249 -17
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.js +87 -7
- package/dist/html/widgets.js.map +1 -1
- package/dist/html/woff2.d.ts +14 -0
- package/dist/html/woff2.d.ts.map +1 -0
- package/dist/html/woff2.js +608 -0
- package/dist/html/woff2.js.map +1 -0
- package/dist/maps/controller.d.ts +17 -1
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +27 -7
- package/dist/maps/controller.js.map +1 -1
- package/dist/richtext/node.d.ts +8 -1
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +5 -1
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +25 -6
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +240 -24
- package/dist/richtext/runs.js.map +1 -1
- package/package.json +1 -1
- package/src/html/controls.ts +85 -2
- package/src/html/css/cascade.ts +23 -0
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/style.ts +664 -147
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +15 -2
- package/src/html/css/values.ts +4 -0
- package/src/html/fonts.ts +312 -29
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +444 -37
- package/src/html/layout/boxes.ts +65 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +95 -22
- package/src/html/layout/flex.ts +671 -77
- package/src/html/layout/inline.ts +149 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +62 -19
- package/src/html/node.ts +300 -37
- package/src/html/paint.ts +1406 -206
- package/src/html/svg.ts +277 -19
- package/src/html/widgets.ts +95 -9
- package/src/html/woff2.ts +612 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
package/dist/html/layout/flex.js
CHANGED
|
@@ -20,13 +20,15 @@
|
|
|
20
20
|
// independent Yoga pass inside the measure call. Not a nested Yoga node:
|
|
21
21
|
// that would need the whole child subtree mirrored into Yoga's tree, and the
|
|
22
22
|
// measure seam already answers the only question the outer pass asks. What
|
|
23
|
-
// the leaf shape costs is
|
|
24
|
-
//
|
|
23
|
+
// the leaf shape costs is a layout: an item the flex pass made taller or
|
|
24
|
+
// shorter than its content, which lays what is in it out by its height — a
|
|
25
|
+
// flex or grid container, or a box holding a percentage of a height — is
|
|
26
|
+
// laid out again at that height (`layoutItemAt`).
|
|
25
27
|
import { Yoga, layoutLoaded } from 'react-x11/yoga';
|
|
26
28
|
import { AUTO, gapOf, isPct, resolve, resolveOrNull } from '../css/values.js';
|
|
27
29
|
import { scrolls } from '../css/style.js';
|
|
28
30
|
import { Box, PAINT_ORDER, isBlank } from './boxes.js';
|
|
29
|
-
import { FLEXED_HEIGHT, MIN_CONTENT_PROBE, clampHeight, clampWidth, contentSizedWidth, exactMinContent, intrinsicWidth, measureIntrinsicWidth, moveTo, percentBaseInside, percentHeightsIn, positionOutOfFlow, replacedRatio, transferredHeight, transferredWidth, heightFromWidth, widthFromHeight, resolveEdges, } from './block.js';
|
|
31
|
+
import { FLEXED_HEIGHT, MIN_CONTENT_PROBE, USED_HEIGHT, centreButton, clampHeight, clampWidth, contentSizedWidth, exactMinContent, intrinsicWidth, measureIntrinsicWidth, moveTo, percentBaseInside, percentHeightsIn, positionOutOfFlow, replacedRatio, transferredHeight, transferredWidth, heightFromWidth, widthFromHeight, resolveEdges, } from './block.js';
|
|
30
32
|
import { layoutGrid } from './css-grid.js';
|
|
31
33
|
import { firstBaselineIn } from './inline.js';
|
|
32
34
|
// `react-x11/yoga` re-exports yoga's own declarations, so the node shape and
|
|
@@ -97,9 +99,14 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
97
99
|
// percentage that resolves, or what `aspect-ratio` makes of the width —
|
|
98
100
|
// less the padding and borders a `border-box` height holds, which put
|
|
99
101
|
// `h-16 py-2 items-center` eight pixels low. Where it is not, the limits
|
|
100
|
-
// on it, so a column `min-h-screen` gives its `flex-1` the rest
|
|
102
|
+
// on it, so a column `min-h-screen` gives its `flex-1` the rest — or the
|
|
103
|
+
// height the column it is an item of flexed it to (`USED_HEIGHT`), which
|
|
104
|
+
// its items are laid out in and take no percentage of.
|
|
101
105
|
const definite = percentBaseInside(box);
|
|
102
|
-
const
|
|
106
|
+
const own = Number.isFinite(definite);
|
|
107
|
+
const height = USED_HEIGHT.get(box) ?? (own ? definite : null);
|
|
108
|
+
// whether its content may come to more than it is tall
|
|
109
|
+
let capped = height !== null;
|
|
103
110
|
if (height !== null)
|
|
104
111
|
root.setHeight(height);
|
|
105
112
|
else {
|
|
@@ -109,8 +116,10 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
109
116
|
if (min > 0)
|
|
110
117
|
root.setMinHeight(min);
|
|
111
118
|
const max = clampHeight(box, Infinity) - extra;
|
|
112
|
-
if (Number.isFinite(max))
|
|
119
|
+
if (Number.isFinite(max)) {
|
|
113
120
|
root.setMaxHeight(Math.max(0, max));
|
|
121
|
+
capped = true;
|
|
122
|
+
}
|
|
114
123
|
}
|
|
115
124
|
const flowing = [];
|
|
116
125
|
let reordered = false;
|
|
@@ -138,21 +147,59 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
138
147
|
for (const child of flowing) {
|
|
139
148
|
const node = Y.Node.create(flexConfig());
|
|
140
149
|
resolveEdges(child, contentWidth);
|
|
141
|
-
const laid = {
|
|
142
|
-
|
|
150
|
+
const laid = {
|
|
151
|
+
width: NaN,
|
|
152
|
+
height: NaN,
|
|
153
|
+
tall: NaN,
|
|
154
|
+
set: NaN,
|
|
155
|
+
stretch: NaN,
|
|
156
|
+
main: NaN,
|
|
157
|
+
least: NaN,
|
|
158
|
+
held: NaN,
|
|
159
|
+
auto: false,
|
|
160
|
+
};
|
|
161
|
+
applyItem(node, child, ctx, contentWidth, laid, height !== null, own);
|
|
143
162
|
root.insertChild(node, items.length);
|
|
144
163
|
items.push({ box: child, node, laid });
|
|
145
164
|
}
|
|
146
165
|
const direction = box.style.direction === 'rtl' ? Y.DIRECTION_RTL : Y.DIRECTION_LTR;
|
|
147
166
|
const row = box.style.flexDirection.startsWith('row');
|
|
148
|
-
|
|
167
|
+
// frozen at its least size on the one line of a box that does not
|
|
168
|
+
// wrap, by a basis where Yoga takes one along the main axis
|
|
169
|
+
// (`applyItem`'s `setBasis`)
|
|
170
|
+
const hold = {
|
|
171
|
+
frozen: box.style.flexWrap === 'nowrap',
|
|
172
|
+
basis: row ? bounded : height !== null,
|
|
173
|
+
};
|
|
174
|
+
const ask = () => root.calculateLayout(bounded ? contentWidth : Number.NaN, height ?? Number.NaN, direction);
|
|
175
|
+
// the one line of a box that does not wrap may be short of room, where
|
|
176
|
+
// the box has a size along it that its items do not make: and Yoga's
|
|
177
|
+
// answer for a line every item of which its minimum stops is put right
|
|
178
|
+
// (`stoppedLine`)
|
|
179
|
+
const short = hold.frozen && (row ? bounded : capped);
|
|
180
|
+
// each item's size along the main axis, read the once a layout (`Laid`)
|
|
181
|
+
const read = () => {
|
|
182
|
+
for (const { node, laid } of items) {
|
|
183
|
+
laid.main = row ? node.getComputedWidth() : node.getComputedHeight();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const calculate = () => {
|
|
187
|
+
ask();
|
|
188
|
+
read();
|
|
189
|
+
if (!short)
|
|
190
|
+
return;
|
|
191
|
+
const room = row ? contentWidth : (height ?? root.getComputedHeight());
|
|
192
|
+
const gap = row ? columnGap : rowGap;
|
|
193
|
+
if (stoppedLine(items, row, room, gap, hold, ask))
|
|
194
|
+
read();
|
|
195
|
+
};
|
|
149
196
|
ctx.flexDepth = depth + 1;
|
|
150
197
|
try {
|
|
151
198
|
calculate();
|
|
152
199
|
// an item Yoga shrank under what its content comes to is kept to it,
|
|
153
200
|
// and the row is laid out again (`autoMinimums`) — which may shrink
|
|
154
201
|
// another under its own, a few times over at most
|
|
155
|
-
for (let pass = 0; pass < 4 && autoMinimums(items, row, ctx, contentWidth); pass += 1) {
|
|
202
|
+
for (let pass = 0; pass < 4 && autoMinimums(items, row, ctx, contentWidth, hold); pass += 1) {
|
|
156
203
|
calculate();
|
|
157
204
|
}
|
|
158
205
|
}
|
|
@@ -163,7 +210,7 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
163
210
|
// one pass of its own, which drops a margin (`lineMarginFix`)
|
|
164
211
|
const baselines = box.style.alignItems === 'baseline' ||
|
|
165
212
|
items.some(({ box: child }) => child.style.alignSelf === 'baseline');
|
|
166
|
-
let bottom = placeItems(box, ctx, items, row,
|
|
213
|
+
let bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap' || baselines, contentWidth);
|
|
167
214
|
// Items aligned by their baselines, which Yoga cannot see, are aligned
|
|
168
215
|
// with their baselines known (`baselineLines`)
|
|
169
216
|
// by the flex layout again, set at their lines' starts with the margins
|
|
@@ -191,7 +238,7 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
191
238
|
finally {
|
|
192
239
|
ctx.flexDepth = depth;
|
|
193
240
|
}
|
|
194
|
-
bottom = placeItems(box, ctx, items, row,
|
|
241
|
+
bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap', contentWidth, started);
|
|
195
242
|
}
|
|
196
243
|
// A replaced item with a ratio that is stretched across a line of a
|
|
197
244
|
// definite size takes its flex base size from the stretched one through
|
|
@@ -209,7 +256,7 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
209
256
|
finally {
|
|
210
257
|
ctx.flexDepth = depth;
|
|
211
258
|
}
|
|
212
|
-
bottom = placeItems(box, ctx, items, row,
|
|
259
|
+
bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap' || baselines, contentWidth);
|
|
213
260
|
}
|
|
214
261
|
const contentHeight = root.getComputedHeight();
|
|
215
262
|
root.freeRecursive();
|
|
@@ -219,7 +266,7 @@ export function layoutFlex(box, ctx, contentWidth) {
|
|
|
219
266
|
* base size the ratio makes of the stretched size; whether any changed. */
|
|
220
267
|
function keepRatios(container, items, row) {
|
|
221
268
|
let changed = false;
|
|
222
|
-
for (const { box, node } of items) {
|
|
269
|
+
for (const { box, node, laid } of items) {
|
|
223
270
|
if (box.kind !== 'replaced' || !stretches(box, container.style, row)) {
|
|
224
271
|
continue;
|
|
225
272
|
}
|
|
@@ -234,10 +281,17 @@ function keepRatios(container, items, row) {
|
|
|
234
281
|
box.horizontalExtra
|
|
235
282
|
: Math.max(0, node.getComputedWidth() - box.horizontalExtra) / ratio +
|
|
236
283
|
box.verticalExtra;
|
|
237
|
-
|
|
238
|
-
if (Math.abs(basis - main) < 0.5)
|
|
284
|
+
if (Math.abs(basis - laid.main) < 0.5)
|
|
239
285
|
continue;
|
|
240
286
|
node.setFlexBasis(basis);
|
|
287
|
+
// one held at its least size (`holdAt`) flexes from the new basis, no
|
|
288
|
+
// further down than that
|
|
289
|
+
if (!Number.isNaN(laid.held)) {
|
|
290
|
+
laid.held = NaN;
|
|
291
|
+
laid.auto = false;
|
|
292
|
+
node.setFlexGrow(style.flexGrow);
|
|
293
|
+
node.setFlexShrink(style.flexShrink);
|
|
294
|
+
}
|
|
241
295
|
changed = true;
|
|
242
296
|
}
|
|
243
297
|
return changed;
|
|
@@ -357,9 +411,13 @@ function orderOf(box) {
|
|
|
357
411
|
/** Lay each item out where the flex layout put it, at the size it gave it,
|
|
358
412
|
* and answer where the lowest ends. `started` are items set at their
|
|
359
413
|
* lines' starts whatever their own alignment says (`baselineLines`). */
|
|
360
|
-
function placeItems(box, ctx, items, row,
|
|
414
|
+
function placeItems(box, ctx, items, row,
|
|
415
|
+
/** Whether the flex box has a definite height. */
|
|
416
|
+
own,
|
|
361
417
|
/** Whether Yoga aligned the lines in its pass that drops a margin. */
|
|
362
|
-
lined,
|
|
418
|
+
lined,
|
|
419
|
+
/** The flex box's content width: its items' containing block's. */
|
|
420
|
+
contentWidth, started) {
|
|
363
421
|
let bottom = 0;
|
|
364
422
|
for (const { box: child, node, laid } of items) {
|
|
365
423
|
const across = lined
|
|
@@ -367,8 +425,8 @@ lined, started) {
|
|
|
367
425
|
: 0;
|
|
368
426
|
const left = box.contentX + node.getComputedLeft() + (row ? 0 : across);
|
|
369
427
|
const top = box.contentY + node.getComputedTop() + (row ? across : 0);
|
|
370
|
-
const width = meant(node.getComputedWidth(), laid.set, laid.stretch, laid.width);
|
|
371
|
-
const itemHeight = node.getComputedHeight();
|
|
428
|
+
const width = meant(row ? laid.main : node.getComputedWidth(), laid.set, laid.stretch, laid.width, row ? laid.held : NaN);
|
|
429
|
+
const itemHeight = row ? node.getComputedHeight() : laid.main;
|
|
372
430
|
// The item is laid out again at the width the flex pass settled on: the
|
|
373
431
|
// measure function answered a question, and the answer is not a layout —
|
|
374
432
|
// its line breaks were computed against a width that may have changed
|
|
@@ -378,15 +436,18 @@ lined, started) {
|
|
|
378
436
|
// 9.8): stretched across a row's line, or flexed in a column of a
|
|
379
437
|
// height of its own. What is in it takes its percentages of that —
|
|
380
438
|
// the `h-full` in a stretched sidebar — where they had nothing to be
|
|
381
|
-
// of, and the item is laid out again for them
|
|
439
|
+
// of, and the item is laid out again for them; and so is one that is a
|
|
440
|
+
// flex or grid container itself, whose own items are laid out in that
|
|
441
|
+
// height (9.4, step 11: "redo layout for its contents, treating this
|
|
442
|
+
// used size as its definite cross size").
|
|
382
443
|
const definite = row
|
|
383
444
|
? stretches(child, box.style)
|
|
384
445
|
? itemHeight
|
|
385
446
|
: null
|
|
386
|
-
:
|
|
447
|
+
: own
|
|
387
448
|
? itemHeight
|
|
388
449
|
: null;
|
|
389
|
-
layoutItemAt(child, ctx, left, top, width, itemHeight, laid, definite, !row);
|
|
450
|
+
layoutItemAt(child, ctx, left, top, width, itemHeight, laid, definite, contentWidth, !row);
|
|
390
451
|
// to the end of its margin box, as Yoga measured the box: a negative
|
|
391
452
|
// margin takes the item's end back in, which Codex's search field
|
|
392
453
|
// hangs a pixel over its form's border with, top and bottom
|
|
@@ -397,6 +458,8 @@ lined, started) {
|
|
|
397
458
|
function layoutItemAt(box, ctx, x, y, width, height, laid,
|
|
398
459
|
/** Its border box's height where the flex layout made it definite. */
|
|
399
460
|
definite,
|
|
461
|
+
/** The flex box's content width, the item's containing block's. */
|
|
462
|
+
containing,
|
|
400
463
|
/** Whether its height is the one the flex layout gave it, in a column,
|
|
401
464
|
* whatever its own says: flexed, it is shrunk as well as grown. */
|
|
402
465
|
column = false) {
|
|
@@ -409,32 +472,65 @@ column = false) {
|
|
|
409
472
|
box.height = height;
|
|
410
473
|
return;
|
|
411
474
|
}
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
475
|
+
const sameWidth = Math.abs(laid.width - width) <= 0.01;
|
|
476
|
+
// The content height the item is laid out at, where the flex layout gave
|
|
477
|
+
// it one that is not its own already: NaN where its content says how
|
|
478
|
+
// tall it is.
|
|
479
|
+
let tall = NaN;
|
|
480
|
+
// A flex or grid container shares its height out among its items: an
|
|
481
|
+
// `auto` margin takes what is free of it (8.1), a `flex: 1` grows into it
|
|
482
|
+
// (9.7), its lines are as tall as it (9.4), its `fr` rows fill it. So it
|
|
483
|
+
// is laid out in the height the flex layout gave it, definite or not —
|
|
484
|
+
// the one a column with no height of its own flexed it to is its used
|
|
485
|
+
// size all the same (9.7), and `min-h-screen flex flex-col` around a
|
|
486
|
+
// `flex-1 flex items-center` centres what is in that. At its content's
|
|
487
|
+
// own height there is nothing to share, and the layout it has is that
|
|
488
|
+
// one: the tallest card of a row is laid out the once.
|
|
489
|
+
const given = definite ?? (column ? height : null);
|
|
490
|
+
const shares = box.kind === 'flex' &&
|
|
491
|
+
given !== null &&
|
|
492
|
+
!(Math.abs(naturalHeight(box, ctx, width, containing, laid) - given) <= 0.01);
|
|
493
|
+
if (given !== null) {
|
|
494
|
+
const inner = Math.max(0, given - box.verticalExtra);
|
|
495
|
+
if (Object.is(inner, percentBaseInside(box))) {
|
|
496
|
+
// the height it has of its own
|
|
419
497
|
}
|
|
420
|
-
|
|
421
|
-
|
|
498
|
+
else if (shares || (definite !== null && percentHeightsIn(box))) {
|
|
499
|
+
tall = inner;
|
|
422
500
|
}
|
|
423
501
|
}
|
|
424
502
|
// `measureBox` lays the box out at (0, 0); re-running it at the final width
|
|
425
503
|
// and then moving it is one pass, not two, because the second call is the
|
|
426
|
-
// one whose result is kept — or no pass, where the last
|
|
427
|
-
// this width. An item Yoga never measured has none, NaN,
|
|
428
|
-
|
|
504
|
+
// one whose result is kept — or no pass, where the last layout was at
|
|
505
|
+
// this width and this height. An item Yoga never measured has none, NaN,
|
|
506
|
+
// and is laid out.
|
|
507
|
+
if (sameWidth && Object.is(laid.tall, tall))
|
|
429
508
|
box.height = laid.height;
|
|
430
|
-
else
|
|
431
|
-
|
|
509
|
+
else if (Number.isNaN(tall)) {
|
|
510
|
+
layoutNaturally(box, ctx, width, containing, laid);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
// what is in it takes its percentages of a definite height alone (9.8)
|
|
514
|
+
const heights = definite !== null ? FLEXED_HEIGHT : USED_HEIGHT;
|
|
515
|
+
heights.set(box, tall);
|
|
516
|
+
try {
|
|
517
|
+
ctx.layoutSubtree(box, width, containing);
|
|
518
|
+
}
|
|
519
|
+
finally {
|
|
520
|
+
heights.delete(box);
|
|
521
|
+
}
|
|
522
|
+
laid.width = box.width;
|
|
523
|
+
laid.height = box.height;
|
|
524
|
+
laid.tall = tall;
|
|
525
|
+
}
|
|
432
526
|
// A stretched item is taller than its content, and the box has to say so
|
|
433
527
|
// or its background stops short of the row — and it is no taller than its
|
|
434
528
|
// line where its content is, or than what its ratio makes of its width:
|
|
435
529
|
// its height is the line's (CSS Flexbox 9.4, step 11).
|
|
436
530
|
if (height > box.height || column || definite !== null)
|
|
437
531
|
box.height = height;
|
|
532
|
+
// and a button's content is centred in the height it came to
|
|
533
|
+
centreButton(box);
|
|
438
534
|
moveTo(box, x, y);
|
|
439
535
|
}
|
|
440
536
|
// What a new Yoga node already is is not said again. A call into the
|
|
@@ -461,8 +557,11 @@ function applyContainer(node, style) {
|
|
|
461
557
|
node.setAlignContent(lines);
|
|
462
558
|
}
|
|
463
559
|
function applyItem(node, box, ctx, containingWidth, laid,
|
|
464
|
-
/** Whether the flex box has a height
|
|
465
|
-
tall
|
|
560
|
+
/** Whether the flex box has a height its items are laid out in. */
|
|
561
|
+
tall,
|
|
562
|
+
/** Whether that height is definite, and a percentage may be of it: one a
|
|
563
|
+
* column flexed the box to is not (`USED_HEIGHT`). */
|
|
564
|
+
own) {
|
|
466
565
|
const style = box.style;
|
|
467
566
|
laid.stretch = containingWidth - box.marginLeft - box.marginRight;
|
|
468
567
|
// an `auto` margin takes the free space on its side, which is how
|
|
@@ -492,11 +591,20 @@ tall) {
|
|
|
492
591
|
if (style.width !== AUTO) {
|
|
493
592
|
laid.set = setLength(node, true, style.width, containingWidth, across);
|
|
494
593
|
}
|
|
495
|
-
|
|
594
|
+
// a percentage of a height that is not definite is `auto` (CSS 2.1 10.5),
|
|
595
|
+
// which Yoga makes of one where the flex box has no height at all
|
|
596
|
+
if (style.height !== AUTO && (own || !tall || !isPct(style.height))) {
|
|
496
597
|
setLength(node, false, style.height, NaN, down);
|
|
598
|
+
}
|
|
599
|
+
// the least it may be along each axis, its border box's, as Yoga is
|
|
600
|
+
// told it
|
|
601
|
+
let leastWidth = NaN;
|
|
602
|
+
let leastHeight = NaN;
|
|
497
603
|
const minWidth = resolveOrNull(style.minWidth, containingWidth);
|
|
498
|
-
if (minWidth !== null)
|
|
499
|
-
|
|
604
|
+
if (minWidth !== null) {
|
|
605
|
+
leastWidth = minWidth + across;
|
|
606
|
+
node.setMinWidth(leastWidth);
|
|
607
|
+
}
|
|
500
608
|
if (style.maxWidth !== 'none') {
|
|
501
609
|
const maxWidth = resolveOrNull(style.maxWidth, containingWidth);
|
|
502
610
|
if (maxWidth !== null)
|
|
@@ -512,20 +620,25 @@ tall) {
|
|
|
512
620
|
laid.set = size(style.widthKeyword);
|
|
513
621
|
node.setWidth(laid.set);
|
|
514
622
|
}
|
|
515
|
-
if (style.minWidthKeyword)
|
|
516
|
-
|
|
623
|
+
if (style.minWidthKeyword) {
|
|
624
|
+
leastWidth = size(style.minWidthKeyword);
|
|
625
|
+
node.setMinWidth(leastWidth);
|
|
626
|
+
}
|
|
517
627
|
if (style.maxWidthKeyword)
|
|
518
628
|
node.setMaxWidth(size(style.maxWidthKeyword));
|
|
519
629
|
}
|
|
520
630
|
const minHeight = resolveOrNull(style.minHeight, NaN);
|
|
521
|
-
if (minHeight !== null)
|
|
522
|
-
|
|
631
|
+
if (minHeight !== null) {
|
|
632
|
+
leastHeight = minHeight + down;
|
|
633
|
+
node.setMinHeight(leastHeight);
|
|
634
|
+
}
|
|
523
635
|
if (style.maxHeight !== 'none') {
|
|
524
636
|
const maxHeight = resolveOrNull(style.maxHeight, NaN);
|
|
525
637
|
if (maxHeight !== null)
|
|
526
638
|
node.setMaxHeight(maxHeight + down);
|
|
527
639
|
}
|
|
528
640
|
const row = box.parent?.style.flexDirection.startsWith('row') ?? true;
|
|
641
|
+
laid.least = row ? leastWidth : leastHeight;
|
|
529
642
|
// Yoga takes a length for a basis only where the flex box's main size is
|
|
530
643
|
// definite, and else the item's own size along it: down a column of no
|
|
531
644
|
// height of its own it read `flex: 0 0 3rem` as the item's height, or
|
|
@@ -561,6 +674,10 @@ tall) {
|
|
|
561
674
|
else if (style.flexBasis === 'content' || style.flexBasis === AUTO) {
|
|
562
675
|
// Yoga's own
|
|
563
676
|
}
|
|
677
|
+
else if (isPct(style.flexBasis) && !row && tall && !own) {
|
|
678
|
+
// a percentage of a main size that is not definite is `content` (CSS
|
|
679
|
+
// Flexbox 7.2.3): Yoga's own, down a column
|
|
680
|
+
}
|
|
564
681
|
else if (isPct(style.flexBasis)) {
|
|
565
682
|
// a percentage of the main size, which is known across a row
|
|
566
683
|
const basis = style.flexBasis;
|
|
@@ -656,7 +773,12 @@ tall) {
|
|
|
656
773
|
const width = meant(w, laid.set - extra, laid.stretch - extra);
|
|
657
774
|
const given = hm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(h);
|
|
658
775
|
const exact = wm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(width);
|
|
659
|
-
|
|
776
|
+
// the height it was flexed to, that is: one its automatic minimum
|
|
777
|
+
// holds it at (`holdAt`) is its content's, which a replaced element's
|
|
778
|
+
// width follows through its ratio and no other's does (CSS Sizing 4,
|
|
779
|
+
// 5.1)
|
|
780
|
+
const flexed = box.kind === 'replaced' || !laid.auto;
|
|
781
|
+
const through = widthThrough && given && !exact && flexed ? widthThrough(h) : null;
|
|
660
782
|
// along a row, a replaced element is its own width wherever there is
|
|
661
783
|
// room for less — its flex base size is not fitted to the room (9.2)
|
|
662
784
|
// — and across a column it is fitted, as any item is
|
|
@@ -673,10 +795,8 @@ tall) {
|
|
|
673
795
|
}
|
|
674
796
|
let answer = answers.get(inner);
|
|
675
797
|
if (answer === undefined) {
|
|
676
|
-
answer = measureBox(box, ctx, inner);
|
|
798
|
+
answer = measureBox(box, ctx, inner, containingWidth, laid);
|
|
677
799
|
answers.set(inner, answer);
|
|
678
|
-
laid.width = box.width;
|
|
679
|
-
laid.height = box.height;
|
|
680
800
|
}
|
|
681
801
|
return answer;
|
|
682
802
|
});
|
|
@@ -752,13 +872,174 @@ extra) {
|
|
|
752
872
|
* 9.2, `flex-basis: auto`), rather than as wide as the row; asked for one
|
|
753
873
|
* exactly, it is that wide. Its height is its content's at that width.
|
|
754
874
|
*/
|
|
755
|
-
function measureBox(box, ctx, inner
|
|
756
|
-
|
|
875
|
+
function measureBox(box, ctx, inner,
|
|
876
|
+
/** The flex box's content width, the item's containing block's. */
|
|
877
|
+
containing, laid) {
|
|
878
|
+
// its padding as Yoga has it, a percentage of the flex box's width: a
|
|
879
|
+
// probe of its content's width leaves a percentage at none
|
|
880
|
+
resolveEdges(box, containing);
|
|
881
|
+
const width = inner + box.horizontalExtra;
|
|
882
|
+
// its height at this width is known, where a layout since has left it
|
|
883
|
+
// another: the answer is the height, and the layout `layoutItemAt`'s
|
|
884
|
+
const kept = keptNatural(box, ctx, width, containing);
|
|
885
|
+
if (kept !== null && kept.serial !== box.layoutSerial) {
|
|
886
|
+
return {
|
|
887
|
+
width: inner,
|
|
888
|
+
height: Math.max(0, kept.height - box.verticalExtra),
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
layoutNaturally(box, ctx, width, containing, laid);
|
|
757
892
|
return {
|
|
758
893
|
width: inner,
|
|
759
894
|
height: Math.max(0, box.height - box.verticalExtra),
|
|
760
895
|
};
|
|
761
896
|
}
|
|
897
|
+
/** An item's height as its content's own makes it at a border-box width,
|
|
898
|
+
* where this pass has laid it out so (`layoutNaturally`), and else NaN. */
|
|
899
|
+
function naturalHeight(box, ctx, width, containing, laid) {
|
|
900
|
+
if (Math.abs(laid.width - width) <= 0.01 && Number.isNaN(laid.tall)) {
|
|
901
|
+
return laid.height;
|
|
902
|
+
}
|
|
903
|
+
return keptNatural(box, ctx, width, containing)?.height ?? NaN;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Give an item the layout its content's own height makes of it at a
|
|
907
|
+
* border-box width, and note it (`Laid`): the one this pass has made of it
|
|
908
|
+
* there already, where it still has it.
|
|
909
|
+
*
|
|
910
|
+
* A flex box laid out a second time — at the height a stretch gave it, its
|
|
911
|
+
* own flex layout made anew (`layoutItemAt`) — asks of each item what it
|
|
912
|
+
* asked the first time, and the item's layout at a width is what it was.
|
|
913
|
+
* Kept for the pass, a card laid out again for its height lays nothing in
|
|
914
|
+
* it out that is where it was; made again at every layout of the box
|
|
915
|
+
* around it, a card in a card in a card laid its innermost out twice a
|
|
916
|
+
* level.
|
|
917
|
+
*/
|
|
918
|
+
function layoutNaturally(box, ctx, width,
|
|
919
|
+
/** The flex box's content width, the item's containing block's. */
|
|
920
|
+
containing, laid) {
|
|
921
|
+
const kept = keptNatural(box, ctx, width, containing);
|
|
922
|
+
if (kept !== null && kept.serial === box.layoutSerial) {
|
|
923
|
+
// put back, where the flex layout stretched it
|
|
924
|
+
box.height = kept.height;
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
const at = ctx.positioned.length;
|
|
928
|
+
ctx.layoutSubtree(box, width, containing);
|
|
929
|
+
// a replaced box is sized, not laid out, and has no layout to count
|
|
930
|
+
if (box.kind !== 'replaced') {
|
|
931
|
+
NATURAL.set(box, {
|
|
932
|
+
ctx,
|
|
933
|
+
serial: box.layoutSerial,
|
|
934
|
+
width,
|
|
935
|
+
containing: readsContaining(box) ? containing : NaN,
|
|
936
|
+
base: box.percentHeightBase,
|
|
937
|
+
edges: edgesOf(box),
|
|
938
|
+
height: box.height,
|
|
939
|
+
queued: ctx.positioned.length > at ? { at, first: ctx.positioned[at] } : null,
|
|
940
|
+
drawn: NaN,
|
|
941
|
+
content: NaN,
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
laid.width = box.width;
|
|
946
|
+
laid.height = box.height;
|
|
947
|
+
laid.tall = NaN;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* The layout this pass made of an item at its content's own height
|
|
951
|
+
* (`layoutNaturally`), where one at `width` now would be the same: the same
|
|
952
|
+
* width, the same edges — a percentage among them resolved, as the layout
|
|
953
|
+
* leaves them — the same containing block's width where a limit on its own
|
|
954
|
+
* is a percentage of it, and the same height for its percentages, where its
|
|
955
|
+
* own style reads that at all, which the flex box around it coming to a
|
|
956
|
+
* definite height changes for every item in it. Its `height` is the item's
|
|
957
|
+
* there whatever has laid it out since, and the layout is the one the box
|
|
958
|
+
* has where nothing has (`Box.layoutSerial`).
|
|
959
|
+
*/
|
|
960
|
+
function keptNatural(box, ctx, width, containing) {
|
|
961
|
+
const kept = NATURAL.get(box);
|
|
962
|
+
if (kept === undefined || kept.ctx !== ctx || kept.width !== width) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
if (!Number.isNaN(kept.containing) && kept.containing !== containing) {
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
if (!Object.is(kept.base, box.percentHeightBase) && readsPercentBase(box)) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
// the boxes in it that are positioned last are still waiting to be: a
|
|
972
|
+
// `line-clamp: auto` box laid out a second time forgets the ones its
|
|
973
|
+
// first layout found
|
|
974
|
+
if (kept.queued && ctx.positioned[kept.queued.at] !== kept.queued.first) {
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
const edges = kept.edges;
|
|
978
|
+
return edges[0] === box.marginTop &&
|
|
979
|
+
edges[1] === box.marginRight &&
|
|
980
|
+
edges[2] === box.marginBottom &&
|
|
981
|
+
edges[3] === box.marginLeft &&
|
|
982
|
+
edges[4] === box.borderTop &&
|
|
983
|
+
edges[5] === box.borderRight &&
|
|
984
|
+
edges[6] === box.borderBottom &&
|
|
985
|
+
edges[7] === box.borderLeft &&
|
|
986
|
+
edges[8] === box.padTop &&
|
|
987
|
+
edges[9] === box.padRight &&
|
|
988
|
+
edges[10] === box.padBottom &&
|
|
989
|
+
edges[11] === box.padLeft
|
|
990
|
+
? kept
|
|
991
|
+
: null;
|
|
992
|
+
}
|
|
993
|
+
const NATURAL = new WeakMap();
|
|
994
|
+
/** Note what was read from the layout an item has, where that is the one
|
|
995
|
+
* its content's own height makes of it at `width`. */
|
|
996
|
+
function remember(box, ctx, width, containing, field, value) {
|
|
997
|
+
const made = keptNatural(box, ctx, width, containing);
|
|
998
|
+
if (made !== null && made.serial === box.layoutSerial)
|
|
999
|
+
made[field] = value;
|
|
1000
|
+
}
|
|
1001
|
+
/** A box's edges, which a layout resolves again against the width it is
|
|
1002
|
+
* given: margins, borders, padding, each from the top round. */
|
|
1003
|
+
function edgesOf(box) {
|
|
1004
|
+
return [
|
|
1005
|
+
box.marginTop,
|
|
1006
|
+
box.marginRight,
|
|
1007
|
+
box.marginBottom,
|
|
1008
|
+
box.marginLeft,
|
|
1009
|
+
box.borderTop,
|
|
1010
|
+
box.borderRight,
|
|
1011
|
+
box.borderBottom,
|
|
1012
|
+
box.borderLeft,
|
|
1013
|
+
box.padTop,
|
|
1014
|
+
box.padRight,
|
|
1015
|
+
box.padBottom,
|
|
1016
|
+
box.padLeft,
|
|
1017
|
+
];
|
|
1018
|
+
}
|
|
1019
|
+
/** Whether the width a box is laid out at is held to a percentage of its
|
|
1020
|
+
* containing block's: a least or greatest width that is one. */
|
|
1021
|
+
function readsContaining(box) {
|
|
1022
|
+
const style = box.style;
|
|
1023
|
+
return (isPct(style.minWidth) ||
|
|
1024
|
+
(style.maxWidth !== 'none' && isPct(style.maxWidth)));
|
|
1025
|
+
}
|
|
1026
|
+
/** Whether a box's own layout reads the height its percentages are of: a
|
|
1027
|
+
* height, a least or greatest one, or an offset that is a percentage, a
|
|
1028
|
+
* height that is a keyword — `stretch` fills it — or an anonymous box,
|
|
1029
|
+
* which hands it on to what is in it (`percentBaseInside`). */
|
|
1030
|
+
function readsPercentBase(box) {
|
|
1031
|
+
if (!box.el && !box.pseudo)
|
|
1032
|
+
return true;
|
|
1033
|
+
const style = box.style;
|
|
1034
|
+
return (isPct(style.height) ||
|
|
1035
|
+
isPct(style.minHeight) ||
|
|
1036
|
+
(style.maxHeight !== 'none' && isPct(style.maxHeight)) ||
|
|
1037
|
+
isPct(style.top) ||
|
|
1038
|
+
isPct(style.bottom) ||
|
|
1039
|
+
style.heightKeyword !== null ||
|
|
1040
|
+
style.minHeightKeyword !== null ||
|
|
1041
|
+
style.maxHeightKeyword !== null);
|
|
1042
|
+
}
|
|
762
1043
|
/** The width an item's content is measured at: the width Yoga gives it
|
|
763
1044
|
* exactly, or its max-content width, no wider than any it may take up to
|
|
764
1045
|
* (CSS Flexbox 9.2). */
|
|
@@ -789,7 +1070,7 @@ function innerWidth(width, mode, maxContent) {
|
|
|
789
1070
|
* (`Laid`), so a row with room pays for nothing. True where some item
|
|
790
1071
|
* was given a minimum and the row has to be laid out again.
|
|
791
1072
|
*/
|
|
792
|
-
function autoMinimums(items, row, ctx, containingWidth) {
|
|
1073
|
+
function autoMinimums(items, row, ctx, containingWidth, hold) {
|
|
793
1074
|
let changed = false;
|
|
794
1075
|
for (const { box, node, laid } of items) {
|
|
795
1076
|
if (box.kind === 'text' || box.kind === 'break')
|
|
@@ -801,7 +1082,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
801
1082
|
if (!asked && scrolls(style)) {
|
|
802
1083
|
continue;
|
|
803
1084
|
}
|
|
804
|
-
const width = meant(node.getComputedWidth(), laid.set, laid.stretch, laid.width);
|
|
1085
|
+
const width = meant(row ? laid.main : node.getComputedWidth(), laid.set, laid.stretch, laid.width, row ? laid.held : NaN);
|
|
805
1086
|
// a replaced item's content along a row is its natural width
|
|
806
1087
|
if (row && box.kind === 'replaced') {
|
|
807
1088
|
if (style.minWidth !== AUTO || style.minWidthKeyword)
|
|
@@ -809,7 +1090,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
809
1090
|
const least = replacedMinimum(box, containingWidth);
|
|
810
1091
|
if (least === null || width >= least - 0.01)
|
|
811
1092
|
continue;
|
|
812
|
-
node
|
|
1093
|
+
holdAt(node, laid, row, least, hold);
|
|
813
1094
|
changed = true;
|
|
814
1095
|
continue;
|
|
815
1096
|
}
|
|
@@ -836,7 +1117,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
836
1117
|
}
|
|
837
1118
|
if (width >= least - 0.01)
|
|
838
1119
|
continue;
|
|
839
|
-
node
|
|
1120
|
+
holdAt(node, laid, row, least, hold);
|
|
840
1121
|
changed = true;
|
|
841
1122
|
continue;
|
|
842
1123
|
}
|
|
@@ -865,9 +1146,9 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
865
1146
|
if (width >= least - 0.01)
|
|
866
1147
|
continue;
|
|
867
1148
|
least = Math.min(least, minContentOf(box, ctx, laid));
|
|
868
|
-
if (
|
|
1149
|
+
if (laid.main >= least - 0.01)
|
|
869
1150
|
continue;
|
|
870
|
-
node
|
|
1151
|
+
holdAt(node, laid, row, least, hold);
|
|
871
1152
|
changed = true;
|
|
872
1153
|
continue;
|
|
873
1154
|
}
|
|
@@ -879,17 +1160,37 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
879
1160
|
transferredWidth(box, widest + box.horizontalExtra) <= width + 0.5) {
|
|
880
1161
|
continue;
|
|
881
1162
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1163
|
+
// What its content's own height makes of it at this width — how wide
|
|
1164
|
+
// that drew, along a row, and how far down, along a column — is read
|
|
1165
|
+
// from that layout, which the item is given where it has another: or
|
|
1166
|
+
// is what this pass read from it before, where the flex box is laid
|
|
1167
|
+
// out a second time (`layoutNaturally`)
|
|
1168
|
+
const kept = keptNatural(box, ctx, width, containingWidth);
|
|
1169
|
+
const read = kept === null ? NaN : row ? kept.drawn : kept.content;
|
|
1170
|
+
if (Number.isNaN(read) && !(Math.abs(laid.width - width) <= 0.01)) {
|
|
1171
|
+
layoutNaturally(box, ctx, width, containingWidth, laid);
|
|
886
1172
|
}
|
|
1173
|
+
const height = Number.isNaN(read) ? laid.height : kept.height;
|
|
887
1174
|
let least;
|
|
888
1175
|
if (row) {
|
|
889
|
-
// or than what it drew at the width it has: nothing overflows it
|
|
890
|
-
|
|
891
|
-
|
|
1176
|
+
// or than what it drew at the width it has: nothing overflows it —
|
|
1177
|
+
// unless a line in it fits for being cut short by `text-overflow`,
|
|
1178
|
+
// which is how the line is drawn and no part of how wide it is: a
|
|
1179
|
+
// button whose label is a `truncate` span is no narrower than the
|
|
1180
|
+
// label, where it was shrunk and the label cut. No width, then, that
|
|
1181
|
+
// it drew at, which is what is kept of it
|
|
1182
|
+
let drawn = read;
|
|
1183
|
+
if (Number.isNaN(drawn)) {
|
|
1184
|
+
const seen = { cut: false };
|
|
1185
|
+
drawn = intrinsicWidth(box, seen) + box.horizontalExtra;
|
|
1186
|
+
if (seen.cut)
|
|
1187
|
+
drawn = Infinity;
|
|
1188
|
+
remember(box, ctx, width, containingWidth, 'drawn', drawn);
|
|
1189
|
+
}
|
|
1190
|
+
if (Number.isFinite(drawn) &&
|
|
1191
|
+
transferredWidth(box, drawn) <= width + 0.5) {
|
|
892
1192
|
continue;
|
|
1193
|
+
}
|
|
893
1194
|
// within what a ratio makes of its least and greatest heights (4.5's
|
|
894
1195
|
// content size suggestion)
|
|
895
1196
|
least = transferredWidth(box, minContentOf(box, ctx, laid));
|
|
@@ -899,15 +1200,18 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
899
1200
|
if (most !== null)
|
|
900
1201
|
least = Math.min(least, most + extra);
|
|
901
1202
|
}
|
|
902
|
-
if (
|
|
1203
|
+
if (laid.main >= least - 0.01)
|
|
903
1204
|
continue;
|
|
904
|
-
node
|
|
1205
|
+
holdAt(node, laid, row, least, hold);
|
|
905
1206
|
}
|
|
906
1207
|
else {
|
|
907
1208
|
// its height, or where it has one of its own and its content comes to
|
|
908
1209
|
// less, its content's: the lesser of the two (4.5)
|
|
909
|
-
let content;
|
|
910
|
-
if (
|
|
1210
|
+
let content = read;
|
|
1211
|
+
if (!Number.isNaN(content)) {
|
|
1212
|
+
// read before
|
|
1213
|
+
}
|
|
1214
|
+
else if (box.kind === 'replaced')
|
|
911
1215
|
content = laid.height;
|
|
912
1216
|
else if (style.height !== AUTO && percentHeightsIn(box)) {
|
|
913
1217
|
// what its content comes to where it has no height to take
|
|
@@ -915,43 +1219,226 @@ function autoMinimums(items, row, ctx, containingWidth) {
|
|
|
915
1219
|
// apart, and the final pass lays it out again
|
|
916
1220
|
FLEXED_HEIGHT.set(box, NaN);
|
|
917
1221
|
try {
|
|
918
|
-
ctx.layoutSubtree(box, width);
|
|
1222
|
+
ctx.layoutSubtree(box, width, containingWidth);
|
|
919
1223
|
}
|
|
920
1224
|
finally {
|
|
921
1225
|
FLEXED_HEIGHT.delete(box);
|
|
922
1226
|
}
|
|
923
|
-
content = contentBottom(box) + box.verticalExtra;
|
|
1227
|
+
content = ratioContent(box, contentBottom(box) + box.verticalExtra);
|
|
924
1228
|
laid.width = NaN;
|
|
925
1229
|
}
|
|
926
|
-
else
|
|
927
|
-
content = contentBottom(box) + box.verticalExtra;
|
|
928
|
-
|
|
929
|
-
// height of its own or not (CSS Sizing 4, 5.1)
|
|
930
|
-
const aspect = style.aspectRatio;
|
|
931
|
-
if (aspect && box.kind !== 'replaced') {
|
|
932
|
-
content = Math.max(content, style.boxSizing === 'border-box'
|
|
933
|
-
? box.width / aspect.ratio
|
|
934
|
-
: box.contentWidth / aspect.ratio + box.verticalExtra);
|
|
1230
|
+
else {
|
|
1231
|
+
content = ratioContent(box, contentBottom(box) + box.verticalExtra);
|
|
1232
|
+
remember(box, ctx, width, containingWidth, 'content', content);
|
|
935
1233
|
}
|
|
936
1234
|
// within what the ratio makes of its least and greatest widths
|
|
937
1235
|
if (box.kind !== 'replaced') {
|
|
938
1236
|
content = transferredHeight(box, content, containingWidth);
|
|
939
1237
|
}
|
|
940
|
-
least = Math.min(
|
|
1238
|
+
least = Math.min(height, content);
|
|
941
1239
|
const extra = style.boxSizing === 'border-box' ? 0 : box.verticalExtra;
|
|
942
1240
|
if (style.maxHeight !== 'none') {
|
|
943
1241
|
const most = resolveOrNull(style.maxHeight, NaN);
|
|
944
1242
|
if (most !== null)
|
|
945
1243
|
least = Math.min(least, most + extra);
|
|
946
1244
|
}
|
|
947
|
-
if (
|
|
1245
|
+
if (laid.main >= least - 0.01)
|
|
948
1246
|
continue;
|
|
949
|
-
node
|
|
1247
|
+
holdAt(node, laid, row, least, hold);
|
|
950
1248
|
}
|
|
951
1249
|
changed = true;
|
|
952
1250
|
}
|
|
953
1251
|
return changed;
|
|
954
1252
|
}
|
|
1253
|
+
/** A laid-out item's content height, its border box's: no less than its
|
|
1254
|
+
* width through its ratio, height of its own or not (CSS Sizing 4, 5.1). */
|
|
1255
|
+
function ratioContent(box, content) {
|
|
1256
|
+
const aspect = box.style.aspectRatio;
|
|
1257
|
+
if (!aspect || box.kind === 'replaced')
|
|
1258
|
+
return content;
|
|
1259
|
+
return Math.max(content, box.style.boxSizing === 'border-box'
|
|
1260
|
+
? box.width / aspect.ratio
|
|
1261
|
+
: box.contentWidth / aspect.ratio + box.verticalExtra);
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Hold an item to its least size along the main axis, the size Yoga gave
|
|
1265
|
+
* it being under that. It is frozen there, as CSS Flexbox freezes an item
|
|
1266
|
+
* its minimum stops (9.7, step 4): inflexible, at that size, the rest of
|
|
1267
|
+
* the line sharing what is left. Handed the minimum as a `min-width`
|
|
1268
|
+
* alone, Yoga makes it the item's flex base size, which is two things the
|
|
1269
|
+
* specification does not do:
|
|
1270
|
+
*
|
|
1271
|
+
* - where the line has room to share out, the item takes its share on top
|
|
1272
|
+
* of the minimum: of three `flex: 1` items in 300 pixels, the one with a
|
|
1273
|
+
* word 200 wide was 233, where it is 200 and the others 50 each;
|
|
1274
|
+
* - where every item of a line is held, Yoga divides what the line is
|
|
1275
|
+
* short of by what is left of the shrink factors once it has taken each
|
|
1276
|
+
* item's away — nothing, or a float's rounding of it. Two items 110.992
|
|
1277
|
+
* and 62.706 pixels wide in a row too narrow for them came out billions
|
|
1278
|
+
* of pixels wide, and 31.3 and 17.7 came out as they are: by the digits
|
|
1279
|
+
* of their sizes, which are a text's widths in whatever font it is set.
|
|
1280
|
+
*
|
|
1281
|
+
* On the one line of a box that does not wrap, an item Yoga had under its
|
|
1282
|
+
* minimum has no more room when another is held too, so frozen is where it
|
|
1283
|
+
* ends. In a box that wraps it may have: the minimums move the line breaks
|
|
1284
|
+
* (9.3), and an item held on a crowded line may be on one with room to
|
|
1285
|
+
* grow in once they have. There the minimum is left to Yoga, as a minimum;
|
|
1286
|
+
* only a line of one item is short of room there, and what Yoga takes away
|
|
1287
|
+
* from its shrink factors is exactly what it added.
|
|
1288
|
+
*/
|
|
1289
|
+
function holdAt(node, laid, row, least, hold) {
|
|
1290
|
+
if (row)
|
|
1291
|
+
node.setMinWidth(least);
|
|
1292
|
+
else
|
|
1293
|
+
node.setMinHeight(least);
|
|
1294
|
+
if (!hold.frozen)
|
|
1295
|
+
return;
|
|
1296
|
+
laid.auto = true;
|
|
1297
|
+
freezeAt(node, laid, row, least, hold);
|
|
1298
|
+
}
|
|
1299
|
+
/** Freeze an item at a size along the main axis: inflexible, and that size
|
|
1300
|
+
* its flex base size. */
|
|
1301
|
+
function freezeAt(node, laid, row, size, hold) {
|
|
1302
|
+
laid.held = size;
|
|
1303
|
+
node.setFlexGrow(0);
|
|
1304
|
+
node.setFlexShrink(0);
|
|
1305
|
+
if (hold.basis)
|
|
1306
|
+
node.setFlexBasis(size);
|
|
1307
|
+
else if (row)
|
|
1308
|
+
node.setWidth(size);
|
|
1309
|
+
else
|
|
1310
|
+
node.setHeight(size);
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Put right the one line of a box that does not wrap, where every item of
|
|
1314
|
+
* it that may shrink is stopped by its minimum and Yoga made them wider for
|
|
1315
|
+
* it; whether Yoga was asked again. The line is laid out with each of them
|
|
1316
|
+
* frozen at its minimum, which is where CSS Flexbox leaves them (9.7, step
|
|
1317
|
+
* 4): the line is short of room with every one of them as small as it
|
|
1318
|
+
* goes.
|
|
1319
|
+
*
|
|
1320
|
+
* Yoga shares what a line is short of in two passes. The first takes each
|
|
1321
|
+
* item a minimum stops out of the sum of the scaled shrink factors, one
|
|
1322
|
+
* subtraction an item, and the second divides what the line is still short
|
|
1323
|
+
* of by what is left of the sum. With every item stopped that is nothing:
|
|
1324
|
+
* `(a + b) - a - b`, in float32s, which is 0 for some sizes and a rounding
|
|
1325
|
+
* either side of it for others, 2^-18 for sizes about a hundred. Yoga
|
|
1326
|
+
* 3.2.1 tests for 0, and the guard it has had since, a sum under 1e-6, is
|
|
1327
|
+
* under that rounding (react/yoga#1665, #1974). A negative rounding sends
|
|
1328
|
+
* every item under its minimum, which stops it, and a positive one makes
|
|
1329
|
+
* each of them wider by billions of pixels: `width: 110.992px; min-width:
|
|
1330
|
+
* 110.992px` beside `62.705625px` of the same, in a row 50 wide, were
|
|
1331
|
+
* 3599091712 and 2033329408 wide, where 100 and 60 were as they are. So
|
|
1332
|
+
* were two items with no minimum but their padding, and two with
|
|
1333
|
+
* `min-width: 0` beside one that does not shrink and is wider than the row
|
|
1334
|
+
* alone.
|
|
1335
|
+
*
|
|
1336
|
+
* A minimum of the item's own is Yoga's to hold it to, so this looks at
|
|
1337
|
+
* its answer, for a line that is two things at once. It is short of room
|
|
1338
|
+
* with every item that may shrink at its minimum — where 9.7 leaves each
|
|
1339
|
+
* of them at that, whatever their sizes. And one of them is wider than its
|
|
1340
|
+
* hypothetical size (9.7, step 2), which no sharing out of what a line is
|
|
1341
|
+
* short of makes an item: Yoga is asked for the line with nothing
|
|
1342
|
+
* shrinking, where each item is that size. Any other line is put back as
|
|
1343
|
+
* it was, the two layouts that took being the cost of one that is short of
|
|
1344
|
+
* room at its minimums and was not shared out to them; a line with room
|
|
1345
|
+
* for its minimums, or one whose every item sits at its own, has cost the
|
|
1346
|
+
* sum of its sizes.
|
|
1347
|
+
*
|
|
1348
|
+
* An automatic minimum is no part of it: an item held at one is frozen
|
|
1349
|
+
* already (`holdAt`), and one not yet held has no minimum to Yoga but its
|
|
1350
|
+
* padding and border, where this leaves it for `autoMinimums` to find
|
|
1351
|
+
* short of its content.
|
|
1352
|
+
*/
|
|
1353
|
+
function stoppedLine(items, row,
|
|
1354
|
+
/** The flex box's size along the main axis. */
|
|
1355
|
+
room,
|
|
1356
|
+
/** The gap between two items. */
|
|
1357
|
+
gap, hold,
|
|
1358
|
+
/** Lay the flex box out, as its nodes now are. */
|
|
1359
|
+
ask) {
|
|
1360
|
+
// what is between the items' border boxes: the gaps, and their margins
|
|
1361
|
+
let between = gap * (items.length - 1);
|
|
1362
|
+
// the line's size with each item that may shrink at its minimum
|
|
1363
|
+
let least = 0;
|
|
1364
|
+
let shrinking = 0;
|
|
1365
|
+
let over = false;
|
|
1366
|
+
for (const { box, laid } of items) {
|
|
1367
|
+
const style = box.style;
|
|
1368
|
+
// an `auto` margin is none on a line with no room (8.1)
|
|
1369
|
+
if (row) {
|
|
1370
|
+
if (style.marginLeft !== AUTO)
|
|
1371
|
+
between += box.marginLeft;
|
|
1372
|
+
if (style.marginRight !== AUTO)
|
|
1373
|
+
between += box.marginRight;
|
|
1374
|
+
}
|
|
1375
|
+
else {
|
|
1376
|
+
if (style.marginTop !== AUTO)
|
|
1377
|
+
between += box.marginTop;
|
|
1378
|
+
if (style.marginBottom !== AUTO)
|
|
1379
|
+
between += box.marginBottom;
|
|
1380
|
+
}
|
|
1381
|
+
if (!shrinks(box, laid))
|
|
1382
|
+
least += laid.main;
|
|
1383
|
+
else {
|
|
1384
|
+
const min = leastOf(box, laid, row);
|
|
1385
|
+
shrinking += 1;
|
|
1386
|
+
least += min;
|
|
1387
|
+
if (laid.main > min + 0.01)
|
|
1388
|
+
over = true;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
// two at the least: what is left of a sum of one, less it, is 0 exactly.
|
|
1392
|
+
// A line within a thousandth of a pixel of room is short of it: what
|
|
1393
|
+
// Yoga makes of that is as far out, and the minimums are that near
|
|
1394
|
+
if (shrinking < 2 || !over || !(between + least > room - SHORT)) {
|
|
1395
|
+
return false;
|
|
1396
|
+
}
|
|
1397
|
+
const stopped = items.filter(({ box, laid }) => shrinks(box, laid));
|
|
1398
|
+
for (const { node } of stopped)
|
|
1399
|
+
node.setFlexShrink(0);
|
|
1400
|
+
ask();
|
|
1401
|
+
// each item its hypothetical size, which one that does not shrink is on
|
|
1402
|
+
// a line short of room — where Yoga may have grown it, on a line it took
|
|
1403
|
+
// for having some
|
|
1404
|
+
let wider = false;
|
|
1405
|
+
least = 0;
|
|
1406
|
+
for (const { box, node, laid } of items) {
|
|
1407
|
+
const hypothetical = row
|
|
1408
|
+
? node.getComputedWidth()
|
|
1409
|
+
: node.getComputedHeight();
|
|
1410
|
+
if (!shrinks(box, laid))
|
|
1411
|
+
least += hypothetical;
|
|
1412
|
+
else {
|
|
1413
|
+
least += leastOf(box, laid, row);
|
|
1414
|
+
if (laid.main > hypothetical && !nearly(laid.main, hypothetical)) {
|
|
1415
|
+
wider = true;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
const freeze = wider && between + least > room - SHORT;
|
|
1420
|
+
for (const { box, node, laid } of stopped) {
|
|
1421
|
+
if (freeze)
|
|
1422
|
+
freezeAt(node, laid, row, leastOf(box, laid, row), hold);
|
|
1423
|
+
else
|
|
1424
|
+
node.setFlexShrink(box.style.flexShrink);
|
|
1425
|
+
}
|
|
1426
|
+
ask();
|
|
1427
|
+
return true;
|
|
1428
|
+
}
|
|
1429
|
+
/** Whether Yoga may shrink an item: it has a shrink factor, and is not
|
|
1430
|
+
* frozen (`freezeAt`). */
|
|
1431
|
+
function shrinks(box, laid) {
|
|
1432
|
+
return box.style.flexShrink > 0 && Number.isNaN(laid.held);
|
|
1433
|
+
}
|
|
1434
|
+
/** How near to its room a line may be and be short of it (`stoppedLine`). */
|
|
1435
|
+
const SHORT = 0.001;
|
|
1436
|
+
/** The least an item may be along the main axis, its border box: the
|
|
1437
|
+
* minimum of its own, and no less than its padding and borders. */
|
|
1438
|
+
function leastOf(box, laid, row) {
|
|
1439
|
+
const extra = row ? box.horizontalExtra : box.verticalExtra;
|
|
1440
|
+
return Number.isNaN(laid.least) ? extra : Math.max(laid.least, extra);
|
|
1441
|
+
}
|
|
955
1442
|
/** An item's min-content width, its border box's, taken the once: its
|
|
956
1443
|
* content's, whatever width it has of its own (`keywordWidth`'s too). */
|
|
957
1444
|
function minContentOf(box, ctx, laid) {
|