@milaboratories/miplots4 1.2.1 → 1.2.3

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 (92) hide show
  1. package/dist/DataFrame.d.ts.map +1 -1
  2. package/dist/DataFrame.js +2 -1
  3. package/dist/DataFrame.js.map +1 -1
  4. package/dist/common/ContinuousGrid.d.ts.map +1 -1
  5. package/dist/common/ContinuousGrid.js +90 -56
  6. package/dist/common/ContinuousGrid.js.map +1 -1
  7. package/dist/histogram/ChartRenderer.d.ts.map +1 -1
  8. package/dist/histogram/ChartRenderer.js +6 -0
  9. package/dist/histogram/ChartRenderer.js.map +1 -1
  10. package/dist/histogram/HistogramSettingsImpl.d.ts +5 -1
  11. package/dist/histogram/HistogramSettingsImpl.d.ts.map +1 -1
  12. package/dist/histogram/HistogramSettingsImpl.js.map +1 -1
  13. package/dist/scatterplot/components/Grid.d.ts.map +1 -1
  14. package/dist/scatterplot/components/Grid.js +109 -71
  15. package/dist/scatterplot/components/Grid.js.map +1 -1
  16. package/dist/selection/ChartRenderer.d.ts.map +1 -1
  17. package/dist/selection/ChartRenderer.js +9 -11
  18. package/dist/selection/ChartRenderer.js.map +1 -1
  19. package/dist/selection/SelectionSettingsImpl.d.ts +0 -1
  20. package/dist/selection/SelectionSettingsImpl.d.ts.map +1 -1
  21. package/dist/selection/SelectionSettingsImpl.js +0 -1
  22. package/dist/selection/SelectionSettingsImpl.js.map +1 -1
  23. package/dist/selection/components/Chart/Bars/GroupSelectedBars.js +1 -1
  24. package/dist/selection/components/Chart/Bars/GroupSelectedBars.js.map +1 -1
  25. package/dist/selection/components/Chart/Bars/StackedBars.d.ts.map +1 -1
  26. package/dist/selection/components/Chart/Bars/StackedBars.js +11 -11
  27. package/dist/selection/components/Chart/Bars/StackedBars.js.map +1 -1
  28. package/dist/selection/components/Chart/ChartAxes.d.ts.map +1 -1
  29. package/dist/selection/components/Chart/ChartAxes.js +7 -8
  30. package/dist/selection/components/Chart/ChartAxes.js.map +1 -1
  31. package/dist/selection/components/Chart/ChartFrame.d.ts +1 -2
  32. package/dist/selection/components/Chart/ChartFrame.d.ts.map +1 -1
  33. package/dist/selection/components/Chart/ChartFrame.js +8 -8
  34. package/dist/selection/components/Chart/ChartFrame.js.map +1 -1
  35. package/dist/selection/components/Chart/Gridlines.d.ts +2 -6
  36. package/dist/selection/components/Chart/Gridlines.d.ts.map +1 -1
  37. package/dist/selection/components/Chart/Gridlines.js +14 -26
  38. package/dist/selection/components/Chart/Gridlines.js.map +1 -1
  39. package/dist/selection/components/Chart/Ribbons/GroupFlowRibbon.js +1 -1
  40. package/dist/selection/components/Chart/Ribbons/GroupFlowRibbon.js.map +1 -1
  41. package/dist/selection/components/Chart/Ribbons/StackedGroupFlowRibbon.d.ts.map +1 -1
  42. package/dist/selection/components/Chart/Ribbons/StackedGroupFlowRibbon.js +27 -27
  43. package/dist/selection/components/Chart/Ribbons/StackedGroupFlowRibbon.js.map +1 -1
  44. package/dist/selection/components/Chart/SelectionXAxis.d.ts +18 -0
  45. package/dist/selection/components/Chart/SelectionXAxis.d.ts.map +1 -0
  46. package/dist/selection/components/Chart/SelectionXAxis.js +47 -0
  47. package/dist/selection/components/Chart/SelectionXAxis.js.map +1 -0
  48. package/dist/selection/components/Chart/types.d.ts +0 -1
  49. package/dist/selection/components/Chart/types.d.ts.map +1 -1
  50. package/dist/selection/components/Chart.d.ts.map +1 -1
  51. package/dist/selection/components/Chart.js +7 -12
  52. package/dist/selection/components/Chart.js.map +1 -1
  53. package/dist/selection/components/ChartsGroup.d.ts.map +1 -1
  54. package/dist/selection/components/ChartsGroup.js +73 -72
  55. package/dist/selection/components/ChartsGroup.js.map +1 -1
  56. package/dist/selection/components/types.d.ts +0 -1
  57. package/dist/selection/components/types.d.ts.map +1 -1
  58. package/dist/selection/constants.d.ts +1 -2
  59. package/dist/selection/constants.d.ts.map +1 -1
  60. package/dist/selection/constants.js +2 -2
  61. package/dist/selection/constants.js.map +1 -1
  62. package/dist/selection/createSelectionData.d.ts +2 -6
  63. package/dist/selection/createSelectionData.d.ts.map +1 -1
  64. package/dist/selection/createSelectionData.js +45 -59
  65. package/dist/selection/createSelectionData.js.map +1 -1
  66. package/dist/types/bubble.d.ts +8 -8
  67. package/dist/types/common.d.ts +62 -6
  68. package/dist/types/common.d.ts.map +1 -1
  69. package/dist/types/common.js +15 -6
  70. package/dist/types/common.js.map +1 -1
  71. package/dist/types/dendro.d.ts +6 -6
  72. package/dist/types/discrete.d.ts +41 -13
  73. package/dist/types/discrete.d.ts.map +1 -1
  74. package/dist/types/heatmap.d.ts +4 -4
  75. package/dist/types/histogram.d.ts +59 -10
  76. package/dist/types/histogram.d.ts.map +1 -1
  77. package/dist/types/histogram.js +60 -66
  78. package/dist/types/histogram.js.map +1 -1
  79. package/dist/types/scatterplot-umap.d.ts +74 -18
  80. package/dist/types/scatterplot-umap.d.ts.map +1 -1
  81. package/dist/types/scatterplot.d.ts +74 -18
  82. package/dist/types/scatterplot.d.ts.map +1 -1
  83. package/dist/types/selection.d.ts +8 -13
  84. package/dist/types/selection.d.ts.map +1 -1
  85. package/dist/types/selection.js +0 -1
  86. package/dist/types/selection.js.map +1 -1
  87. package/dist/utils/getChartEdgeSides.d.ts +1 -1
  88. package/dist/utils/significantLinesStyle.d.ts +8 -0
  89. package/dist/utils/significantLinesStyle.d.ts.map +1 -0
  90. package/dist/utils/significantLinesStyle.js +12 -0
  91. package/dist/utils/significantLinesStyle.js.map +1 -0
  92. package/package.json +1 -1
@@ -3,12 +3,12 @@ import { Tooltip as t, useTooltip as n } from "../../common/Tooltip.js";
3
3
  import { ErrorBoundary as r } from "../../common/ErrorBoundary.js";
4
4
  import { ChartsTitle as i } from "../../common/ChartsTitle.js";
5
5
  import { Legend as a } from "../../common/Legend.js";
6
- import { getChartEdgeSides as o } from "../../utils/getChartEdgeSides.js";
7
- import { BAR_WIDTH_RATIO as s } from "../constants.js";
8
- import { formatCount as c } from "../utils.js";
9
- import { FacetCell as ee } from "./FacetCell.js";
10
- import { scaleBand as te, scaleLinear as l } from "d3-scale";
11
- import { useCallback as u, useEffect as ne, useMemo as d, useRef as re, useState as f } from "react";
6
+ import { getChartEdgeSides as ee } from "../../utils/getChartEdgeSides.js";
7
+ import { BAR_WIDTH_RATIO as o } from "../constants.js";
8
+ import { formatCount as s } from "../utils.js";
9
+ import { FacetCell as te } from "./FacetCell.js";
10
+ import { scaleBand as c, scaleLinear as ne } from "d3-scale";
11
+ import { useCallback as l, useEffect as u, useMemo as d, useRef as re, useState as f } from "react";
12
12
  import { jsx as p, jsxs as m } from "react/jsx-runtime";
