flexdesk 0.17.7 → 0.17.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.8 (2026-10-24)
4
+
5
+ A patch release: everything below is additive. Every fix is documented in a
6
+ section of the docs headed "(0.17.8)"; each entry links to it.
7
+
8
+ ### Added and changed
9
+
10
+ - [A series transform knows its series, and takes a choice parameter](docs/components/charts.md#a-series-transform-knows-its-series-and-takes-a-choice-parameter-0178): a `seriesTransforms` entry's `run` gets a third argument, `run(y, params, { series, subplot, results, resolveSeries })`: the subplot's series entry the trace was drawn from (null for any other trace, such as an overlay), the subplot config, the results the figure was drawn from and the `resolveSeries` it was drawn with, so a transform can resolve the same series from other results (a baseline scenario) the same way. `renderPlotGrid`, and so `PlotPopoutWindow`, run transforms on the same traces as in 0.17.7 and draw the same derived traces; `deriveSeriesTraces` takes the new optional `seriesOf`, `results` and `resolveSeries` options, and without them passes `series`, `results` and `resolveSeries` as null. A parameter declared `{ type: 'choice', options }` (an array of `{ value, label }`, or a function `(subplot) => options`) is drawn by `renderSeriesTransformControls` as a select (choice_controls' `selectBox`, styled by the new `.twm-series-transforms__select` rule) instead of a number field; the pick is saved under `subplot.transforms.<id>.params` and passed to `run` like a number, and without a `default` or a saved value it is the first enabled option. A transform written as `run(y, params)` with number parameters works and draws exactly as before (STORY-0111).
11
+
12
+ ### Removed or renamed exports
13
+
14
+ None. Compared with 0.17.7, across every entry point (`core`, `widgets`, `wm`,
15
+ `host`, `canvas`, `flow`, `charts`, `tiles`, `editor`) and the package
16
+ `exports` map, no export was added, removed or renamed. Not an export but
17
+ worth knowing: a series transform's `run` gets a third argument and
18
+ `deriveSeriesTraces` takes three more options, all optional, and flexdesk.css
19
+ gains a `.twm-series-transforms__select` rule (see above).
20
+
3
21
  ## 0.17.7 (2026-10-24)
4
22
 
5
23
  A patch release: everything below is additive. Every fix is documented in a
package/css/flexdesk.css CHANGED
@@ -10808,8 +10808,8 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
10808
10808
  /* ══ SERIES TRANSFORMS ═══════════════════════════════════════════════
10809
10809
  *
10810
10810
  * `renderSeriesTransformControls` (src/charts/series_transforms.js): per
10811
- * injected transform, a group with a toggle per output and a number field per
10812
- * parameter, in a subplot's config. */
10811
+ * injected transform, a group with a toggle per output and a number field (or,
10812
+ * for a choice parameter, a select) per parameter, in a subplot's config. */
10813
10813
  .twm-series-transforms {
10814
10814
  display: flex;
10815
10815
  flex-direction: column;
@@ -10849,6 +10849,13 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
10849
10849
  .twm-series-transforms__param:disabled {
10850
10850
  opacity: 0.5;
10851
10851
  }
10852
+ /* A choice parameter's select (0.17.8): as wide as its option labels. */
10853
+ .twm-series-transforms__select {
10854
+ width: auto;
10855
+ min-width: 96px;
10856
+ max-width: 60%;
10857
+ cursor: pointer;
10858
+ }
10852
10859
  /* ══ end SERIES TRANSFORMS ══ */
10853
10860
 
10854
10861
  /* ══ PLOT GRID ════════════════════════════════════════════════════════
package/dist/charts.js CHANGED
@@ -55,7 +55,7 @@ import {
55
55
  strideIndices,
56
56
  uid,
57
57
  updateChart
58
- } from "./chunk-RMTRWWEO.js";
58
+ } from "./chunk-EIYNF4BD.js";
59
59
  import {
60
60
  AutocompleteField,
61
61
  createRafResizeObserver
@@ -3,7 +3,9 @@ import {
3
3
  createRafResizeObserver
4
4
  } from "./chunk-LWV23YVE.js";
5
5
  import {
6
- ManagedWindow
6
+ ManagedWindow,
7
+ flatChoices,
8
+ selectBox
7
9
  } from "./chunk-CVMCZZX2.js";
8
10
 
9
11
  // src/charts/plotly_wrapper.js
@@ -1177,12 +1179,25 @@ function migrateSingleChart(data) {
1177
1179
  }
1178
1180
 
1179
1181
  // src/charts/series_transforms.js
1180
- function resolveParams(transform, state) {
1182
+ var isChoice = (spec) => spec?.type === "choice";
1183
+ function choiceOptions(spec, subplot) {
1184
+ const options = typeof spec.options === "function" ? spec.options(subplot) : spec.options;
1185
+ return Array.isArray(options) ? options : [];
1186
+ }
1187
+ function firstChoice(spec, subplot) {
1188
+ const flat = flatChoices(choiceOptions(spec, subplot));
1189
+ return (flat.find((o) => !o.disabled) ?? flat[0])?.value;
1190
+ }
1191
+ function resolveParams(transform, state, subplot) {
1192
+ const saved = state?.params ?? {};
1181
1193
  const params = {};
1182
1194
  for (const [name, spec] of Object.entries(transform.params ?? {})) {
1183
1195
  params[name] = spec?.default;
1196
+ if (isChoice(spec) && spec.default === void 0 && saved[name] === void 0) {
1197
+ params[name] = firstChoice(spec, subplot);
1198
+ }
1184
1199
  }
1185
- return { ...params, ...state?.params ?? {} };
1200
+ return { ...params, ...saved };
1186
1201
  }
1187
1202
  function enabledOutputs(transform, state) {
1188
1203
  return Object.keys(transform.outputs ?? {}).filter((name) => state?.outputs?.[name] === true);
@@ -1191,19 +1206,20 @@ function isEligible(trace) {
1191
1206
  const type = trace.type ?? "scatter";
1192
1207
  return (type === "scatter" || type === "scattergl") && Array.isArray(trace.y);
1193
1208
  }
1194
- async function deriveSeriesTraces(traces, subplot, seriesTransforms, { onError } = {}) {
1209
+ async function deriveSeriesTraces(traces, subplot, seriesTransforms, { onError, seriesOf, results, resolveSeries } = {}) {
1195
1210
  const derived = [];
1196
1211
  if (!seriesTransforms) return derived;
1197
- const sources = traces.filter(isEligible);
1212
+ const sources = traces.filter(isEligible).map((trace) => ({ trace, series: typeof seriesOf === "function" ? seriesOf(trace) ?? null : null }));
1198
1213
  for (const [transformId, transform] of Object.entries(seriesTransforms)) {
1199
1214
  const state = subplot?.transforms?.[transformId];
1200
1215
  const outputs = enabledOutputs(transform, state);
1201
1216
  if (!outputs.length || typeof transform?.run !== "function") continue;
1202
- const params = resolveParams(transform, state);
1203
- for (const source of sources) {
1217
+ const params = resolveParams(transform, state, subplot);
1218
+ for (const { trace: source, series } of sources) {
1219
+ const context = { series, subplot, results: results ?? null, resolveSeries: resolveSeries ?? null };
1204
1220
  let result;
1205
1221
  try {
1206
- result = await transform.run(source.y, params);
1222
+ result = await transform.run(source.y, params, context);
1207
1223
  } catch (error) {
1208
1224
  onError?.({ transformId, traceName: source.name, error });
1209
1225
  continue;
@@ -1284,22 +1300,43 @@ function renderSeriesTransformControls(container, subplot, seriesTransforms, { o
1284
1300
  row.className = "twm-series-transforms__row";
1285
1301
  const text = document.createElement("span");
1286
1302
  text.textContent = spec?.label ?? name;
1303
+ const writeParam = (value) => {
1304
+ const state = stateOf(transformId);
1305
+ const params = { ...state.params ?? {} };
1306
+ if (value === void 0) delete params[name];
1307
+ else params[name] = value;
1308
+ state.params = params;
1309
+ onChange?.();
1310
+ };
1311
+ const saved = subplot.transforms?.[transformId]?.params?.[name];
1312
+ if (isChoice(spec)) {
1313
+ const current2 = saved ?? spec.default ?? firstChoice(spec, subplot);
1314
+ const choice = selectBox(choiceOptions(spec, subplot), current2, {
1315
+ label: spec?.label ?? name,
1316
+ valueAttr: "value",
1317
+ className: "twm-series-transforms__param twm-series-transforms__select"
1318
+ });
1319
+ choice.box.addEventListener("change", () => {
1320
+ const value = choice.valueOf();
1321
+ choice.setOptions(choiceOptions(spec, subplot), value);
1322
+ writeParam(value);
1323
+ });
1324
+ paramInputs.push(choice.box);
1325
+ row.append(text, choice.box);
1326
+ group.appendChild(row);
1327
+ continue;
1328
+ }
1287
1329
  const input = document.createElement("input");
1288
1330
  input.type = "number";
1289
1331
  input.className = "twm-series-transforms__param";
1290
1332
  for (const attr of ["min", "max", "step"]) {
1291
1333
  if (spec?.[attr] != null) input[attr] = String(spec[attr]);
1292
1334
  }
1293
- const current = subplot.transforms?.[transformId]?.params?.[name] ?? spec?.default;
1335
+ const current = saved ?? spec?.default;
1294
1336
  input.value = current == null ? "" : String(current);
1295
1337
  input.addEventListener("change", () => {
1296
1338
  const value = parseFloat(input.value);
1297
- const state = stateOf(transformId);
1298
- const params = { ...state.params ?? {} };
1299
- if (Number.isFinite(value)) params[name] = value;
1300
- else delete params[name];
1301
- state.params = params;
1302
- onChange?.();
1339
+ writeParam(Number.isFinite(value) ? value : void 0);
1303
1340
  });
1304
1341
  paramInputs.push(input);
1305
1342
  row.append(text, input);
@@ -1427,6 +1464,7 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
1427
1464
  const yRefByAxisId = {};
1428
1465
  let ownCount = 0;
1429
1466
  let hiddenCount = 0;
1467
+ const seriesByTrace = /* @__PURE__ */ new Map();
1430
1468
  const useX = sp.xAxis && sp.xAxis.useTime === false && sp.xAxis.variable;
1431
1469
  const xData = useX ? opts.resolveSeries({ variable: sp.xAxis.variable }, data, sp)?.y ?? time : time;
1432
1470
  const yAxes = sp.yAxes ?? [];
@@ -1461,7 +1499,7 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
1461
1499
  if (!resolved?.y) continue;
1462
1500
  const x = resolved.x ?? xData;
1463
1501
  const y = Array.isArray(resolved.y) ? applyNanHandling(resolved.y, sp.nanHandling ?? "gap", x) : resolved.y;
1464
- traces.push(buildTrace({
1502
+ const trace = buildTrace({
1465
1503
  chartType: sp.chartType || "line",
1466
1504
  x,
1467
1505
  y,
@@ -1473,7 +1511,9 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
1473
1511
  yAxisId: yRef,
1474
1512
  showMarkers: sp.showDataPoints ?? false,
1475
1513
  stackGroup: s.stackGroup ?? null
1476
- }));
1514
+ });
1515
+ traces.push(trace);
1516
+ seriesByTrace.set(trace, s);
1477
1517
  const overlay = s.overlay ? opts.overlays?.[s.id] : null;
1478
1518
  if (!overlay?.years?.length) continue;
1479
1519
  const ov = s.overlay;
@@ -1503,12 +1543,12 @@ function renderPlotGrid(container, plotConfig, results, options = {}) {
1503
1543
  traces.push(ovTrace);
1504
1544
  }
1505
1545
  });
