react-x11 2.11.0 → 2.13.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.
Files changed (100) hide show
  1. package/README.md +278 -129
  2. package/package.json +12 -4
  3. package/src/Reconciler.js +19 -31
  4. package/src/a11y.js +2 -2
  5. package/src/anchor.js +7 -5
  6. package/src/appcontext.js +59 -30
  7. package/src/bootstrap.js +14 -0
  8. package/src/clientmessage.js +1 -1
  9. package/src/cocoa/app.js +303 -49
  10. package/src/cocoa/bezels.js +175 -30
  11. package/src/cocoa/dnd.js +27 -13
  12. package/src/cocoa/fonts.js +3 -3
  13. package/src/cocoa/glarea.js +24 -5
  14. package/src/cocoa/main.d.ts +8 -0
  15. package/src/cocoa/main.js +43 -0
  16. package/src/cocoa/overlay.js +159 -0
  17. package/src/cocoa/panehost.js +15 -5
  18. package/src/cocoa/presenter.js +13 -9
  19. package/src/cocoa/promotion.js +17 -7
  20. package/src/cocoa/relaunch.js +207 -0
  21. package/src/cocoa/threaded.js +246 -0
  22. package/src/cocoa/window.js +256 -42
  23. package/src/components/Select.js +2 -2
  24. package/src/components/anchor.js +3 -3
  25. package/src/components/native.js +12 -7
  26. package/src/components/theme.js +2 -2
  27. package/src/debug.js +1 -1
  28. package/src/decorations.js +1 -1
  29. package/src/editmenu.js +2 -2
  30. package/src/embedding.js +31 -0
  31. package/src/errors.js +46 -0
  32. package/src/events.js +78 -18
  33. package/src/foreignnodes.js +59 -5
  34. package/src/frames.js +2 -2
  35. package/src/glnodes.js +172 -41
  36. package/src/gloverlay.js +383 -0
  37. package/src/grid.js +1653 -0
  38. package/src/host.d.ts +230 -1
  39. package/src/host.js +11 -3
  40. package/src/imagesource.js +1 -1
  41. package/src/index.d.ts +34 -4
  42. package/src/index.js +9 -1
  43. package/src/layouts.js +721 -0
  44. package/src/node.d.ts +16 -3
  45. package/src/node.js +19 -21
  46. package/src/nodes/animation.js +644 -0
  47. package/src/nodes/box.js +21 -0
  48. package/src/nodes/boxpaint.js +473 -0
  49. package/src/nodes/canvas.js +269 -0
  50. package/src/nodes/cascade.js +600 -0
  51. package/src/nodes/damage.js +183 -0
  52. package/src/nodes/edithistory.js +124 -0
  53. package/src/nodes/editmenupopup.js +260 -0
  54. package/src/nodes/hittest.js +185 -0
  55. package/src/nodes/image.js +266 -0
  56. package/src/nodes/install.js +75 -0
  57. package/src/nodes/invalidate.js +465 -0
  58. package/src/nodes/kinds.js +31 -0
  59. package/src/nodes/layout.js +439 -0
  60. package/src/nodes/layouthost.js +949 -0
  61. package/src/nodes/node.js +868 -0
  62. package/src/nodes/paint.js +466 -0
  63. package/src/nodes/position.js +366 -0
  64. package/src/nodes/preedit.js +127 -0
  65. package/src/nodes/queries.js +330 -0
  66. package/src/nodes/rects.js +102 -0
  67. package/src/nodes/scrollable.js +891 -0
  68. package/src/nodes/scrollbars.js +138 -0
  69. package/src/nodes/scrollblit.js +1034 -0
  70. package/src/nodes/selectable.js +142 -0
  71. package/src/nodes/styling.js +225 -0
  72. package/src/nodes/text.js +649 -0
  73. package/src/nodes/textarea.js +391 -0
  74. package/src/nodes/textinput.js +1146 -0
  75. package/src/nodes/util.js +17 -0
  76. package/src/nodes/window/anchoring.js +161 -0
  77. package/src/nodes/window/capabilities.js +190 -0
  78. package/src/nodes/window/debugpaint.js +83 -0
  79. package/src/nodes/window/droptarget.js +145 -0
  80. package/src/nodes/window/floors.js +577 -0
  81. package/src/nodes/window/flush.js +369 -0
  82. package/src/nodes/window/hints.js +482 -0
  83. package/src/nodes/window/listeners.js +222 -0
  84. package/src/nodes/window/popup.js +71 -0
  85. package/src/nodes/window/size.js +591 -0
  86. package/src/nodes/window/window.js +954 -0
  87. package/src/palette.js +1 -1
  88. package/src/registry.js +7 -3
  89. package/src/styles.js +137 -15
  90. package/src/svgnodes.js +2 -1
  91. package/src/testing/harness.js +2 -2
  92. package/src/textselection.js +5 -3
  93. package/src/trace-registry.js +1 -1
  94. package/src/types/components.d.ts +38 -6
  95. package/src/types/elements.d.ts +26 -14
  96. package/src/types/nodes.d.ts +17 -2
  97. package/src/types/style.d.ts +94 -3
  98. package/src/windowstate.js +1 -1
  99. package/src/yoga.js +1 -1
  100. package/src/nodes.js +0 -13120