13
13
  var h = (e, t) => ({
14
14
  col: e % t,
@@ -17,71 +17,73 @@ var h = (e, t) => ({
17
17
  function g(e, t) {
18
18
  return e ? e === "null" ? t ?? "N/A" : e : "All";
19
19
  }
20
- function _({ facetData: _, gridLayout: ie, xAxis: v, yAxis: y, style: b, title: x, legend: S, showFacetLabels: ae, grouped: oe, orderedGroupNames: C }) {
21
- let { faceted: w, facetLabels: T, facetTitles: E } = _, { nRows: D, nCols: O, totalWidth: k, totalHeight: A, margins: j, cellPlotWidth: M, cellPlotHeight: N, cellLabelHeight: P, topPad: F, rightPad: I, colLeftPads: L, rowBottomPads: R, colXOffsets: z, rowYOffsets: B, sharedX: V, sharedY: H } = ie, { nullValueLabel: U } = b, W = n(), [G, K] = f(null), [q, se] = f(null), [ce, le] = f(), J = re(null);
22
- ne(() => {
20
+ function _({ facetData: _, gridLayout: ie, xAxis: v, yAxis: y, style: b, title: x, legend: S, showFacetLabels: C, grouped: w, orderedGroupNames: ae }) {
21
+ let { faceted: T, facetLabels: E, facetTitles: D } = _, { nRows: O, nCols: k, totalWidth: A, totalHeight: j, margins: M, cellPlotWidth: N, cellPlotHeight: P, cellLabelHeight: F, topPad: I, rightPad: L, colLeftPads: R, rowBottomPads: z, colXOffsets: B, rowYOffsets: V, sharedX: H, sharedY: U } = ie, { nullValueLabel: W } = b, G = n(), [K, q] = f(null), [oe, se] = f(null), [ce, le] = f(), J = re(null);
22
+ u(() => {
23
23
  J.current && le(J.current);
24
24
  }, []);
25
- let Y = u((e) => {
26
- se((t) => t === e ? null : e), K(null), W.onClose();
27
- }, [W]), X = k - j.left - j.right, ue = d(() => l().domain([0, 100]).range([N, 0]), [N]), Z = d(() => {
28
- let e = w.byFacet[w.facetKeys[0]];
25
+ let Y = l((e) => {
26
+ se((t) => t === e ? null : e), q(null), G.onClose();
27
+ }, [G]), X = A - M.left - M.right, ue = d(() => ne().domain([0, 100]).range([P, 0]), [P]), Z = d(() => {
28
+ let e = T.byFacet[T.facetKeys[0]];
29
29
  return e ? e.stages.map((e, t) => String(t)) : [];
30
- }, [w]), de = d(() => {
31
- let e = te().domain(Z).range([0, M]), t = Z.length;
30
+ }, [T]), de = d(() => {
31
+ let e = c().domain(Z).range([0, N]), t = Z.length;
32
32
  if (t > 0) {
33
- let n = Math.min(80, Math.max(12, M / t * s)), r = M / t, i = r > 0 ? Math.max(0, Math.min(.95, 1 - n / r)) : 0;
33
+ let n = Math.min(80, Math.max(12, N / t * o)), r = N / t, i = r > 0 ? Math.max(0, Math.min(.95, 1 - n / r)) : 0;
34
34
  e.paddingInner(i), e.paddingOuter(i / 2);
35
35
  }
36
36
  return e;
37
- }, [M, Z]), fe = j.left + X - I + 20, Q = j.top + F + P, pe = d(() => {
38
- let e = W.selectedData;
37
+ }, [N, Z]), fe = M.left + X - L + 20, Q = M.top + I + F, pe = d(() => {
38
+ let e = G.selectedData;
39
39
  if (!e) return [];
40
- let t = e.section === "preserved" ? "Preserved" : "Discarded", n = g(e.groupName, U), r = (T[W.currentFacet] ?? []).join(" / ");
41
- return [`${r ? `${r} · ${n}` : n} — ${t}`, `${c(e.count)} / ${c(e.total)} (${e.percent.toFixed(1)}%)`];
40
+ let t = e.section === "preserved" ? "Preserved" : "Discarded", n = w ? g(e.groupName, W) : void 0, r = [(E[G.currentFacet] ?? []).join(" / "), n].filter(Boolean).join(" · ");
41
+ return [r ? `${r} ${t}` : t, `${s(e.count)} / ${s(e.total)} (${e.percent.toFixed(1)}%)`];
42
42
  }, [
43
- W.selectedData,
44
- W.currentFacet,
45
- T
46
- ]), $ = W.selectedData, me = d(() => {
43
+ G.selectedData,
44
+ G.currentFacet,
45
+ E,
46
+ w,
47
+ W
48
+ ]), $ = G.selectedData, me = d(() => {
47
49
  if (!$) return {
48
50
  left: 0,
49
51
  right: 0,
50
52
  top: 0,
51
53
  bottom: 0
52
54
  };
53
- let { col: e, row: t } = h(w.facetKeys.indexOf(W.currentFacet), O), n = j.left + z[e], r = j.top + B[t], i = n + L[e], a = r + F + P;
55
+ let { col: e, row: t } = h(T.facetKeys.indexOf(G.currentFacet), k), n = M.left + B[e], r = M.top + V[t], i = n + R[e], a = r + I + F;
54
56
  return {
55
57
  left: i,
56
- right: k - i,
58
+ right: A - i,
57
59
  top: a,
58
- bottom: A - a
60
+ bottom: j - a
59
61
  };
60
62
  }, [
61
63
  $,
62
- W.currentFacet,
63
- w.facetKeys,
64
- O,
65
- j,
66
- z,
64
+ G.currentFacet,
65
+ T.facetKeys,
66
+ k,
67
+ M,
67
68
  B,
68
- L,
69
+ V,
70
+ R,
71
+ I,
69
72
  F,
70
- P,
71
- k,
72
- A
73
- ]), he = $?.x ?? 0, ge = $?.y ?? 0, _e = w.facetKeys.length;
73
+ A,
74
+ j
75
+ ]), he = $?.x ?? 0, ge = $?.y ?? 0, _e = T.facetKeys.length;
74
76
  return /* @__PURE__ */ p(r, {
75
77
  dataId: "selection-chart",
76
78
  children: /* @__PURE__ */ m("svg", {
77
79
  xmlns: "http://www.w3.org/2000/svg",
78
- viewBox: `0 0 ${k} ${A}`,
79
- width: k,
80
- height: A,
80
+ viewBox: `0 0 ${A} ${j}`,
81
+ width: A,
82
+ height: j,
81
83
  children: [
82
84
  /* @__PURE__ */ p("defs", { children: e }),
83
85
  /* @__PURE__ */ m("g", {
84
- transform: `translate(${j.left},${j.top})`,
86
+ transform: `translate(${M.left},${M.top})`,
85
87
  fontFamily: "Manrope",
86
88
  children: [/* @__PURE__ */ p(i, {
87
89
  title: x.text,
@@ -90,58 +92,57 @@ function _({ facetData: _, gridLayout: ie, xAxis: v, yAxis: y, style: b, title:
90
92
  leftPadding: 0,
91
93
  rightPadding: 0,
92
94
  chartsWidth: X
93
- }), w.facetKeys.map((e, t) => {
94
- let { col: n, row: r } = h(t, O), i = w.byFacet[e];
95
+ }), T.facetKeys.map((e, t) => {
96
+ let { col: n, row: r } = h(t, k), i = T.byFacet[e];
95
97
  if (!i) return null;
96
- let a = o(t, _e, O, D), s = a.includes("left"), c = a.includes("bottom");
98
+ let a = ee(t, _e, k, O), o = a.includes("left"), s = a.includes("bottom");
97
99
  return /* @__PURE__ */ p("g", {
98
- transform: `translate(${z[n]},${B[r]})`,
99
- children: /* @__PURE__ */ p(ee, {
100
+ transform: `translate(${B[n]},${V[r]})`,
101
+ children: /* @__PURE__ */ p(te, {
100
102
  titleStrip: {
101
- lines: E[e] ?? [],
102
- show: ae,
103
- x: L[n],
104
- height: P
103
+ lines: D[e] ?? [],
104
+ show: C,
105
+ x: R[n],
106
+ height: F
105
107
  },
106
108
  data: i,
107
- grouped: oe,
108
- orderedGroupNames: C,
109
+ grouped: w,
110
+ orderedGroupNames: ae,
109
111
  layout: {
110
- width: M,
111
- height: N,
112
+ width: N,
113
+ height: P,
112
114
  dimensionsLeft: 0,
113
- dimensionsTop: P,
114
- paddingLeft: L[n],
115
- paddingTop: F,
116
- paddingBottom: R[r],
115
+ dimensionsTop: F,
116
+ paddingLeft: R[n],
117
+ paddingTop: I,
118
+ paddingBottom: z[r],
117
119
  xScale: de,
118
120
  yScale: ue,
119
- showXAxis: !V || c,
120
- showYAxis: !H || s
121
+ showXAxis: !H || s,
122
+ showYAxis: !U || o
121
123
  },
122
124
  xAxis: {
123
125
  labels: v.labelsByFacet[e],
124
126
  counts: v.countsByFacet[e],
125
127
  countsFontSize: v.countsFontSize,
126
128
  labelsPosition: v.labelsPosition,
127
- showGrid: v.showGrid,
128
129
  showTicks: v.showTicks,
129
130
  hiddenLabels: v.hiddenLabels,
130
- title: c ? v.title : ""
131
+ title: s ? v.title : ""
131
132
  },
132
133
  yAxis: {
133
134
  showGrid: y.showGrid,
134
135
  showTicks: y.showTicks,
135
136
  hiddenLabels: y.hiddenLabels,
136
- title: s ? y.title : ""
137
+ title: o ? y.title : ""
137
138
  },
138
139
  style: b,
139
140
  interaction: {
140
- hoveredGroup: G,
141
- selectedGroup: q,
141
+ hoveredGroup: K,
142
+ selectedGroup: oe,
142
143
  onGroupClick: Y,
143
- onGroupHover: K,
144
- tooltipsData: W,
144
+ onGroupHover: q,
145
+ tooltipsData: G,
145
146
  facetKey: e
146
147
  }
147
148
  })
@@ -153,19 +154,19 @@ function _({ facetData: _, gridLayout: ie, xAxis: v, yAxis: y, style: b, title:
153
154
  fontFamily: "Manrope",
154
155
  children: /* @__PURE__ */ p(a, {
155
156
  legendData: S,
156
- onItemHover: K,
157
+ onItemHover: q,
157
158
  onItemClick: Y
158
159
  })
159
160
  }),
160
- W.active && /* @__PURE__ */ p(t, {
161
+ G.active && /* @__PURE__ */ p(t, {
161
162
  content: pe,
162
163
  x: he,
163
164
  y: ge,
164
- active: W.active,
165
- fixed: W.fixed,
165
+ active: G.active,
166
+ fixed: G.fixed,
166
167
  container: ce,
167
168
  sideDistances: me,
168
- onClose: W.onClose,
169
+ onClose: G.onClose,
169
170
  horizontal: !1
170
171
  }),
171
172
  /* @__PURE__ */ p("g", {
@@ -1 +1 @@
1
- {"version":3,"file":"ChartsGroup.js","names":[],"sources":["../../../src/selection/components/ChartsGroup.tsx"],"sourcesContent":["import { scaleBand, scaleLinear } from 'd3-scale';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { ChartsTitle } from '../../common/ChartsTitle';\nimport { ErrorBoundary } from '../../common/ErrorBoundary';\nimport fonts from '../../common/fonts';\nimport { Legend } from '../../common/Legend';\nimport { Tooltip, useTooltip } from '../../common/Tooltip';\nimport type { LegendData } from '../../common/types';\nimport { getChartEdgeSides } from '../../utils/getChartEdgeSides';\nimport { BAR_WIDTH_RATIO, LEGEND_OFFSET, MAX_BAR_WIDTH, MIN_BAR_WIDTH } from '../constants';\nimport type { AggregatedSelectionData } from '../createSelectionData';\nimport { formatCount } from '../utils';\nimport type { ChartYAxis } from './Chart/types';\nimport { type StageTooltipData } from './Chart/types';\nimport { FacetCell } from './FacetCell';\nimport type {\n ChartsGroupStyle,\n ChartsGroupTitle,\n ChartsGroupXAxis,\n FacetData,\n GridLayout,\n} from './types';\n\nconst facetPosition = (idx: number, nCols: number) => ({\n col: idx % nCols,\n row: Math.floor(idx / nCols),\n});\n\nfunction displayGroup(name: string | undefined, nullValueLabel: string | undefined): string {\n if (!name) return 'All';\n if (name === 'null') return nullValueLabel ?? 'N/A';\n return name;\n}\n\ntype Props = {\n facetData: FacetData;\n gridLayout: GridLayout;\n xAxis: ChartsGroupXAxis;\n yAxis: ChartYAxis;\n style: ChartsGroupStyle;\n title: ChartsGroupTitle;\n legend: LegendData;\n showFacetLabels: boolean;\n grouped: boolean;\n orderedGroupNames: string[];\n};\n\nexport function ChartsGroup({\n facetData,\n gridLayout,\n xAxis,\n yAxis,\n style,\n title,\n legend,\n showFacetLabels,\n grouped,\n orderedGroupNames,\n}: Props) {\n const { faceted, facetLabels, facetTitles } = facetData;\n const {\n nRows,\n nCols,\n totalWidth,\n totalHeight,\n margins,\n cellPlotWidth,\n cellPlotHeight,\n cellLabelHeight,\n topPad,\n rightPad,\n colLeftPads,\n rowBottomPads,\n colXOffsets,\n rowYOffsets,\n sharedX,\n sharedY,\n } = gridLayout;\n const { nullValueLabel } = style;\n const tooltipsData = useTooltip<StageTooltipData>();\n const [hoveredGroup, setHoveredGroup] = useState<string | null>(null);\n const [selectedGroup, setSelectedGroup] = useState<string | null>(null);\n const [tooltipsContainer, setTooltipsContainer] = useState<SVGGElement>();\n const tooltipsRef = useRef<SVGGElement>(null);\n\n useEffect(() => {\n if (tooltipsRef.current) setTooltipsContainer(tooltipsRef.current);\n }, []);\n\n // Clicking a bar swaps StackedBars ↔ GroupSelectedBars; the originally\n // hovered <rect> unmounts mid-hover so its onMouseLeave never fires. Clear\n // hover + tooltip state explicitly so the tooltip doesn't stay stuck.\n const handleGroupClick = useCallback(\n (name: string) => {\n setSelectedGroup((prev) => (prev === name ? null : name));\n setHoveredGroup(null);\n tooltipsData.onClose();\n },\n [tooltipsData],\n );\n\n const chartsWidth = totalWidth - margins.left - margins.right;\n\n // Shared y scale (percent 0..100 → pixels within the cell plot body).\n const yScale = useMemo(\n () => scaleLinear().domain([0, 100]).range([cellPlotHeight, 0]),\n [cellPlotHeight],\n );\n\n // x scale uses stage count from the first non-empty facet. Stage lists\n // should be identical across facets because valueLabelsMap drives them —\n // but we still guard against empty.\n const stageKeys = useMemo(() => {\n const firstFacet: AggregatedSelectionData | undefined = faceted.byFacet[faceted.facetKeys[0]];\n return firstFacet ? firstFacet.stages.map((_, i) => String(i)) : [];\n }, [faceted]);\n const xScale = useMemo(() => {\n const s = scaleBand<string>().domain(stageKeys).range([0, cellPlotWidth]);\n const nStages = stageKeys.length;\n if (nStages > 0) {\n const desiredBar = Math.min(\n MAX_BAR_WIDTH,\n Math.max(MIN_BAR_WIDTH, (cellPlotWidth / nStages) * BAR_WIDTH_RATIO),\n );\n const step = cellPlotWidth / nStages;\n const paddingInner = step > 0 ? Math.max(0, Math.min(0.95, 1 - desiredBar / step)) : 0;\n s.paddingInner(paddingInner);\n s.paddingOuter(paddingInner / 2);\n }\n return s;\n }, [cellPlotWidth, stageKeys]);\n\n const legendLeft = margins.left + chartsWidth - rightPad + LEGEND_OFFSET;\n const legendTop = margins.top + topPad + cellLabelHeight;\n\n const tooltipContent = useMemo(() => {\n const d = tooltipsData.selectedData;\n if (!d) return [];\n const sectionLabel = d.section === 'preserved' ? 'Preserved' : 'Discarded';\n const groupLabel = displayGroup(d.groupName, nullValueLabel);\n const facetLabel = (facetLabels[tooltipsData.currentFacet] ?? []).join(' / ');\n const header = facetLabel ? `${facetLabel} · ${groupLabel}` : groupLabel;\n return [\n `${header} — ${sectionLabel}`,\n `${formatCount(d.count)} / ${formatCount(d.total)} (${d.percent.toFixed(1)}%)`,\n ];\n }, [tooltipsData.selectedData, tooltipsData.currentFacet, facetLabels]);\n\n // Tooltip side distances — computed relative to the active cell.\n const active = tooltipsData.selectedData;\n const sideDistances = useMemo(() => {\n if (!active) return { left: 0, right: 0, top: 0, bottom: 0 };\n const idx = faceted.facetKeys.indexOf(tooltipsData.currentFacet);\n const { col, row } = facetPosition(idx, nCols);\n const cellX = margins.left + colXOffsets[col];\n const cellY = margins.top + rowYOffsets[row];\n const plotOriginX = cellX + colLeftPads[col];\n const plotOriginY = cellY + topPad + cellLabelHeight;\n return {\n left: plotOriginX,\n right: totalWidth - plotOriginX,\n top: plotOriginY,\n bottom: totalHeight - plotOriginY,\n };\n }, [\n active,\n tooltipsData.currentFacet,\n faceted.facetKeys,\n nCols,\n margins,\n colXOffsets,\n rowYOffsets,\n colLeftPads,\n topPad,\n cellLabelHeight,\n totalWidth,\n totalHeight,\n ]);\n\n const tooltipAnchorX = active?.x ?? 0;\n const tooltipAnchorY = active?.y ?? 0;\n\n const n = faceted.facetKeys.length;\n\n return (\n <ErrorBoundary dataId=\"selection-chart\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox={`0 0 ${totalWidth} ${totalHeight}`}\n width={totalWidth}\n height={totalHeight}\n >\n <defs>{fonts}</defs>\n <g transform={`translate(${margins.left},${margins.top})`} fontFamily=\"Manrope\">\n <ChartsTitle\n title={title.text}\n show={title.show}\n position={title.position}\n leftPadding={0}\n rightPadding={0}\n chartsWidth={chartsWidth}\n />\n {faceted.facetKeys.map((fk, idx) => {\n const { col, row } = facetPosition(idx, nCols);\n const cellData = faceted.byFacet[fk];\n if (!cellData) return null;\n const edges = getChartEdgeSides(idx, n, nCols, nRows);\n const isLeftEdge = edges.includes('left');\n const isBottomEdge = edges.includes('bottom');\n return (\n <g key={fk} transform={`translate(${colXOffsets[col]},${rowYOffsets[row]})`}>\n <FacetCell\n titleStrip={{\n lines: facetTitles[fk] ?? [],\n show: showFacetLabels,\n x: colLeftPads[col],\n height: cellLabelHeight,\n }}\n data={cellData}\n grouped={grouped}\n orderedGroupNames={orderedGroupNames}\n layout={{\n width: cellPlotWidth,\n height: cellPlotHeight,\n dimensionsLeft: 0,\n dimensionsTop: cellLabelHeight,\n paddingLeft: colLeftPads[col],\n paddingTop: topPad,\n paddingBottom: rowBottomPads[row],\n xScale,\n yScale,\n showXAxis: !sharedX || isBottomEdge,\n showYAxis: !sharedY || isLeftEdge,\n }}\n xAxis={{\n labels: xAxis.labelsByFacet[fk],\n counts: xAxis.countsByFacet[fk],\n countsFontSize: xAxis.countsFontSize,\n labelsPosition: xAxis.labelsPosition,\n showGrid: xAxis.showGrid,\n showTicks: xAxis.showTicks,\n hiddenLabels: xAxis.hiddenLabels,\n title: isBottomEdge ? xAxis.title : '',\n }}\n yAxis={{\n showGrid: yAxis.showGrid,\n showTicks: yAxis.showTicks,\n hiddenLabels: yAxis.hiddenLabels,\n title: isLeftEdge ? yAxis.title : '',\n }}\n style={style}\n interaction={{\n hoveredGroup,\n selectedGroup,\n onGroupClick: handleGroupClick,\n onGroupHover: setHoveredGroup,\n tooltipsData,\n facetKey: fk,\n }}\n />\n </g>\n );\n })}\n </g>\n <g transform={`translate(${legendLeft},${legendTop})`} fontFamily=\"Manrope\">\n <Legend\n legendData={legend}\n onItemHover={setHoveredGroup}\n onItemClick={handleGroupClick}\n />\n </g>\n {tooltipsData.active && (\n <Tooltip\n content={tooltipContent}\n x={tooltipAnchorX}\n y={tooltipAnchorY}\n active={tooltipsData.active}\n fixed={tooltipsData.fixed}\n container={tooltipsContainer}\n sideDistances={sideDistances}\n onClose={tooltipsData.onClose}\n horizontal={false}\n />\n )}\n <g ref={tooltipsRef} style={{ outline: 'none' }} />\n </svg>\n </ErrorBoundary>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ChartsGroup.js","names":[],"sources":["../../../src/selection/components/ChartsGroup.tsx"],"sourcesContent":["import { scaleBand, scaleLinear } from 'd3-scale';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { ChartsTitle } from '../../common/ChartsTitle';\nimport { ErrorBoundary } from '../../common/ErrorBoundary';\nimport fonts from '../../common/fonts';\nimport { Legend } from '../../common/Legend';\nimport { Tooltip, useTooltip } from '../../common/Tooltip';\nimport type { LegendData } from '../../common/types';\nimport { getChartEdgeSides } from '../../utils/getChartEdgeSides';\nimport { BAR_WIDTH_RATIO, LEGEND_OFFSET, MAX_BAR_WIDTH, MIN_BAR_WIDTH } from '../constants';\nimport type { AggregatedSelectionData } from '../createSelectionData';\nimport { formatCount } from '../utils';\nimport type { ChartYAxis } from './Chart/types';\nimport { type StageTooltipData } from './Chart/types';\nimport { FacetCell } from './FacetCell';\nimport type {\n ChartsGroupStyle,\n ChartsGroupTitle,\n ChartsGroupXAxis,\n FacetData,\n GridLayout,\n} from './types';\n\nconst facetPosition = (idx: number, nCols: number) => ({\n col: idx % nCols,\n row: Math.floor(idx / nCols),\n});\n\nfunction displayGroup(name: string | undefined, nullValueLabel: string | undefined): string {\n if (!name) return 'All';\n if (name === 'null') return nullValueLabel ?? 'N/A';\n return name;\n}\n\ntype Props = {\n facetData: FacetData;\n gridLayout: GridLayout;\n xAxis: ChartsGroupXAxis;\n yAxis: ChartYAxis;\n style: ChartsGroupStyle;\n title: ChartsGroupTitle;\n legend: LegendData;\n showFacetLabels: boolean;\n grouped: boolean;\n orderedGroupNames: string[];\n};\n\nexport function ChartsGroup({\n facetData,\n gridLayout,\n xAxis,\n yAxis,\n style,\n title,\n legend,\n showFacetLabels,\n grouped,\n orderedGroupNames,\n}: Props) {\n const { faceted, facetLabels, facetTitles } = facetData;\n const {\n nRows,\n nCols,\n totalWidth,\n totalHeight,\n margins,\n cellPlotWidth,\n cellPlotHeight,\n cellLabelHeight,\n topPad,\n rightPad,\n colLeftPads,\n rowBottomPads,\n colXOffsets,\n rowYOffsets,\n sharedX,\n sharedY,\n } = gridLayout;\n const { nullValueLabel } = style;\n const tooltipsData = useTooltip<StageTooltipData>();\n const [hoveredGroup, setHoveredGroup] = useState<string | null>(null);\n const [selectedGroup, setSelectedGroup] = useState<string | null>(null);\n const [tooltipsContainer, setTooltipsContainer] = useState<SVGGElement>();\n const tooltipsRef = useRef<SVGGElement>(null);\n\n useEffect(() => {\n if (tooltipsRef.current) setTooltipsContainer(tooltipsRef.current);\n }, []);\n\n // Clicking a bar swaps StackedBars ↔ GroupSelectedBars; the originally\n // hovered <rect> unmounts mid-hover so its onMouseLeave never fires. Clear\n // hover + tooltip state explicitly so the tooltip doesn't stay stuck.\n const handleGroupClick = useCallback(\n (name: string) => {\n setSelectedGroup((prev) => (prev === name ? null : name));\n setHoveredGroup(null);\n tooltipsData.onClose();\n },\n [tooltipsData],\n );\n\n const chartsWidth = totalWidth - margins.left - margins.right;\n\n // Shared y scale (percent 0..100 → pixels within the cell plot body).\n const yScale = useMemo(\n () => scaleLinear().domain([0, 100]).range([cellPlotHeight, 0]),\n [cellPlotHeight],\n );\n\n // x scale uses stage count from the first non-empty facet. Stage lists\n // should be identical across facets because valueLabelsMap drives them —\n // but we still guard against empty.\n const stageKeys = useMemo(() => {\n const firstFacet: AggregatedSelectionData | undefined = faceted.byFacet[faceted.facetKeys[0]];\n return firstFacet ? firstFacet.stages.map((_, i) => String(i)) : [];\n }, [faceted]);\n const xScale = useMemo(() => {\n const s = scaleBand<string>().domain(stageKeys).range([0, cellPlotWidth]);\n const nStages = stageKeys.length;\n if (nStages > 0) {\n const desiredBar = Math.min(\n MAX_BAR_WIDTH,\n Math.max(MIN_BAR_WIDTH, (cellPlotWidth / nStages) * BAR_WIDTH_RATIO),\n );\n const step = cellPlotWidth / nStages;\n const paddingInner = step > 0 ? Math.max(0, Math.min(0.95, 1 - desiredBar / step)) : 0;\n s.paddingInner(paddingInner);\n s.paddingOuter(paddingInner / 2);\n }\n return s;\n }, [cellPlotWidth, stageKeys]);\n\n const legendLeft = margins.left + chartsWidth - rightPad + LEGEND_OFFSET;\n const legendTop = margins.top + topPad + cellLabelHeight;\n\n const tooltipContent = useMemo(() => {\n const d = tooltipsData.selectedData;\n if (!d) return [];\n const sectionLabel = d.section === 'preserved' ? 'Preserved' : 'Discarded';\n const groupLabel = grouped ? displayGroup(d.groupName, nullValueLabel) : undefined;\n const facetLabel = (facetLabels[tooltipsData.currentFacet] ?? []).join(' / ');\n const header = [facetLabel, groupLabel].filter(Boolean).join(' · ');\n return [\n header ? `${header} — ${sectionLabel}` : sectionLabel,\n `${formatCount(d.count)} / ${formatCount(d.total)} (${d.percent.toFixed(1)}%)`,\n ];\n }, [tooltipsData.selectedData, tooltipsData.currentFacet, facetLabels, grouped, nullValueLabel]);\n\n // Tooltip side distances — computed relative to the active cell.\n const active = tooltipsData.selectedData;\n const sideDistances = useMemo(() => {\n if (!active) return { left: 0, right: 0, top: 0, bottom: 0 };\n const idx = faceted.facetKeys.indexOf(tooltipsData.currentFacet);\n const { col, row } = facetPosition(idx, nCols);\n const cellX = margins.left + colXOffsets[col];\n const cellY = margins.top + rowYOffsets[row];\n const plotOriginX = cellX + colLeftPads[col];\n const plotOriginY = cellY + topPad + cellLabelHeight;\n return {\n left: plotOriginX,\n right: totalWidth - plotOriginX,\n top: plotOriginY,\n bottom: totalHeight - plotOriginY,\n };\n }, [\n active,\n tooltipsData.currentFacet,\n faceted.facetKeys,\n nCols,\n margins,\n colXOffsets,\n rowYOffsets,\n colLeftPads,\n topPad,\n cellLabelHeight,\n totalWidth,\n totalHeight,\n ]);\n\n const tooltipAnchorX = active?.x ?? 0;\n const tooltipAnchorY = active?.y ?? 0;\n\n const n = faceted.facetKeys.length;\n\n return (\n <ErrorBoundary dataId=\"selection-chart\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox={`0 0 ${totalWidth} ${totalHeight}`}\n width={totalWidth}\n height={totalHeight}\n >\n <defs>{fonts}</defs>\n <g transform={`translate(${margins.left},${margins.top})`} fontFamily=\"Manrope\">\n <ChartsTitle\n title={title.text}\n show={title.show}\n position={title.position}\n leftPadding={0}\n rightPadding={0}\n chartsWidth={chartsWidth}\n />\n {faceted.facetKeys.map((fk, idx) => {\n const { col, row } = facetPosition(idx, nCols);\n const cellData = faceted.byFacet[fk];\n if (!cellData) return null;\n const edges = getChartEdgeSides(idx, n, nCols, nRows);\n const isLeftEdge = edges.includes('left');\n const isBottomEdge = edges.includes('bottom');\n return (\n <g key={fk} transform={`translate(${colXOffsets[col]},${rowYOffsets[row]})`}>\n <FacetCell\n titleStrip={{\n lines: facetTitles[fk] ?? [],\n show: showFacetLabels,\n x: colLeftPads[col],\n height: cellLabelHeight,\n }}\n data={cellData}\n grouped={grouped}\n orderedGroupNames={orderedGroupNames}\n layout={{\n width: cellPlotWidth,\n height: cellPlotHeight,\n dimensionsLeft: 0,\n dimensionsTop: cellLabelHeight,\n paddingLeft: colLeftPads[col],\n paddingTop: topPad,\n paddingBottom: rowBottomPads[row],\n xScale,\n yScale,\n showXAxis: !sharedX || isBottomEdge,\n showYAxis: !sharedY || isLeftEdge,\n }}\n xAxis={{\n labels: xAxis.labelsByFacet[fk],\n counts: xAxis.countsByFacet[fk],\n countsFontSize: xAxis.countsFontSize,\n labelsPosition: xAxis.labelsPosition,\n showTicks: xAxis.showTicks,\n hiddenLabels: xAxis.hiddenLabels,\n title: isBottomEdge ? xAxis.title : '',\n }}\n yAxis={{\n showGrid: yAxis.showGrid,\n showTicks: yAxis.showTicks,\n hiddenLabels: yAxis.hiddenLabels,\n title: isLeftEdge ? yAxis.title : '',\n }}\n style={style}\n interaction={{\n hoveredGroup,\n selectedGroup,\n onGroupClick: handleGroupClick,\n onGroupHover: setHoveredGroup,\n tooltipsData,\n facetKey: fk,\n }}\n />\n </g>\n );\n })}\n </g>\n <g transform={`translate(${legendLeft},${legendTop})`} fontFamily=\"Manrope\">\n <Legend\n legendData={legend}\n onItemHover={setHoveredGroup}\n onItemClick={handleGroupClick}\n />\n </g>\n {tooltipsData.active && (\n <Tooltip\n content={tooltipContent}\n x={tooltipAnchorX}\n y={tooltipAnchorY}\n active={tooltipsData.active}\n fixed={tooltipsData.fixed}\n container={tooltipsContainer}\n sideDistances={sideDistances}\n onClose={tooltipsData.onClose}\n horizontal={false}\n />\n )}\n <g ref={tooltipsRef} style={{ outline: 'none' }} />\n </svg>\n </ErrorBoundary>\n );\n}\n"],"mappings":"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"}
@@ -34,7 +34,6 @@ export type ChartsGroupXAxis = {
34
34
  countsByFacet: Record<string, Record<string, string>>;
35
35
  countsFontSize?: number;
36
36
  labelsPosition?: 'center' | '45deg' | '90deg';
37
- showGrid?: boolean;
38
37
  showTicks?: boolean;
39
38
  hiddenLabels?: boolean;
40
39
  title?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/selection/components/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,6FAA6F;AAC7F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACxD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oGAAoG;AACpG,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/selection/components/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,6FAA6F;AAC7F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACxD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,oGAAoG;AACpG,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  export declare const DESATURATE_ALPHA = 0.4;
2
2
  export declare const DIM_OPACITY = 0.35;
3
3
  export declare const RIBBON_OPACITY = 0.25;
4
- export declare const BAR_WIDTH_RATIO = 0.35;
4
+ export declare const BAR_WIDTH_RATIO = 0.3;
5
5
  export declare const MIN_BAR_WIDTH = 12;
6
6
  export declare const MAX_BAR_WIDTH = 80;
7
7
  export declare const MIN_MARGIN = 24;
@@ -11,7 +11,6 @@ export declare const FACET_TITLE_LINE = 48;
11
11
  export declare const FACET_TITLE_OFFSET = 24;
12
12
  export declare const FACET_TITLE_LINE_HEIGHT = 24;
13
13
  export declare const FACET_TITLE_FONT_PX = 20;
14
- export declare const LABEL_ROTATION_THRESHOLD = 0.65;
15
14
  export declare const LABEL_WRAP_WIDTH_PX = 120;
16
15
  export declare const LABEL_LINE_HEIGHT_PX = 14;
17
16
  export declare const AXIS_LABEL_LINE_HEIGHT = 20;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/selection/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,WAAW,KAAK,CAAC;AAG9B,eAAO,MAAM,aAAa,KAAK,CAAC;AAKhC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAG7C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAGvC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,cAAc,SAAS,CAAC;AAQrC,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAGhD,eAAO,MAAM,gBAAgB,YAAY,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/selection/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,eAAO,MAAM,WAAW,KAAK,CAAC;AAG9B,eAAO,MAAM,aAAa,KAAK,CAAC;AAKhC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAGvC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,cAAc,SAAS,CAAC;AAQrC,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAGhD,eAAO,MAAM,gBAAgB,YAAY,CAAC"}
@@ -1,4 +1,4 @@
1
- const e = .4, t = .35, n = .25, r = .35, i = .65, a = "#333", o = "#929BAD", s = "#1c1c1c";
2
- export { o as AGGREGATE_RIBBON_COLOR, r as BAR_WIDTH_RATIO, e as DESATURATE_ALPHA, t as DIM_OPACITY, a as DIVIDER_STROKE, s as FACET_LABEL_FILL, i as LABEL_ROTATION_THRESHOLD, n as RIBBON_OPACITY };
1
+ const e = .4, t = .35, n = .25, r = .3, i = "#333", a = "#929BAD", o = "#1c1c1c";
2
+ export { a as AGGREGATE_RIBBON_COLOR, r as BAR_WIDTH_RATIO, e as DESATURATE_ALPHA, t as DIM_OPACITY, i as DIVIDER_STROKE, o as FACET_LABEL_FILL, n as RIBBON_OPACITY };
3
3
 
4
4
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../../src/selection/constants.ts"],"sourcesContent":["export const DESATURATE_ALPHA = 0.4;\nexport const DIM_OPACITY = 0.35;\nexport const RIBBON_OPACITY = 0.25;\nexport const BAR_WIDTH_RATIO = 0.35;\n\nexport const MIN_BAR_WIDTH = 12;\nexport const MAX_BAR_WIDTH = 80;\n\nexport const MIN_MARGIN = 24;\nexport const MIN_PADDING = 12;\n\n// Horizontal gap between the chart's right edge and the legend column.\nexport const LEGEND_OFFSET = 20;\n\n// Facet header strip mirrors heatmap's: 48px tall, 24px between title baseline\n// and plot frame top. Multi-line titles add `FACET_TITLE_LINE_HEIGHT` per\n// extra line.\nexport const FACET_TITLE_LINE = 48;\nexport const FACET_TITLE_OFFSET = 24;\nexport const FACET_TITLE_LINE_HEIGHT = 24;\nexport const FACET_TITLE_FONT_PX = 20;\n\n// Auto-rotate stage labels to 90° when their measured width exceeds this\n// fraction of the band width (centered labels still fit below the threshold).\nexport const LABEL_ROTATION_THRESHOLD = 0.65;\n// Wrap centered (non-rotated) stage labels at this pixel width before\n// `splitTextByWidth` breaks them onto a second line.\nexport const LABEL_WRAP_WIDTH_PX = 120;\n// Line height used when measuring wrapped stage labels.\nexport const LABEL_LINE_HEIGHT_PX = 14;\n\n// Y-axis label line height (used when measuring tick label clearance).\nexport const AXIS_LABEL_LINE_HEIGHT = 20;\n// Reserved width for Y-axis tick text (matches the widest tick label budget).\nexport const Y_TICK_RESERVE = 40;\n// Vertical gap between the X-axis tick line and the top of the X label.\nexport const X_LABEL_TOP_PAD = 6;\n// Line height of the per-bar count label that floats above each bar.\nexport const COUNT_LINE_HEIGHT = 16;\n// Space between count label (above bar) and the top of the plot area.\nexport const X_COUNT_BOTTOM_PAD = 4;\n\n// Dashed-divider stroke between a group's preserved and discarded segments.\nexport const DIVIDER_STROKE = '#333';\n\n// Medium grey (\"Grey\" in graph-maker's FIXED_COLORS), kept readable at the\n// 0.25 RIBBON_OPACITY used here. Drives both the unified ribbon (when bars\n// are grouped but no group is click-selected) and the rare colorMap-miss\n// fallback in the active-group branch. Local because per-chart neutral\n// colors aren't a concern of consumers — see scatterplot's UNKNOWN_COLOR\n// (kept in graph-maker, never threaded through ScatterplotSettings).\nexport const AGGREGATE_RIBBON_COLOR = '#929BAD';\n// Text fill for facet titles and stage count labels — slightly lighter than\n// the chart's BLACK so secondary text doesn't compete with axis labels.\nexport const FACET_LABEL_FILL = '#1c1c1c';\n"],"mappings":"AAAA,MAAa,IAAmB,IACnB,IAAc,KACd,IAAiB,KACjB,IAAkB,KAqBlB,IAA2B,KAmB3B,IAAiB,QAQjB,IAAyB,WAGzB,IAAmB"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../src/selection/constants.ts"],"sourcesContent":["export const DESATURATE_ALPHA = 0.4;\nexport const DIM_OPACITY = 0.35;\nexport const RIBBON_OPACITY = 0.25;\nexport const BAR_WIDTH_RATIO = 0.3;\n\nexport const MIN_BAR_WIDTH = 12;\nexport const MAX_BAR_WIDTH = 80;\n\nexport const MIN_MARGIN = 24;\nexport const MIN_PADDING = 12;\n\n// Horizontal gap between the chart's right edge and the legend column.\nexport const LEGEND_OFFSET = 20;\n\n// Facet header strip mirrors heatmap's: 48px tall, 24px between title baseline\n// and plot frame top. Multi-line titles add `FACET_TITLE_LINE_HEIGHT` per\n// extra line.\nexport const FACET_TITLE_LINE = 48;\nexport const FACET_TITLE_OFFSET = 24;\nexport const FACET_TITLE_LINE_HEIGHT = 24;\nexport const FACET_TITLE_FONT_PX = 20;\n\n// Wrap centered (non-rotated) stage labels at this pixel width before\n// `splitTextByWidth` breaks them onto a second line.\nexport const LABEL_WRAP_WIDTH_PX = 120;\n// Line height used when measuring wrapped stage labels.\nexport const LABEL_LINE_HEIGHT_PX = 14;\n\n// Y-axis label line height (used when measuring tick label clearance).\nexport const AXIS_LABEL_LINE_HEIGHT = 20;\n// Reserved width for Y-axis tick text (matches the widest tick label budget).\nexport const Y_TICK_RESERVE = 40;\n// Vertical gap between the X-axis tick line and the top of the X label.\nexport const X_LABEL_TOP_PAD = 6;\n// Line height of the per-bar count label that floats above each bar.\nexport const COUNT_LINE_HEIGHT = 16;\n// Space between count label (above bar) and the top of the plot area.\nexport const X_COUNT_BOTTOM_PAD = 4;\n\n// Dashed-divider stroke between a group's preserved and discarded segments.\nexport const DIVIDER_STROKE = '#333';\n\n// Medium grey (\"Grey\" in graph-maker's FIXED_COLORS), kept readable at the\n// 0.25 RIBBON_OPACITY used here. Drives both the unified ribbon (when bars\n// are grouped but no group is click-selected) and the rare colorMap-miss\n// fallback in the active-group branch. Local because per-chart neutral\n// colors aren't a concern of consumers — see scatterplot's UNKNOWN_COLOR\n// (kept in graph-maker, never threaded through ScatterplotSettings).\nexport const AGGREGATE_RIBBON_COLOR = '#929BAD';\n// Text fill for facet titles and stage count labels — slightly lighter than\n// the chart's BLACK so secondary text doesn't compete with axis labels.\nexport const FACET_LABEL_FILL = '#1c1c1c';\n"],"mappings":"AAAA,MAAa,IAAmB,IACnB,IAAc,KACd,IAAiB,KACjB,IAAkB,IAqClB,IAAiB,QAQjB,IAAyB,WAGzB,IAAmB"}
@@ -1,26 +1,22 @@
1
1
  import { DataFrame, GroupKey, RowIndex } from '../DataFrame';
2
2
  export type StageInfo = {
3
- index: number;
4
- label: string;
5
3
  total: number;
6
4
  surviving: number;
7
5
  discarded: number;
8
- groups: GroupStageData[];
6
+ groups: Map<string, GroupStageData>;
9
7
  };
10
8
  export type GroupStageData = {
11
- name: string;
12
9
  surviving: number;
13
10
  discarded: number;
14
11
  };
15
12
  export type AggregatedSelectionData = {
16
13
  stages: StageInfo[];
14
+ filterNames: string[];
17
15
  groupNames: string[];
18
- maxTotal: number;
19
16
  };
20
17
  export type FacetedSelectionData = {
21
18
  facetKeys: string[];
22
19
  byFacet: Record<string, AggregatedSelectionData>;
23
- /** Union of group names across all facets, for a shared legend. */
24
20
  groupNames: string[];
25
21
  };
26
22
  export type SelectionDataParams = {
@@ -1 +1 @@
1
- {"version":3,"file":"createSelectionData.d.ts","sourceRoot":"","sources":["../../src/selection/createSelectionData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGlE,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IACjD,mEAAmE;IACnE,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,UAAU,EACV,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,GACf,EAAE,mBAAmB,GAAG,uBAAuB,CAqH/C;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,qBAAqB,EAAE,QAAQ,EAAE,EAAE,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAC1C,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,GACf,EAAE,2BAA2B,GAAG,oBAAoB,CA0BpD"}
1
+ {"version":3,"file":"createSelectionData.d.ts","sourceRoot":"","sources":["../../src/selection/createSelectionData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGlE,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IACjD,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,UAAU,EACV,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,GACf,EAAE,mBAAmB,GAAG,uBAAuB,CAgG/C;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,SAAS,CAAC;IAChB,qBAAqB,EAAE,QAAQ,EAAE,EAAE,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,EAC1C,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,GACf,EAAE,2BAA2B,GAAG,oBAAoB,CA0BpD"}
@@ -1,70 +1,56 @@
1
1
  import { getFacetStringKey as e } from "../discrete/utils/getFacetStringKey.js";
2
2
  function t({ data: e, rowIndexes: t, stageColumnKey: n, weightColumnKey: r, groupColumnKey: i, valueLabelsMap: a }) {
3
- let o = a ?? {}, s = Array.from(t), c = /* @__PURE__ */ new Set();
4
- for (let e of Object.keys(o)) {
5
- let t = Number(e);
6
- Number.isFinite(t) && c.add(t);
7
- }
8
- if (c.size === 0) for (let t of s) {
9
- let r = e.getColumnValue(n, t);
10
- r !== null && typeof r == "number" && c.add(r);
11
- }
12
- let l = [...c].sort((e, t) => e - t);
13
- if (l.length === 0 || s.length === 0) return {
3
+ let o = a ?? {}, s = Object.keys(o).map(Number).filter(Number.isFinite), c = (s.length ? Math.max(...s) : 0) + 1;
4
+ if (t.length === 0) return {
14
5
  stages: [],
15
- groupNames: [],
16
- maxTotal: 0
6
+ filterNames: [],
7
+ groupNames: []
17
8
  };
18
- let u = /* @__PURE__ */ new Set();
19
- if (i) for (let t of s) {
20
- let n = e.getColumnValue(i, t);
21
- u.add(n === null ? "null" : String(n));
9
+ let l = Array.from({ length: c - 1 }, (e, t) => o[String(t + 1)] ?? `Filter ${t + 1}`), u = e.getColumn(n), d = r ? e.getColumn(r) : void 0, f = i ? e.getColumn(i) : void 0, p = new Float64Array(c + 1), m = /* @__PURE__ */ new Map(), h = 0;
10
+ for (let e = 0; e < t.length; e++) {
11
+ let n = t[e], r = u[n];
12
+ if (typeof r != "number" || r < 1 || r > c || !Number.isInteger(r)) {
13
+ h++;
14
+ continue;
15
+ }
16
+ let i = 1;
17
+ if (d) {
18
+ let e = d[n];
19
+ i = typeof e == "number" && !Number.isNaN(e) ? e : 0;
20
+ }
21
+ if (p[r] += i, f) {
22
+ let e = f[n], t = e === null ? "null" : String(e), a = m.get(t);
23
+ a === void 0 && (a = new Float64Array(c + 1), m.set(t, a)), a[r] += i;
24
+ }
22
25
  }
23
- let d = [...u].sort(), f = (t) => {
24
- if (!r) return 1;
25
- let n = e.getColumnValue(r, t);
26
- return typeof n == "number" && !Number.isNaN(n) ? n : 0;
27
- }, p = (t) => {
28
- if (!i) return "All";
29
- let n = e.getColumnValue(i, t);
30
- return n === null ? "null" : String(n);
31
- }, m = l.map((t, r) => {
32
- let i = r === l.length - 1 ? t : l[r + 1], a = 0, c = 0, u = 0, m = /* @__PURE__ */ new Map();
33
- if (d.length > 0) for (let e of d) m.set(e, {
34
- surviving: 0,
35
- discarded: 0
36
- });
37
- for (let r of s) {
38
- let o = e.getColumnValue(n, r);
39
- if (!(o === null || typeof o != "number") && o >= t) {
40
- let e = f(r);
41
- if (a += e, o >= i ? c += e : u += e, d.length > 0) {
42
- let t = p(r), n = m.get(t);
43
- n && (o >= i ? n.surviving += e : n.discarded += e);
44
- }
45
- }
26
+ h > 0 && console.warn(`Selection Plot: ${h} row(s) had a selectionStage outside the expected [1, ${c}] range and were skipped.`);
27
+ let g = [...m.keys()].sort(), _ = (e) => {
28
+ let t = new Float64Array(c + 2);
29
+ for (let n = c; n >= 1; n--) t[n] = t[n + 1] + e[n];
30
+ return t;
31
+ }, v = _(p), y = /* @__PURE__ */ new Map();
32
+ for (let e of g) y.set(e, _(m.get(e)));
33
+ let b = [];
34
+ for (let e = 1; e <= c; e++) {
35
+ let t = e === c, n = /* @__PURE__ */ new Map();
36
+ for (let r of g) {
37
+ let i = y.get(r), a = m.get(r);
38
+ n.set(r, {
39
+ surviving: t ? i[e] : i[e + 1],
40
+ discarded: t ? 0 : a[e]
41
+ });
46
42
  }
47
- let h = d.map((e) => {
48
- let t = m.get(e);
49
- return {
50
- name: e,
51
- surviving: t?.surviving ?? 0,
52
- discarded: t?.discarded ?? 0
53
- };
43
+ b.push({
44
+ total: v[e],
45
+ surviving: t ? v[e] : v[e + 1],
46
+ discarded: t ? 0 : p[e],
47
+ groups: n
54
48
  });
55
- return {
56
- index: t,
57
- label: o[String(t)] ?? `Stage ${t}`,
58
- total: a,
59
- surviving: c,
60
- discarded: u,
61
- groups: h
62
- };
63
- });
49
+ }
64
50
  return {
65
- stages: m,
66
- groupNames: d,
67
- maxTotal: m.length > 0 ? m[0].total : 0
51
+ stages: b,
52
+ filterNames: l,
53
+ groupNames: g
68
54
  };
69
55
  }
70
56
  function n({ data: n, facetKeysCombinations: r, stageColumnKey: i, weightColumnKey: a, groupColumnKey: o, valueLabelsMap: s }) {
@@ -1 +1 @@
1
- {"version":3,"file":"createSelectionData.js","names":[],"sources":["../../src/selection/createSelectionData.ts"],"sourcesContent":["import type { DataFrame, GroupKey, RowIndex } from '../DataFrame';\nimport { getFacetStringKey } from '../discrete/utils/getFacetStringKey';\n\nexport type StageInfo = {\n index: number;\n label: string;\n total: number;\n surviving: number;\n discarded: number;\n groups: GroupStageData[];\n};\n\nexport type GroupStageData = {\n name: string;\n surviving: number;\n discarded: number;\n};\n\nexport type AggregatedSelectionData = {\n stages: StageInfo[];\n groupNames: string[];\n maxTotal: number;\n};\n\nexport type FacetedSelectionData = {\n facetKeys: string[];\n byFacet: Record<string, AggregatedSelectionData>;\n /** Union of group names across all facets, for a shared legend. */\n groupNames: string[];\n};\n\nexport type SelectionDataParams = {\n data: DataFrame;\n rowIndexes: ArrayLike<RowIndex>;\n stageColumnKey: string;\n weightColumnKey: string | undefined;\n groupColumnKey: string | undefined;\n valueLabelsMap: Record<string, string> | undefined;\n};\n\nexport function createSelectionData({\n data,\n rowIndexes,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n}: SelectionDataParams): AggregatedSelectionData {\n const valueLabels = valueLabelsMap ?? {};\n const rows = Array.from(rowIndexes);\n\n // Stage indices come from valueLabelsMap when present (authoritative: one\n // entry per filter + survivors + optional selected stage). Falling back to\n // stages observed in the data would drop filters that eliminated zero clones,\n // since filter.py only emits rows for non-empty elimination stages.\n const stageSet = new Set<number>();\n for (const key of Object.keys(valueLabels)) {\n const n = Number(key);\n if (Number.isFinite(n)) stageSet.add(n);\n }\n if (stageSet.size === 0) {\n for (const r of rows) {\n const v = data.getColumnValue(stageColumnKey, r);\n if (v !== null && typeof v === 'number') stageSet.add(v);\n }\n }\n const sortedStageIndices = [...stageSet].sort((a, b) => a - b);\n\n if (sortedStageIndices.length === 0 || rows.length === 0) {\n return { stages: [], groupNames: [], maxTotal: 0 };\n }\n\n // Null values use the literal key \"null\" so the aesthetic mapping treats\n // the null group like any other (palette color, not the standalone fallback).\n // Display labels (\"Unknown\", etc.) are handled at the consumer.\n const groupNameSet = new Set<string>();\n if (groupColumnKey) {\n for (const r of rows) {\n const v = data.getColumnValue(groupColumnKey, r);\n groupNameSet.add(v === null ? 'null' : String(v));\n }\n }\n const groupNames = [...groupNameSet].sort();\n\n const getWeight = (i: RowIndex): number => {\n if (!weightColumnKey) return 1;\n const w = data.getColumnValue(weightColumnKey, i);\n return typeof w === 'number' && !Number.isNaN(w) ? w : 0;\n };\n\n const getGroup = (i: RowIndex): string => {\n if (!groupColumnKey) return 'All';\n const g = data.getColumnValue(groupColumnKey, i);\n return g === null ? 'null' : String(g);\n };\n\n const stages: StageInfo[] = sortedStageIndices.map((stageIdx, pos) => {\n const isLastStage = pos === sortedStageIndices.length - 1;\n const nextStageIdx = isLastStage\n ? stageIdx // last stage: all surviving entities are preserved\n : sortedStageIndices[pos + 1];\n\n let total = 0;\n let surviving = 0;\n let discarded = 0;\n const groupTotals = new Map<string, { surviving: number; discarded: number }>();\n\n if (groupNames.length > 0) {\n for (const name of groupNames) {\n groupTotals.set(name, { surviving: 0, discarded: 0 });\n }\n }\n\n for (const i of rows) {\n const sv = data.getColumnValue(stageColumnKey, i);\n if (sv === null || typeof sv !== 'number') continue;\n\n // Entity survives this stage if its selectionStage >= current stage index\n if (sv >= stageIdx) {\n const w = getWeight(i);\n total += w;\n\n if (sv >= nextStageIdx) {\n surviving += w;\n } else {\n discarded += w;\n }\n\n if (groupNames.length > 0) {\n const g = getGroup(i);\n const gt = groupTotals.get(g);\n if (gt) {\n if (sv >= nextStageIdx) {\n gt.surviving += w;\n } else {\n gt.discarded += w;\n }\n }\n }\n }\n }\n\n const groups: GroupStageData[] = groupNames.map((name) => {\n const gt = groupTotals.get(name);\n return {\n name,\n surviving: gt?.surviving ?? 0,\n discarded: gt?.discarded ?? 0,\n };\n });\n\n return {\n index: stageIdx,\n label: valueLabels[String(stageIdx)] ?? `Stage ${stageIdx}`,\n total,\n surviving,\n discarded,\n groups,\n };\n });\n\n const maxTotal = stages.length > 0 ? stages[0].total : 0;\n\n return { stages, groupNames, maxTotal };\n}\n\n/**\n * Build per-facet selection data from pre-computed facet-key combinations.\n * The caller is responsible for computing combinations via `getKeysCombinations`\n * and seeding `data.setGrouping(...)` so `getRowsByGrouping` returns the\n * matching row indexes — same contract as histogram's `createHistogramDataByFacets`.\n */\nexport type SelectionDataByFacetsParams = {\n data: DataFrame;\n facetKeysCombinations: GroupKey[][];\n stageColumnKey: string;\n weightColumnKey: string | undefined;\n groupColumnKey: string | undefined;\n valueLabelsMap: Record<string, string> | undefined;\n};\n\nexport function createSelectionDataByFacets({\n data,\n facetKeysCombinations,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n}: SelectionDataByFacetsParams): FacetedSelectionData {\n const facetKeys: string[] = [];\n const byFacet: Record<string, AggregatedSelectionData> = {};\n const groupNamesSet = new Set<string>();\n\n for (const combo of facetKeysCombinations) {\n const facetKey = getFacetStringKey(combo);\n const indexes = data.getRowsByGrouping(combo);\n const agg = createSelectionData({\n data,\n rowIndexes: indexes,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n });\n facetKeys.push(facetKey);\n byFacet[facetKey] = agg;\n for (const g of agg.groupNames) groupNamesSet.add(g);\n }\n\n return {\n facetKeys,\n byFacet,\n groupNames: [...groupNamesSet].sort(),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"createSelectionData.js","names":[],"sources":["../../src/selection/createSelectionData.ts"],"sourcesContent":["import type { DataFrame, GroupKey, RowIndex } from '../DataFrame';\nimport { getFacetStringKey } from '../discrete/utils/getFacetStringKey';\n\nexport type StageInfo = {\n total: number;\n surviving: number;\n discarded: number;\n groups: Map<string, GroupStageData>;\n};\n\nexport type GroupStageData = {\n surviving: number;\n discarded: number;\n};\n\nexport type AggregatedSelectionData = {\n stages: StageInfo[];\n filterNames: string[];\n groupNames: string[];\n};\n\nexport type FacetedSelectionData = {\n facetKeys: string[];\n byFacet: Record<string, AggregatedSelectionData>;\n groupNames: string[];\n};\n\nexport type SelectionDataParams = {\n data: DataFrame;\n rowIndexes: ArrayLike<RowIndex>;\n stageColumnKey: string;\n weightColumnKey: string | undefined;\n groupColumnKey: string | undefined;\n valueLabelsMap: Record<string, string> | undefined;\n};\n\nexport function createSelectionData({\n data,\n rowIndexes,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n}: SelectionDataParams): AggregatedSelectionData {\n const valueLabels = valueLabelsMap ?? {};\n\n // Bar count = max label key + 1, derived from labels (never a row scan): each\n // label names the filter in a gap, so N labels N+1 bars (the terminal bar's\n // survivors are unlabeled).\n const stageKeys = Object.keys(valueLabels).map(Number).filter(Number.isFinite);\n const maxStage = (stageKeys.length ? Math.max(...stageKeys) : 0) + 1;\n if (rowIndexes.length === 0) {\n return { stages: [], filterNames: [], groupNames: [] };\n }\n\n // filterNames[i] = the filter in the gap after bar i.\n const filterNames = Array.from(\n { length: maxStage - 1 },\n (_, i) => valueLabels[String(i + 1)] ?? `Filter ${i + 1}`,\n );\n\n // Single O(rows) pass: tally each row's weight at its drop stage (eliminated\n // at, or terminal if survived). Columns hoisted for direct indexed reads.\n const stageColumn = data.getColumn(stageColumnKey);\n const weightColumn = weightColumnKey ? data.getColumn(weightColumnKey) : undefined;\n const groupColumn = groupColumnKey ? data.getColumn(groupColumnKey) : undefined;\n\n // drop[stage] = total weight of rows whose selectionStage === stage.\n const drop = new Float64Array(maxStage + 1);\n const groupDrop = new Map<string, Float64Array>();\n let outOfRangeRows = 0;\n\n for (let position = 0; position < rowIndexes.length; position++) {\n const rowIndex = rowIndexes[position];\n const stage = stageColumn[rowIndex];\n if (typeof stage !== 'number' || stage < 1 || stage > maxStage || !Number.isInteger(stage)) {\n outOfRangeRows++;\n continue;\n }\n\n let weight = 1;\n if (weightColumn) {\n const weightValue = weightColumn[rowIndex];\n weight = typeof weightValue === 'number' && !Number.isNaN(weightValue) ? weightValue : 0;\n }\n drop[stage] += weight;\n\n if (groupColumn) {\n const groupValue = groupColumn[rowIndex];\n const groupKey = groupValue === null ? 'null' : String(groupValue);\n let groupDropForKey = groupDrop.get(groupKey);\n if (groupDropForKey === undefined) {\n groupDropForKey = new Float64Array(maxStage + 1);\n groupDrop.set(groupKey, groupDropForKey);\n }\n groupDropForKey[stage] += weight;\n }\n }\n\n if (outOfRangeRows > 0) {\n console.warn(\n `Selection Plot: ${outOfRangeRows} row(s) had a selectionStage outside the `\n + `expected [1, ${maxStage}] range and were skipped.`\n );\n }\n\n const groupNames = [...groupDrop.keys()].sort();\n\n // total[s] = rows that reached stage s = sum of drop[k >= s].\n const cumulative = (perStage: Float64Array): Float64Array => {\n const total = new Float64Array(maxStage + 2);\n for (let stage = maxStage; stage >= 1; stage--) total[stage] = total[stage + 1] + perStage[stage];\n return total;\n };\n const total = cumulative(drop);\n const groupTotal = new Map<string, Float64Array>();\n for (const name of groupNames) groupTotal.set(name, cumulative(groupDrop.get(name)!));\n\n const stages: StageInfo[] = [];\n for (let stage = 1; stage <= maxStage; stage++) {\n const isLast = stage === maxStage;\n const groups = new Map<string, GroupStageData>();\n for (const name of groupNames) {\n const groupTotalForName = groupTotal.get(name)!;\n const groupDropForName = groupDrop.get(name)!;\n groups.set(name, {\n surviving: isLast ? groupTotalForName[stage] : groupTotalForName[stage + 1],\n discarded: isLast ? 0 : groupDropForName[stage],\n });\n }\n stages.push({\n total: total[stage],\n surviving: isLast ? total[stage] : total[stage + 1],\n discarded: isLast ? 0 : drop[stage],\n groups,\n });\n }\n\n return { stages, filterNames, groupNames };\n}\n\n/**\n * Build per-facet selection data from pre-computed facet-key combinations.\n * The caller is responsible for computing combinations via `getKeysCombinations`\n * and seeding `data.setGrouping(...)` so `getRowsByGrouping` returns the\n * matching row indexes — same contract as histogram's `createHistogramDataByFacets`.\n */\nexport type SelectionDataByFacetsParams = {\n data: DataFrame;\n facetKeysCombinations: GroupKey[][];\n stageColumnKey: string;\n weightColumnKey: string | undefined;\n groupColumnKey: string | undefined;\n valueLabelsMap: Record<string, string> | undefined;\n};\n\nexport function createSelectionDataByFacets({\n data,\n facetKeysCombinations,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n}: SelectionDataByFacetsParams): FacetedSelectionData {\n const facetKeys: string[] = [];\n const byFacet: Record<string, AggregatedSelectionData> = {};\n const groupNamesSet = new Set<string>();\n\n for (const combo of facetKeysCombinations) {\n const facetKey = getFacetStringKey(combo);\n const indexes = data.getRowsByGrouping(combo);\n const aggregated = createSelectionData({\n data,\n rowIndexes: indexes,\n stageColumnKey,\n weightColumnKey,\n groupColumnKey,\n valueLabelsMap,\n });\n facetKeys.push(facetKey);\n byFacet[facetKey] = aggregated;\n for (const groupName of aggregated.groupNames) groupNamesSet.add(groupName);\n }\n\n return {\n facetKeys,\n byFacet,\n groupNames: [...groupNamesSet].sort(),\n };\n}\n"],"mappings":"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"}