@pilates/core 1.1.0 → 2.0.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/algorithm/cache.d.ts +7 -19
- package/dist/algorithm/cache.d.ts.map +1 -1
- package/dist/algorithm/cache.js +31 -27
- package/dist/algorithm/cache.js.map +1 -1
- package/dist/algorithm/index.js +22 -14
- package/dist/algorithm/index.js.map +1 -1
- package/dist/algorithm/round.d.ts.map +1 -1
- package/dist/algorithm/round.js +19 -19
- package/dist/algorithm/round.js.map +1 -1
- package/dist/algorithm/spineless/classify.d.ts +47 -0
- package/dist/algorithm/spineless/classify.d.ts.map +1 -0
- package/dist/algorithm/spineless/classify.js +109 -0
- package/dist/algorithm/spineless/classify.js.map +1 -0
- package/dist/algorithm/spineless/field-id-pool.d.ts +28 -0
- package/dist/algorithm/spineless/field-id-pool.d.ts.map +1 -0
- package/dist/algorithm/spineless/field-id-pool.js +35 -0
- package/dist/algorithm/spineless/field-id-pool.js.map +1 -0
- package/dist/algorithm/spineless/flex-grammar.d.ts.map +1 -1
- package/dist/algorithm/spineless/flex-grammar.js +194 -58
- package/dist/algorithm/spineless/flex-grammar.js.map +1 -1
- package/dist/algorithm/spineless/grammar.d.ts +6 -0
- package/dist/algorithm/spineless/grammar.d.ts.map +1 -1
- package/dist/algorithm/spineless/grammar.js +2 -1
- package/dist/algorithm/spineless/grammar.js.map +1 -1
- package/dist/algorithm/spineless/layout.d.ts +52 -15
- package/dist/algorithm/spineless/layout.d.ts.map +1 -1
- package/dist/algorithm/spineless/layout.js +450 -312
- package/dist/algorithm/spineless/layout.js.map +1 -1
- package/dist/algorithm/spineless/order-maintenance.d.ts +9 -0
- package/dist/algorithm/spineless/order-maintenance.d.ts.map +1 -1
- package/dist/algorithm/spineless/order-maintenance.js +6 -0
- package/dist/algorithm/spineless/order-maintenance.js.map +1 -1
- package/dist/algorithm/spineless/runtime.d.ts +61 -8
- package/dist/algorithm/spineless/runtime.d.ts.map +1 -1
- package/dist/algorithm/spineless/runtime.js +201 -50
- package/dist/algorithm/spineless/runtime.js.map +1 -1
- package/dist/dirty-flags.d.ts +30 -0
- package/dist/dirty-flags.d.ts.map +1 -0
- package/dist/dirty-flags.js +35 -0
- package/dist/dirty-flags.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/layout-pool.d.ts +49 -0
- package/dist/layout-pool.d.ts.map +1 -0
- package/dist/layout-pool.js +75 -0
- package/dist/layout-pool.js.map +1 -0
- package/dist/node.d.ts +20 -3
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +63 -42
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monotonic integer ID allocator for Spineless `Field` objects.
|
|
3
|
+
* Phase 15I indexes the runtime's typed-array storage by field.id.
|
|
4
|
+
*
|
|
5
|
+
* IDs are never recycled — Fields are interned per (node, name) and
|
|
6
|
+
* live as long as their node. The max id is the live-field high-water
|
|
7
|
+
* mark, matching LayoutPool's accepted tradeoff.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
let _nextFieldId = 0;
|
|
12
|
+
/** Allocate a fresh unique integer id for a Field. */
|
|
13
|
+
export function allocateFieldId() {
|
|
14
|
+
return _nextFieldId++;
|
|
15
|
+
}
|
|
16
|
+
/** Current id count — the exclusive upper bound of allocated ids.
|
|
17
|
+
* Runtime typed arrays size to at least this. @internal */
|
|
18
|
+
export function fieldIdCount() {
|
|
19
|
+
return _nextFieldId;
|
|
20
|
+
}
|
|
21
|
+
/** Reset for tests. @internal
|
|
22
|
+
*
|
|
23
|
+
* WARNING: Only safe to call when no live `Field` objects exist
|
|
24
|
+
* anywhere — the `FIELD_REGISTRY` WeakMap in `grammar.ts` is NOT
|
|
25
|
+
* cleared, so any `Field` retained across this call still holds
|
|
26
|
+
* its old id. Newly-allocated fields will then collide with retained
|
|
27
|
+
* ids, aliasing entries in the runtime's typed-array storage. The
|
|
28
|
+
* standard test harness creates fresh `Node`s per test, so retained
|
|
29
|
+
* cross-test references are the only way to hit this; don't introduce
|
|
30
|
+
* them.
|
|
31
|
+
*/
|
|
32
|
+
export function _resetFieldIdsForTesting() {
|
|
33
|
+
_nextFieldId = 0;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=field-id-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-id-pool.js","sourceRoot":"","sources":["../../../src/algorithm/spineless/field-id-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,IAAI,YAAY,GAAG,CAAC,CAAC;AAErB,sDAAsD;AACtD,MAAM,UAAU,eAAe;IAC7B,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED;4DAC4D;AAC5D,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB;IACtC,YAAY,GAAG,CAAC,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flex-grammar.d.ts","sourceRoot":"","sources":["../../../src/algorithm/spineless/flex-grammar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAsB,MAAM,cAAc,CAAC;AAE5F;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtB,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,UAAU,EAAE;QACV,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KACpB,CAAC;IACF,uEAAuE;IACvE,SAAS,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,IAAI,CAAC;QACX,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KACpB,CAAC,CAAC;IACH;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,WAAW,EAAE,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC;;;;;OAKG;IACH,eAAe,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC;IACnE;;;;;;OAMG;IACH,wBAAwB,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;CAClE;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"flex-grammar.d.ts","sourceRoot":"","sources":["../../../src/algorithm/spineless/flex-grammar.ts"],"names":[],"mappings":"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"}
|
|
@@ -395,6 +395,43 @@ function makeEmitter(ctx) {
|
|
|
395
395
|
}
|
|
396
396
|
return f;
|
|
397
397
|
}
|
|
398
|
+
/** Fold `minWidth`/`minHeight` (default 0) or `maxWidth`/`maxHeight`
|
|
399
|
+
* (default `undefined` → ∞). Returns a constant when at default;
|
|
400
|
+
* emits a leaf Field (via `minMaxInput`) otherwise. */
|
|
401
|
+
function foldMinMax(n, prop) {
|
|
402
|
+
const isMax = prop === 'maxWidth' || prop === 'maxHeight';
|
|
403
|
+
const raw = n.style[prop];
|
|
404
|
+
// Default sentinels MUST match nodeSig exactly (layout.ts):
|
|
405
|
+
// minWidth/minHeight default === 0
|
|
406
|
+
// maxWidth/maxHeight default === undefined
|
|
407
|
+
if (isMax ? raw === undefined : raw === 0) {
|
|
408
|
+
return { kind: 'const', value: isMax ? Number.POSITIVE_INFINITY : 0 };
|
|
409
|
+
}
|
|
410
|
+
return { kind: 'field', field: minMaxInput(n, prop) };
|
|
411
|
+
}
|
|
412
|
+
/** Fold one margin `edge` (default 0). Returns a constant when margin
|
|
413
|
+
* is 0; emits a leaf Field (via `marginInput`) otherwise. */
|
|
414
|
+
function foldMargin(n, edge) {
|
|
415
|
+
// Default sentinel MUST match nodeSig exactly (layout.ts):
|
|
416
|
+
// margin[edge] default === 0
|
|
417
|
+
if ((n.style.margin[edge] ?? 0) === 0)
|
|
418
|
+
return { kind: 'const', value: 0 };
|
|
419
|
+
return { kind: 'field', field: marginInput(n, edge) };
|
|
420
|
+
}
|
|
421
|
+
/** Read a FoldedInput inside a compute callback. */
|
|
422
|
+
function readFolded(fi, read) {
|
|
423
|
+
return fi.kind === 'field' ? read(fi.field) : fi.value;
|
|
424
|
+
}
|
|
425
|
+
/** Collect only the Field entries from a FoldedInput list. */
|
|
426
|
+
function foldedDeps(fis) {
|
|
427
|
+
const deps = [];
|
|
428
|
+
for (const fi of fis) {
|
|
429
|
+
if (fi.kind === 'field')
|
|
430
|
+
deps.push(fi.field);
|
|
431
|
+
}
|
|
432
|
+
return deps;
|
|
433
|
+
}
|
|
434
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
398
435
|
function visit(node, parent, indexInParent, priorSiblings) {
|
|
399
436
|
const width = field(node, 'width');
|
|
400
437
|
const height = field(node, 'height');
|
|
@@ -448,6 +485,7 @@ function makeEmitter(ctx) {
|
|
|
448
485
|
const mainPosField = parentDirection === 'column' ? top : left;
|
|
449
486
|
const crossPosField = parentDirection === 'column' ? left : top;
|
|
450
487
|
const mainSizeName = parentDirection === 'column' ? 'height' : 'width';
|
|
488
|
+
const mainPosName = parentDirection === 'column' ? 'top' : 'left';
|
|
451
489
|
// Spacing inputs for this child. Both the parent's padding and
|
|
452
490
|
// this child's own margin are modelled as leaf input Fields (see
|
|
453
491
|
// `paddingInput` / `marginInput`) — one per [top,right,bottom,
|
|
@@ -465,9 +503,42 @@ function makeEmitter(ctx) {
|
|
|
465
503
|
const padMainEndF = parent === null ? null : paddingInput(parent, mainEndEdge(parentDirection));
|
|
466
504
|
const padCrossStartF = parent === null ? null : paddingInput(parent, crossStartEdge(parentDirection));
|
|
467
505
|
const padCrossEndF = parent === null ? null : paddingInput(parent, crossEndEdge(parentDirection));
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
506
|
+
// Phase 17: margin fields are resolved lazily at each rule site via
|
|
507
|
+
// foldMargin() (fold-eligible rules) or marginInput() (always-needed).
|
|
508
|
+
// Pre-compute edge indices once to avoid repeating edge-name calls.
|
|
509
|
+
const mainStartEdgeIdx = parent === null ? -1 : mainStartEdge(parentDirection);
|
|
510
|
+
const crossStartEdgeIdx = parent === null ? -1 : crossStartEdge(parentDirection);
|
|
511
|
+
const crossEndEdgeIdx = parent === null ? -1 : crossEndEdge(parentDirection);
|
|
512
|
+
// Rules that always require the full tracked Field (e.g. stretch
|
|
513
|
+
// crossSizeField, flex-end/center crossPos, reverse cumulative-sum)
|
|
514
|
+
// use these pre-resolved fields so the idempotent marginInput is called
|
|
515
|
+
// at most once per edge.
|
|
516
|
+
// NOTE: calling marginInput here forces Field creation for these edges
|
|
517
|
+
// regardless of whether they end up in a fold path. This is acceptable
|
|
518
|
+
// because the rules that read them (stretch/flex-end/center/reverse) are
|
|
519
|
+
// not fold-eligible — they always need tracked incremental propagation.
|
|
520
|
+
// For nodes on fully fold-eligible paths (flex-start, no-stretch, no-
|
|
521
|
+
// reverse), the Field is created if the code-path reaches it below;
|
|
522
|
+
// the fold-eligible rules that invoke foldMargin() bypass this and
|
|
523
|
+
// create no Field when margin is at default.
|
|
524
|
+
let myMarginMainStartF = null;
|
|
525
|
+
let myMarginCrossStartF = null;
|
|
526
|
+
let myMarginCrossEndF = null;
|
|
527
|
+
function getMarginMainStart() {
|
|
528
|
+
if (myMarginMainStartF === null)
|
|
529
|
+
myMarginMainStartF = marginInput(node, mainStartEdgeIdx);
|
|
530
|
+
return myMarginMainStartF;
|
|
531
|
+
}
|
|
532
|
+
function getMarginCrossStart() {
|
|
533
|
+
if (myMarginCrossStartF === null)
|
|
534
|
+
myMarginCrossStartF = marginInput(node, crossStartEdgeIdx);
|
|
535
|
+
return myMarginCrossStartF;
|
|
536
|
+
}
|
|
537
|
+
function getMarginCrossEnd() {
|
|
538
|
+
if (myMarginCrossEndF === null)
|
|
539
|
+
myMarginCrossEndF = marginInput(node, crossEndEdgeIdx);
|
|
540
|
+
return myMarginCrossEndF;
|
|
541
|
+
}
|
|
471
542
|
// Alignment for this child: justify-content lives on the parent,
|
|
472
543
|
// applies along the main axis once per line. align-items lives
|
|
473
544
|
// on the parent; align-self overrides per child (with 'auto'
|
|
@@ -492,24 +563,29 @@ function makeEmitter(ctx) {
|
|
|
492
563
|
const crossKey = parentDirection === 'column' ? 'width' : 'height';
|
|
493
564
|
const crossIsContentAuto = typeof node.style[crossKey] !== 'number' && !aspectDerivable(node, crossKey);
|
|
494
565
|
const crossSizeInput = preferredSizeInput(node, crossKey, 'cross', parent);
|
|
495
|
-
|
|
496
|
-
const
|
|
566
|
+
// Phase 17: fold min/max cross inputs when at default (0 / undefined→∞).
|
|
567
|
+
const fMinCross = foldMinMax(node, crossKey === 'width' ? 'minWidth' : 'minHeight');
|
|
568
|
+
const fMaxCross = foldMinMax(node, crossKey === 'width' ? 'maxWidth' : 'maxHeight');
|
|
497
569
|
if (crossIsContentAuto && parent !== null && align === 'stretch') {
|
|
570
|
+
// Stretch: resize to fill the parent's inner cross minus margins.
|
|
571
|
+
// The margins cannot be folded here (they appear in a subtraction
|
|
572
|
+
// expression), so use the always-needed field accessors.
|
|
573
|
+
const mcs = getMarginCrossStart();
|
|
574
|
+
const mce = getMarginCrossEnd();
|
|
498
575
|
const parentCrossF = field(parent, crossKey);
|
|
499
576
|
grammar.set(crossSizeField, {
|
|
500
577
|
deps: [
|
|
501
578
|
parentCrossF,
|
|
502
579
|
padCrossStartF,
|
|
503
580
|
padCrossEndF,
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
maxCrossInput,
|
|
581
|
+
mcs,
|
|
582
|
+
mce,
|
|
583
|
+
...foldedDeps([fMinCross, fMaxCross]),
|
|
508
584
|
],
|
|
509
585
|
compute: (read) => {
|
|
510
586
|
const innerCross = Math.max(0, read(parentCrossF) - read(padCrossStartF) - read(padCrossEndF));
|
|
511
|
-
const lineInner = innerCross - read(
|
|
512
|
-
return clampMinMax(Math.max(0, lineInner), read
|
|
587
|
+
const lineInner = innerCross - read(mcs) - read(mce);
|
|
588
|
+
return clampMinMax(Math.max(0, lineInner), readFolded(fMinCross, read), readFolded(fMaxCross, read));
|
|
513
589
|
},
|
|
514
590
|
});
|
|
515
591
|
}
|
|
@@ -522,13 +598,10 @@ function makeEmitter(ctx) {
|
|
|
522
598
|
});
|
|
523
599
|
}
|
|
524
600
|
else {
|
|
601
|
+
// Phase 17: build deps only from non-default inputs.
|
|
525
602
|
grammar.set(crossSizeField, {
|
|
526
|
-
deps: [
|
|
527
|
-
|
|
528
|
-
minCrossInput,
|
|
529
|
-
maxCrossInput,
|
|
530
|
-
],
|
|
531
|
-
compute: (read) => clampMinMax(read(crossSizeInput), read(minCrossInput), read(maxCrossInput)),
|
|
603
|
+
deps: [crossSizeInput, ...foldedDeps([fMinCross, fMaxCross])],
|
|
604
|
+
compute: (read) => clampMinMax(read(crossSizeInput), readFolded(fMinCross, read), readFolded(fMaxCross, read)),
|
|
532
605
|
});
|
|
533
606
|
}
|
|
534
607
|
// When the parent has flex-wrap='wrap', all three position fields
|
|
@@ -670,8 +743,9 @@ function makeEmitter(ctx) {
|
|
|
670
743
|
// never consults it — so the root's main size is its preferred
|
|
671
744
|
// size directly, no `flexBasis` short-circuit.
|
|
672
745
|
const mainInput = preferredSizeInput(node, mainSizeName, 'main', parent);
|
|
673
|
-
|
|
674
|
-
const
|
|
746
|
+
// Phase 17: fold min/max main inputs when at default (0 / undefined→∞).
|
|
747
|
+
const fMinMain = foldMinMax(node, mainSizeName === 'width' ? 'minWidth' : 'minHeight');
|
|
748
|
+
const fMaxMain = foldMinMax(node, mainSizeName === 'width' ? 'maxWidth' : 'maxHeight');
|
|
675
749
|
if (parent === null) {
|
|
676
750
|
if (rootAxisIsBareZero(node, mainSizeName)) {
|
|
677
751
|
// `'auto'` root, no `available` → bare 0, unclamped.
|
|
@@ -681,28 +755,38 @@ function makeEmitter(ctx) {
|
|
|
681
755
|
});
|
|
682
756
|
}
|
|
683
757
|
else {
|
|
758
|
+
// Phase 17: deps only include non-default min/max.
|
|
759
|
+
grammar.set(mainSizeField, {
|
|
760
|
+
deps: [mainInput, ...foldedDeps([fMinMain, fMaxMain])],
|
|
761
|
+
compute: (read) => clampMinMax(read(mainInput), readFolded(fMinMain, read), readFolded(fMaxMain, read)),
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
// Phase 17: flexBasis 'auto' is folded — when flexBasis === 'auto',
|
|
767
|
+
// resolveBasisFromRead degenerates to read(mainInput). nodeSig already
|
|
768
|
+
// captures typeof s.flexBasis (the 'auto' vs numeric boundary), so a
|
|
769
|
+
// change from 'auto' to a numeric basis triggers a full rebuild.
|
|
770
|
+
const flexBasisIsAuto = node.style.flexBasis === 'auto';
|
|
771
|
+
if (flexBasisIsAuto) {
|
|
772
|
+
// Degenerate form: basis auto → just use mainInput (no flexBasisInput field).
|
|
773
|
+
grammar.set(mainSizeField, {
|
|
774
|
+
deps: [mainInput, ...foldedDeps([fMinMain, fMaxMain])],
|
|
775
|
+
compute: (read) => clampMinMax(read(mainInput), readFolded(fMinMain, read), readFolded(fMaxMain, read)),
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
const flexBasisInput = styleSizeInput(node, 'flexBasis');
|
|
684
780
|
grammar.set(mainSizeField, {
|
|
685
781
|
deps: [
|
|
782
|
+
flexBasisInput,
|
|
686
783
|
mainInput,
|
|
687
|
-
|
|
688
|
-
maxMainInput,
|
|
784
|
+
...foldedDeps([fMinMain, fMaxMain]),
|
|
689
785
|
],
|
|
690
|
-
compute: (read) => clampMinMax(read
|
|
786
|
+
compute: (read) => clampMinMax(resolveBasisFromRead(read, flexBasisInput, mainInput), readFolded(fMinMain, read), readFolded(fMaxMain, read)),
|
|
691
787
|
});
|
|
692
788
|
}
|
|
693
789
|
}
|
|
694
|
-
else {
|
|
695
|
-
const flexBasisInput = styleSizeInput(node, 'flexBasis');
|
|
696
|
-
grammar.set(mainSizeField, {
|
|
697
|
-
deps: [
|
|
698
|
-
flexBasisInput,
|
|
699
|
-
mainInput,
|
|
700
|
-
minMainInput,
|
|
701
|
-
maxMainInput,
|
|
702
|
-
],
|
|
703
|
-
compute: (read) => clampMinMax(resolveBasisFromRead(read, flexBasisInput, mainInput), read(minMainInput), read(maxMainInput)),
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
790
|
}
|
|
707
791
|
else {
|
|
708
792
|
// Flex distribution. Capture the in-flow siblings + this
|
|
@@ -815,9 +899,11 @@ function makeEmitter(ctx) {
|
|
|
815
899
|
});
|
|
816
900
|
}
|
|
817
901
|
else {
|
|
902
|
+
// Phase 17: fold myMarginMainStart when at default (0).
|
|
903
|
+
const fMyMarginMainStart = foldMargin(node, mainStartEdgeIdx);
|
|
818
904
|
grammar.set(mainPosField, {
|
|
819
|
-
deps: [padMainStartF,
|
|
820
|
-
compute: (read) => read(padMainStartF) + read
|
|
905
|
+
deps: [padMainStartF, ...foldedDeps([fMyMarginMainStart])],
|
|
906
|
+
compute: (read) => read(padMainStartF) + readFolded(fMyMarginMainStart, read),
|
|
821
907
|
});
|
|
822
908
|
}
|
|
823
909
|
}
|
|
@@ -840,25 +926,63 @@ function makeEmitter(ctx) {
|
|
|
840
926
|
compute: (read) => read(parentMainDist).positions[myIndexCapture],
|
|
841
927
|
});
|
|
842
928
|
}
|
|
929
|
+
else if (!parentReverse) {
|
|
930
|
+
// Phase 16: linear recurrence. This child's main position is the
|
|
931
|
+
// immediate predecessor's position + its box + one gap. O(1) deps
|
|
932
|
+
// regardless of sibling count (was O(N) cumulative-sum). Unrolls
|
|
933
|
+
// to the same total — see the Phase 16 design doc.
|
|
934
|
+
// Note: only applies to forward directions; reverse directions use
|
|
935
|
+
// the cumulative-sum below because applyReverseMainPos overwrites
|
|
936
|
+
// each sibling's mainPosField with a reflected value, breaking the
|
|
937
|
+
// chain (the predecessor's field holds its reflected position, not
|
|
938
|
+
// the forward cursor this recurrence relies on).
|
|
939
|
+
const prevSibling = priorSiblings[priorSiblings.length - 1];
|
|
940
|
+
const prevMainPos = field(prevSibling, mainPosName);
|
|
941
|
+
const prevMainSize = field(prevSibling, mainSizeName);
|
|
942
|
+
const mainEndEdgeIdx = mainEndEdge(parentDirection);
|
|
943
|
+
// Phase 17: fold prevMarginEnd and myMarginMainStart when at default (0).
|
|
944
|
+
const fPrevMarginEnd = foldMargin(prevSibling, mainEndEdgeIdx);
|
|
945
|
+
const fMyMarginMainStart = foldMargin(node, mainStartEdgeIdx);
|
|
946
|
+
const mainGapInput = gapInput(parent, parentDirection === 'column' ? 'row' : 'column');
|
|
947
|
+
grammar.set(mainPosField, {
|
|
948
|
+
deps: [
|
|
949
|
+
prevMainPos,
|
|
950
|
+
prevMainSize,
|
|
951
|
+
...foldedDeps([fPrevMarginEnd, fMyMarginMainStart]),
|
|
952
|
+
mainGapInput,
|
|
953
|
+
],
|
|
954
|
+
compute: (read) => read(prevMainPos) +
|
|
955
|
+
read(prevMainSize) +
|
|
956
|
+
readFolded(fPrevMarginEnd, read) +
|
|
957
|
+
readFolded(fMyMarginMainStart, read) +
|
|
958
|
+
read(mainGapInput),
|
|
959
|
+
});
|
|
960
|
+
}
|
|
843
961
|
else {
|
|
844
|
-
//
|
|
845
|
-
//
|
|
962
|
+
// Reverse direction: keep the cumulative-sum rule. The recurrence
|
|
963
|
+
// cannot chain through the predecessor's mainPosField here because
|
|
964
|
+
// applyReverseMainPos (applied per-sibling below) overwrites that
|
|
965
|
+
// field with a reflected value — the predecessor's field no longer
|
|
966
|
+
// carries the forward cursor the recurrence depends on.
|
|
846
967
|
const priorMainSizes = priorSiblings.map((s) => field(s, mainSizeName));
|
|
847
968
|
const priorMargins = priorSiblings.map((s) => ({
|
|
848
969
|
start: marginInput(s, mainStartEdge(parentDirection)),
|
|
849
970
|
end: marginInput(s, mainEndEdge(parentDirection)),
|
|
850
971
|
}));
|
|
851
972
|
const mainGapInput = gapInput(parent, parentDirection === 'column' ? 'row' : 'column');
|
|
973
|
+
// Reverse cumulative-sum: cannot fold margins — the expression
|
|
974
|
+
// accumulates all sibling margins and this node's own marginMainStart.
|
|
975
|
+
const myMMS = getMarginMainStart();
|
|
852
976
|
grammar.set(mainPosField, {
|
|
853
977
|
deps: [
|
|
854
978
|
mainGapInput,
|
|
855
979
|
padMainStartF,
|
|
856
|
-
|
|
980
|
+
myMMS,
|
|
857
981
|
...priorMainSizes,
|
|
858
982
|
...priorMargins.flatMap((m) => [m.start, m.end]),
|
|
859
983
|
],
|
|
860
984
|
compute: (read) => {
|
|
861
|
-
let sum = read(padMainStartF) + read(
|
|
985
|
+
let sum = read(padMainStartF) + read(myMMS) + indexInParent * read(mainGapInput);
|
|
862
986
|
for (const m of priorMargins)
|
|
863
987
|
sum += read(m.start) + read(m.end);
|
|
864
988
|
for (const m of priorMainSizes)
|
|
@@ -885,13 +1009,14 @@ function makeEmitter(ctx) {
|
|
|
885
1009
|
if (parent === null || align === 'flex-end') {
|
|
886
1010
|
if (parent !== null && align === 'flex-end') {
|
|
887
1011
|
const parentCrossField = field(parent, parentDirection === 'column' ? 'width' : 'height');
|
|
1012
|
+
const mce = getMarginCrossEnd();
|
|
888
1013
|
grammar.set(crossPosField, {
|
|
889
1014
|
deps: [
|
|
890
1015
|
parentCrossField,
|
|
891
1016
|
crossSizeField,
|
|
892
1017
|
padCrossStartF,
|
|
893
1018
|
padCrossEndF,
|
|
894
|
-
|
|
1019
|
+
mce,
|
|
895
1020
|
],
|
|
896
1021
|
// Anchor against the line's inner cross, which the
|
|
897
1022
|
// imperative `crossAlignItemsInLine` clamps to >= 0 — a
|
|
@@ -900,10 +1025,7 @@ function makeEmitter(ctx) {
|
|
|
900
1025
|
compute: (read) => {
|
|
901
1026
|
const padStart = read(padCrossStartF);
|
|
902
1027
|
const innerCross = Math.max(0, read(parentCrossField) - padStart - read(padCrossEndF));
|
|
903
|
-
return
|
|
904
|
-
innerCross -
|
|
905
|
-
read(crossSizeField) -
|
|
906
|
-
read(myMarginCrossEndF));
|
|
1028
|
+
return padStart + innerCross - read(crossSizeField) - read(mce);
|
|
907
1029
|
},
|
|
908
1030
|
});
|
|
909
1031
|
}
|
|
@@ -917,20 +1039,22 @@ function makeEmitter(ctx) {
|
|
|
917
1039
|
}
|
|
918
1040
|
else if (align === 'center') {
|
|
919
1041
|
const parentCrossField = field(parent, parentDirection === 'column' ? 'width' : 'height');
|
|
1042
|
+
const mcs2 = getMarginCrossStart();
|
|
1043
|
+
const mce2 = getMarginCrossEnd();
|
|
920
1044
|
grammar.set(crossPosField, {
|
|
921
1045
|
deps: [
|
|
922
1046
|
parentCrossField,
|
|
923
1047
|
crossSizeField,
|
|
924
1048
|
padCrossStartF,
|
|
925
1049
|
padCrossEndF,
|
|
926
|
-
|
|
927
|
-
|
|
1050
|
+
mcs2,
|
|
1051
|
+
mce2,
|
|
928
1052
|
],
|
|
929
1053
|
compute: (read) => {
|
|
930
1054
|
const padStart = read(padCrossStartF);
|
|
931
1055
|
const innerCross = Math.max(0, read(parentCrossField) - padStart - read(padCrossEndF));
|
|
932
|
-
const marginStart = read(
|
|
933
|
-
const innerLine = innerCross - marginStart - read(
|
|
1056
|
+
const marginStart = read(mcs2);
|
|
1057
|
+
const innerLine = innerCross - marginStart - read(mce2);
|
|
934
1058
|
const myCross = read(crossSizeField);
|
|
935
1059
|
return padStart + marginStart + Math.max(0, (innerLine - myCross) / 2);
|
|
936
1060
|
},
|
|
@@ -940,11 +1064,13 @@ function makeEmitter(ctx) {
|
|
|
940
1064
|
// flex-start, stretch (with explicit cross size — no resize),
|
|
941
1065
|
// and any other value (the imperative falls through to
|
|
942
1066
|
// flex-start) all share this offset: the parent's cross-start
|
|
943
|
-
// padding plus this child's cross-start margin
|
|
944
|
-
//
|
|
1067
|
+
// padding plus this child's cross-start margin.
|
|
1068
|
+
// Phase 17: fold myMarginCrossStart when at default (0) — no Field
|
|
1069
|
+
// created, constant 0 inlined.
|
|
1070
|
+
const fMyMarginCrossStart = foldMargin(node, crossStartEdgeIdx);
|
|
945
1071
|
grammar.set(crossPosField, {
|
|
946
|
-
deps: [padCrossStartF,
|
|
947
|
-
compute: (read) => read(padCrossStartF) + read
|
|
1072
|
+
deps: [padCrossStartF, ...foldedDeps([fMyMarginCrossStart])],
|
|
1073
|
+
compute: (read) => read(padCrossStartF) + readFolded(fMyMarginCrossStart, read),
|
|
948
1074
|
});
|
|
949
1075
|
}
|
|
950
1076
|
allFields.push({ node, width, height, left, top });
|
|
@@ -1049,8 +1175,11 @@ function mergeStyleInputs(a, b) {
|
|
|
1049
1175
|
* which gains a main-end margin input when it acquires a follower —
|
|
1050
1176
|
* has its `StyleInputs` deep-merged rather than overwritten.
|
|
1051
1177
|
*/
|
|
1052
|
-
function mergeStyleInputsMap(base, extra) {
|
|
1053
|
-
|
|
1178
|
+
function mergeStyleInputsMap(base, extra, mutateBase) {
|
|
1179
|
+
// When mutateBase is true the caller owns `base` exclusively (it is
|
|
1180
|
+
// the previous grammar output, swapped out by the caller right after
|
|
1181
|
+
// this returns) — mutate it directly and skip the ~1,100-entry clone.
|
|
1182
|
+
const merged = mutateBase ? base : new Map(base);
|
|
1054
1183
|
for (const [node, entry] of extra) {
|
|
1055
1184
|
const existing = merged.get(node);
|
|
1056
1185
|
merged.set(node, existing === undefined ? entry : mergeStyleInputs(existing, entry));
|
|
@@ -1151,7 +1280,7 @@ export function buildAppendFragment(prev, root, parent, child, available = {}) {
|
|
|
1151
1280
|
grammar: prev.grammar,
|
|
1152
1281
|
rootFields: prev.rootFields,
|
|
1153
1282
|
allFields: [...prev.allFields, ...ctx.allFields],
|
|
1154
|
-
styleInputs: mergeStyleInputsMap(prev.styleInputs, ctx.styleInputs),
|
|
1283
|
+
styleInputs: mergeStyleInputsMap(prev.styleInputs, ctx.styleInputs, true),
|
|
1155
1284
|
availableInputs: prev.availableInputs,
|
|
1156
1285
|
mainDistributionByParent: prev.mainDistributionByParent,
|
|
1157
1286
|
};
|
|
@@ -1337,13 +1466,20 @@ export function buildRemoveFragment(prev, root, parent, child, available = {}) {
|
|
|
1337
1466
|
removed.push(f);
|
|
1338
1467
|
}
|
|
1339
1468
|
}
|
|
1469
|
+
// Mutate prev.styleInputs and prev.mainDistributionByParent in place:
|
|
1470
|
+
// `prev` is the old grammar output, single-use — the caller swaps
|
|
1471
|
+
// built.output to the new fragment immediately after this returns.
|
|
1472
|
+
for (const n of removedNodes) {
|
|
1473
|
+
prev.styleInputs.delete(n);
|
|
1474
|
+
prev.mainDistributionByParent.delete(n);
|
|
1475
|
+
}
|
|
1340
1476
|
const next = {
|
|
1341
1477
|
grammar: prev.grammar,
|
|
1342
1478
|
rootFields: prev.rootFields,
|
|
1343
1479
|
allFields: prev.allFields.filter((e) => !removedNodes.has(e.node)),
|
|
1344
|
-
styleInputs:
|
|
1480
|
+
styleInputs: prev.styleInputs,
|
|
1345
1481
|
availableInputs: prev.availableInputs,
|
|
1346
|
-
mainDistributionByParent:
|
|
1482
|
+
mainDistributionByParent: prev.mainDistributionByParent,
|
|
1347
1483
|
};
|
|
1348
1484
|
return { removed, rebinds: [], next };
|
|
1349
1485
|
}
|