@ai-matrx/design-system 0.49.54 → 0.49.55

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 (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/data-table/copy-helpers.d.cts +2 -2
  3. package/dist/data-table/copy-helpers.d.ts +2 -2
  4. package/dist/data-table/drill-chart.cjs +44 -6
  5. package/dist/data-table/drill-chart.cjs.map +1 -1
  6. package/dist/data-table/drill-chart.d.cts +8 -3
  7. package/dist/data-table/drill-chart.d.ts +8 -3
  8. package/dist/data-table/drill-chart.js +44 -6
  9. package/dist/data-table/drill-chart.js.map +1 -1
  10. package/dist/data-table/filter-engine.d.cts +2 -2
  11. package/dist/data-table/filter-engine.d.ts +2 -2
  12. package/dist/data-table/host.d.cts +2 -2
  13. package/dist/data-table/host.d.ts +2 -2
  14. package/dist/data-table/index.cjs +116 -40
  15. package/dist/data-table/index.cjs.map +1 -1
  16. package/dist/data-table/index.d.cts +28 -6
  17. package/dist/data-table/index.d.ts +28 -6
  18. package/dist/data-table/index.js +116 -40
  19. package/dist/data-table/index.js.map +1 -1
  20. package/dist/data-table/infer-filter.d.cts +2 -2
  21. package/dist/data-table/infer-filter.d.ts +2 -2
  22. package/dist/data-table/layered-filters.d.cts +2 -2
  23. package/dist/data-table/layered-filters.d.ts +2 -2
  24. package/dist/data-table/query-control.d.cts +2 -2
  25. package/dist/data-table/query-control.d.ts +2 -2
  26. package/dist/data-table/types.d.cts +2 -2
  27. package/dist/data-table/types.d.ts +2 -2
  28. package/dist/data-table/url-state.d.cts +2 -2
  29. package/dist/data-table/url-state.d.ts +2 -2
  30. package/dist/data-table/xlsx.d.cts +2 -2
  31. package/dist/data-table/xlsx.d.ts +2 -2
  32. package/dist/{drill-cISpem5b.d.cts → drill-qJ5gHL2F.d.cts} +11 -0
  33. package/dist/{drill-cISpem5b.d.ts → drill-qJ5gHL2F.d.ts} +11 -0
  34. package/dist/{layered-filters-ma_P0GDG.d.ts → layered-filters-CTdPPgxf.d.ts} +1 -1
  35. package/dist/{layered-filters-RHe2Kzrs.d.cts → layered-filters-HFquDviC.d.cts} +1 -1
  36. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,10 +2,18 @@
2
2
 
3
3
  ### Unreleased
4
4
 
5
+ - **The grouped answer shows a group's attributes at a glance, says what each number counts, sorts by a pivot column, and the chart stacks Measures** (lane DRILL-FLIP-FIXES, VERIFY-DRILL-FINAL R2, R5, L2, L3, L5 — the old Spend and users usage screens' last did-better rows, 2026-10-01). `MatrxDrillAnswerTable` takes `attributes` (`MatrxDrillAttribute`: text columns beside the numbers, each the one value an outermost group holds of another Dimension — the costliest requests' person, agent, model; `read` returns undefined while reading, null for none); a Measure's or a Dimension's `description` (≤140 characters) is its column header's tooltip; each named pivot column's header sorts the groups by its own cell (`drillPivotSortKey(measure, value)` = `across:<measure>:<value>`, read by `buildDrillTree`). `MatrxDrillChart` / `drillChartQuestions` take `stack` (two or more additive Measures drawn as one stacked bar per period, no split; a Measure that does not add up is refused in words; `MatrxDrillChartPlan.stack`). Proof: `drill-flip-fixes.test.tsx` (7; red on HEAD). Consumer action: none required; a host passes `attributes`, `description` and `stack` to adopt.
6
+
5
7
  - **The grouped answer searches, opens a group's record, keeps a value's colour, and reads a moment** (lane DRILL-PARITY-LAST, the old users usage page's did-better rows, 2026-09-30). `MatrxDrillAnswerTable` takes `search` (a box that keeps the groups whose words hold the text — a person's email, a choice's words; an outer group stays when one of its own matches; "N of M groups"); a Dimension's `entity` (a record kind) makes each group row offer that record's own doors — open, preview and `TableDoors.menu` (new, the record kind's own menu items from the host's `resolveEntityDoors`) — in the row's ⋯ menu and on a right-click through the host's `ContextMenuBoundary` (rows carry `data-row-id`, the table its `data-matrx-table-id`); a Dimension's `colorFor` (a chart token, `drillTokenColor` / `drillValueColor`) colours that value's share bar and its chart series; a Measure's `moment` (e.g. "Last active", epoch ms) never shows a share or a change. Proof: `drill-parity-last.test.tsx` (9; 8 red on HEAD). Consumer action: none required; a host passes `search`, `entity`, `colorFor`, `moment` and `TableDoors.menu` to adopt.
6
8
  - **The drill answer's coverage and Pareto lines are labels, not sentences** (lane DRILL-LIVE-FIXES, interface text is layout, 2026-09-30). Coverage reads `$100.00 of $400.00 (25%)` (was "This slice is $100.00 of the $400.00 the whole window holds (25%)."), with "This slice's share of the whole window" in its tooltip; a Pareto line that never reaches its share reads `All 5 shown make 20% of Cost` (the groups past the limit are already the table's own "N more" row). Proof: `drill-usage-adoptions.test.tsx`, `drill-pareto-and-row-actions.test.tsx`. Consumer action: none.
7
9
  - **The drill chart's value axis says the number the Measure printed, never the raw value** (localhost walk of `/administration/usage`, 2026-09-30). When a Measure's format converts its unit (the usage explorer stores dollars and prints points at 20,000 a dollar) and the printed tick is too long for the axis, the tick used to compact the RAW value, so a 9,000,000-point day read `450` under "Cost (points) by day". `drillChartAxisNumber` now compacts the number the format printed (`9.0M`), keeping a one-character currency sign (`$1.2k`). Proof: `drill-chart-axis.test.ts`. Consumer action: none.
8
10
 
11
+ ## 0.49.55
12
+
13
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
14
+ `git diff npm/design-system/v0.49.54..npm/design-system/v0.49.55 -- apps/shared/design-system`).
15
+ No source changes intended and no consumer action required.
16
+
9
17
  ## 0.49.54
10
18
 
11
19
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.cjs';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../layered-filters-RHe2Kzrs.cjs';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../layered-filters-HFquDviC.cjs';
3
3
  import '../content-transfer.cjs';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -7,7 +7,7 @@ import '../content-transfer/icon.cjs';
7
7
  import './table-style.cjs';
8
8
  import '../types-JnZD8wM0.cjs';
9
9
  import './grouping.cjs';
10
- import '../drill-cISpem5b.cjs';
10
+ import '../drill-qJ5gHL2F.cjs';
11
11
  import './fill.cjs';
12
12
  import './facets.cjs';
13
13
  import './spreadsheet.cjs';
@@ -1,5 +1,5 @@
1
1
  import { AgentPayloadInput } from './copy-types.js';
2
- import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../layered-filters-ma_P0GDG.js';
2
+ import { g as MatrxDataTableCopyConfig, M as MatrxColumnDef } from '../layered-filters-CTdPPgxf.js';
3
3
  import '../content-transfer.js';
4
4
  import 'react';
5
5
  import '@ai-matrx/kit/content-transfer';
@@ -7,7 +7,7 @@ import '../content-transfer/icon.js';
7
7
  import './table-style.js';
8
8
  import '../types-JnZD8wM0.js';
9
9
  import './grouping.js';
10
- import '../drill-cISpem5b.js';
10
+ import '../drill-qJ5gHL2F.js';
11
11
  import './fill.js';
12
12
  import './facets.js';
13
13
  import './spreadsheet.js';
@@ -310,9 +310,16 @@ function drillChartQuestions(question, seriesLimit = MATRX_DRILL_CHART_SERIES_LI
310
310
  const timeInBy = question.by.find((ref) => parseDimensionRef(ref).grain !== null);
311
311
  const firstTime = context.dimensions.find((d) => d.kind === "time");
312
312
  const time = timeInBy ?? context.time ?? (firstTime ? `${firstTime.key}:${drillAutoGrain(question.window ?? null, context.now, firstTime.grains ?? MATRX_DRILL_DEFAULT_GRAINS)}` : null);
313
- const split = question.by.find((ref) => parseDimensionRef(ref).grain === null && dimensionFor(context.dimensions, ref)?.kind !== "time") ?? null;
313
+ const stack = context.stack && context.stack.length >= 2 ? [...context.stack] : null;
314
+ const split = stack ? null : question.by.find((ref) => parseDimensionRef(ref).grain === null && dimensionFor(context.dimensions, ref)?.kind !== "time") ?? null;
314
315
  const refuse = (sentence) => ({ ...base, time, split, refused: sentence, first: [], periodsHint: null });
315
316
  if (!time) return refuse("This answer has no time to chart it over.");
317
+ if (stack) {
318
+ const missing = stack.find((k) => !context.measures.some((m) => m.key === k));
319
+ if (missing) return refuse(`There is no Measure "${missing}" to chart.`);
320
+ const loose = stack.map((k) => context.measures.find((m) => m.key === k)).find((m) => !m.additive || m.moment);
321
+ if (loose) return refuse(`${loose.label} does not add up, so it cannot be stacked.`);
322
+ }
316
323
  if (!measure) return refuse(`There is no Measure "${measureKey}" to chart.`);
317
324
  if (split && !measure.additive) {
318
325
  return refuse(
@@ -325,13 +332,13 @@ function drillChartQuestions(question, seriesLimit = MATRX_DRILL_CHART_SERIES_LI
325
332
  const periodsHint = Number.isFinite(days) && days > 0 ? Math.ceil(days / GRAIN_DAYS[grain]) + 1 : null;
326
333
  const where = trailWhere(question);
327
334
  const window = question.window ?? null;
328
- const show = [measureKey];
335
+ const show = stack ?? [measureKey];
329
336
  const first = [];
330
337
  if (split) {
331
338
  first.push({ key: "series", by: [split], where, show, sort: { key: measureKey, direction: "desc" }, limit, window });
332
339
  }
333
340
  first.push({ key: "periods", by: [time], where, show, sort: null, limit: periodsHint ? clampLimit(periodsHint, ceiling) : null, window });
334
- return { ...base, time, split, refused: null, first, periodsHint };
341
+ return { ...base, ...stack ? { measure: stack[0], stack } : {}, time, split, refused: null, first, periodsHint };
335
342
  }
336
343
  function drillChartCellsQuestion(plan, question, series) {
337
344
  if (plan.refused || !plan.split || !plan.time) return null;
@@ -405,6 +412,35 @@ function drillChartData(plan, answers, dimensions, measures, emptyLabel = "No va
405
412
  values: {}
406
413
  };
407
414
  });
415
+ if (plan.stack) {
416
+ const keys = plan.stack;
417
+ const readOf = (row, k) => {
418
+ const v = row?.measures[k];
419
+ return v === null || v === void 0 ? null : v;
420
+ };
421
+ const totalRow = periodRows.find((r) => rowKind(r) === "total");
422
+ for (const [i, p] of periodBase.entries()) {
423
+ const row = groups[i];
424
+ let sum = null;
425
+ for (const k of keys) {
426
+ const v = readOf(row, k);
427
+ p.values[`m:${k}`] = v;
428
+ if (v !== null) sum = (sum ?? 0) + v;
429
+ }
430
+ p.total = sum;
431
+ }
432
+ const totals = keys.map((k) => readOf(totalRow, k));
433
+ const total = totals.every((t) => t === null) ? null : totals.reduce((a, t) => a + (t ?? 0), 0);
434
+ return {
435
+ time,
436
+ split: null,
437
+ measure: m,
438
+ series: keys.map((k, i) => ({ key: `m:${k}`, value: null, label: measures.find((x) => x.key === k)?.label ?? k, other: false, total: totals[i] ?? null })),
439
+ periods: periodBase,
440
+ total,
441
+ says
442
+ };
443
+ }
408
444
  if (!split) {
409
445
  const key = "all";
410
446
  for (const p of periodBase) p.values[key] = p.total;
@@ -531,6 +567,7 @@ function MatrxDrillChart({
531
567
  answers,
532
568
  seriesLimit = MATRX_DRILL_CHART_SERIES_LIMIT,
533
569
  measure: measureKey,
570
+ stack,
534
571
  time,
535
572
  ceiling,
536
573
  paths = NO_PATHS,
@@ -542,8 +579,8 @@ function MatrxDrillChart({
542
579
  className
543
580
  }) {
544
581
  const plan = (0, import_react.useMemo)(
545
- () => drillChartQuestions(question, seriesLimit, { dimensions, measures, measure: measureKey, time, ceiling }),
546
- [question, seriesLimit, dimensions, measures, measureKey, time, ceiling]
582
+ () => drillChartQuestions(question, seriesLimit, { dimensions, measures, measure: measureKey, stack, time, ceiling }),
583
+ [question, seriesLimit, dimensions, measures, measureKey, stack, time, ceiling]
547
584
  );
548
585
  const data = (0, import_react.useMemo)(() => drillChartData(plan, answers, dimensions, measures, emptyLabel), [plan, answers, dimensions, measures, emptyLabel]);
549
586
  const measure = measures.find((m) => m.key === plan.measure);
@@ -551,7 +588,8 @@ function MatrxDrillChart({
551
588
  const [hover, setHover] = (0, import_react.useState)(null);
552
589
  const segmentClicked = (0, import_react.useRef)(false);
553
590
  const plotRef = (0, import_react.useRef)(null);
554
- const title = plan.time ? `${measure?.label ?? plan.measure} by ${dimensionRefLabel(dimensions, plan.time).replace(/^.* by /, "")}${plan.split ? `, split by ${dimensionRefLabel(dimensions, plan.split)}` : ""}` : measure?.label ?? plan.measure;
591
+ const measureWords = plan.stack ? plan.stack.map((k) => measures.find((m) => m.key === k)?.label ?? k).join(" and ") : measure?.label ?? plan.measure;
592
+ const title = plan.time ? `${measureWords} by ${dimensionRefLabel(dimensions, plan.time).replace(/^.* by /, "")}${plan.split ? `, split by ${dimensionRefLabel(dimensions, plan.split)}` : ""}` : measureWords;
555
593
  const frame = (body) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { "data-matrx-drill-chart": true, "aria-label": title, className: cn("flex min-w-0 flex-col gap-1.5 px-2 py-2", className), children: [
556
594
  note ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { "data-matrx-drill-chart-note": true, className: "text-xs text-muted-foreground", children: note }) : null,
557
595
  body