package/src/grid.js ADDED
@@ -0,0 +1,1653 @@
1
+ // CSS grid, built in (docs/styling.md "Grid"; the design record is
2
+ // docs/architecture/grid-layout.md). A box with `display: 'grid'` — or
3
+ // `layout: 'grid'`, the same request under the name every layout goes by —
4
+ // arranges its children by CSS Grid's placement and track sizing algorithms
5
+ // (css-grid-2 §8.5 and §12), inside the pass that lays the window out. It is
6
+ // a layout on the seam (nodes/layouthost.js), written against the contract a
7
+ // registered one is handed; what makes it CSS's is the style, which is CSS's
8
+ // own and flat — the box says `gridTemplateColumns: 'auto 1fr'`, a child
9
+ // says `gridColumn: '1 / -1'` — so a grid copied from the web lays out here
10
+ // as it was written.
11
+ //
12
+ // Pure: strings and sizes in, rects out. The parsers are memoized on the
13
+ // value written, and styles.js runs them to validate a style, so a track
14
+ // list that would not lay out is an error where it was written rather than a
15
+ // box that fell back to flexbox.
16
+ //
17
+ // Left out, on purpose: subgrid (a child's tree is its own on this seam, so
18
+ // its items cannot join the parent's track sizing), baselines, named lines,
19
+ // and the second column pass of §12.1 step 3 — see the design record.
20
+
21
+ const EPS = 1e-6;
22
+
23
+ /**
24
+ * The furthest a line number, a span or a repeat count reaches. CSS lets a
25
+ * UA clamp the grid, and asks that lines within ±10000 be kept, so a list
26
+ * placing its rows by number stays exact while a typo like
27
+ * `gridColumn: 1e9` costs a wide grid rather than the process.
28
+ */
29
+ const MAX_TRACKS = 10000;
30
+
31
+ // --- values -------------------------------------------------------------------
32
+
33
+ const AUTO = Object.freeze({ kind: 'auto' });
34
+ const MIN_CONTENT = Object.freeze({ kind: 'min-content' });
35
+ const MAX_CONTENT = Object.freeze({ kind: 'max-content' });
36
+ const ZERO = Object.freeze({ kind: 'fixed', px: 0 });
37
+ const track = (min, max, fit = null) => Object.freeze({ min, max, fit });
38
+
39
+ /** `minmax(0, 1fr)`: a share of the room that ignores what is in it — what a
40
+ * bare count of tracks means, as it does in Tailwind's `grid-cols-3`. */
41
+ const EQUAL = track(ZERO, Object.freeze({ kind: 'fr', f: 1 }));
42
+ const AUTO_TRACK = track(AUTO, AUTO);
43
+
44
+ /** An auto-fit track nothing landed in: no size, and no gap either side. */
45
+ const COLLAPSED = Object.freeze({
46
+ min: ZERO,
47
+ max: ZERO,
48
+ fit: null,
49
+ collapsed: true,
50
+ });
51
+
52
+ const NO_TRACKS = Object.freeze({ tracks: Object.freeze([]), repeat: null });
53
+ const AUTO_TRACKS = Object.freeze([AUTO_TRACK]);
54
+
55
+ /** A side of a placement that names neither a line nor a span. */
56
+ const AUTO_LINE = Object.freeze({});
57
+
58
+ const FLOWS = Object.freeze({
59
+ row: { column: false, dense: false },
60
+ column: { column: true, dense: false },
61
+ dense: { column: false, dense: true },
62
+ 'row dense': { column: false, dense: true },
63
+ 'dense row': { column: false, dense: true },
64
+ 'column dense': { column: true, dense: true },
65
+ 'dense column': { column: true, dense: true },
66
+ });
67
+
68
+ const ITEM_ALIGNMENTS = ['flex-start', 'center', 'flex-end', 'stretch'];
69
+ const SELF_ALIGNMENTS = ['auto', ...ITEM_ALIGNMENTS];
70
+
71
+ /** The properties a grid reads off the box that yoga never sees — a change
72
+ * to one asks the layout again (nodes/animation.js `_retarget`). */
73
+ export const GRID_CONTAINER_PROPS = Object.freeze([
74
+ 'gridTemplateColumns',
75
+ 'gridTemplateRows',
76
+ 'gridTemplateAreas',
77
+ 'gridAutoColumns',
78
+ 'gridAutoRows',
79
+ 'gridAutoFlow',
80
+ 'justifyItems',
81
+ ]);
82
+
83
+ /** …and off each child. */
84
+ export const GRID_ITEM_PROPS = Object.freeze([
85
+ 'gridColumn',
86
+ 'gridRow',
87
+ 'gridArea',
88
+ 'justifySelf',
89
+ ]);
90
+
91
+ // --- parsing ------------------------------------------------------------------
92
+
93
+ const parsed = new Map();
94
+
95
+ /** A parse, remembered by the value written. Bounded: a transition over a
96
+ * track list writes a new string every frame. */
97
+ function memo(key, make) {
98
+ let value = parsed.get(key);
99
+ if (value === undefined) {
100
+ if (parsed.size >= 512) parsed.clear();
101
+ value = make();
102
+ parsed.set(key, value);
103
+ }
104
+ return value;
105
+ }
106
+
107
+ function invalid(prop, value, why) {
108
+ return new SyntaxError(
109
+ `react-x11: invalid ${prop} ${JSON.stringify(value)} — ${why}`,
110
+ );
111
+ }
112
+
113
+ const isFixedBreadth = (b) => b.kind === 'fixed' || b.kind === 'percent';
114
+
115
+ /** CSS's `<fixed-size>`: a track whose size can be counted before anything
116
+ * is in it, which is what an auto repeat is made of. */
117
+ const isFixedSize = (t) =>
118
+ t.fit === null && (isFixedBreadth(t.min) || isFixedBreadth(t.max));
119
+
120
+ const LENGTH = /^(\d+(?:\.\d*)?|\.\d+)([a-z%]*)/i;
121
+
122
+ /**
123
+ * A CSS track list: sizes, `minmax()`, `fit-content()` and — in a template —
124
+ * `repeat()` with a count, `auto-fill` or `auto-fit`. Lengths are logical
125
+ * pixels and come out in device pixels (`scale`), the way every length in a
126
+ * style arrives at layout.
127
+ */
128
+ function parseTrackList(src, scale, prop, template) {
129
+ let i = 0;
130
+ const n = src.length;
131
+ const fail = (why) => {
132
+ throw invalid(prop, src, why);
133
+ };
134
+ const space = () => {
135
+ while (i < n && /\s/.test(src[i])) i++;
136
+ };
137
+ const word = () => {
138
+ space();
139
+ const m = /^[a-z][a-z-]*/i.exec(src.slice(i));
140
+ if (m === null) return null;
141
+ i += m[0].length;
142
+ return m[0].toLowerCase();
143
+ };
144
+ const expect = (ch) => {
145
+ space();
146
+ if (src[i] !== ch) {
147
+ fail(
148
+ i < n
149
+ ? `expected "${ch}" before "${src.slice(i)}"`
150
+ : `expected "${ch}" at the end`,
151
+ );
152
+ }
153
+ i++;
154
+ };
155
+ const breadth = () => {
156
+ space();
157
+ if (src[i] === '[') fail('named lines ("[name]") are not supported');
158
+ if (src[i] === '-') fail('a track size is never negative');
159
+ const m = LENGTH.exec(src.slice(i));
160
+ if (m !== null) {
161
+ i += m[0].length;
162
+ const v = Number(m[1]);
163
+ const unit = m[2].toLowerCase();
164
+ if (unit === 'px') return { kind: 'fixed', px: v * scale };
165
+ if (unit === 'fr') return { kind: 'fr', f: v };
166
+ if (unit === '%') return { kind: 'percent', f: v / 100 };
167
+ if (unit === '' && v === 0) return ZERO;
168
+ if (unit === '') {
169
+ fail(
170
+ `a length in a track list is written with its unit, "${m[1]}px"` +
171
+ (template
172
+ ? ` — a number on its own is a count of equal tracks, ${prop}: ${m[1]}`
173
+ : ''),
174
+ );
175
+ }
176
+ fail(`"${unit}" is not a unit a track takes — px, % and fr are`);
177
+ }
178
+ const w = word();
179
+ if (w === 'auto') return AUTO;
180
+ if (w === 'min-content') return MIN_CONTENT;
181
+ if (w === 'max-content') return MAX_CONTENT;
182
+ if (w === 'subgrid') {
183
+ fail('subgrid is not supported: a grid item lays its own children out');
184
+ }
185
+ return fail(
186
+ w === null
187
+ ? `expected a track size before "${src.slice(i)}"`
188
+ : `"${w}" is not a track size`,
189
+ );
190
+ };
191
+ const size = () => {
192
+ const at = i;
193
+ const w = word();
194
+ if (w === 'minmax') {
195
+ expect('(');
196
+ const min = breadth();
197
+ if (min.kind === 'fr') {
198
+ fail(
199
+ 'minmax() cannot take a flexible minimum — minmax(0, 1fr) is the usual one',
200
+ );
201
+ }
202
+ expect(',');
203
+ const max = breadth();
204
+ expect(')');
205
+ return track(min, max);
206
+ }
207
+ if (w === 'fit-content') {
208
+ expect('(');
209
+ const limit = breadth();
210
+ if (!isFixedBreadth(limit)) {
211
+ fail('fit-content() takes a length or a percentage');
212
+ }
213
+ expect(')');
214
+ return track(AUTO, MAX_CONTENT, limit);
215
+ }
216
+ i = at;
217
+ const b = breadth();
218
+ // `1fr` is `minmax(auto, 1fr)`: a share of the room, but never narrower
219
+ // than what is in it
220
+ return b.kind === 'fr' ? track(AUTO, b) : track(b, b);
221
+ };
222
+ const tracks = [];
223
+ let repeat = null;
224
+ for (;;) {
225
+ space();
226
+ if (i >= n) break;
227
+ const at = i;
228
+ if (word() !== 'repeat') {
229
+ i = at;
230
+ tracks.push(size());
231
+ continue;
232
+ }
233
+ if (!template) {
234
+ fail(
235
+ 'repeat() belongs in a template — an implicit track list is its sizes, taken in turn',
236
+ );
237
+ }
238
+ expect('(');
239
+ space();
240
+ let times;
241
+ const m = /^\d+/.exec(src.slice(i));
242
+ if (m !== null) {
243
+ i += m[0].length;
244
+ times = Math.min(Number(m[0]), MAX_TRACKS);
245
+ if (times < 1) fail('repeat() repeats its tracks at least once');
246
+ } else {
247
+ times = word();
248
+ if (times !== 'auto-fill' && times !== 'auto-fit') {
249
+ fail('repeat() takes a count, auto-fill or auto-fit first');
250
+ }
251
+ }
252
+ expect(',');
253
+ const body = [];
254
+ for (;;) {
255
+ space();
256
+ if (i >= n || src[i] === ')') break;
257
+ body.push(size());
258
+ }
259
+ expect(')');
260
+ if (body.length === 0) fail('repeat() has no tracks to repeat');
261
+ if (typeof times === 'number') {
262
+ for (let k = 0; k < times && tracks.length < MAX_TRACKS; k++) {
263
+ for (const t of body) tracks.push(t);
264
+ }
265
+ } else if (repeat !== null) {
266
+ fail('a track list takes one repeat(auto-fill) or repeat(auto-fit)');
267
+ } else if (!body.every(isFixedSize)) {
268
+ fail(
269
+ `repeat(${times}, …) repeats tracks whose size it can count — ` +
270
+ 'minmax(200px, 1fr) or 120px, not 1fr or auto',
271
+ );
272
+ } else {
273
+ repeat = { at: tracks.length, tracks: body, fit: times === 'auto-fit' };
274
+ }
275
+ }
276
+ if (repeat !== null && !tracks.every(isFixedSize)) {
277
+ fail(
278
+ `beside repeat(${repeat.fit ? 'auto-fit' : 'auto-fill'}, …) every ` +
279
+ 'track has a size it can count, so the room left for the repeats is known',
280
+ );
281
+ }
282
+ if (!template && tracks.length === 0) {
283
+ fail('an implicit track list names at least one size');
284
+ }
285
+ return { tracks, repeat };
286
+ }
287
+
288
+ /**
289
+ * `gridTemplateColumns` / `gridTemplateRows`: a CSS track list, or a number
290
+ * — that many equal tracks, `repeat(n, minmax(0, 1fr))`, the way a count
291
+ * reads everywhere grid is written short (Tailwind's `grid-cols-3`). A count
292
+ * rather than a length because a track list is not a length: `maxLines: 3`
293
+ * and `flex: 1` are numbers that are not pixels either.
294
+ */
295
+ export function templateTracks(value, scale = 1, prop = 'gridTemplateColumns') {
296
+ if (value == null || value === 'none') return NO_TRACKS;
297
+ if (typeof value === 'number') {
298
+ if (!Number.isInteger(value) || value < 1) {
299
+ throw invalid(
300
+ prop,
301
+ value,
302
+ 'a number is a count of equal tracks, a whole number from 1',
303
+ );
304
+ }
305
+ return memo(`#${value}`, () => ({
306
+ tracks: Object.freeze(Array(Math.min(value, MAX_TRACKS)).fill(EQUAL)),
307
+ repeat: null,
308
+ }));
309
+ }
310
+ if (typeof value !== 'string') {
311
+ throw invalid(
312
+ prop,
313
+ value,
314
+ 'expected a track list like "200px 1fr", or a count of equal tracks',
315
+ );
316
+ }
317
+ return memo(`t${scale}|${value}`, () =>
318
+ parseTrackList(value, scale, prop, true),
319
+ );
320
+ }
321
+
322
+ /**
323
+ * `gridAutoColumns` / `gridAutoRows`: the sizes an implicit track takes, in
324
+ * turn — a track list without `repeat()`, or a number, which here is a
325
+ * length: an implicit track has a size and never a count.
326
+ */
327
+ export function autoTracks(value, scale = 1, prop = 'gridAutoRows') {
328
+ if (value == null) return AUTO_TRACKS;
329
+ if (typeof value === 'number') {
330
+ if (!(value >= 0) || !Number.isFinite(value)) {
331
+ throw invalid(prop, value, 'a number is a length, 0 or more');
332
+ }
333
+ return memo(`a${scale}#${value}`, () => {
334
+ const px = { kind: 'fixed', px: value * scale };
335
+ return Object.freeze([track(px, px)]);
336
+ });
337
+ }
338
+ if (typeof value !== 'string') {
339
+ throw invalid(
340
+ prop,
341
+ value,
342
+ 'expected a track size like "120px" or "minmax(40px, auto)"',
343
+ );
344
+ }
345
+ return memo(
346
+ `a${scale}|${value}`,
347
+ () => parseTrackList(value, scale, prop, false).tracks,
348
+ );
349
+ }
350
+
351
+ const AREA_NAME = /^[\w\-€-￿]+$/;
352
+
353
+ /**
354
+ * `gridTemplateAreas`: CSS's one quoted string per row, or — which reads
355
+ * better in a style object — an array of the rows. Every row has as many
356
+ * cells, a run of dots is an empty one, and every name covers a rectangle.
357
+ */
358
+ export function templateAreas(value, prop = 'gridTemplateAreas') {
359
+ if (value == null || value === 'none') return null;
360
+ if (Array.isArray(value)) {
361
+ if (!value.every((row) => typeof row === 'string')) {
362
+ throw invalid(prop, value, 'an array of areas is one string per row');
363
+ }
364
+ return memo(`r|${value.join('\n')}`, () => parseAreas(value, prop, value));
365
+ }
366
+ if (typeof value !== 'string') {
367
+ throw invalid(
368
+ prop,
369
+ value,
370
+ `expected one quoted string per row — '"head head" "side main"' — or an array of rows`,
371
+ );
372
+ }
373
+ return memo(`s|${value}`, () =>
374
+ parseAreas(quotedRows(value, prop), prop, value),
375
+ );
376
+ }
377
+
378
+ function quotedRows(src, prop) {
379
+ const rows = [];
380
+ const quoted = /\s*(?:"([^"]*)"|'([^']*)')\s*/y;
381
+ let i = 0;
382
+ while (i < src.length) {
383
+ quoted.lastIndex = i;
384
+ const m = quoted.exec(src);
385
+ if (m === null) {
386
+ if (/^\s*$/.test(src.slice(i))) break;
387
+ throw invalid(
388
+ prop,
389
+ src,
390
+ `each row is a quoted string — '"head head" "side main"' — or pass an array of rows`,
391
+ );
392
+ }
393
+ rows.push(m[1] ?? m[2]);
394
+ i = quoted.lastIndex;
395
+ }
396
+ return rows;
397
+ }
398
+
399
+ function parseAreas(rows, prop, written) {
400
+ if (rows.length === 0) throw invalid(prop, written, 'it names no rows');
401
+ const grid = rows.map((row, r) => {
402
+ const cells = row.match(/\.+|[^\s.]+/g) ?? [];
403
+ if (cells.length === 0) {
404
+ throw invalid(prop, written, `row ${r + 1} names no cells`);
405
+ }
406
+ for (const cell of cells) {
407
+ if (cell[0] !== '.' && !AREA_NAME.test(cell)) {
408
+ throw invalid(prop, written, `"${cell}" is not an area name`);
409
+ }
410
+ }
411
+ return cells;
412
+ });
413
+ const cols = grid[0].length;
414
+ grid.forEach((cells, r) => {
415
+ if (cells.length !== cols) {
416
+ throw invalid(
417
+ prop,
418
+ written,
419
+ `every row has as many cells as the first — row 1 has ${cols}, ` +
420
+ `row ${r + 1} has ${cells.length}`,
421
+ );
422
+ }
423
+ });
424
+ const names = new Map();
425
+ grid.forEach((cells, r) =>
426
+ cells.forEach((cell, c) => {
427
+ if (cell[0] === '.') return;
428
+ const a = names.get(cell);
429
+ if (a === undefined) {
430
+ names.set(cell, { r0: r, r1: r + 1, c0: c, c1: c + 1, cells: 1 });
431
+ } else {
432
+ a.r0 = Math.min(a.r0, r);
433
+ a.r1 = Math.max(a.r1, r + 1);
434
+ a.c0 = Math.min(a.c0, c);
435
+ a.c1 = Math.max(a.c1, c + 1);
436
+ a.cells++;
437
+ }
438
+ }),
439
+ );
440
+ for (const [name, a] of names) {
441
+ // every cell in the bounding box is this name's, or there are fewer of
442
+ // them than the box has cells
443
+ if (a.cells !== (a.r1 - a.r0) * (a.c1 - a.c0)) {
444
+ throw invalid(prop, written, `area "${name}" is not a rectangle`);
445
+ }
446
+ // the placement it stands for, made once
447
+ a.rows = Object.freeze({
448
+ start: { line: a.r0 + 1 },
449
+ end: { line: a.r1 + 1 },
450
+ });
451
+ a.columns = Object.freeze({
452
+ start: { line: a.c0 + 1 },
453
+ end: { line: a.c1 + 1 },
454
+ });
455
+ }
456
+ return { names, rows: grid.length, cols };
457
+ }
458
+
459
+ const clampLine = (line) => Math.max(-MAX_TRACKS, Math.min(MAX_TRACKS, line));
460
+
461
+ /** One side of a placement: a line number, `span <n>`, or `auto`. */
462
+ function gridLine(text, prop, value) {
463
+ const t = text.trim();
464
+ if (t === '') throw invalid(prop, value, 'one side of the "/" is empty');
465
+ if (t === 'auto') return AUTO_LINE;
466
+ const words = t.split(/\s+/);
467
+ if (words.length === 1) {
468
+ if (/^[+-]?\d+$/.test(t)) {
469
+ const line = Number(t);
470
+ if (line === 0) {
471
+ throw invalid(
472
+ prop,
473
+ value,
474
+ 'there is no line 0 — lines count from 1 at the start, and from -1 at the end',
475
+ );
476
+ }
477
+ return { line: clampLine(line) };
478
+ }
479
+ if (t === 'span') {
480
+ throw invalid(prop, value, '"span" needs a number of tracks — span 2');
481
+ }
482
+ throw invalid(
483
+ prop,
484
+ value,
485
+ `"${t}" would name a line, and named lines are not supported — name ` +
486
+ "an area in the grid's gridTemplateAreas and place this with gridArea",
487
+ );
488
+ }
489
+ if (words.length === 2 && (words[0] === 'span' || words[1] === 'span')) {
490
+ const count = words[0] === 'span' ? words[1] : words[0];
491
+ if (!/^\d+$/.test(count) || Number(count) < 1) {
492
+ throw invalid(
493
+ prop,
494
+ value,
495
+ 'span takes a whole number of tracks, 1 or more — span 2',
496
+ );
497
+ }
498
+ return { span: Math.min(Number(count), MAX_TRACKS) };
499
+ }
500
+ throw invalid(
501
+ prop,
502
+ value,
503
+ `"${t}" is not a grid line — a line number, "span 2" or "auto"`,
504
+ );
505
+ }
506
+
507
+ /**
508
+ * `gridColumn` / `gridRow`: CSS's shorthand, `start / end`. A number is a
509
+ * line, as it is in CSS (and in React DOM, which never gives it `px`):
510
+ * counting from 1 at the start and from -1 at the end, so `'1 / -1'` is the
511
+ * whole width however many columns there are.
512
+ */
513
+ export function gridLines(value, prop = 'gridColumn') {
514
+ if (typeof value === 'number') {
515
+ if (!Number.isInteger(value) || value === 0) {
516
+ throw invalid(
517
+ prop,
518
+ value,
519
+ 'a number is a line: a whole number, counting from 1 at the start or from -1 at the end',
520
+ );
521
+ }
522
+ return memo(`l#${value}`, () =>
523
+ Object.freeze({ start: { line: clampLine(value) }, end: AUTO_LINE }),
524
+ );
525
+ }
526
+ if (typeof value !== 'string') {
527
+ throw invalid(prop, value, "expected a line like 2, 'span 2' or '1 / -1'");
528
+ }
529
+ return memo(`l|${value}`, () => {
530
+ const sides = value.split('/');
531
+ if (sides.length > 2) {
532
+ throw invalid(
533
+ prop,
534
+ value,
535
+ 'it takes a start and an end — "1 / 3" — with one "/"',
536
+ );
537
+ }
538
+ return Object.freeze({
539
+ start: gridLine(sides[0], prop, value),
540
+ end: sides.length === 2 ? gridLine(sides[1], prop, value) : AUTO_LINE,
541
+ });
542
+ });
543
+ }
544
+
545
+ const IDENT = /^-?[A-Za-z_€-￿][\w\-€-￿]*$/;
546
+
547
+ /**
548
+ * `gridArea`: the name of an area in the grid's `gridTemplateAreas`, or
549
+ * CSS's four lines, `row-start / column-start / row-end / column-end`.
550
+ * `gridColumn` and `gridRow` written beside it win for their axis, the way a
551
+ * longhand wins over the shorthand it refines.
552
+ */
553
+ export function gridArea(value, prop = 'gridArea') {
554
+ if (typeof value === 'number') {
555
+ return memo(`g#${value}`, () =>
556
+ Object.freeze({ rows: gridLines(value, prop), columns: null }),
557
+ );
558
+ }
559
+ if (typeof value !== 'string') {
560
+ throw invalid(
561
+ prop,
562
+ value,
563
+ "expected an area's name, or lines — 'row-start / column-start / row-end / column-end'",
564
+ );
565
+ }
566
+ return memo(`g|${value}`, () => {
567
+ const t = value.trim();
568
+ if (IDENT.test(t) && t !== 'auto' && t !== 'span') {
569
+ return Object.freeze({ name: t });
570
+ }
571
+ const sides = t.split('/');
572
+ if (sides.length > 4) {
573
+ throw invalid(
574
+ prop,
575
+ value,
576
+ 'it takes at most four lines — row-start / column-start / row-end / column-end',
577
+ );
578
+ }
579
+ const side = (k) =>
580
+ k < sides.length ? gridLine(sides[k], prop, value) : AUTO_LINE;
581
+ return Object.freeze({
582
+ rows: { start: side(0), end: side(2) },
583
+ columns: { start: side(1), end: side(3) },
584
+ });
585
+ });
586
+ }
587
+
588
+ function oneOf(prop, value, allowed) {
589
+ if (!allowed.includes(value)) {
590
+ throw new Error(
591
+ `react-x11: invalid ${prop} ${JSON.stringify(value)} (expected one of ${allowed.join(', ')})`,
592
+ );
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Throw, naming the property and what is wrong, for a grid value that would
598
+ * not lay out — styles.js's validation, which runs in development wherever a
599
+ * style is written. The same parsers the layout runs, so the two cannot
600
+ * disagree about what is valid.
601
+ */
602
+ export function validateGridValue(prop, value) {
603
+ if (value == null) return;
604
+ switch (prop) {
605
+ case 'gridTemplateColumns':
606
+ case 'gridTemplateRows':
607
+ templateTracks(value, 1, prop);
608
+ break;
609
+ case 'gridAutoColumns':
610
+ case 'gridAutoRows':
611
+ autoTracks(value, 1, prop);
612
+ break;
613
+ case 'gridTemplateAreas':
614
+ templateAreas(value, prop);
615
+ break;
616
+ case 'gridColumn':
617
+ case 'gridRow':
618
+ gridLines(value, prop);
619
+ break;
620
+ case 'gridArea':
621
+ gridArea(value, prop);
622
+ break;
623
+ case 'gridAutoFlow':
624
+ oneOf(prop, value, Object.keys(FLOWS));
625
+ break;
626
+ case 'justifyItems':
627
+ oneOf(prop, value, ITEM_ALIGNMENTS);
628
+ break;
629
+ case 'justifySelf':
630
+ oneOf(prop, value, SELF_ALIGNMENTS);
631
+ break;
632
+ default:
633
+ break;
634
+ }
635
+ }
636
+
637
+ // --- placement (§8) -------------------------------------------------------------
638
+
639
+ /** Where an item with no definite line in an axis starts, until placed. */
640
+ const AUTO_POS = -0x40000000;
641
+
642
+ const lineIndex = (line, explicit) =>
643
+ line > 0 ? line - 1 : explicit + 1 + line;
644
+
645
+ /** A placement's two sides, as a start and a span (§8.3.1). Negative lines
646
+ * count back from the end of the explicit grid. */
647
+ function resolve(spec, explicit, starts, spans, i) {
648
+ if (spec === null) {
649
+ starts[i] = AUTO_POS;
650
+ spans[i] = 1;
651
+ return;
652
+ }
653
+ const a = spec.start.line;
654
+ const b = spec.end.line;
655
+ if (a !== undefined && b !== undefined) {
656
+ let s = lineIndex(a, explicit);
657
+ let e = lineIndex(b, explicit);
658
+ if (s > e) {
659
+ const t = s;
660
+ s = e;
661
+ e = t;
662
+ }
663
+ starts[i] = s;
664
+ // the same line twice is one track from it
665
+ spans[i] = Math.max(1, e - s);
666
+ return;
667
+ }
668
+ if (a !== undefined) {
669
+ starts[i] = lineIndex(a, explicit);
670
+ spans[i] = spec.end.span ?? 1;
671
+ return;
672
+ }
673
+ if (b !== undefined) {
674
+ const span = spec.start.span ?? 1;
675
+ starts[i] = lineIndex(b, explicit) - span;
676
+ spans[i] = span;
677
+ return;
678
+ }
679
+ // no line in this axis: placed automatically, with the span one side names
680
+ // — the start's, when both do, as CSS drops the end's
681
+ starts[i] = AUTO_POS;
682
+ spans[i] = spec.start.span ?? spec.end.span ?? 1;
683
+ }
684
+
685
+ /**
686
+ * CSS's grid item placement algorithm (§8.5), in the frame of the flow: the
687
+ * _major_ axis is the one the flow runs along and grows (rows, for
688
+ * `gridAutoFlow: 'row'`), the _minor_ one is fixed once the definite items
689
+ * say how many tracks it has. A definite line before the first explicit one
690
+ * adds tracks at the start, and the offset they make is returned.
691
+ */
692
+ function placeItems(n, cS, cN, rS, rN, explicitCols, explicitRows, flow) {
693
+ let cOff = 0;
694
+ let rOff = 0;
695
+ for (let i = 0; i < n; i++) {
696
+ if (cS[i] !== AUTO_POS && -cS[i] > cOff) cOff = -cS[i];
697
+ if (rS[i] !== AUTO_POS && -rS[i] > rOff) rOff = -rS[i];
698
+ }
699
+ if (cOff !== 0 || rOff !== 0) {
700
+ for (let i = 0; i < n; i++) {
701
+ if (cS[i] !== AUTO_POS) cS[i] += cOff;
702
+ if (rS[i] !== AUTO_POS) rS[i] += rOff;
703
+ }
704
+ }
705
+ const byColumn = flow.column;
706
+ const mS = byColumn ? cS : rS;
707
+ const mN = byColumn ? cN : rN;
708
+ const kS = byColumn ? rS : cS;
709
+ const kN = byColumn ? rN : cN;
710
+ const occupied = [];
711
+ const filled = [];
712
+ const free = (m, k, ms, ks) => {
713
+ for (let y = m; y < m + ms; y++) {
714
+ const line = occupied[y];
715
+ if (line === undefined) continue;
716
+ for (let x = k; x < k + ks; x++) if (line[x] === 1) return false;
717
+ }
718
+ return true;
719
+ };
720
+ const take = (m, k, ms, ks) => {
721
+ for (let y = m; y < m + ms; y++) {
722
+ const line = (occupied[y] ??= []);
723
+ for (let x = k; x < k + ks; x++) line[x] = 1;
724
+ filled[y] = (filled[y] ?? 0) + ks;
725
+ }
726
+ };
727
+ // 1. what is definite in both axes
728
+ for (let i = 0; i < n; i++) {
729
+ if (mS[i] !== AUTO_POS && kS[i] !== AUTO_POS) {
730
+ take(mS[i], kS[i], mN[i], kN[i]);
731
+ }
732
+ }
733
+ // 2. what is locked to a line of the flow: the first place along it that
734
+ // is free (and, sparse, past what this step put there before)
735
+ const behind = new Map();
736
+ for (let i = 0; i < n; i++) {
737
+ if (mS[i] === AUTO_POS || kS[i] !== AUTO_POS) continue;
738
+ const m = mS[i];
739
+ let k = flow.dense ? 0 : (behind.get(m) ?? 0);
740
+ while (!free(m, k, mN[i], kN[i])) k++;
741
+ kS[i] = k;
742
+ take(m, k, mN[i], kN[i]);
743
+ behind.set(m, k + kN[i]);
744
+ }
745
+ // 3. how many tracks the minor axis has: the explicit ones, every definite
746
+ // item's reach, and the widest span still to place
747
+ let minor = byColumn ? explicitRows + rOff : explicitCols + cOff;
748
+ for (let i = 0; i < n; i++) {
749
+ minor = Math.max(minor, (kS[i] === AUTO_POS ? 0 : kS[i]) + kN[i]);
750
+ }
751
+ // 4. everything else, in order, behind a cursor — reset to the start for
752
+ // every item when dense, from the first line with a free cell in it
753
+ let lowest = 0;
754
+ const firstOpen = () => {
755
+ while ((filled[lowest] ?? 0) >= minor) lowest++;
756
+ return lowest;
757
+ };
758
+ let cm = 0;
759
+ let ck = 0;
760
+ for (let i = 0; i < n; i++) {
761
+ if (mS[i] !== AUTO_POS) continue;
762
+ const ms = mN[i];
763
+ const ks = kN[i];
764
+ if (kS[i] !== AUTO_POS) {
765
+ const k = kS[i];
766
+ if (flow.dense) cm = firstOpen();
767
+ else if (k < ck) cm++;
768
+ ck = k;
769
+ while (!free(cm, k, ms, ks)) cm++;
770
+ mS[i] = cm;
771
+ take(cm, k, ms, ks);
772
+ } else {
773
+ if (flow.dense) {
774
+ cm = firstOpen();
775
+ ck = 0;
776
+ }
777
+ for (;;) {
778
+ if (ck + ks > minor) {
779
+ cm++;
780
+ ck = 0;
781
+ } else if (free(cm, ck, ms, ks)) {
782
+ break;
783
+ } else {
784
+ ck++;
785
+ }
786
+ }
787
+ mS[i] = cm;
788
+ kS[i] = ck;
789
+ take(cm, ck, ms, ks);
790
+ ck += ks;
791
+ }
792
+ }
793
+ let major = byColumn ? explicitCols + cOff : explicitRows + rOff;
794
+ for (let i = 0; i < n; i++) major = Math.max(major, mS[i] + mN[i]);
795
+ return byColumn
796
+ ? { cols: major, rows: minor, cOff, rOff }
797
+ : { cols: minor, rows: major, cOff, rOff };
798
+ }
799
+
800
+ const definite = (b, room) =>
801
+ b.kind === 'fixed'
802
+ ? b.px
803
+ : b.kind === 'percent'
804
+ ? room === null
805
+ ? null
806
+ : b.f * room
807
+ : null;
808
+
809
+ const repeated = new WeakMap();
810
+
811
+ /**
812
+ * The explicit tracks, with an auto repeat written out: as many repetitions
813
+ * as fit in `room` without overflowing it (§7.2.3.2), each track counted at
814
+ * its maximum if that is definite and its minimum otherwise — and one when
815
+ * there is no room to count against.
816
+ */
817
+ function repeatOut(list, room, gap) {
818
+ const rep = list.repeat;
819
+ if (rep === null) return { tracks: list.tracks, from: 0, to: 0, fit: false };
820
+ const last = repeated.get(list);
821
+ if (last !== undefined && last.room === room && last.gap === gap) {
822
+ return last.out;
823
+ }
824
+ let times = 1;
825
+ if (room !== null) {
826
+ const size = (t) => definite(t.max, room) ?? definite(t.min, room) ?? 0;
827
+ let others = 0;
828
+ for (const t of list.tracks) others += size(t);
829
+ let body = 0;
830
+ for (const t of rep.tracks) body += size(t);
831
+ const per = body + gap * rep.tracks.length;
832
+ if (per > 0) {
833
+ times = Math.floor(
834
+ (room - others - gap * (list.tracks.length - 1) + EPS) / per,
835
+ );
836
+ }
837
+ times = Math.max(1, Math.min(times, MAX_TRACKS));
838
+ }
839
+ const tracks = list.tracks.slice(0, rep.at);
840
+ for (let k = 0; k < times; k++) for (const t of rep.tracks) tracks.push(t);
841
+ for (let k = rep.at; k < list.tracks.length; k++) tracks.push(list.tracks[k]);
842
+ const out = {
843
+ tracks,
844
+ from: rep.at,
845
+ to: rep.at + times * rep.tracks.length,
846
+ fit: rep.fit,
847
+ };
848
+ repeated.set(list, { room, gap, out });
849
+ return out;
850
+ }
851
+
852
+ /**
853
+ * An axis's tracks, explicit and implicit: the template's where it has one,
854
+ * and the implicit sizes in turn around it — forwards after the explicit
855
+ * grid, backwards before it. An auto-fit repeat nothing landed in collapses.
856
+ */
857
+ function axisTracks(count, offset, explicit, auto, starts, spans, n) {
858
+ const list = explicit.tracks;
859
+ let used = null;
860
+ if (explicit.fit) {
861
+ used = new Uint8Array(count);
862
+ for (let i = 0; i < n; i++) {
863
+ for (let k = starts[i]; k < starts[i] + spans[i]; k++) used[k] = 1;
864
+ }
865
+ }
866
+ const a = auto.length;
867
+ const tracks = new Array(count);
868
+ for (let k = 0; k < count; k++) {
869
+ const e = k - offset;
870
+ let t;
871
+ if (e >= 0 && e < list.length) t = list[e];
872
+ else if (e >= 0) t = auto[(e - list.length) % a];
873
+ else t = auto[(a - (-e % a)) % a];
874
+ if (used !== null && e >= explicit.from && e < explicit.to && !used[k]) {
875
+ t = COLLAPSED;
876
+ }
877
+ tracks[k] = t;
878
+ }
879
+ return tracks;
880
+ }
881
+
882
+ // --- track sizing (§12) -----------------------------------------------------
883
+
884
+ // what a track's minimum and maximum sizing functions are, for the arrays
885
+ const FIXED = 0;
886
+ const AUTO_K = 1;
887
+ const MIN_K = 2;
888
+ const MAX_K = 3;
889
+ const FLEX = 4;
890
+ const FIT = 5;
891
+
892
+ const ALL = () => true;
893
+
894
+ const grow = (limit, v) => (limit === Infinity ? v : Math.max(limit, v));
895
+
896
+ /**
897
+ * The track sizing algorithm, one axis (§12.3–12.8): base sizes and growth
898
+ * limits from the tracks' own functions, raised by what the items in them
899
+ * contribute — single-track items, then spanning ones in order of span, then
900
+ * the ones that cross a flexible track — then the free space shared out, the
901
+ * `fr` tracks expanded, and the `auto` ones stretched.
902
+ *
903
+ * `ask` is what the items say about this axis — `min(i)` and `max(i)`,
904
+ * their min- and max-content contributions, and `declared(i)`, whether they
905
+ * name a minimum of their own — each asked only if a track needs it, which
906
+ * is what keeps a grid of fixed and `minmax(0, 1fr)` tracks from asking its
907
+ * children anything. `constraint` is `null` for a definite `avail`, or
908
+ * `'min'`/`'max'` for a grid sized to its min- or max-content.
909
+ */
910
+ function sizeTracks(
911
+ tracks,
912
+ starts,
913
+ spans,
914
+ count,
915
+ ask,
916
+ clips,
917
+ avail,
918
+ gap,
919
+ constraint,
920
+ stretchAuto,
921
+ ) {
922
+ const n = tracks.length;
923
+ const base = new Float64Array(n);
924
+ const limit = new Float64Array(n);
925
+ const minK = new Uint8Array(n);
926
+ const maxK = new Uint8Array(n);
927
+ const factor = new Float64Array(n);
928
+ const fixedMax = new Float64Array(n);
929
+ const fitCap = new Float64Array(n).fill(Infinity);
930
+ const dead = new Uint8Array(n);
931
+ let live = 0;
932
+ for (let k = 0; k < n; k++) {
933
+ const t = tracks[k];
934
+ if (t.collapsed) {
935
+ dead[k] = 1;
936
+ continue;
937
+ }
938
+ live++;
939
+ const lo = definite(t.min, avail);
940
+ if (lo !== null) base[k] = lo;
941
+ // a percentage with nothing to be a percentage of is `auto`
942
+ else if (t.min.kind === 'min-content') minK[k] = MIN_K;
943
+ else if (t.min.kind === 'max-content') minK[k] = MAX_K;
944
+ else minK[k] = AUTO_K;
945
+ if (t.fit !== null) {
946
+ maxK[k] = FIT;
947
+ limit[k] = Infinity;
948
+ const cap = definite(t.fit, avail);
949
+ if (cap !== null) fitCap[k] = cap;
950
+ } else {
951
+ const hi = definite(t.max, avail);
952
+ if (hi !== null) {
953
+ limit[k] = hi;
954
+ fixedMax[k] = hi;
955
+ } else if (t.max.kind === 'fr') {
956
+ maxK[k] = FLEX;
957
+ factor[k] = t.max.f;
958
+ limit[k] = Infinity;
959
+ } else {
960
+ maxK[k] =
961
+ t.max.kind === 'min-content'
962
+ ? MIN_K
963
+ : t.max.kind === 'max-content'
964
+ ? MAX_K
965
+ : AUTO_K;
966
+ limit[k] = Infinity;
967
+ }
968
+ }
969
+ if (limit[k] < base[k]) limit[k] = base[k];
970
+ }
971
+ const gaps = gap * Math.max(0, live - 1);
972
+ const intrinsicMax = (k) => maxK[k] !== FIXED && maxK[k] !== FLEX;
973
+ const maxContentMax = (k) =>
974
+ maxK[k] === MAX_K || maxK[k] === AUTO_K || maxK[k] === FIT;
975
+
976
+ // which items go where, and whether CSS's automatic minimum is theirs: a
977
+ // floor of their content where they span an `auto`-minimum track and, if
978
+ // they span more than one, no flexible one — and never for a box that
979
+ // clips what it holds (§6.6)
980
+ const autoMin = new Uint8Array(count);
981
+ const singles = [];
982
+ const spanning = [];
983
+ const flexers = [];
984
+ for (let i = 0; i < count; i++) {
985
+ const s = starts[i];
986
+ const e = s + spans[i];
987
+ let flexed = false;
988
+ let auto = false;
989
+ for (let k = s; k < e; k++) {
990
+ if (maxK[k] === FLEX) flexed = true;
991
+ if (minK[k] === AUTO_K) auto = true;
992
+ }
993
+ if (auto && (spans[i] === 1 || !flexed) && !clips(i)) autoMin[i] = 1;
994
+ if (flexed) flexers.push(i);
995
+ else if (spans[i] === 1) singles.push(i);
996
+ else spanning.push(i);
997
+ }
998
+ // CSS's minimum contribution: the content floor where the automatic
999
+ // minimum applies or a minimum is written down (the floor measured for a
1000
+ // child that says `minWidth` is that minimum), and nothing otherwise
1001
+ const minimum = (i) => (autoMin[i] || ask.declared(i) ? ask.min(i) : 0);
1002
+ // what the spanned tracks' fixed maximums allow, when all of them have one
1003
+ const cap = (i) => {
1004
+ let sum = gap * (spans[i] - 1);
1005
+ for (let k = starts[i], e = k + spans[i]; k < e; k++) {
1006
+ if (maxK[k] === FIXED) sum += fixedMax[k];
1007
+ else if (maxK[k] === FIT && fitCap[k] !== Infinity) sum += fitCap[k];
1008
+ else return Infinity;
1009
+ }
1010
+ return sum;
1011
+ };
1012
+ const limitedMin = (i) => Math.max(Math.min(ask.min(i), cap(i)), minimum(i));
1013
+ const limitedMax = (i) => Math.max(Math.min(ask.max(i), cap(i)), minimum(i));
1014
+ // what an `auto` minimum takes: the min- or max-content contribution when
1015
+ // the grid is being sized to its own, the minimum contribution otherwise
1016
+ const low =
1017
+ constraint === 'min'
1018
+ ? limitedMin
1019
+ : constraint === 'max'
1020
+ ? limitedMax
1021
+ : minimum;
1022
+
1023
+ // §12.5 step 2: the items in one track, where it is not flexible
1024
+ for (const i of singles) {
1025
+ const k = starts[i];
1026
+ if (minK[k] === MIN_K) base[k] = Math.max(base[k], ask.min(i));
1027
+ else if (minK[k] === MAX_K) base[k] = Math.max(base[k], ask.max(i));
1028
+ else if (minK[k] === AUTO_K) base[k] = Math.max(base[k], low(i));
1029
+ if (maxK[k] === MIN_K) limit[k] = grow(limit[k], ask.min(i));
1030
+ else if (maxK[k] === MAX_K || maxK[k] === AUTO_K) {
1031
+ limit[k] = grow(limit[k], ask.max(i));
1032
+ } else if (maxK[k] === FIT) {
1033
+ limit[k] = grow(limit[k], Math.min(ask.max(i), fitCap[k]));
1034
+ }
1035
+ }
1036
+ for (let k = 0; k < n; k++) if (limit[k] < base[k]) limit[k] = base[k];
1037
+
1038
+ // §12.5.1: share out what a group of items still needs among the tracks
1039
+ // they span — up to each track's limit first, then past it — and take, for
1040
+ // each track, the most any one item asked of it
1041
+ const planned = new Float64Array(n);
1042
+ const incurred = new Float64Array(n);
1043
+ const touched = new Uint8Array(n);
1044
+ const growable = new Uint8Array(n);
1045
+ const fill = (ks, space, weighted, room) => {
1046
+ let open = ks;
1047
+ while (space > EPS && open.length > 0) {
1048
+ let total = 0;
1049
+ for (const k of open) total += weighted ? factor[k] : 1;
1050
+ if (!(total > 0)) {
1051
+ weighted = false;
1052
+ total = open.length;
1053
+ }
1054
+ let given = 0;
1055
+ const next = [];
1056
+ for (const k of open) {
1057
+ const share = (space * (weighted ? factor[k] : 1)) / total;
1058
+ const r = room(k);
1059
+ if (r > share + EPS) {
1060
+ incurred[k] += share;
1061
+ given += share;
1062
+ next.push(k);
1063
+ } else if (r > 0) {
1064
+ incurred[k] += r;
1065
+ given += r;
1066
+ }
1067
+ }
1068
+ space -= given;
1069
+ if (given <= EPS) break;
1070
+ open = next;
1071
+ }
1072
+ return space;
1073
+ };
1074
+ const distribute = (group, toBase, affected, contribution, beyond, flex) => {
1075
+ planned.fill(0);
1076
+ touched.fill(0);
1077
+ const current = (k) =>
1078
+ (toBase || limit[k] === Infinity ? base[k] : limit[k]) + incurred[k];
1079
+ const upTo = toBase
1080
+ ? (k) => Math.min(limit[k], fitCap[k]) - current(k)
1081
+ : (k) => (growable[k] || limit[k] === Infinity ? Infinity : 0);
1082
+ // a fit-content() track counts as max-content past its limit only as far
1083
+ // as its argument, and as fixed beyond it
1084
+ const past = (k) => (maxK[k] === FIT ? fitCap[k] - current(k) : Infinity);
1085
+ for (const i of group) {
1086
+ const s = starts[i];
1087
+ const e = s + spans[i];
1088
+ let size = gap * (e - s - 1);
1089
+ const ks = [];
1090
+ for (let k = s; k < e; k++) {
1091
+ size += toBase || limit[k] === Infinity ? base[k] : limit[k];
1092
+ if (!dead[k] && affected(k)) ks.push(k);
1093
+ }
1094
+ if (ks.length === 0) continue;
1095
+ for (const k of ks) {
1096
+ touched[k] = 1;
1097
+ incurred[k] = 0;
1098
+ }
1099
+ let space = contribution(i) - size;
1100
+ if (!(space > EPS)) continue;
1101
+ // flexible tracks share by their factors, when those add up to one
1102
+ let weighted = false;
1103
+ if (flex) {
1104
+ let sum = 0;
1105
+ for (const k of ks) sum += factor[k];
1106
+ weighted = sum >= 1;
1107
+ }
1108
+ space = fill(ks, space, weighted, upTo);
1109
+ if (space > EPS) {
1110
+ let into = ks.filter(beyond);
1111
+ if (into.length === 0) into = ks;
1112
+ space = fill(into, space, weighted, past);
1113
+ if (space > EPS) fill(into, space, weighted, () => Infinity);
1114
+ }
1115
+ for (const k of ks)
1116
+ if (incurred[k] > planned[k]) planned[k] = incurred[k];
1117
+ }
1118
+ for (let k = 0; k < n; k++) {
1119
+ if (touched[k] === 0) continue;
1120
+ if (toBase) base[k] += planned[k];
1121
+ else {
1122
+ const was = limit[k];
1123
+ limit[k] = (was === Infinity ? base[k] : was) + planned[k];
1124
+ if (was === Infinity) growable[k] = 1;
1125
+ }
1126
+ }
1127
+ };
1128
+ const increase = (group, flex) => {
1129
+ const only = flex
1130
+ ? (pred) => (k) => maxK[k] === FLEX && pred(k)
1131
+ : (pred) => pred;
1132
+ distribute(
1133
+ group,
1134
+ true,
1135
+ only((k) => minK[k] !== FIXED),
1136
+ low,
1137
+ intrinsicMax,
1138
+ flex,
1139
+ );
1140
+ distribute(
1141
+ group,
1142
+ true,
1143
+ only((k) => minK[k] === MIN_K || minK[k] === MAX_K),
1144
+ ask.min,
1145
+ intrinsicMax,
1146
+ flex,
1147
+ );
1148
+ distribute(
1149
+ group,
1150
+ true,
1151
+ only(
1152
+ constraint === 'max'
1153
+ ? (k) => minK[k] === AUTO_K || minK[k] === MAX_K
1154
+ : (k) => minK[k] === MAX_K,
1155
+ ),
1156
+ constraint === 'max' ? limitedMax : ask.max,
1157
+ maxContentMax,
1158
+ flex,
1159
+ );
1160
+ for (let k = 0; k < n; k++) if (limit[k] < base[k]) limit[k] = base[k];
1161
+ // a flexible track's maximum is not intrinsic: nothing below reaches it
1162
+ if (flex) return;
1163
+ // a growth limit going from infinite to finite here is still free to
1164
+ // grow in the next step, and only then (§12.5 step 3)
1165
+ distribute(group, false, intrinsicMax, ask.min, ALL, false);
1166
+ distribute(group, false, maxContentMax, ask.max, ALL, false);
1167
+ growable.fill(0);
1168
+ };
1169
+ // §12.5 step 3: spanning items, the fewest tracks first
1170
+ if (spanning.length > 0) {
1171
+ spanning.sort((a, b) => spans[a] - spans[b]);
1172
+ for (let g = 0; g < spanning.length;) {
1173
+ let e = g + 1;
1174
+ while (e < spanning.length && spans[spanning[e]] === spans[spanning[g]]) {
1175
+ e++;
1176
+ }
1177
+ increase(spanning.slice(g, e), false);
1178
+ g = e;
1179
+ }
1180
+ }
1181
+ // §12.5 step 4: the ones crossing a flexible track, all together, into the
1182
+ // flexible tracks alone
1183
+ if (flexers.length > 0) increase(flexers, true);
1184
+ for (let k = 0; k < n; k++) if (limit[k] === Infinity) limit[k] = base[k];
1185
+
1186
+ const sum = () => {
1187
+ let total = 0;
1188
+ for (let k = 0; k < n; k++) total += base[k];
1189
+ return total;
1190
+ };
1191
+
1192
+ // §12.6: the free space, equally, up to each track's growth limit —
1193
+ // all of it, sized to its max-content, and none to its min-content
1194
+ if (constraint === 'max') {
1195
+ for (let k = 0; k < n; k++) base[k] = limit[k];
1196
+ } else if (constraint === null && avail !== null) {
1197
+ let free = avail - sum() - gaps;
1198
+ let open = [];
1199
+ for (let k = 0; k < n; k++) if (limit[k] > base[k] + EPS) open.push(k);
1200
+ while (free > EPS && open.length > 0) {
1201
+ const each = free / open.length;
1202
+ const next = [];
1203
+ for (const k of open) {
1204
+ const room = limit[k] - base[k];
1205
+ if (room > each + EPS) {
1206
+ base[k] += each;
1207
+ free -= each;
1208
+ next.push(k);
1209
+ } else {
1210
+ base[k] = limit[k];
1211
+ free -= room;
1212
+ }
1213
+ }
1214
+ open = next;
1215
+ }
1216
+ }
1217
+
1218
+ // §12.7: the flexible tracks, at the size of an fr
1219
+ const flexed = [];
1220
+ for (let k = 0; k < n; k++) if (maxK[k] === FLEX) flexed.push(k);
1221
+ if (flexed.length > 0 && constraint !== 'min') {
1222
+ const frSize = (from, to, space) => {
1223
+ const inflexible = new Uint8Array(n);
1224
+ for (;;) {
1225
+ let leftover = space;
1226
+ let tracksIn = 0;
1227
+ let factors = 0;
1228
+ for (let k = from; k < to; k++) {
1229
+ if (dead[k]) continue;
1230
+ tracksIn++;
1231
+ if (maxK[k] === FLEX && !inflexible[k]) factors += factor[k];
1232
+ else leftover -= base[k];
1233
+ }
1234
+ leftover -= gap * Math.max(0, tracksIn - 1);
1235
+ const hypothetical = leftover / Math.max(factors, 1);
1236
+ let again = false;
1237
+ for (let k = from; k < to; k++) {
1238
+ if (
1239
+ maxK[k] === FLEX &&
1240
+ !inflexible[k] &&
1241
+ hypothetical * factor[k] < base[k]
1242
+ ) {
1243
+ inflexible[k] = 1;
1244
+ again = true;
1245
+ }
1246
+ }
1247
+ if (!again) return Math.max(0, hypothetical);
1248
+ }
1249
+ };
1250
+ let fr = 0;
1251
+ if (constraint === null && avail !== null) {
1252
+ if (avail - sum() - gaps > EPS) fr = frSize(0, n, avail);
1253
+ } else {
1254
+ // no room to share: an fr is as big as it has to be for every flexible
1255
+ // track to keep its base size, and every item crossing one its content
1256
+ for (const k of flexed) {
1257
+ fr = Math.max(fr, factor[k] > 1 ? base[k] / factor[k] : base[k]);
1258
+ }
1259
+ for (const i of flexers) {
1260
+ fr = Math.max(fr, frSize(starts[i], starts[i] + spans[i], ask.max(i)));
1261
+ }
1262
+ }
1263
+ for (const k of flexed) {
1264
+ if (fr * factor[k] > base[k]) base[k] = fr * factor[k];
1265
+ }
1266
+ }
1267
+
1268
+ // §12.8: what is left goes to the `auto` tracks, unless the box distributes
1269
+ // its content some other way
1270
+ if (stretchAuto && constraint === null && avail !== null) {
1271
+ const free = avail - sum() - gaps;
1272
+ if (free > EPS) {
1273
+ let autos = 0;
1274
+ for (let k = 0; k < n; k++) if (maxK[k] === AUTO_K && !dead[k]) autos++;
1275
+ if (autos > 0) {
1276
+ for (let k = 0; k < n; k++) {
1277
+ if (maxK[k] === AUTO_K && !dead[k]) base[k] += free / autos;
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+ return base;
1283
+ }
1284
+
1285
+ /** The tracks laid end to end, gaps between. */
1286
+ function extent(sizes, tracks, gap) {
1287
+ let total = 0;
1288
+ let live = 0;
1289
+ for (let k = 0; k < sizes.length; k++) {
1290
+ total += sizes[k];
1291
+ if (!tracks[k].collapsed) live++;
1292
+ }
1293
+ return total + gap * Math.max(0, live - 1);
1294
+ }
1295
+
1296
+ /**
1297
+ * Where each track starts and ends, distributed by `justifyContent` /
1298
+ * `alignContent` when there is room to, and **snapped to whole pixels at the
1299
+ * lines** rather than per item: two items sharing a line share its pixel,
1300
+ * so neighbours in `1fr 1fr 1fr` meet with no seam, and a child's tree is
1301
+ * never laid out at a fraction — the input yoga divides a rounding residue
1302
+ * by (issue #411).
1303
+ */
1304
+ function trackLines(sizes, tracks, gap, room, distribution) {
1305
+ const n = sizes.length;
1306
+ let live = 0;
1307
+ let total = 0;
1308
+ for (let k = 0; k < n; k++) {
1309
+ total += sizes[k];
1310
+ if (!tracks[k].collapsed) live++;
1311
+ }
1312
+ total += gap * Math.max(0, live - 1);
1313
+ let at = 0;
1314
+ let extra = 0;
1315
+ if (room !== null && distribution != null) {
1316
+ const free = room - total;
1317
+ switch (distribution) {
1318
+ case 'center':
1319
+ at = free / 2;
1320
+ break;
1321
+ case 'flex-end':
1322
+ at = free;
1323
+ break;
1324
+ case 'space-between':
1325
+ if (free > 0 && live > 1) extra = free / (live - 1);
1326
+ break;
1327
+ case 'space-around':
1328
+ if (free > 0 && live > 0) {
1329
+ extra = free / live;
1330
+ at = extra / 2;
1331
+ } else at = free / 2;
1332
+ break;
1333
+ case 'space-evenly':
1334
+ if (free > 0) {
1335
+ extra = free / (live + 1);
1336
+ at = extra;
1337
+ } else at = free / 2;
1338
+ break;
1339
+ default:
1340
+ break;
1341
+ }
1342
+ }
1343
+ const x0 = new Int32Array(n);
1344
+ const x1 = new Int32Array(n);
1345
+ for (let k = 0; k < n; k++) {
1346
+ x0[k] = Math.round(at);
1347
+ at += sizes[k];
1348
+ x1[k] = Math.round(at);
1349
+ if (!tracks[k].collapsed) at += gap + extra;
1350
+ }
1351
+ return { x0, x1 };
1352
+ }
1353
+
1354
+ // --- the layout -----------------------------------------------------------------
1355
+
1356
+ const START = 0;
1357
+ const CENTER = 1;
1358
+ const END = 2;
1359
+ const STRETCH = 3;
1360
+ const MODES = {
1361
+ 'flex-start': START,
1362
+ center: CENTER,
1363
+ 'flex-end': END,
1364
+ stretch: STRETCH,
1365
+ };
1366
+
1367
+ /** An alignment keyword as a mode: `auto` (or nothing) is the fallback, and a
1368
+ * value grid has no use for — `baseline`, the `space-*` ones — the start. */
1369
+ const modeOf = (value, fallback) =>
1370
+ value == null || value === 'auto' ? fallback : (MODES[value] ?? START);
1371
+
1372
+ const lengthOf = (v) => (typeof v === 'number' && v > 0 ? v : 0);
1373
+
1374
+ /** Up to a whole pixel, with a thousandth of slack for a sum that should have
1375
+ * been whole. */
1376
+ const wholePixels = (v) => Math.ceil(v - 1e-3);
1377
+
1378
+ const offsetBy = (mode, free) =>
1379
+ mode === CENTER ? Math.round(free / 2) : mode === END ? free : 0;
1380
+
1381
+ const noop = () => {};
1382
+
1383
+ /**
1384
+ * The layout the seam runs (`registerLayout('grid', …)` in layouts.js). One
1385
+ * call answers how big the grid's content is for the room on offer, and —
1386
+ * with both modes `'exactly'` — where each child goes: the rect of its grid
1387
+ * area, or of its own size inside the area where it is not stretched.
1388
+ */
1389
+ export function gridLayout(children, c, _options, info) {
1390
+ const style = info.style;
1391
+ const scale = info.scale ?? 1;
1392
+ const report = info.report ?? noop;
1393
+ const colGap = lengthOf(style.columnGap ?? style.gap);
1394
+ const rowGap = lengthOf(style.rowGap ?? style.gap);
1395
+ const areas = templateAreas(style.gridTemplateAreas);
1396
+ const flow = FLOWS[style.gridAutoFlow] ?? FLOWS.row;
1397
+ const colRoom = c.widthMode === 'exactly' ? c.width : null;
1398
+ const rowRoom = c.heightMode === 'exactly' ? c.height : null;
1399
+ const colList = repeatOut(
1400
+ templateTracks(style.gridTemplateColumns, scale, 'gridTemplateColumns'),
1401
+ colRoom,
1402
+ colGap,
1403
+ );
1404
+ const rowList = repeatOut(
1405
+ templateTracks(style.gridTemplateRows, scale, 'gridTemplateRows'),
1406
+ rowRoom,
1407
+ rowGap,
1408
+ );
1409
+ const explicitCols = Math.max(
1410
+ colList.tracks.length,
1411
+ areas === null ? 0 : areas.cols,
1412
+ );
1413
+ const explicitRows = Math.max(
1414
+ rowList.tracks.length,
1415
+ areas === null ? 0 : areas.rows,
1416
+ );
1417
+
1418
+ // where each child asked to go
1419
+ const n = children.length;
1420
+ const cS = new Int32Array(n);
1421
+ const cN = new Int32Array(n);
1422
+ const rS = new Int32Array(n);
1423
+ const rN = new Int32Array(n);
1424
+ const justify = new Uint8Array(n);
1425
+ const align = new Uint8Array(n);
1426
+ const itemsJustify = modeOf(style.justifyItems, STRETCH);
1427
+ const itemsAlign = modeOf(style.alignItems, STRETCH);
1428
+ for (let i = 0; i < n; i++) {
1429
+ const s = children[i].style;
1430
+ let columns = null;
1431
+ let rows = null;
1432
+ if (s.gridArea != null) {
1433
+ const a = gridArea(s.gridArea);
1434
+ if (a.name === undefined) {
1435
+ rows = a.rows;
1436
+ columns = a.columns;
1437
+ } else {
1438
+ const box = areas === null ? undefined : areas.names.get(a.name);
1439
+ if (box !== undefined) {
1440
+ rows = box.rows;
1441
+ columns = box.columns;
1442
+ } else {
1443
+ report(
1444
+ `react-x11: gridArea ${JSON.stringify(a.name)} names no area of ` +
1445
+ (areas === null
1446
+ ? 'this grid, which has no gridTemplateAreas'
1447
+ : `this grid's gridTemplateAreas (${[...areas.names.keys()]
1448
+ .map((name) => JSON.stringify(name))
1449
+ .join(', ')})`),
1450
+ 'The child is placed automatically',
1451
+ );
1452
+ }
1453
+ }
1454
+ }
1455
+ if (s.gridColumn != null) columns = gridLines(s.gridColumn, 'gridColumn');
1456
+ if (s.gridRow != null) rows = gridLines(s.gridRow, 'gridRow');
1457
+ resolve(columns, explicitCols, cS, cN, i);
1458
+ resolve(rows, explicitRows, rS, rN, i);
1459
+ justify[i] = modeOf(s.justifySelf, itemsJustify);
1460
+ align[i] = modeOf(s.alignSelf, itemsAlign);
1461
+ if (s.layoutItem != null) {
1462
+ report(
1463
+ 'react-x11: a grid does not read layoutItem — its children are ' +
1464
+ 'placed by gridColumn, gridRow and gridArea in their own style',
1465
+ 'It is ignored',
1466
+ );
1467
+ }
1468
+ }
1469
+ const placed = placeItems(
1470
+ n,
1471
+ cS,
1472
+ cN,
1473
+ rS,
1474
+ rN,
1475
+ explicitCols,
1476
+ explicitRows,
1477
+ flow,
1478
+ );
1479
+ const colTracks = axisTracks(
1480
+ placed.cols,
1481
+ placed.cOff,
1482
+ colList,
1483
+ autoTracks(style.gridAutoColumns, scale, 'gridAutoColumns'),
1484
+ cS,
1485
+ cN,
1486
+ n,
1487
+ );
1488
+ const rowTracks = axisTracks(
1489
+ placed.rows,
1490
+ placed.rOff,
1491
+ rowList,
1492
+ autoTracks(style.gridAutoRows, scale, 'gridAutoRows'),
1493
+ rS,
1494
+ rN,
1495
+ n,
1496
+ );
1497
+ const clips = (i) => {
1498
+ const o = children[i].style.overflow;
1499
+ return o === 'hidden' || o === 'scroll';
1500
+ };
1501
+
1502
+ // the columns, from what each child says about its width
1503
+ const minW = new Float64Array(n).fill(Number.NaN);
1504
+ const maxW = new Float64Array(n).fill(Number.NaN);
1505
+ const widths = {
1506
+ min: (i) => {
1507
+ if (Number.isNaN(minW[i])) {
1508
+ minW[i] = children[i].intrinsicSizes().minContentWidth;
1509
+ }
1510
+ return minW[i];
1511
+ },
1512
+ max: (i) => {
1513
+ if (Number.isNaN(maxW[i])) {
1514
+ maxW[i] = children[i].intrinsicSizes().maxContentWidth;
1515
+ }
1516
+ return maxW[i];
1517
+ },
1518
+ declared: (i) => {
1519
+ const s = children[i].style;
1520
+ return s.minWidth != null || typeof s.width === 'number';
1521
+ },
1522
+ };
1523
+ const columnSizes = (avail, constraint) =>
1524
+ sizeTracks(
1525
+ colTracks,
1526
+ cS,
1527
+ cN,
1528
+ n,
1529
+ widths,
1530
+ clips,
1531
+ avail,
1532
+ colGap,
1533
+ constraint,
1534
+ constraint === null && style.justifyContent == null,
1535
+ );
1536
+ let colSizes;
1537
+ let width;
1538
+ if (c.widthMode === 'exactly') {
1539
+ colSizes = columnSizes(c.width, null);
1540
+ width = c.width;
1541
+ } else if (c.widthMode === 'at-most' && c.width > 0) {
1542
+ // shrink to fit: the width its columns would like, if there is room for
1543
+ // it, the narrowest they can be if there is not room even for that, and
1544
+ // otherwise the room
1545
+ colSizes = columnSizes(null, 'max');
1546
+ width = extent(colSizes, colTracks, colGap);
1547
+ if (width > c.width + EPS) {
1548
+ const narrowest = columnSizes(null, 'min');
1549
+ const least = extent(narrowest, colTracks, colGap);
1550
+ if (least >= c.width - EPS) {
1551
+ colSizes = narrowest;
1552
+ width = least;
1553
+ } else {
1554
+ colSizes = columnSizes(c.width, null);
1555
+ width = c.width;
1556
+ }
1557
+ }
1558
+ } else {
1559
+ colSizes = columnSizes(
1560
+ null,
1561
+ c.widthMode === 'unconstrained' ? 'max' : 'min',
1562
+ );
1563
+ width = extent(colSizes, colTracks, colGap);
1564
+ }
1565
+ const cols = trackLines(
1566
+ colSizes,
1567
+ colTracks,
1568
+ colGap,
1569
+ colRoom,
1570
+ style.justifyContent,
1571
+ );
1572
+
1573
+ // the rows, from each child's height at the width its columns came to —
1574
+ // asked at the snapped width the child is then laid out at, so the answer
1575
+ // the seam remembers is the one the placement reads
1576
+ const measured = new Array(n);
1577
+ const measureItem = (i) => {
1578
+ let m = measured[i];
1579
+ if (m === undefined) {
1580
+ const w = cols.x1[cS[i] + cN[i] - 1] - cols.x0[cS[i]];
1581
+ m = measured[i] =
1582
+ justify[i] === STRETCH
1583
+ ? children[i].measure({ width: w })
1584
+ : children[i].measure({ width: w, widthMode: 'at-most' });
1585
+ }
1586
+ return m;
1587
+ };
1588
+ const heights = {
1589
+ min: (i) => measureItem(i).height,
1590
+ max: (i) => measureItem(i).height,
1591
+ declared: (i) => {
1592
+ const s = children[i].style;
1593
+ return s.minHeight != null || typeof s.height === 'number';
1594
+ },
1595
+ };
1596
+ let rowSizes = null;
1597
+ let height;
1598
+ if (c.heightMode === 'exactly' && c.widthMode !== 'exactly') {
1599
+ // nothing asks where the rows are, and the height is given
1600
+ height = c.height;
1601
+ } else {
1602
+ const content = style.alignContent;
1603
+ rowSizes = sizeTracks(
1604
+ rowTracks,
1605
+ rS,
1606
+ rN,
1607
+ n,
1608
+ heights,
1609
+ clips,
1610
+ rowRoom,
1611
+ rowGap,
1612
+ rowRoom === null ? 'max' : null,
1613
+ content == null ||
1614
+ content === 'stretch' ||
1615
+ content === 'auto' ||
1616
+ content === 'baseline',
1617
+ );
1618
+ height = rowRoom ?? extent(rowSizes, rowTracks, rowGap);
1619
+ }
1620
+ if (c.widthMode !== 'exactly' || c.heightMode !== 'exactly') {
1621
+ return { width, height };
1622
+ }
1623
+
1624
+ const rows = trackLines(
1625
+ rowSizes,
1626
+ rowTracks,
1627
+ rowGap,
1628
+ rowRoom,
1629
+ style.alignContent,
1630
+ );
1631
+ const rects = new Array(n);
1632
+ for (let i = 0; i < n; i++) {
1633
+ const x0 = cols.x0[cS[i]];
1634
+ const areaW = cols.x1[cS[i] + cN[i] - 1] - x0;
1635
+ const y0 = rows.x0[rS[i]];
1636
+ const areaH = rows.x1[rS[i] + rN[i] - 1] - y0;
1637
+ const rect = { x: x0, y: y0, width: areaW };
1638
+ if (justify[i] !== STRETCH) {
1639
+ // its own width — CSS's fit-content in the area — placed in it
1640
+ const w = wholePixels(measureItem(i).width);
1641
+ rect.width = w;
1642
+ rect.x += offsetBy(justify[i], areaW - w);
1643
+ }
1644
+ if (align[i] === STRETCH) {
1645
+ rect.height = areaH;
1646
+ } else {
1647
+ // its own height, at that width; the rect leaves it to the child
1648
+ rect.y += offsetBy(align[i], areaH - wholePixels(measureItem(i).height));
1649
+ }
1650
+ rects[i] = rect;
1651
+ }
1652
+ return { width, height, children: rects };
1653
+ }