postext 1.49.1 → 1.51.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/__tests__/composed/fenceCondition.test.d.ts +2 -0
- package/dist/__tests__/composed/fenceCondition.test.d.ts.map +1 -0
- package/dist/__tests__/composed/fenceCondition.test.js +171 -0
- package/dist/__tests__/composed/fenceCondition.test.js.map +1 -0
- package/dist/__tests__/composed/fixtures/moduleNesting.d.ts +25 -0
- package/dist/__tests__/composed/fixtures/moduleNesting.d.ts.map +1 -0
- package/dist/__tests__/composed/fixtures/moduleNesting.js +36 -0
- package/dist/__tests__/composed/fixtures/moduleNesting.js.map +1 -0
- package/dist/__tests__/composed/fixtures/moduleRows.d.ts +40 -0
- package/dist/__tests__/composed/fixtures/moduleRows.d.ts.map +1 -0
- package/dist/__tests__/composed/fixtures/moduleRows.js +65 -0
- package/dist/__tests__/composed/fixtures/moduleRows.js.map +1 -0
- package/dist/__tests__/composed/frameLooks.test.d.ts +2 -0
- package/dist/__tests__/composed/frameLooks.test.d.ts.map +1 -0
- package/dist/__tests__/composed/frameLooks.test.js +151 -0
- package/dist/__tests__/composed/frameLooks.test.js.map +1 -0
- package/dist/__tests__/composed/moduleNesting.test.d.ts +2 -0
- package/dist/__tests__/composed/moduleNesting.test.d.ts.map +1 -0
- package/dist/__tests__/composed/moduleNesting.test.js +177 -0
- package/dist/__tests__/composed/moduleNesting.test.js.map +1 -0
- package/dist/__tests__/composed/moduleRows.test.d.ts +2 -0
- package/dist/__tests__/composed/moduleRows.test.d.ts.map +1 -0
- package/dist/__tests__/composed/moduleRows.test.js +239 -0
- package/dist/__tests__/composed/moduleRows.test.js.map +1 -0
- package/dist/__tests__/composed/modules.test.js +3 -2
- package/dist/__tests__/composed/modules.test.js.map +1 -1
- package/dist/__tests__/composed/pillWord.test.js +35 -0
- package/dist/__tests__/composed/pillWord.test.js.map +1 -1
- package/dist/__tests__/exports.test.js +9 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/composed/groups.d.ts.map +1 -1
- package/dist/composed/groups.js +9 -1
- package/dist/composed/groups.js.map +1 -1
- package/dist/composed/modules.d.ts +9 -0
- package/dist/composed/modules.d.ts.map +1 -1
- package/dist/composed/modules.js +43 -2
- package/dist/composed/modules.js.map +1 -1
- package/dist/composed/reflowLog.js +1 -1
- package/dist/composed/reflowLog.js.map +1 -1
- package/dist/composed/slotChecks.d.ts +10 -0
- package/dist/composed/slotChecks.d.ts.map +1 -1
- package/dist/composed/slotChecks.js +39 -0
- package/dist/composed/slotChecks.js.map +1 -1
- package/dist/composed/templates.d.ts +17 -1
- package/dist/composed/templates.d.ts.map +1 -1
- package/dist/composed/templates.js +82 -6
- package/dist/composed/templates.js.map +1 -1
- package/dist/composed/types.d.ts +76 -4
- package/dist/composed/types.d.ts.map +1 -1
- package/dist/composed/validate.d.ts.map +1 -1
- package/dist/composed/validate.js +79 -6
- package/dist/composed/validate.js.map +1 -1
- package/dist/configWarnings.d.ts +3 -1
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +32 -3
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/headerFooter.d.ts +10 -0
- package/dist/defaults/headerFooter.d.ts.map +1 -1
- package/dist/defaults/headerFooter.js +21 -1
- package/dist/defaults/headerFooter.js.map +1 -1
- package/dist/design/fenceCondition.d.ts +45 -0
- package/dist/design/fenceCondition.d.ts.map +1 -0
- package/dist/design/fenceCondition.js +66 -0
- package/dist/design/fenceCondition.js.map +1 -0
- package/dist/design/layout.d.ts.map +1 -1
- package/dist/design/layout.js +33 -1
- package/dist/design/layout.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/pipeline/composedLayout.d.ts +5 -1
- package/dist/pipeline/composedLayout.d.ts.map +1 -1
- package/dist/pipeline/composedLayout.js +355 -83
- package/dist/pipeline/composedLayout.js.map +1 -1
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +8 -1
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +8 -1
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/types.d.ts +68 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +25 -2
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/package.json +1 -1
|
@@ -41,12 +41,12 @@ import { chainHead, FRAME_ATTR } from '../composed/wrap';
|
|
|
41
41
|
import { framePlaceable, nextFrameOf, groupPlaceable, placeBox, unionRect } from '../composed/geometry';
|
|
42
42
|
import { composedOrder, groupOfFrames, layoutGroup } from '../composed/groups';
|
|
43
43
|
import { reflowEvents } from '../composed/reflowLog';
|
|
44
|
-
import { anchorTarget, repeatInstance, repeatedFrames, resolveContinueIn } from '../composed/templates';
|
|
44
|
+
import { anchorTarget, lookOver, repeatInstance, repeatedFrames, resolveContinueIn } from '../composed/templates';
|
|
45
45
|
import { collapsedOnAddedPage, collapseFrames, continuationTemplate, continuesCollapsed, continuesOffPage, trailingFrames } from '../composed/continuation';
|
|
46
46
|
import { isModuleTemplate, resolvePageTemplate } from '../composed/templates';
|
|
47
47
|
import { frameElementId } from '../composed/ids';
|
|
48
48
|
import { expandRepeats, instanceOf, repeatItemsOf } from '../composed/repeat';
|
|
49
|
-
import { moduleOfFrame } from '../composed/modules';
|
|
49
|
+
import { moduleOfFrame, nestedChain } from '../composed/modules';
|
|
50
50
|
import { frameLinePx, frameRuleBlocks } from '../composed/rules';
|
|
51
51
|
import { overlapChecks } from '../composed/overlap';
|
|
52
52
|
import { offsetCalloutToAbsolute } from './calloutLayout';
|
|
@@ -520,6 +520,7 @@ export function layoutComposedPage(asked) {
|
|
|
520
520
|
templateSource: templateAttr ? { sourceStart: templateAttr.sourceStart + input.bodyOffset, sourceEnd: templateAttr.sourceEnd + input.bodyOffset } : undefined,
|
|
521
521
|
budgetLeft: () => runs < COMPOSE_BUDGET,
|
|
522
522
|
holdsText: holdsText(frame),
|
|
523
|
+
dividesWord,
|
|
523
524
|
});
|
|
524
525
|
warnings.push({
|
|
525
526
|
kind: 'frameOverflow',
|
|
@@ -830,24 +831,51 @@ function overflowProposals(c) {
|
|
|
830
831
|
const path = (key) => frame.fieldPaths[key] ?? `${frame.configPath}/${key}`;
|
|
831
832
|
const fits = (next, tpl) => c.budgetLeft() && !c.overflows(c.compose(next, tpl), name);
|
|
832
833
|
// More shrink: the largest text scale, in 2.5 % steps, at which it fits.
|
|
834
|
+
// When that scale divides a word wider than the frame's measure, the
|
|
835
|
+
// largest smaller one that fits with every word whole, as the ladder
|
|
836
|
+
// prefers (#1084, #1090); none, and the largest that fits, saying the
|
|
837
|
+
// word will be divided.
|
|
833
838
|
const head = c.headOf(name);
|
|
834
839
|
const from = frame.fit.shrinkText ? Math.min(frame.fit.minScale, vdt.scale) : 1;
|
|
835
840
|
const steps = scaleSteps(PROPOSAL_MIN_SCALE, from);
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
841
|
+
const shrunk = (i) => c.withText(fit, head, steps[i]);
|
|
842
|
+
if (c.holdsText && steps.length > 0 && fits(shrunk(steps.length - 1))) {
|
|
843
|
+
const last = steps.length - 1;
|
|
844
|
+
/** The largest step from `lo` down at which `ok` holds, given it holds
|
|
845
|
+
* at the last one: by bisection. */
|
|
846
|
+
const largest = (lo, ok) => {
|
|
847
|
+
let hi = last;
|
|
848
|
+
while (lo < hi && c.budgetLeft()) {
|
|
849
|
+
const mid = Math.floor((lo + hi) / 2);
|
|
850
|
+
if (ok(mid))
|
|
851
|
+
hi = mid;
|
|
852
|
+
else
|
|
853
|
+
lo = mid + 1;
|
|
854
|
+
}
|
|
855
|
+
return hi;
|
|
856
|
+
};
|
|
857
|
+
let at = largest(0, (i) => fits(shrunk(i)));
|
|
858
|
+
let word;
|
|
859
|
+
if (c.budgetLeft() && c.dividesWord(c.compose(shrunk(at)), name)) {
|
|
860
|
+
const whole = (i) => {
|
|
861
|
+
if (!c.budgetLeft())
|
|
862
|
+
return false;
|
|
863
|
+
const comp = c.compose(shrunk(i));
|
|
864
|
+
return !c.overflows(comp, name) && !c.dividesWord(comp, name);
|
|
865
|
+
};
|
|
866
|
+
if (at < last && whole(last)) {
|
|
867
|
+
at = largest(at + 1, whole);
|
|
868
|
+
word = 'whole';
|
|
869
|
+
}
|
|
843
870
|
else
|
|
844
|
-
|
|
871
|
+
word = 'divided';
|
|
845
872
|
}
|
|
846
873
|
out.push({
|
|
847
874
|
kind: 'allowShrink',
|
|
848
875
|
path: path('fit/minScale'),
|
|
849
|
-
value: steps[
|
|
876
|
+
value: steps[at],
|
|
850
877
|
...(frame.fit.shrinkText ? {} : { also: [{ path: path('fit/shrinkText'), value: true }] }),
|
|
878
|
+
...(word ? { word } : {}),
|
|
851
879
|
});
|
|
852
880
|
}
|
|
853
881
|
// A frame that holds content (an empty one is short of its own box, #911:
|
|
@@ -991,9 +1019,25 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
991
1019
|
* numbered (`VDTDesignTextBlock.pageMark`). */
|
|
992
1020
|
const toGuess = String(input.page.index + 2);
|
|
993
1021
|
const fromGuess = String(Math.max(1, input.page.index));
|
|
1022
|
+
/** The named look (#1089) the `:::frame` fence of a frame's story names
|
|
1023
|
+
* (`look="<id>"`), when `frameLooks` has it: the story's, so every
|
|
1024
|
+
* frame of a chain and the frames it goes on in on later pages wear it. */
|
|
1025
|
+
const frameLooks = pass.resolved.frameLooks;
|
|
1026
|
+
const namedLookOf = (frame) => {
|
|
1027
|
+
if (!frameLooks)
|
|
1028
|
+
return undefined;
|
|
1029
|
+
const id = input.stories.get(headOf(frame.name))?.plan.attrs.look?.trim();
|
|
1030
|
+
return id ? frameLooks.find((l) => l.id === id) : undefined;
|
|
1031
|
+
};
|
|
1032
|
+
/** The look a frame's story names that `frameLooks` does not have
|
|
1033
|
+
* (#1095): the canvas marks the frame. */
|
|
1034
|
+
const unknownLookOf = (frame) => {
|
|
1035
|
+
const id = input.stories.get(headOf(frame.name))?.plan.attrs.look?.trim();
|
|
1036
|
+
return id && !frameLooks?.some((l) => l.id === id) ? id : undefined;
|
|
1037
|
+
};
|
|
994
1038
|
/** The style a frame is laid out in: its box as drawn at any text scale. */
|
|
995
1039
|
const styleOf = (frame) => {
|
|
996
|
-
const style = keepChromeAt(input.styleOf(frame), textScale(frame));
|
|
1040
|
+
const style = keepChromeAt(input.styleOf(withNamedLook(frame, namedLookOf(frame))), textScale(frame));
|
|
997
1041
|
if (!fit.goOn.has(frame.name))
|
|
998
1042
|
return style;
|
|
999
1043
|
// A frame cut for another page (#859) ends with its mark: its own
|
|
@@ -1125,6 +1169,9 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1125
1169
|
const moduleRects = new Map();
|
|
1126
1170
|
/** The foot a module cut at its group's foot is clipped to (#1062). */
|
|
1127
1171
|
let clipFoot;
|
|
1172
|
+
/** A module a stretching row lays taller than it is (#1095): its
|
|
1173
|
+
* frames, its own foot (`at`) and the row's. */
|
|
1174
|
+
let stretchTo;
|
|
1128
1175
|
const pushed = seed
|
|
1129
1176
|
? [...seed].map(([name, box]) => ({ name, box, ...(seedShapes?.get(name) ? { contour: seedShapes.get(name) } : {}) }))
|
|
1130
1177
|
: [];
|
|
@@ -1722,6 +1769,7 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1722
1769
|
...(unmirroredAt(frame) !== undefined ? { unmirrorX: unmirroredAt(frame) } : {}),
|
|
1723
1770
|
layer: frame.layer,
|
|
1724
1771
|
...(frame.style ? { styleId: frame.style } : {}),
|
|
1772
|
+
...(holds && unknownLookOf(frame) !== undefined ? { lookUnknown: unknownLookOf(frame) } : {}),
|
|
1725
1773
|
scale: s,
|
|
1726
1774
|
...(pic !== undefined && holds ? { imageScale: pic } : {}),
|
|
1727
1775
|
overflowPx,
|
|
@@ -1814,6 +1862,27 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1814
1862
|
rects.set(name, out);
|
|
1815
1863
|
return out;
|
|
1816
1864
|
};
|
|
1865
|
+
/** A module's `'auto'` box that ends at the module's foot, in a row
|
|
1866
|
+
* that stretches the module (#1095): to the row's foot, its declared
|
|
1867
|
+
* box by as much (the template asks for it). A frame as wide as its
|
|
1868
|
+
* content (a pill, a tab) keeps its height. */
|
|
1869
|
+
const stretchAuto = (name, r) => {
|
|
1870
|
+
const s = stretchTo;
|
|
1871
|
+
if (!s || !s.names.has(name))
|
|
1872
|
+
return r;
|
|
1873
|
+
const f = byName.get(name);
|
|
1874
|
+
if (!f || f.height !== 'auto' || f.width === 'auto' || f.pin || heldByFoot(f))
|
|
1875
|
+
return r;
|
|
1876
|
+
const bottom = r.y + r.height;
|
|
1877
|
+
if (Math.abs(bottom - s.at) > 0.5 || s.foot - bottom <= 0.01)
|
|
1878
|
+
return r;
|
|
1879
|
+
const out = { ...r, height: s.foot - r.y };
|
|
1880
|
+
rects.set(name, out);
|
|
1881
|
+
const d = declared.get(name);
|
|
1882
|
+
if (d)
|
|
1883
|
+
declared.set(name, { ...d, height: d.height + (s.foot - bottom) });
|
|
1884
|
+
return out;
|
|
1885
|
+
};
|
|
1817
1886
|
/** The frames of `names` whose content goes on whole on another page
|
|
1818
1887
|
* (#1061, #1062): none of it is laid here. */
|
|
1819
1888
|
const spillWhole = (names, advanced, lastLaid) => {
|
|
@@ -1830,7 +1899,7 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1830
1899
|
const runStep = (step, filling) => {
|
|
1831
1900
|
if (step.kind === 'frame') {
|
|
1832
1901
|
const p = prepare(step.frame);
|
|
1833
|
-
const r = clipAtFoot(step.frame.name, placeFrame(p));
|
|
1902
|
+
const r = clipAtFoot(step.frame.name, stretchAuto(step.frame.name, placeFrame(p)));
|
|
1834
1903
|
if (filling)
|
|
1835
1904
|
fill(p, r);
|
|
1836
1905
|
return;
|
|
@@ -1851,7 +1920,7 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1851
1920
|
let lastLaid;
|
|
1852
1921
|
for (const f of step.frames) {
|
|
1853
1922
|
if (!grid.spilled.has(f.name)) {
|
|
1854
|
-
fill(prepare(f), clipAtFoot(f.name, grid.rects.get(f.name)));
|
|
1923
|
+
fill(prepare(f), clipAtFoot(f.name, stretchAuto(f.name, grid.rects.get(f.name))));
|
|
1855
1924
|
lastLaid = frameIdOf(f.name);
|
|
1856
1925
|
continue;
|
|
1857
1926
|
}
|
|
@@ -1863,31 +1932,136 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1863
1932
|
// A frame of a chain inside the group is reached once the frame
|
|
1864
1933
|
// before it was filled: each is prepared again as it is filled.
|
|
1865
1934
|
for (const f of step.frames) {
|
|
1866
|
-
const r = clipAtFoot(f.name, solved.get(f.name));
|
|
1935
|
+
const r = clipAtFoot(f.name, stretchAuto(f.name, solved.get(f.name)));
|
|
1867
1936
|
if (filling)
|
|
1868
1937
|
fill(prepare(f), r);
|
|
1869
1938
|
}
|
|
1870
1939
|
};
|
|
1871
|
-
/** Lay a modules group (#1062):
|
|
1872
|
-
*
|
|
1873
|
-
*
|
|
1874
|
-
*
|
|
1875
|
-
*
|
|
1876
|
-
*
|
|
1877
|
-
*
|
|
1878
|
-
*
|
|
1879
|
-
*
|
|
1880
|
-
*
|
|
1881
|
-
*
|
|
1882
|
-
*
|
|
1883
|
-
* the
|
|
1940
|
+
/** Lay a modules group (#1062): its modules in rows down the group's
|
|
1941
|
+
* box (its own, else the area). A stack gives each module a row of
|
|
1942
|
+
* its own, as wide as the group (or its template's `width`, #1088), a
|
|
1943
|
+
* `'row'` puts them all in one, start to end (each its `width`, else
|
|
1944
|
+
* an equal share of what is left), a `'grid'` `columns` to a row in
|
|
1945
|
+
* equal columns; a run of modules a group inside it takes (`within`)
|
|
1946
|
+
* is laid as that group says where its first module comes: its rows
|
|
1947
|
+
* among a stack's, one cell of a row or a grid (#1095: to any depth).
|
|
1948
|
+
* A row's room past its modules' widths goes by `grow`, what it is
|
|
1949
|
+
* short by comes back by `shrink` (#1095). Rows run from the start side of the page's lines (the flow frame is
|
|
1950
|
+
* mirrored on a right-to-left page, so its left is the start). A
|
|
1951
|
+
* module is placed as its template draws its frames in its box
|
|
1952
|
+
* (`'container'` is the box) and its groups laid in it: first in the
|
|
1953
|
+
* room left, to find how tall it is (the frames held by the box's foot
|
|
1954
|
+
* or middle, or sized by its height, left out), then in a box that
|
|
1955
|
+
* tall, or (`rowAlign` `'stretch'`, a row of several) as tall as its
|
|
1956
|
+
* row, its `'auto'` boxes that end at its foot reaching the row's
|
|
1957
|
+
* (#1095). A row that does not fit is cut at the group's foot (its frames
|
|
1958
|
+
* clipped there), or, when the group goes on to another page
|
|
1959
|
+
* (`FitState.gridsOn`), it and every row after it go on, whole. The
|
|
1960
|
+
* modules' declared boxes follow each other at the heights their
|
|
1961
|
+
* frames declare, so the log tells a module moved by the one before
|
|
1962
|
+
* it. */
|
|
1884
1963
|
const placeModules = (group, members) => {
|
|
1885
1964
|
const byMember = new Map(members.map((f) => [f.name, f]));
|
|
1886
1965
|
const own = groupPlaceable(group);
|
|
1887
1966
|
const ownBox = own ? placeBox(own, template, pageInput, refOf, () => 0) : undefined;
|
|
1888
1967
|
const box = ownBox ?? pageInput.areas.area;
|
|
1889
1968
|
const foot = box.y + box.height;
|
|
1890
|
-
const
|
|
1969
|
+
const toPx = (d) => dimensionToPx(d, dpi, pageInput.emPx);
|
|
1970
|
+
const groupNamed = new Map(template.groups.map((g) => [g.name, g]));
|
|
1971
|
+
const mods = [];
|
|
1972
|
+
for (const item of group.items ?? []) {
|
|
1973
|
+
const mod = template.modules?.find((m) => m.name === item.module);
|
|
1974
|
+
const names = item.frames.filter((n) => byMember.has(n));
|
|
1975
|
+
if (mod && names.length > 0)
|
|
1976
|
+
mods.push({ mod, names, chain: nestedChain(template.groups, mod.nested) });
|
|
1977
|
+
}
|
|
1978
|
+
/** A module's width by its template in a group `width` wide
|
|
1979
|
+
* (`'fill'`: none). */
|
|
1980
|
+
const fixedWidth = (l, width) => {
|
|
1981
|
+
const w = l.mod.width;
|
|
1982
|
+
if (w === undefined || w === 'fill')
|
|
1983
|
+
return undefined;
|
|
1984
|
+
return 'percent' in w ? (width * w.percent) / 100 : toPx(w);
|
|
1985
|
+
};
|
|
1986
|
+
/** The cells of `list` in `g`: each module `g` lays itself, and each
|
|
1987
|
+
* run of the modules of a group inside `g`. */
|
|
1988
|
+
const cellsOf = (list, g) => {
|
|
1989
|
+
const out = [];
|
|
1990
|
+
for (const l of list) {
|
|
1991
|
+
const name = l.chain.find((n) => groupNamed.get(n)?.within === g.name);
|
|
1992
|
+
const child = name !== undefined ? groupNamed.get(name) : undefined;
|
|
1993
|
+
const last = out[out.length - 1];
|
|
1994
|
+
if (child && last?.kind === 'group' && last.group === child)
|
|
1995
|
+
last.list.push(l);
|
|
1996
|
+
else
|
|
1997
|
+
out.push(child ? { kind: 'group', group: child, list: [l] } : { kind: 'module', l });
|
|
1998
|
+
}
|
|
1999
|
+
return out;
|
|
2000
|
+
};
|
|
2001
|
+
const namesOf = (c) => (c.kind === 'module' ? c.l.names : c.list.flatMap((l) => l.names));
|
|
2002
|
+
/** `cells` side by side across `width` from `x`, `gap` between them:
|
|
2003
|
+
* each as wide as its template says, then the room left shared by
|
|
2004
|
+
* `grow` (a `'fill'` module, and a group's cell, 1 at least), or,
|
|
2005
|
+
* when the widths run past the end, given back by `shrink` in
|
|
2006
|
+
* proportion to each width (#1095). */
|
|
2007
|
+
const across = (cells, gap, x, width) => {
|
|
2008
|
+
const fixed = cells.map((c) => (c.kind === 'module' ? fixedWidth(c.l, width) : undefined));
|
|
2009
|
+
const taken = fixed.reduce((a, w) => a + (w ?? 0), 0) + gap * Math.max(0, cells.length - 1);
|
|
2010
|
+
const spare = width - taken;
|
|
2011
|
+
let widths;
|
|
2012
|
+
if (spare >= 0) {
|
|
2013
|
+
const grows = cells.map((c, k) => (c.kind !== 'module' ? 1 : fixed[k] === undefined ? Math.max(1, c.l.mod.grow ?? 1) : c.l.mod.grow ?? 0));
|
|
2014
|
+
const total = grows.reduce((a, g) => a + g, 0);
|
|
2015
|
+
widths = fixed.map((w, k) => Math.max(0, total > 0 ? (w ?? 0) + (spare * grows[k]) / total : w ?? 0));
|
|
2016
|
+
}
|
|
2017
|
+
else {
|
|
2018
|
+
const weights = cells.map((c, k) => (c.kind === 'module' && fixed[k] !== undefined ? (c.l.mod.shrink ?? 0) * fixed[k] : 0));
|
|
2019
|
+
const total = weights.reduce((a, g) => a + g, 0);
|
|
2020
|
+
widths = fixed.map((w, k) => Math.max(0, total > 0 ? (w ?? 0) + (spare * weights[k]) / total : w ?? 0));
|
|
2021
|
+
}
|
|
2022
|
+
const xs = [];
|
|
2023
|
+
let at = x;
|
|
2024
|
+
for (const w of widths) {
|
|
2025
|
+
xs.push(at);
|
|
2026
|
+
at += w + gap;
|
|
2027
|
+
}
|
|
2028
|
+
return { xs, widths };
|
|
2029
|
+
};
|
|
2030
|
+
/** The rows `list` makes in a group of `g`'s kind across `width`
|
|
2031
|
+
* from `x`, the first `first` after the row before it. A group
|
|
2032
|
+
* inside a stack adds its rows to the stack's; inside a row or a
|
|
2033
|
+
* grid, a run of its modules is one cell. */
|
|
2034
|
+
const rowsOf = (list, g, x, width, first) => {
|
|
2035
|
+
const gap = toPx(g.gap);
|
|
2036
|
+
const align = g.rowAlign ?? 'stretch';
|
|
2037
|
+
const cells = cellsOf(list, g);
|
|
2038
|
+
if (g.kind === 'row')
|
|
2039
|
+
return [{ cells, ...across(cells, gap, x, width), align, gapBefore: first }];
|
|
2040
|
+
if (g.kind === 'grid') {
|
|
2041
|
+
const columns = Math.max(1, g.columns ?? 1);
|
|
2042
|
+
const colGap = toPx(g.columnGap ?? g.gap);
|
|
2043
|
+
const rowGap = toPx(g.rowGap ?? g.gap);
|
|
2044
|
+
const colW = Math.max(1, (width - colGap * (columns - 1)) / columns);
|
|
2045
|
+
const out = [];
|
|
2046
|
+
for (let k = 0; k < cells.length; k += columns) {
|
|
2047
|
+
const chunk = cells.slice(k, k + columns);
|
|
2048
|
+
out.push({ cells: chunk, xs: chunk.map((_, c) => x + c * (colW + colGap)), widths: chunk.map(() => colW), align, gapBefore: k === 0 ? first : rowGap });
|
|
2049
|
+
}
|
|
2050
|
+
return out;
|
|
2051
|
+
}
|
|
2052
|
+
// A stack: a row of its own per module, at its width from the
|
|
2053
|
+
// start; a group's rows in turn.
|
|
2054
|
+
const out = [];
|
|
2055
|
+
cells.forEach((c, k) => {
|
|
2056
|
+
const before = k === 0 ? first : gap;
|
|
2057
|
+
if (c.kind === 'module')
|
|
2058
|
+
out.push({ cells: [c], xs: [x], widths: [fixedWidth(c.l, width) ?? width], align: 'start', gapBefore: before });
|
|
2059
|
+
else
|
|
2060
|
+
out.push(...rowsOf(c.list, c.group, x, width, before));
|
|
2061
|
+
});
|
|
2062
|
+
return out;
|
|
2063
|
+
};
|
|
2064
|
+
const rows = rowsOf(mods, group, box.x, box.width, 0);
|
|
1891
2065
|
let cursor = box.y;
|
|
1892
2066
|
let declCursor = box.y;
|
|
1893
2067
|
let out = false;
|
|
@@ -1895,40 +2069,31 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1895
2069
|
let lastLaid;
|
|
1896
2070
|
const boxes = [];
|
|
1897
2071
|
const declBoxes = [];
|
|
2072
|
+
const nestedBoxes = new Map();
|
|
1898
2073
|
const restore = (to, from) => {
|
|
1899
2074
|
to.clear();
|
|
1900
2075
|
for (const [k, v] of from)
|
|
1901
2076
|
to.set(k, v);
|
|
1902
2077
|
};
|
|
1903
|
-
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
spillWhole(names, laidAny, lastLaid);
|
|
1910
|
-
continue;
|
|
2078
|
+
const lay = (l, x, y, width, height, filling) => {
|
|
2079
|
+
const steps = composedOrder({ frames: l.names.map((n) => byMember.get(n)), groups: template.groups.filter((g) => l.mod.groups.includes(g.name)) });
|
|
2080
|
+
input = { ...pageInput, areas: { ...pageInput.areas, area: { x, y, width, height: Math.max(0, height) } } };
|
|
2081
|
+
try {
|
|
2082
|
+
for (const step of steps)
|
|
2083
|
+
runStep(step, filling);
|
|
1911
2084
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
}
|
|
1920
|
-
finally {
|
|
1921
|
-
input = pageInput;
|
|
1922
|
-
}
|
|
1923
|
-
};
|
|
1924
|
-
// 1. How tall the module is: its frames placed in the room left,
|
|
1925
|
-
// then everything they set put back.
|
|
2085
|
+
finally {
|
|
2086
|
+
input = pageInput;
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
/** How tall a module is at `(x, y)`, `width` wide: its frames placed
|
|
2090
|
+
* in the room left, then everything they set put back. */
|
|
2091
|
+
const measure = (l, x, y, width) => {
|
|
1926
2092
|
const kept = { rects: new Map(rects), declared: new Map(declared), groupRects: new Map(groupRects), groupDeclared: new Map(groupDeclared), pinned: pinned.length, boxShort: new Map(boxShort), fitted: new Map(fitted) };
|
|
1927
|
-
lay(foot -
|
|
1928
|
-
const placed = names.filter((n) => rects.has(n));
|
|
2093
|
+
lay(l, x, y, width, foot - y, false);
|
|
2094
|
+
const placed = l.names.filter((n) => rects.has(n));
|
|
1929
2095
|
const measured = placed.filter((n) => !heldByFoot(byMember.get(n)));
|
|
1930
2096
|
const reach = unionRect((measured.length > 0 ? measured : placed).map((n) => rects.get(n)));
|
|
1931
|
-
const height = reach ? Math.max(0, reach.y + reach.height - cursor) : 0;
|
|
1932
2097
|
restore(rects, kept.rects);
|
|
1933
2098
|
restore(declared, kept.declared);
|
|
1934
2099
|
restore(groupRects, kept.groupRects);
|
|
@@ -1936,41 +2101,129 @@ function composePage(pageInput, template, fit, collapsed) {
|
|
|
1936
2101
|
restore(boxShort, kept.boxShort);
|
|
1937
2102
|
restore(fitted, kept.fitted);
|
|
1938
2103
|
pinned.length = kept.pinned;
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2104
|
+
return reach ? Math.max(0, reach.y + reach.height - y) : 0;
|
|
2105
|
+
};
|
|
2106
|
+
/** How tall a cell is at `(x, y)`, `width` wide: a module's height,
|
|
2107
|
+
* or a group's rows one after another (#1095). */
|
|
2108
|
+
const measureCell = (c, x, y, width) => {
|
|
2109
|
+
if (c.kind === 'module')
|
|
2110
|
+
return measure(c.l, x, y, width);
|
|
2111
|
+
let at = y;
|
|
2112
|
+
rowsOf(c.list, c.group, x, width, 0).forEach((row, r) => {
|
|
2113
|
+
if (r > 0)
|
|
2114
|
+
at += row.gapBefore;
|
|
2115
|
+
at += Math.max(0, ...row.cells.map((d, k) => measureCell(d, row.xs[k], at, row.widths[k])));
|
|
2116
|
+
});
|
|
2117
|
+
return at - y;
|
|
2118
|
+
};
|
|
2119
|
+
/** Lay `row` at `rowTop` (its declared top `declTop`), its cells
|
|
2120
|
+
* `heights` tall: each module in a box that tall (cut at the foot
|
|
2121
|
+
* when the row does not fit), a shorter one placed by `rowAlign`; a
|
|
2122
|
+
* group's cell its rows in turn. The row's foot, laid and declared. */
|
|
2123
|
+
const layRow = (row, rowTop, declTop, heights, fits) => {
|
|
2124
|
+
const rowH = Math.max(0, ...heights);
|
|
2125
|
+
const several = row.cells.length > 1;
|
|
2126
|
+
let rowBottom = rowTop;
|
|
2127
|
+
let rowDeclBottom = declTop;
|
|
2128
|
+
row.cells.forEach((c, k) => {
|
|
2129
|
+
const x = row.xs[k];
|
|
2130
|
+
const width = row.widths[k];
|
|
2131
|
+
const spare = rowH - heights[k];
|
|
2132
|
+
const stretched = fits && several && row.align === 'stretch' && c.kind === 'module';
|
|
2133
|
+
const dy = fits && several && spare > 0.01 ? (row.align === 'end' ? spare : row.align === 'center' ? spare / 2 : 0) : 0;
|
|
2134
|
+
const y = rowTop + dy;
|
|
2135
|
+
if (c.kind === 'group') {
|
|
2136
|
+
// A run of a group inside this one (#1095): its rows, from the
|
|
2137
|
+
// cell's top.
|
|
2138
|
+
const laid = layRows(rowsOf(c.list, c.group, x, width, 0), y, declTop + dy, fits);
|
|
2139
|
+
rowBottom = Math.max(rowBottom, laid.bottom);
|
|
2140
|
+
rowDeclBottom = Math.max(rowDeclBottom, laid.declBottom);
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
const l = c.l;
|
|
2144
|
+
clipFoot = fits ? undefined : foot;
|
|
2145
|
+
// Its 'auto' boxes that end at its foot reach the row's (#1095).
|
|
2146
|
+
stretchTo = stretched && spare > 0.01 ? { at: y + heights[k], foot: y + rowH, names: new Set(l.names) } : undefined;
|
|
2147
|
+
try {
|
|
2148
|
+
lay(l, x, y, width, fits ? (stretched ? rowH : heights[k]) : foot - rowTop, true);
|
|
2149
|
+
}
|
|
2150
|
+
finally {
|
|
2151
|
+
clipFoot = undefined;
|
|
2152
|
+
stretchTo = undefined;
|
|
2153
|
+
}
|
|
2154
|
+
const u = unionRect(l.names.flatMap((n) => (rects.has(n) ? [rects.get(n)] : [])));
|
|
2155
|
+
const bottom = Math.max(stretched ? y + rowH : y, u ? u.y + u.height : y);
|
|
2156
|
+
const moduleBox = { x, y, width, height: bottom - y };
|
|
2157
|
+
moduleRects.set(l.mod.name, moduleBox);
|
|
2158
|
+
boxes.push(moduleBox);
|
|
2159
|
+
// Declared: after the rows before it as they declare themselves.
|
|
2160
|
+
const shift = declTop - rowTop;
|
|
2161
|
+
for (const n of l.names) {
|
|
2162
|
+
const d = declared.get(n);
|
|
2163
|
+
if (d && Math.abs(shift) > 0.001)
|
|
2164
|
+
declared.set(n, { ...d, y: d.y + shift });
|
|
2165
|
+
}
|
|
2166
|
+
const declY = declTop + dy;
|
|
2167
|
+
const du = unionRect(l.names.flatMap((n) => (declared.has(n) ? [declared.get(n)] : [])));
|
|
2168
|
+
const declBottom = du ? Math.max(declY, du.y + du.height) : declY;
|
|
2169
|
+
const declBox = { x, y: declY, width, height: declBottom - declY };
|
|
2170
|
+
declBoxes.push(declBox);
|
|
2171
|
+
for (const name of l.chain) {
|
|
2172
|
+
const n = nestedBoxes.get(name) ?? { laid: [], declared: [] };
|
|
2173
|
+
n.laid.push(moduleBox);
|
|
2174
|
+
n.declared.push(declBox);
|
|
2175
|
+
nestedBoxes.set(name, n);
|
|
2176
|
+
}
|
|
2177
|
+
rowBottom = Math.max(rowBottom, bottom);
|
|
2178
|
+
rowDeclBottom = Math.max(rowDeclBottom, declBottom);
|
|
2179
|
+
});
|
|
2180
|
+
return { bottom: rowBottom, declBottom: rowDeclBottom };
|
|
2181
|
+
};
|
|
2182
|
+
/** The rows of a group's cell, one after another from `top`. */
|
|
2183
|
+
const layRows = (list, top, declTop, fits) => {
|
|
2184
|
+
let at = top;
|
|
2185
|
+
let declAt = declTop;
|
|
2186
|
+
list.forEach((row, r) => {
|
|
2187
|
+
if (r > 0) {
|
|
2188
|
+
at += row.gapBefore;
|
|
2189
|
+
declAt += row.gapBefore;
|
|
2190
|
+
}
|
|
2191
|
+
const heights = row.cells.map((c, k) => measureCell(c, row.xs[k], at, row.widths[k]));
|
|
2192
|
+
const laid = layRow(row, at, declAt, heights, fits);
|
|
2193
|
+
at = laid.bottom;
|
|
2194
|
+
declAt = laid.declBottom;
|
|
2195
|
+
});
|
|
2196
|
+
return { bottom: at, declBottom: declAt };
|
|
2197
|
+
};
|
|
2198
|
+
rows.forEach((row, r) => {
|
|
2199
|
+
const names = row.cells.flatMap(namesOf);
|
|
2200
|
+
if (out) {
|
|
1943
2201
|
spillWhole(names, laidAny, lastLaid);
|
|
1944
|
-
|
|
1945
|
-
}
|
|
1946
|
-
// 2. The module laid in a box that tall (cut at the foot when it
|
|
1947
|
-
// does not fit).
|
|
1948
|
-
clipFoot = fits ? undefined : foot;
|
|
1949
|
-
try {
|
|
1950
|
-
lay(fits ? height : foot - cursor, true);
|
|
2202
|
+
return;
|
|
1951
2203
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
2204
|
+
if (r > 0) {
|
|
2205
|
+
cursor += row.gapBefore;
|
|
2206
|
+
declCursor += row.gapBefore;
|
|
1954
2207
|
}
|
|
1955
|
-
const
|
|
1956
|
-
const
|
|
1957
|
-
const
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
const d = declared.get(n);
|
|
1964
|
-
if (d && Math.abs(shift) > 0.001)
|
|
1965
|
-
declared.set(n, { ...d, y: d.y + shift });
|
|
2208
|
+
const heights = row.cells.map((c, k) => measureCell(c, row.xs[k], cursor, row.widths[k]));
|
|
2209
|
+
const rowH = Math.max(0, ...heights);
|
|
2210
|
+
const fits = cursor + rowH <= foot + 0.5;
|
|
2211
|
+
if (!fits && fit.gridsOn.has(group.name)) {
|
|
2212
|
+
// This row and the ones after it go on, whole.
|
|
2213
|
+
out = true;
|
|
2214
|
+
spillWhole(names, laidAny, lastLaid);
|
|
2215
|
+
return;
|
|
1966
2216
|
}
|
|
1967
|
-
const
|
|
1968
|
-
const declBottom = du ? Math.max(declCursor, du.y + du.height) : declCursor;
|
|
1969
|
-
declBoxes.push({ x: box.x, y: declCursor, width: box.width, height: declBottom - declCursor });
|
|
2217
|
+
const laid = layRow(row, cursor, declCursor, heights, fits);
|
|
1970
2218
|
laidAny = true;
|
|
1971
2219
|
lastLaid = frameIdOf(names[names.length - 1]);
|
|
1972
|
-
cursor = bottom
|
|
1973
|
-
declCursor = declBottom
|
|
2220
|
+
cursor = laid.bottom;
|
|
2221
|
+
declCursor = laid.declBottom;
|
|
2222
|
+
});
|
|
2223
|
+
// A group inside another (#1088, #1095) is the box of its modules.
|
|
2224
|
+
for (const [name, b] of nestedBoxes) {
|
|
2225
|
+
groupRects.set(name, unionRect(b.laid));
|
|
2226
|
+
groupDeclared.set(name, unionRect(b.declared));
|
|
1974
2227
|
}
|
|
1975
2228
|
groupRects.set(group.name, ownBox ?? unionRect(boxes) ?? { ...box, height: 0 });
|
|
1976
2229
|
groupDeclared.set(group.name, ownBox ?? unionRect(declBoxes) ?? { ...box, height: 0 });
|
|
@@ -2123,6 +2376,25 @@ export function frameStyleOf(base, frame) {
|
|
|
2123
2376
|
};
|
|
2124
2377
|
}
|
|
2125
2378
|
const lookedStyles = new WeakMap();
|
|
2379
|
+
/** Frames with a named look laid over theirs (#1089), per look and frame:
|
|
2380
|
+
* the same look object each time, so `styleWithLook` keeps its styles. */
|
|
2381
|
+
const namedLookFrames = new WeakMap();
|
|
2382
|
+
/** `frame` with the named look `named` (#1089) laid over its own `look`
|
|
2383
|
+
* (`lookOver`): the order is the style, the template frame's look, then
|
|
2384
|
+
* the named look. `frame` itself when there is none. */
|
|
2385
|
+
export function withNamedLook(frame, named) {
|
|
2386
|
+
if (!named)
|
|
2387
|
+
return frame;
|
|
2388
|
+
let byFrame = namedLookFrames.get(named);
|
|
2389
|
+
if (!byFrame)
|
|
2390
|
+
namedLookFrames.set(named, (byFrame = new WeakMap()));
|
|
2391
|
+
const known = byFrame.get(frame);
|
|
2392
|
+
if (known)
|
|
2393
|
+
return known;
|
|
2394
|
+
const out = { ...frame, look: lookOver(frame.look, named) };
|
|
2395
|
+
byFrame.set(frame, out);
|
|
2396
|
+
return out;
|
|
2397
|
+
}
|
|
2126
2398
|
/** `base` with what a frame's `look` sets (#976) written over it: colours
|
|
2127
2399
|
* of the fill, border, stripe, icon, marker, title, text and label tab,
|
|
2128
2400
|
* and the tracking of the body. A fill colour turns the fill on. A part
|