plotto 0.4.0 → 0.4.1
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/README.md +3 -2
- package/package.json +1 -1
- package/src/figure.js +21 -7
- package/src/info.js +3 -1
- package/theme.json +1 -0
package/README.md
CHANGED
|
@@ -197,7 +197,8 @@ One-time setup:
|
|
|
197
197
|
Each release (`main` takes changes only through a reviewed pull request):
|
|
198
198
|
|
|
199
199
|
```bash
|
|
200
|
-
# 1. Merge your changes into main through a pull request
|
|
200
|
+
# 1. Merge your changes into main through a pull request,
|
|
201
|
+
# with a line for each change under "## Unreleased" in CHANGELOG.md
|
|
201
202
|
|
|
202
203
|
# 2. Prepare the release
|
|
203
204
|
git switch main && git pull && source .venv/bin/activate
|
|
@@ -211,7 +212,7 @@ scripts/release.sh tag # answer y
|
|
|
211
212
|
npm view plotto version && pip index versions python-plotto
|
|
212
213
|
```
|
|
213
214
|
|
|
214
|
-
- `scripts/release.sh X.Y.Z` checks that `main` is current
|
|
215
|
+
- `scripts/release.sh X.Y.Z` checks that `main` is current, that the version is new and that `CHANGELOG.md` has changes under "Unreleased". It runs every test and lint. Then, on the branch `release/vX.Y.Z`, it sets the version in `package.json`, `package-lock.json` and `python/plotto/__init__.py`, moves the "Unreleased" changes to a dated `X.Y.Z` section, builds `docs/`, commits and pushes.
|
|
215
216
|
- `scripts/release.sh tag` tags the merged `main` as `vX.Y.Z`, asks before it pushes the tag, and deletes the release branch.
|
|
216
217
|
- The pushed tag runs `.github/workflows/release.yml`, which publishes to PyPI and npm. Follow it in the Actions tab.
|
|
217
218
|
|
package/package.json
CHANGED
package/src/figure.js
CHANGED
|
@@ -16,6 +16,8 @@ import { layerEdges, makeView } from "./views.js";
|
|
|
16
16
|
const NARROW = 560; // px: below this width, a right legend moves to the bottom
|
|
17
17
|
const MIN_HEIGHT = 240; // px: the smallest height of a reflowed figure
|
|
18
18
|
const ASPECT_GROWTH = 1.2; // a theme aspect makes a reflowed figure at most this much taller
|
|
19
|
+
const RIGHT_SHARE = 0.25; // a format with "shrink" keeps the right column (legend, info card) to this share of the width
|
|
20
|
+
const SHRINK_STEP = 0.05; // and makes their text smaller by this share of its size at each step
|
|
19
21
|
|
|
20
22
|
// options: document (the browser or jsdom), width (lay out at this width), domains and hidden (keep a view after a resize).
|
|
21
23
|
export function figure(spec, { document: doc = globalThis.document, width, domains, hidden = new Set() } = {}) {
|
|
@@ -25,7 +27,6 @@ export function figure(spec, { document: doc = globalThis.document, width, domai
|
|
|
25
27
|
const reflowed = width && theme.reflow && !theme.fixedHeight;
|
|
26
28
|
let H = reflowed ? Math.max(MIN_HEIGHT, Math.round((W * theme.height) / theme.width)) : theme.height;
|
|
27
29
|
if (theme.legend === "right" && W < NARROW) theme.legend = "bottom";
|
|
28
|
-
const fonts = fontSizes(theme);
|
|
29
30
|
const grid = facetPanels(ns);
|
|
30
31
|
const kind = { x: axisKind(ns, "x"), y: axisKind(ns, "y") };
|
|
31
32
|
const color = colorScale(ns);
|
|
@@ -33,19 +34,32 @@ export function figure(spec, { document: doc = globalThis.document, width, domai
|
|
|
33
34
|
for (const p of grid.panels) p.views = ns.layers.map((layer, li) => makeView(ctx, layer, p.rows[li], new Set()));
|
|
34
35
|
const home = homeDomains(ns, grid, kind);
|
|
35
36
|
const horizontal = theme.legend === "bottom";
|
|
37
|
+
// The legend and the info card fit: a right column of at most RIGHT_SHARE of the width, one legend column,
|
|
38
|
+
// no cut legend label, no cut info key and no "+N more" row. A format with "shrink" (slide) makes their text smaller until they
|
|
39
|
+
// fit, down to that share of the size; the other formats keep the size.
|
|
40
|
+
const fits = (info, legend) => {
|
|
41
|
+
const right = Math.max(legend && !legend.horizontal ? legend.width : 0, info?.position === "right" ? info.width : 0);
|
|
42
|
+
const cut = legend?.items?.some((it) => it.lines.at(-1).endsWith("…") || (!legend.horizontal && it.x > 0));
|
|
43
|
+
return right <= W * RIGHT_SHARE && !cut && !info?.cut;
|
|
44
|
+
};
|
|
36
45
|
const arrange = (H) => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
const base = fontSizes(theme);
|
|
47
|
+
for (let k = 1; ; k -= SHRINK_STEP) {
|
|
48
|
+
const f = { ...base, legend: Math.round(base.legend * k * 10) / 10 };
|
|
49
|
+
const info = infoModel(ns, f, { W, H, narrow: W < NARROW });
|
|
50
|
+
const above = info?.position === "right" ? info.height + f.base : 0; // the info card sits above a right legend
|
|
51
|
+
const legend = legendModel(ns, color, f, { horizontal, maxWidth: W * 0.9, maxHeight: H * 0.8 - above });
|
|
52
|
+
if (fits(info, legend) || k - SHRINK_STEP < (theme.shrink ?? 1) - 1e-9) return { fonts: f, info, legend, layout: place({ ns, grid, kind, home, legend, info, fonts: f, W, H }) };
|
|
53
|
+
}
|
|
41
54
|
};
|
|
42
|
-
let
|
|
55
|
+
let fonts, info, legend, layout;
|
|
56
|
+
({ fonts, info, legend, layout } = arrange(H));
|
|
43
57
|
// aspect: when the height follows the width, it changes so that the panels get their shape, up to 1.2 times the
|
|
44
58
|
// usual height; beyond that, the panels get narrower.
|
|
45
59
|
if (theme.aspect && theme.fitHeight) {
|
|
46
60
|
const fit = H + grid.nrow * (layout.room.w * theme.aspect - layout.room.h);
|
|
47
61
|
H = Math.round(Math.min(H * ASPECT_GROWTH, Math.max(MIN_HEIGHT, fit)));
|
|
48
|
-
({ info, legend, layout } = arrange(H));
|
|
62
|
+
({ fonts, info, legend, layout } = arrange(H));
|
|
49
63
|
}
|
|
50
64
|
|
|
51
65
|
const svg = svgElement(doc, "svg", {
|
package/src/info.js
CHANGED
|
@@ -62,7 +62,9 @@ function model(ns, fonts, entries, position, W, maxHeight) {
|
|
|
62
62
|
kept--;
|
|
63
63
|
t = table([...all.slice(0, kept), { key: `+${all.length - kept} more`, value: "" }]);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
// cut: the entries that do not show, plus the keys that end with an ellipsis.
|
|
66
|
+
const keys = t.columns.flatMap((c) => c.rows).filter((r) => r.key !== r.full).length;
|
|
67
|
+
return { position, size, pad, lineH, keyGap, cut: all.length - kept + keys, ...t };
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
// The table as an SVG group at (x, y): keys in the muted color, values in the ink color, on a strip-colored card.
|