1506
- traces.push(...await deriveSeriesTraces(
1507
- traces,
1508
- sp,
1509
- opts.seriesTransforms,
1510
- { onError: opts.onTransformError ?? void 0 }
1511
- ));
1546
+ traces.push(...await deriveSeriesTraces(traces, sp, opts.seriesTransforms, {
1547
+ onError: opts.onTransformError ?? void 0,
1548
+ seriesOf: (trace) => seriesByTrace.get(trace) ?? null,
1549
+ results: data,
1550
+ resolveSeries: opts.resolveSeries
1551
+ }));
1512
1552
  if (opts.derivedTraces) {
1513
1553
  const extra = await opts.derivedTraces(traces, sp, { results: data, index });
1514
1554
  if (Array.isArray(extra)) traces.push(...extra);
@@ -1667,7 +1707,8 @@ var PlotPopoutWindow = class {
1667
1707
  * renders the Config tab. What it returns is disposed (`dispose()`) when the
1668
1708
  * window closes, so an editor drawn there (renderPlotConfigEditor) goes with it.
1669
1709
  * @param {Object} [options.seriesTransforms] - `{ [id]: transform }` (series_transforms.js);
1670
- * absent => no derived series are drawn
1710
+ * absent => no derived series are drawn. Each runs as
1711
+ * `run(y, params, { series, subplot, results, resolveSeries })` (renderPlotGrid).
1671
1712
  * @param {string} [options.refreshEvent='data:changed'] - Bus event after which the
1672
1713
  * window re-reads `getResults()` and redraws.
1673
1714
  * @param {{delta?: string|null, complete?: string|null}} [options.streamEvents] - Bus
@@ -2799,4 +2840,4 @@ export {
2799
2840
  openPlotPopoutWindow,
2800
2841
  openRawTracesWindow
2801
2842
  };
2802
- //# sourceMappingURL=chunk-RMTRWWEO.js.map
2843
+ //# sourceMappingURL=chunk-EIYNF4BD.js.map