@adminiumjs/charts 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/components/AnomalyChart.d.ts +30 -0
- package/dist/components/AnomalyChart.d.ts.map +1 -0
- package/dist/components/AnomalyChart.js +32 -0
- package/dist/components/AnomalyChart.js.map +1 -0
- package/dist/components/BarChart.d.ts +33 -0
- package/dist/components/BarChart.d.ts.map +1 -0
- package/dist/components/BarChart.js +40 -0
- package/dist/components/BarChart.js.map +1 -0
- package/dist/components/BoxPlotChart.d.ts +23 -0
- package/dist/components/BoxPlotChart.d.ts.map +1 -0
- package/dist/components/BoxPlotChart.js +24 -0
- package/dist/components/BoxPlotChart.js.map +1 -0
- package/dist/components/BulletChart.d.ts +27 -0
- package/dist/components/BulletChart.d.ts.map +1 -0
- package/dist/components/BulletChart.js +24 -0
- package/dist/components/BulletChart.js.map +1 -0
- package/dist/components/BumpChart.d.ts +27 -0
- package/dist/components/BumpChart.d.ts.map +1 -0
- package/dist/components/BumpChart.js +17 -0
- package/dist/components/BumpChart.js.map +1 -0
- package/dist/components/CandlestickChart.d.ts +26 -0
- package/dist/components/CandlestickChart.d.ts.map +1 -0
- package/dist/components/CandlestickChart.js +22 -0
- package/dist/components/CandlestickChart.js.map +1 -0
- package/dist/components/ChartSurface.d.ts +52 -0
- package/dist/components/ChartSurface.d.ts.map +1 -0
- package/dist/components/ChartSurface.js +15 -0
- package/dist/components/ChartSurface.js.map +1 -0
- package/dist/components/Chord.d.ts +30 -0
- package/dist/components/Chord.d.ts.map +1 -0
- package/dist/components/Chord.js +14 -0
- package/dist/components/Chord.js.map +1 -0
- package/dist/components/ChoroplethGridChart.d.ts +32 -0
- package/dist/components/ChoroplethGridChart.d.ts.map +1 -0
- package/dist/components/ChoroplethGridChart.js +15 -0
- package/dist/components/ChoroplethGridChart.js.map +1 -0
- package/dist/components/CohortMatrixChart.d.ts +30 -0
- package/dist/components/CohortMatrixChart.d.ts.map +1 -0
- package/dist/components/CohortMatrixChart.js +13 -0
- package/dist/components/CohortMatrixChart.js.map +1 -0
- package/dist/components/CorrelationMatrixChart.d.ts +24 -0
- package/dist/components/CorrelationMatrixChart.d.ts.map +1 -0
- package/dist/components/CorrelationMatrixChart.js +29 -0
- package/dist/components/CorrelationMatrixChart.js.map +1 -0
- package/dist/components/DonutChart.d.ts +31 -0
- package/dist/components/DonutChart.d.ts.map +1 -0
- package/dist/components/DonutChart.js +19 -0
- package/dist/components/DonutChart.js.map +1 -0
- package/dist/components/ForecastChart.d.ts +37 -0
- package/dist/components/ForecastChart.d.ts.map +1 -0
- package/dist/components/ForecastChart.js +39 -0
- package/dist/components/ForecastChart.js.map +1 -0
- package/dist/components/Funnel.d.ts +27 -0
- package/dist/components/Funnel.d.ts.map +1 -0
- package/dist/components/Funnel.js +25 -0
- package/dist/components/Funnel.js.map +1 -0
- package/dist/components/HeatCalendarChart.d.ts +39 -0
- package/dist/components/HeatCalendarChart.d.ts.map +1 -0
- package/dist/components/HeatCalendarChart.js +37 -0
- package/dist/components/HeatCalendarChart.js.map +1 -0
- package/dist/components/HeatMonthChart.d.ts +30 -0
- package/dist/components/HeatMonthChart.d.ts.map +1 -0
- package/dist/components/HeatMonthChart.js +14 -0
- package/dist/components/HeatMonthChart.js.map +1 -0
- package/dist/components/HexbinChart.d.ts +22 -0
- package/dist/components/HexbinChart.d.ts.map +1 -0
- package/dist/components/HexbinChart.js +31 -0
- package/dist/components/HexbinChart.js.map +1 -0
- package/dist/components/LineAreaChart.d.ts +26 -0
- package/dist/components/LineAreaChart.d.ts.map +1 -0
- package/dist/components/LineAreaChart.js +62 -0
- package/dist/components/LineAreaChart.js.map +1 -0
- package/dist/components/MarimekkoChart.d.ts +24 -0
- package/dist/components/MarimekkoChart.d.ts.map +1 -0
- package/dist/components/MarimekkoChart.js +20 -0
- package/dist/components/MarimekkoChart.js.map +1 -0
- package/dist/components/MultiLineChart.d.ts +36 -0
- package/dist/components/MultiLineChart.d.ts.map +1 -0
- package/dist/components/MultiLineChart.js +62 -0
- package/dist/components/MultiLineChart.js.map +1 -0
- package/dist/components/ParallelCoordinatesChart.d.ts +22 -0
- package/dist/components/ParallelCoordinatesChart.d.ts.map +1 -0
- package/dist/components/ParallelCoordinatesChart.js +16 -0
- package/dist/components/ParallelCoordinatesChart.js.map +1 -0
- package/dist/components/ParetoChart.d.ts +26 -0
- package/dist/components/ParetoChart.d.ts.map +1 -0
- package/dist/components/ParetoChart.js +26 -0
- package/dist/components/ParetoChart.js.map +1 -0
- package/dist/components/Radar.d.ts +27 -0
- package/dist/components/Radar.d.ts.map +1 -0
- package/dist/components/Radar.js +19 -0
- package/dist/components/Radar.js.map +1 -0
- package/dist/components/RadialBar.d.ts +25 -0
- package/dist/components/RadialBar.d.ts.map +1 -0
- package/dist/components/RadialBar.js +12 -0
- package/dist/components/RadialBar.js.map +1 -0
- package/dist/components/RankingBars.d.ts +27 -0
- package/dist/components/RankingBars.d.ts.map +1 -0
- package/dist/components/RankingBars.js +25 -0
- package/dist/components/RankingBars.js.map +1 -0
- package/dist/components/RidgelineChart.d.ts +23 -0
- package/dist/components/RidgelineChart.d.ts.map +1 -0
- package/dist/components/RidgelineChart.js +20 -0
- package/dist/components/RidgelineChart.js.map +1 -0
- package/dist/components/SankeyChart.d.ts +29 -0
- package/dist/components/SankeyChart.d.ts.map +1 -0
- package/dist/components/SankeyChart.js +21 -0
- package/dist/components/SankeyChart.js.map +1 -0
- package/dist/components/ScatterBubbleChart.d.ts +26 -0
- package/dist/components/ScatterBubbleChart.d.ts.map +1 -0
- package/dist/components/ScatterBubbleChart.js +31 -0
- package/dist/components/ScatterBubbleChart.js.map +1 -0
- package/dist/components/SlopeChart.d.ts +28 -0
- package/dist/components/SlopeChart.d.ts.map +1 -0
- package/dist/components/SlopeChart.js +18 -0
- package/dist/components/SlopeChart.js.map +1 -0
- package/dist/components/Sparkline.d.ts +15 -0
- package/dist/components/Sparkline.d.ts.map +1 -0
- package/dist/components/Sparkline.js +27 -0
- package/dist/components/Sparkline.js.map +1 -0
- package/dist/components/StackedBar100.d.ts +30 -0
- package/dist/components/StackedBar100.d.ts.map +1 -0
- package/dist/components/StackedBar100.js +21 -0
- package/dist/components/StackedBar100.js.map +1 -0
- package/dist/components/StreamChart.d.ts +23 -0
- package/dist/components/StreamChart.d.ts.map +1 -0
- package/dist/components/StreamChart.js +56 -0
- package/dist/components/StreamChart.js.map +1 -0
- package/dist/components/Sunburst.d.ts +24 -0
- package/dist/components/Sunburst.d.ts.map +1 -0
- package/dist/components/Sunburst.js +13 -0
- package/dist/components/Sunburst.js.map +1 -0
- package/dist/components/TimelineLanesChart.d.ts +25 -0
- package/dist/components/TimelineLanesChart.d.ts.map +1 -0
- package/dist/components/TimelineLanesChart.js +41 -0
- package/dist/components/TimelineLanesChart.js.map +1 -0
- package/dist/components/Treemap.d.ts +28 -0
- package/dist/components/Treemap.d.ts.map +1 -0
- package/dist/components/Treemap.js +22 -0
- package/dist/components/Treemap.js.map +1 -0
- package/dist/components/ViolinChart.d.ts +22 -0
- package/dist/components/ViolinChart.d.ts.map +1 -0
- package/dist/components/ViolinChart.js +21 -0
- package/dist/components/ViolinChart.js.map +1 -0
- package/dist/components/WaterfallChart.d.ts +23 -0
- package/dist/components/WaterfallChart.d.ts.map +1 -0
- package/dist/components/WaterfallChart.js +29 -0
- package/dist/components/WaterfallChart.js.map +1 -0
- package/dist/components/WordCloud.d.ts +24 -0
- package/dist/components/WordCloud.d.ts.map +1 -0
- package/dist/components/WordCloud.js +22 -0
- package/dist/components/WordCloud.js.map +1 -0
- package/dist/demo/barsRanking.d.ts +26 -0
- package/dist/demo/barsRanking.d.ts.map +1 -0
- package/dist/demo/barsRanking.js +80 -0
- package/dist/demo/barsRanking.js.map +1 -0
- package/dist/demo/generators.d.ts +44 -0
- package/dist/demo/generators.d.ts.map +1 -0
- package/dist/demo/generators.js +72 -0
- package/dist/demo/generators.js.map +1 -0
- package/dist/demo/mulberry32.d.ts +10 -0
- package/dist/demo/mulberry32.d.ts.map +1 -0
- package/dist/demo/mulberry32.js +26 -0
- package/dist/demo/mulberry32.js.map +1 -0
- package/dist/geometry/anomaly.d.ts +22 -0
- package/dist/geometry/anomaly.d.ts.map +1 -0
- package/dist/geometry/anomaly.js +52 -0
- package/dist/geometry/anomaly.js.map +1 -0
- package/dist/geometry/bars.d.ts +46 -0
- package/dist/geometry/bars.d.ts.map +1 -0
- package/dist/geometry/bars.js +68 -0
- package/dist/geometry/bars.js.map +1 -0
- package/dist/geometry/bullet.d.ts +53 -0
- package/dist/geometry/bullet.d.ts.map +1 -0
- package/dist/geometry/bullet.js +67 -0
- package/dist/geometry/bullet.js.map +1 -0
- package/dist/geometry/bump.d.ts +42 -0
- package/dist/geometry/bump.d.ts.map +1 -0
- package/dist/geometry/bump.js +29 -0
- package/dist/geometry/bump.js.map +1 -0
- package/dist/geometry/candles.d.ts +39 -0
- package/dist/geometry/candles.d.ts.map +1 -0
- package/dist/geometry/candles.js +42 -0
- package/dist/geometry/candles.js.map +1 -0
- package/dist/geometry/chord.d.ts +53 -0
- package/dist/geometry/chord.d.ts.map +1 -0
- package/dist/geometry/chord.js +111 -0
- package/dist/geometry/chord.js.map +1 -0
- package/dist/geometry/choropleth.d.ts +58 -0
- package/dist/geometry/choropleth.d.ts.map +1 -0
- package/dist/geometry/choropleth.js +101 -0
- package/dist/geometry/choropleth.js.map +1 -0
- package/dist/geometry/cohort.d.ts +54 -0
- package/dist/geometry/cohort.d.ts.map +1 -0
- package/dist/geometry/cohort.js +70 -0
- package/dist/geometry/cohort.js.map +1 -0
- package/dist/geometry/correlation.d.ts +138 -0
- package/dist/geometry/correlation.d.ts.map +1 -0
- package/dist/geometry/correlation.js +161 -0
- package/dist/geometry/correlation.js.map +1 -0
- package/dist/geometry/distribution.d.ts +115 -0
- package/dist/geometry/distribution.d.ts.map +1 -0
- package/dist/geometry/distribution.js +131 -0
- package/dist/geometry/distribution.js.map +1 -0
- package/dist/geometry/donut.d.ts +37 -0
- package/dist/geometry/donut.d.ts.map +1 -0
- package/dist/geometry/donut.js +46 -0
- package/dist/geometry/donut.js.map +1 -0
- package/dist/geometry/forecast.d.ts +30 -0
- package/dist/geometry/forecast.d.ts.map +1 -0
- package/dist/geometry/forecast.js +75 -0
- package/dist/geometry/forecast.js.map +1 -0
- package/dist/geometry/funnel.d.ts +37 -0
- package/dist/geometry/funnel.d.ts.map +1 -0
- package/dist/geometry/funnel.js +42 -0
- package/dist/geometry/funnel.js.map +1 -0
- package/dist/geometry/heat.d.ts +34 -0
- package/dist/geometry/heat.d.ts.map +1 -0
- package/dist/geometry/heat.js +50 -0
- package/dist/geometry/heat.js.map +1 -0
- package/dist/geometry/heatCalendar.d.ts +43 -0
- package/dist/geometry/heatCalendar.d.ts.map +1 -0
- package/dist/geometry/heatCalendar.js +82 -0
- package/dist/geometry/heatCalendar.js.map +1 -0
- package/dist/geometry/heatMonth.d.ts +47 -0
- package/dist/geometry/heatMonth.d.ts.map +1 -0
- package/dist/geometry/heatMonth.js +72 -0
- package/dist/geometry/heatMonth.js.map +1 -0
- package/dist/geometry/lineArea.d.ts +17 -0
- package/dist/geometry/lineArea.d.ts.map +1 -0
- package/dist/geometry/lineArea.js +37 -0
- package/dist/geometry/lineArea.js.map +1 -0
- package/dist/geometry/marimekko.d.ts +43 -0
- package/dist/geometry/marimekko.d.ts.map +1 -0
- package/dist/geometry/marimekko.js +45 -0
- package/dist/geometry/marimekko.js.map +1 -0
- package/dist/geometry/multiLine.d.ts +19 -0
- package/dist/geometry/multiLine.d.ts.map +1 -0
- package/dist/geometry/multiLine.js +25 -0
- package/dist/geometry/multiLine.js.map +1 -0
- package/dist/geometry/pareto.d.ts +44 -0
- package/dist/geometry/pareto.d.ts.map +1 -0
- package/dist/geometry/pareto.js +65 -0
- package/dist/geometry/pareto.js.map +1 -0
- package/dist/geometry/radar.d.ts +60 -0
- package/dist/geometry/radar.d.ts.map +1 -0
- package/dist/geometry/radar.js +67 -0
- package/dist/geometry/radar.js.map +1 -0
- package/dist/geometry/radialBar.d.ts +31 -0
- package/dist/geometry/radialBar.d.ts.map +1 -0
- package/dist/geometry/radialBar.js +53 -0
- package/dist/geometry/radialBar.js.map +1 -0
- package/dist/geometry/ranking.d.ts +48 -0
- package/dist/geometry/ranking.d.ts.map +1 -0
- package/dist/geometry/ranking.js +42 -0
- package/dist/geometry/ranking.js.map +1 -0
- package/dist/geometry/sankey.d.ts +60 -0
- package/dist/geometry/sankey.d.ts.map +1 -0
- package/dist/geometry/sankey.js +130 -0
- package/dist/geometry/sankey.js.map +1 -0
- package/dist/geometry/scales.d.ts +14 -0
- package/dist/geometry/scales.d.ts.map +1 -0
- package/dist/geometry/scales.js +33 -0
- package/dist/geometry/scales.js.map +1 -0
- package/dist/geometry/slope.d.ts +34 -0
- package/dist/geometry/slope.d.ts.map +1 -0
- package/dist/geometry/slope.js +44 -0
- package/dist/geometry/slope.js.map +1 -0
- package/dist/geometry/sparkline.d.ts +20 -0
- package/dist/geometry/sparkline.d.ts.map +1 -0
- package/dist/geometry/sparkline.js +43 -0
- package/dist/geometry/sparkline.js.map +1 -0
- package/dist/geometry/stacked100.d.ts +33 -0
- package/dist/geometry/stacked100.d.ts.map +1 -0
- package/dist/geometry/stacked100.js +34 -0
- package/dist/geometry/stacked100.js.map +1 -0
- package/dist/geometry/streamStack.d.ts +32 -0
- package/dist/geometry/streamStack.d.ts.map +1 -0
- package/dist/geometry/streamStack.js +56 -0
- package/dist/geometry/streamStack.js.map +1 -0
- package/dist/geometry/sunburst.d.ts +37 -0
- package/dist/geometry/sunburst.d.ts.map +1 -0
- package/dist/geometry/sunburst.js +84 -0
- package/dist/geometry/sunburst.js.map +1 -0
- package/dist/geometry/timelineLanes.d.ts +46 -0
- package/dist/geometry/timelineLanes.d.ts.map +1 -0
- package/dist/geometry/timelineLanes.js +52 -0
- package/dist/geometry/timelineLanes.js.map +1 -0
- package/dist/geometry/treemap.d.ts +42 -0
- package/dist/geometry/treemap.d.ts.map +1 -0
- package/dist/geometry/treemap.js +134 -0
- package/dist/geometry/treemap.js.map +1 -0
- package/dist/geometry/waterfall.d.ts +40 -0
- package/dist/geometry/waterfall.d.ts.map +1 -0
- package/dist/geometry/waterfall.js +80 -0
- package/dist/geometry/waterfall.js.map +1 -0
- package/dist/geometry/wordcloud.d.ts +46 -0
- package/dist/geometry/wordcloud.d.ts.map +1 -0
- package/dist/geometry/wordcloud.js +79 -0
- package/dist/geometry/wordcloud.js.map +1 -0
- package/dist/hooks/useChartSize.d.ts +6 -0
- package/dist/hooks/useChartSize.d.ts.map +1 -0
- package/dist/hooks/useChartSize.js +32 -0
- package/dist/hooks/useChartSize.js.map +1 -0
- package/dist/hooks/useMountAnimation.d.ts +4 -0
- package/dist/hooks/useMountAnimation.d.ts.map +1 -0
- package/dist/hooks/useMountAnimation.js +40 -0
- package/dist/hooks/useMountAnimation.js.map +1 -0
- package/dist/hooks/useRtl.d.ts +7 -0
- package/dist/hooks/useRtl.d.ts.map +1 -0
- package/dist/hooks/useRtl.js +18 -0
- package/dist/hooks/useRtl.js.map +1 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/format.d.ts +5 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +24 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/stats.d.ts +23 -0
- package/dist/utils/stats.d.ts.map +1 -0
- package/dist/utils/stats.js +82 -0
- package/dist/utils/stats.js.map +1 -0
- package/package.json +36 -0
- package/src/styles.css +205 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure ranking-bar layout (`chart-ranking-bars`, research/widget-registry.md
|
|
3
|
+
* §2): label + proportional horizontal bar (leader solid, rest dimmed) + mono
|
|
4
|
+
* value. DOM-free so scheduled-report workers reuse it. Horizontal value axis
|
|
5
|
+
* mirrors in RTL (annex §7.4: categorical/ranking scales flip; bars anchor at
|
|
6
|
+
* inline-start, which is the right edge under `rtl`).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Lay out top-N horizontal bars. Input is assumed pre-sorted descending (the
|
|
10
|
+
* server returns sorted `categorical`); the leader is whichever row holds the
|
|
11
|
+
* maximum value. Bar length is proportional to the maximum value so the leader
|
|
12
|
+
* fills the track.
|
|
13
|
+
*/
|
|
14
|
+
export function layoutRanking(items, options) {
|
|
15
|
+
const { width, height, rtl, barRatio = 0.5, max } = options;
|
|
16
|
+
const rows = max !== undefined ? items.slice(0, max) : [...items];
|
|
17
|
+
const n = rows.length;
|
|
18
|
+
if (n === 0 || width <= 0 || height <= 0)
|
|
19
|
+
return { bars: [], rowHeight: 0 };
|
|
20
|
+
const rowHeight = height / n;
|
|
21
|
+
const barHeight = rowHeight * barRatio;
|
|
22
|
+
const peak = rows.reduce((m, r) => Math.max(m, r.value), 0);
|
|
23
|
+
const leaderIndex = rows.reduce((best, r, i) => (r.value > (rows[best]?.value ?? -Infinity) ? i : best), 0);
|
|
24
|
+
const bars = rows.map((row, index) => {
|
|
25
|
+
const frac = peak > 0 ? Math.max(0, row.value) / peak : 0;
|
|
26
|
+
const barWidth = frac * width;
|
|
27
|
+
const rowY = index * rowHeight;
|
|
28
|
+
return {
|
|
29
|
+
index,
|
|
30
|
+
label: row.label,
|
|
31
|
+
value: row.value,
|
|
32
|
+
rowY,
|
|
33
|
+
y: rowY + (rowHeight - barHeight) / 2,
|
|
34
|
+
x: rtl ? width - barWidth : 0,
|
|
35
|
+
width: barWidth,
|
|
36
|
+
barHeight,
|
|
37
|
+
isLeader: index === leaderIndex,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return { bars, rowHeight };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ranking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ranking.js","sourceRoot":"","sources":["../../src/geometry/ranking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuCH;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAA8B,EAAE,OAA6B;IACzF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,IAAI,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;IAClE,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACtB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IAE5E,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5G,MAAM,IAAI,GAAiB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QACjD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAI,GAAG,KAAK,CAAC;QAC9B,MAAM,IAAI,GAAG,KAAK,GAAG,SAAS,CAAC;QAC/B,OAAO;YACL,KAAK;YACL,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,IAAI;YACJ,CAAC,EAAE,IAAI,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC;YACrC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC7B,KAAK,EAAE,QAAQ;YACf,SAAS;YACT,QAAQ,EAAE,KAAK,KAAK,WAAW;SAChC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure Sankey flow geometry (research/widget-registry.md §2 `chart-sankey`):
|
|
3
|
+
* layered node rects + cubic-bezier ribbon paths weighted by flow. DOM-free and
|
|
4
|
+
* deterministic (workplan/04 §7.1) so the same ribbon path strings render in
|
|
5
|
+
* Node and the browser (acceptance #10).
|
|
6
|
+
*
|
|
7
|
+
* RTL policy (04 §7.4, explicit): Sankey renders as an LTR island — the flow
|
|
8
|
+
* direction never mirrors. The widget's legend/summary pill mirror at the
|
|
9
|
+
* widget layer via logical CSS.
|
|
10
|
+
*/
|
|
11
|
+
export interface SankeyNodeInput {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
/** Explicit column (0-based); inferred by longest-path layering when absent. */
|
|
15
|
+
layer?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SankeyLinkInput {
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
weight: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SankeyNode {
|
|
23
|
+
id: string;
|
|
24
|
+
label: string;
|
|
25
|
+
layer: number;
|
|
26
|
+
index: number;
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
w: number;
|
|
30
|
+
h: number;
|
|
31
|
+
value: number;
|
|
32
|
+
colorVar: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SankeyLink {
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
weight: number;
|
|
38
|
+
width: number;
|
|
39
|
+
path: string;
|
|
40
|
+
sourceColorVar: string;
|
|
41
|
+
}
|
|
42
|
+
export interface SankeyLayout {
|
|
43
|
+
nodes: SankeyNode[];
|
|
44
|
+
links: SankeyLink[];
|
|
45
|
+
layers: number;
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
}
|
|
49
|
+
export interface SankeyOptions {
|
|
50
|
+
width?: number;
|
|
51
|
+
height?: number;
|
|
52
|
+
nodeWidth?: number;
|
|
53
|
+
/** Vertical gap between stacked nodes in a layer. */
|
|
54
|
+
nodePad?: number;
|
|
55
|
+
}
|
|
56
|
+
/** Horizontal-band ribbon between two edges — a smooth filled area. */
|
|
57
|
+
export declare function sankeyRibbonPath(sx: number, tx: number, sy0: number, sy1: number, ty0: number, ty1: number): string;
|
|
58
|
+
/** Lay out a Sankey diagram; nodes keep input order within each column. */
|
|
59
|
+
export declare function sankeyLayout(nodesInput: readonly SankeyNodeInput[], linksInput: readonly SankeyLinkInput[], options?: SankeyOptions): SankeyLayout;
|
|
60
|
+
//# sourceMappingURL=sankey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sankey.d.ts","sourceRoot":"","sources":["../../src/geometry/sankey.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GACV,MAAM,CAUR;AA0BD,2EAA2E;AAC3E,wBAAgB,YAAY,CAC1B,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAuFd"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure Sankey flow geometry (research/widget-registry.md §2 `chart-sankey`):
|
|
3
|
+
* layered node rects + cubic-bezier ribbon paths weighted by flow. DOM-free and
|
|
4
|
+
* deterministic (workplan/04 §7.1) so the same ribbon path strings render in
|
|
5
|
+
* Node and the browser (acceptance #10).
|
|
6
|
+
*
|
|
7
|
+
* RTL policy (04 §7.4, explicit): Sankey renders as an LTR island — the flow
|
|
8
|
+
* direction never mirrors. The widget's legend/summary pill mirror at the
|
|
9
|
+
* widget layer via logical CSS.
|
|
10
|
+
*/
|
|
11
|
+
/** Horizontal-band ribbon between two edges — a smooth filled area. */
|
|
12
|
+
export function sankeyRibbonPath(sx, tx, sy0, sy1, ty0, ty1) {
|
|
13
|
+
const xc = (sx + tx) / 2;
|
|
14
|
+
const r = (n) => (Math.round(n * 1000) / 1000).toString();
|
|
15
|
+
return (`M${r(sx)},${r(sy0)}` +
|
|
16
|
+
`C${r(xc)},${r(sy0)} ${r(xc)},${r(ty0)} ${r(tx)},${r(ty0)}` +
|
|
17
|
+
`L${r(tx)},${r(ty1)}` +
|
|
18
|
+
`C${r(xc)},${r(ty1)} ${r(xc)},${r(sy1)} ${r(sx)},${r(sy1)}` +
|
|
19
|
+
`Z`);
|
|
20
|
+
}
|
|
21
|
+
/** Longest-path column assignment; explicit `node.layer` values win. */
|
|
22
|
+
function assignLayers(nodes, links) {
|
|
23
|
+
const layer = new Map();
|
|
24
|
+
const explicit = new Set();
|
|
25
|
+
for (const node of nodes) {
|
|
26
|
+
layer.set(node.id, node.layer ?? 0);
|
|
27
|
+
if (node.layer !== undefined)
|
|
28
|
+
explicit.add(node.id);
|
|
29
|
+
}
|
|
30
|
+
// Relax target = max(target, source+1) until stable (bounded by node count).
|
|
31
|
+
for (let pass = 0; pass < nodes.length; pass += 1) {
|
|
32
|
+
let changed = false;
|
|
33
|
+
for (const link of links) {
|
|
34
|
+
if (!layer.has(link.from) || !layer.has(link.to) || explicit.has(link.to))
|
|
35
|
+
continue;
|
|
36
|
+
const candidate = (layer.get(link.from) ?? 0) + 1;
|
|
37
|
+
if (candidate > (layer.get(link.to) ?? 0)) {
|
|
38
|
+
layer.set(link.to, candidate);
|
|
39
|
+
changed = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!changed)
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
return layer;
|
|
46
|
+
}
|
|
47
|
+
/** Lay out a Sankey diagram; nodes keep input order within each column. */
|
|
48
|
+
export function sankeyLayout(nodesInput, linksInput, options = {}) {
|
|
49
|
+
const { width = 600, height = 300, nodeWidth = 14, nodePad = 12 } = options;
|
|
50
|
+
const links = linksInput.filter((l) => Number.isFinite(l.weight) && l.weight > 0 && l.from !== l.to);
|
|
51
|
+
const layerOf = assignLayers(nodesInput, links);
|
|
52
|
+
const maxLayer = nodesInput.reduce((max, n) => Math.max(max, layerOf.get(n.id) ?? 0), 0);
|
|
53
|
+
// Flow value per node = max(incoming, outgoing) sum.
|
|
54
|
+
const inSum = new Map();
|
|
55
|
+
const outSum = new Map();
|
|
56
|
+
for (const link of links) {
|
|
57
|
+
outSum.set(link.from, (outSum.get(link.from) ?? 0) + link.weight);
|
|
58
|
+
inSum.set(link.to, (inSum.get(link.to) ?? 0) + link.weight);
|
|
59
|
+
}
|
|
60
|
+
const columns = Array.from({ length: maxLayer + 1 }, () => []);
|
|
61
|
+
for (const node of nodesInput)
|
|
62
|
+
columns[layerOf.get(node.id) ?? 0]?.push(node);
|
|
63
|
+
const valueOf = (id) => Math.max(inSum.get(id) ?? 0, outSum.get(id) ?? 0);
|
|
64
|
+
// Single value→pixel scale so columns share a baseline (min over columns).
|
|
65
|
+
let scale = Infinity;
|
|
66
|
+
for (const column of columns) {
|
|
67
|
+
if (column.length === 0)
|
|
68
|
+
continue;
|
|
69
|
+
const sum = column.reduce((acc, n) => acc + valueOf(n.id), 0);
|
|
70
|
+
const avail = height - (column.length - 1) * nodePad;
|
|
71
|
+
if (sum > 0)
|
|
72
|
+
scale = Math.min(scale, avail / sum);
|
|
73
|
+
}
|
|
74
|
+
if (!Number.isFinite(scale) || scale <= 0)
|
|
75
|
+
scale = 1;
|
|
76
|
+
const columnStep = maxLayer > 0 ? (width - nodeWidth) / maxLayer : 0;
|
|
77
|
+
const nodes = [];
|
|
78
|
+
const nodeById = new Map();
|
|
79
|
+
let globalIndex = 0;
|
|
80
|
+
columns.forEach((column, layerIndex) => {
|
|
81
|
+
let y = 0;
|
|
82
|
+
for (const input of column) {
|
|
83
|
+
const value = valueOf(input.id);
|
|
84
|
+
const h = Math.max(1, value * scale);
|
|
85
|
+
const node = {
|
|
86
|
+
id: input.id,
|
|
87
|
+
label: input.label,
|
|
88
|
+
layer: layerIndex,
|
|
89
|
+
index: globalIndex,
|
|
90
|
+
x: layerIndex * columnStep,
|
|
91
|
+
y,
|
|
92
|
+
w: nodeWidth,
|
|
93
|
+
h,
|
|
94
|
+
value,
|
|
95
|
+
colorVar: `var(--viz-${(globalIndex % 8) + 1})`,
|
|
96
|
+
};
|
|
97
|
+
nodes.push(node);
|
|
98
|
+
nodeById.set(node.id, node);
|
|
99
|
+
y += h + nodePad;
|
|
100
|
+
globalIndex += 1;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// Stack link endpoints along each node's outgoing (right) / incoming (left) edge.
|
|
104
|
+
const outOffset = new Map();
|
|
105
|
+
const inOffset = new Map();
|
|
106
|
+
const outLinks = [];
|
|
107
|
+
for (const link of links) {
|
|
108
|
+
const source = nodeById.get(link.from);
|
|
109
|
+
const target = nodeById.get(link.to);
|
|
110
|
+
if (source === undefined || target === undefined)
|
|
111
|
+
continue;
|
|
112
|
+
const w = Math.max(1, link.weight * scale);
|
|
113
|
+
const so = outOffset.get(source.id) ?? 0;
|
|
114
|
+
const to = inOffset.get(target.id) ?? 0;
|
|
115
|
+
const sy0 = source.y + so;
|
|
116
|
+
const ty0 = target.y + to;
|
|
117
|
+
outLinks.push({
|
|
118
|
+
from: link.from,
|
|
119
|
+
to: link.to,
|
|
120
|
+
weight: link.weight,
|
|
121
|
+
width: w,
|
|
122
|
+
path: sankeyRibbonPath(source.x + source.w, target.x, sy0, sy0 + w, ty0, ty0 + w),
|
|
123
|
+
sourceColorVar: source.colorVar,
|
|
124
|
+
});
|
|
125
|
+
outOffset.set(source.id, so + w);
|
|
126
|
+
inOffset.set(target.id, to + w);
|
|
127
|
+
}
|
|
128
|
+
return { nodes, links: outLinks, layers: maxLayer + 1, width, height };
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=sankey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sankey.js","sourceRoot":"","sources":["../../src/geometry/sankey.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ScaleBand, ScaleLinear, ScalePoint, ScaleTime } from 'd3-scale';
|
|
2
|
+
export interface CategoricalScaleOptions {
|
|
3
|
+
paddingInner?: number;
|
|
4
|
+
paddingOuter?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Band scale for bars/columns. Mirrors (range flip) when `rtl` is true. */
|
|
7
|
+
export declare function categoricalBandScale(domain: readonly string[], width: number, rtl: boolean, options?: CategoricalScaleOptions): ScaleBand<string>;
|
|
8
|
+
/** Point scale for categorical line charts. Mirrors when `rtl` is true. */
|
|
9
|
+
export declare function categoricalPointScale(domain: readonly string[], width: number, rtl: boolean): ScalePoint<string>;
|
|
10
|
+
/** Time scale — NEVER mirrored, regardless of direction. */
|
|
11
|
+
export declare function timeScale(domain: readonly [Date, Date], width: number): ScaleTime<number, number>;
|
|
12
|
+
/** Linear y scale, pixel-down (range [height, 0]). */
|
|
13
|
+
export declare function linearYScale(domain: readonly [number, number], height: number): ScaleLinear<number, number>;
|
|
14
|
+
//# sourceMappingURL=scales.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales.d.ts","sourceRoot":"","sources":["../../src/geometry/scales.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,uBAA4B,GACpC,SAAS,CAAC,MAAM,CAAC,CAOnB;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAKhH;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAEjG;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAE3G"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scale constructors encoding the RTL policy (workplan/04-widget-registry.md
|
|
3
|
+
* §7.4 as corrected by 10-i18n-theming.md §5.5):
|
|
4
|
+
* - horizontal CATEGORICAL scales mirror in RTL (range flipped);
|
|
5
|
+
* - TIME axes NEVER mirror — oldest left, newest right, inside RTL pages.
|
|
6
|
+
* Geometry stays pure and DOM-free so scheduled-report workers can reuse it.
|
|
7
|
+
*/
|
|
8
|
+
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
|
9
|
+
/** Band scale for bars/columns. Mirrors (range flip) when `rtl` is true. */
|
|
10
|
+
export function categoricalBandScale(domain, width, rtl, options = {}) {
|
|
11
|
+
const { paddingInner = 0.3, paddingOuter = 0.15 } = options;
|
|
12
|
+
return scaleBand()
|
|
13
|
+
.domain([...domain])
|
|
14
|
+
.range(rtl ? [width, 0] : [0, width])
|
|
15
|
+
.paddingInner(paddingInner)
|
|
16
|
+
.paddingOuter(paddingOuter);
|
|
17
|
+
}
|
|
18
|
+
/** Point scale for categorical line charts. Mirrors when `rtl` is true. */
|
|
19
|
+
export function categoricalPointScale(domain, width, rtl) {
|
|
20
|
+
return scalePoint()
|
|
21
|
+
.domain([...domain])
|
|
22
|
+
.range(rtl ? [width, 0] : [0, width])
|
|
23
|
+
.padding(0.5);
|
|
24
|
+
}
|
|
25
|
+
/** Time scale — NEVER mirrored, regardless of direction. */
|
|
26
|
+
export function timeScale(domain, width) {
|
|
27
|
+
return scaleTime().domain([...domain]).range([0, width]);
|
|
28
|
+
}
|
|
29
|
+
/** Linear y scale, pixel-down (range [height, 0]). */
|
|
30
|
+
export function linearYScale(domain, height) {
|
|
31
|
+
return scaleLinear().domain([...domain]).range([height, 0]);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=scales.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales.js","sourceRoot":"","sources":["../../src/geometry/scales.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAQzE,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAClC,MAAyB,EACzB,KAAa,EACb,GAAY,EACZ,UAAmC,EAAE;IAErC,MAAM,EAAE,YAAY,GAAG,GAAG,EAAE,YAAY,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAC5D,OAAO,SAAS,EAAU;SACvB,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;SACnB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;SACpC,YAAY,CAAC,YAAY,CAAC;SAC1B,YAAY,CAAC,YAAY,CAAC,CAAC;AAChC,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB,CAAC,MAAyB,EAAE,KAAa,EAAE,GAAY;IAC1F,OAAO,UAAU,EAAU;SACxB,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;SACnB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;SACpC,OAAO,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,SAAS,CAAC,MAA6B,EAAE,KAAa;IACpE,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,MAAiC,EAAE,MAAc;IAC5E,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface SlopeInput {
|
|
2
|
+
label: string;
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SlopeLine {
|
|
7
|
+
index: number;
|
|
8
|
+
label: string;
|
|
9
|
+
a: number;
|
|
10
|
+
b: number;
|
|
11
|
+
xA: number;
|
|
12
|
+
yA: number;
|
|
13
|
+
xB: number;
|
|
14
|
+
yB: number;
|
|
15
|
+
/** b ≥ a → rising (pos tone); otherwise falling (danger tone). */
|
|
16
|
+
rising: boolean;
|
|
17
|
+
/** Two-point connector path. */
|
|
18
|
+
path: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface SlopeLayout {
|
|
21
|
+
lines: SlopeLine[];
|
|
22
|
+
/** Inline-start axis x (period A). */
|
|
23
|
+
xA: number;
|
|
24
|
+
/** Inline-end axis x (period B). */
|
|
25
|
+
xB: number;
|
|
26
|
+
yFor: (value: number) => number;
|
|
27
|
+
}
|
|
28
|
+
export interface SlopeLayoutOptions {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
rtl: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function layoutSlope(items: readonly SlopeInput[], options: SlopeLayoutOptions): SlopeLayout;
|
|
34
|
+
//# sourceMappingURL=slope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slope.d.ts","sourceRoot":"","sources":["../../src/geometry/slope.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,kEAAkE;IAClE,MAAM,EAAE,OAAO,CAAC;IAChB,gCAAgC;IAChC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAsClG"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure slope-chart layout (`chart-slope`, research/widget-registry.md §2): two
|
|
3
|
+
* period axes (A → B) with one connecting line per record, colored by
|
|
4
|
+
* direction, end labels. DOM-free. The two period columns are categorical, so
|
|
5
|
+
* they mirror in RTL (A stays at inline-start — the right edge under `rtl`).
|
|
6
|
+
*/
|
|
7
|
+
import { linePath } from './lineArea.js';
|
|
8
|
+
import { extent } from '../utils/stats.js';
|
|
9
|
+
export function layoutSlope(items, options) {
|
|
10
|
+
const { width, height, rtl } = options;
|
|
11
|
+
if (items.length === 0 || width <= 0 || height <= 0) {
|
|
12
|
+
return { lines: [], xA: 0, xB: width, yFor: () => 0 };
|
|
13
|
+
}
|
|
14
|
+
const values = items.flatMap((item) => [item.a, item.b]);
|
|
15
|
+
const [lo, hi] = extent(values) ?? [0, 1];
|
|
16
|
+
const pad = (hi - lo) * 0.08 || 1;
|
|
17
|
+
const domainLo = lo - pad;
|
|
18
|
+
const domainHi = hi + pad;
|
|
19
|
+
const span = domainHi - domainLo || 1;
|
|
20
|
+
const yFor = (value) => height - ((value - domainLo) / span) * height;
|
|
21
|
+
const xA = rtl ? width : 0;
|
|
22
|
+
const xB = rtl ? 0 : width;
|
|
23
|
+
const lines = items.map((item, index) => {
|
|
24
|
+
const yA = yFor(item.a);
|
|
25
|
+
const yB = yFor(item.b);
|
|
26
|
+
return {
|
|
27
|
+
index,
|
|
28
|
+
label: item.label,
|
|
29
|
+
a: item.a,
|
|
30
|
+
b: item.b,
|
|
31
|
+
xA,
|
|
32
|
+
yA,
|
|
33
|
+
xB,
|
|
34
|
+
yB,
|
|
35
|
+
rising: item.b >= item.a,
|
|
36
|
+
path: linePath([
|
|
37
|
+
{ x: xA, y: yA },
|
|
38
|
+
{ x: xB, y: yB },
|
|
39
|
+
]),
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
return { lines, xA, xB, yFor };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=slope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slope.js","sourceRoot":"","sources":["../../src/geometry/slope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAsC3C,MAAM,UAAU,WAAW,CAAC,KAA4B,EAAE,OAA2B;IACnF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;IACxD,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC;IAC1B,MAAM,QAAQ,GAAG,EAAE,GAAG,GAAG,CAAC;IAC1B,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ,IAAI,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC;IAEtF,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAE3B,MAAM,KAAK,GAAgB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACnD,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO;YACL,KAAK;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,EAAE;YACF,EAAE;YACF,EAAE;YACF,EAAE;YACF,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;YACxB,IAAI,EAAE,QAAQ,CAAC;gBACb,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;gBAChB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;aACjB,CAAC;SACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SparkBar {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
isLast: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Bar layout with fixed gaps; baseline at the bottom edge. */
|
|
9
|
+
export declare function sparkBars(values: readonly number[], width: number, height: number, gap?: number): SparkBar[];
|
|
10
|
+
export interface SparkLineLayout {
|
|
11
|
+
/** `points` attribute for an SVG polyline. */
|
|
12
|
+
points: string;
|
|
13
|
+
last: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
} | null;
|
|
17
|
+
}
|
|
18
|
+
/** Polyline layout with a small vertical inset so the stroke never clips. */
|
|
19
|
+
export declare function sparkLine(values: readonly number[], width: number, height: number, inset?: number): SparkLineLayout;
|
|
20
|
+
//# sourceMappingURL=sparkline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sparkline.d.ts","sourceRoot":"","sources":["../../src/geometry/sparkline.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,+DAA+D;AAC/D,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,QAAQ,EAAE,CAkBvG;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACvC;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,eAAe,CAa9G"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Pure micro-chart layout for sparklines (bar and line variants). */
|
|
2
|
+
import { extent } from '../utils/stats.js';
|
|
3
|
+
/** Bar layout with fixed gaps; baseline at the bottom edge. */
|
|
4
|
+
export function sparkBars(values, width, height, gap = 2) {
|
|
5
|
+
const n = values.length;
|
|
6
|
+
if (n === 0 || width <= 0 || height <= 0)
|
|
7
|
+
return [];
|
|
8
|
+
const [minValue, maxValue] = extent([...values]) ?? [0, 1];
|
|
9
|
+
const lo = Math.min(0, minValue);
|
|
10
|
+
const hi = Math.max(maxValue, lo + 1);
|
|
11
|
+
const span = hi - lo || 1;
|
|
12
|
+
const barWidth = Math.max(1, (width - gap * (n - 1)) / n);
|
|
13
|
+
return values.map((v, i) => {
|
|
14
|
+
const h = Math.max(1, ((v - lo) / span) * height);
|
|
15
|
+
return {
|
|
16
|
+
x: i * (barWidth + gap),
|
|
17
|
+
y: height - h,
|
|
18
|
+
width: barWidth,
|
|
19
|
+
height: h,
|
|
20
|
+
isLast: i === n - 1,
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Polyline layout with a small vertical inset so the stroke never clips. */
|
|
25
|
+
export function sparkLine(values, width, height, inset = 2) {
|
|
26
|
+
const n = values.length;
|
|
27
|
+
if (n === 0 || width <= 0 || height <= 0)
|
|
28
|
+
return { points: '', last: null };
|
|
29
|
+
const [minValue, maxValue] = extent([...values]) ?? [0, 1];
|
|
30
|
+
const span = maxValue - minValue || 1;
|
|
31
|
+
const innerHeight = Math.max(1, height - inset * 2);
|
|
32
|
+
const step = n > 1 ? width / (n - 1) : 0;
|
|
33
|
+
const coords = values.map((v, i) => ({
|
|
34
|
+
x: n > 1 ? i * step : width / 2,
|
|
35
|
+
y: inset + innerHeight - ((v - minValue) / span) * innerHeight,
|
|
36
|
+
}));
|
|
37
|
+
const points = coords.map((c) => `${round2(c.x)},${round2(c.y)}`).join(' ');
|
|
38
|
+
return { points, last: coords.at(-1) ?? null };
|
|
39
|
+
}
|
|
40
|
+
function round2(value) {
|
|
41
|
+
return Math.round(value * 100) / 100;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=sparkline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sparkline.js","sourceRoot":"","sources":["../../src/geometry/sparkline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure 100%-stacked horizontal bar layout (`chart-stacked-bar-100`,
|
|
3
|
+
* research/widget-registry.md §2): one bar split into shares with small gaps.
|
|
4
|
+
* DOM-free. Segment order mirrors in RTL (first share at inline-start = the
|
|
5
|
+
* right edge under `rtl`).
|
|
6
|
+
*/
|
|
7
|
+
export interface StackedInput {
|
|
8
|
+
key: string;
|
|
9
|
+
label: string;
|
|
10
|
+
value: number;
|
|
11
|
+
}
|
|
12
|
+
export interface StackedSegment {
|
|
13
|
+
index: number;
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
value: number;
|
|
17
|
+
/** Share of the total, 0..1. */
|
|
18
|
+
share: number;
|
|
19
|
+
x: number;
|
|
20
|
+
width: number;
|
|
21
|
+
}
|
|
22
|
+
export interface StackedLayout {
|
|
23
|
+
segments: StackedSegment[];
|
|
24
|
+
total: number;
|
|
25
|
+
}
|
|
26
|
+
export interface StackedLayoutOptions {
|
|
27
|
+
width: number;
|
|
28
|
+
rtl: boolean;
|
|
29
|
+
/** Gap between segments in px (default 2). */
|
|
30
|
+
gap?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function layoutStacked100(items: readonly StackedInput[], options: StackedLayoutOptions): StackedLayout;
|
|
33
|
+
//# sourceMappingURL=stacked100.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stacked100.d.ts","sourceRoot":"","sources":["../../src/geometry/stacked100.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;IACb,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA4B7G"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure 100%-stacked horizontal bar layout (`chart-stacked-bar-100`,
|
|
3
|
+
* research/widget-registry.md §2): one bar split into shares with small gaps.
|
|
4
|
+
* DOM-free. Segment order mirrors in RTL (first share at inline-start = the
|
|
5
|
+
* right edge under `rtl`).
|
|
6
|
+
*/
|
|
7
|
+
export function layoutStacked100(items, options) {
|
|
8
|
+
const { width, rtl, gap = 2 } = options;
|
|
9
|
+
const n = items.length;
|
|
10
|
+
if (n === 0 || width <= 0)
|
|
11
|
+
return { segments: [], total: 0 };
|
|
12
|
+
const total = items.reduce((sum, item) => sum + Math.max(0, item.value), 0);
|
|
13
|
+
const totalGap = gap * Math.max(0, n - 1);
|
|
14
|
+
const usableWidth = Math.max(0, width - totalGap);
|
|
15
|
+
let cursor = 0;
|
|
16
|
+
const segments = items.map((item, index) => {
|
|
17
|
+
const value = Math.max(0, item.value);
|
|
18
|
+
const share = total > 0 ? value / total : 1 / n;
|
|
19
|
+
const segWidth = usableWidth * share;
|
|
20
|
+
const inlineStart = cursor;
|
|
21
|
+
cursor += segWidth + gap;
|
|
22
|
+
return {
|
|
23
|
+
index,
|
|
24
|
+
key: item.key,
|
|
25
|
+
label: item.label,
|
|
26
|
+
value: item.value,
|
|
27
|
+
share,
|
|
28
|
+
x: rtl ? width - inlineStart - segWidth : inlineStart,
|
|
29
|
+
width: segWidth,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
return { segments, total };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=stacked100.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stacked100.js","sourceRoot":"","sources":["../../src/geometry/stacked100.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+BH,MAAM,UAAU,gBAAgB,CAAC,KAA8B,EAAE,OAA6B;IAC5F,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAE7D,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,MAAM,QAAQ,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,CAAC;IAElD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,QAAQ,GAAqB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAC;QACrC,MAAM,WAAW,GAAG,MAAM,CAAC;QAC3B,MAAM,IAAI,QAAQ,GAAG,GAAG,CAAC;QACzB,OAAO;YACL,KAAK;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK;YACL,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,WAAW;YACrD,KAAK,EAAE,QAAQ;SAChB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface StreamInputSeries {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
values: readonly number[];
|
|
5
|
+
}
|
|
6
|
+
export interface StreamBand {
|
|
7
|
+
key: string;
|
|
8
|
+
label: string;
|
|
9
|
+
/** Per-index lower edge in value space. */
|
|
10
|
+
low: number[];
|
|
11
|
+
/** Per-index upper edge in value space. */
|
|
12
|
+
high: number[];
|
|
13
|
+
}
|
|
14
|
+
export interface StreamStack {
|
|
15
|
+
bands: StreamBand[];
|
|
16
|
+
/** Value-space extent across every band edge (for the y scale). */
|
|
17
|
+
min: number;
|
|
18
|
+
max: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Stack series into bands. `silhouette` centers the total thickness on zero at
|
|
22
|
+
* each x (the classic streamgraph); `zero` stacks upward from a flat baseline.
|
|
23
|
+
* Negative inputs are clamped to zero (thicknesses are non-negative).
|
|
24
|
+
*/
|
|
25
|
+
export declare function stackStream(series: readonly StreamInputSeries[], offset?: 'silhouette' | 'zero'): StreamStack;
|
|
26
|
+
/**
|
|
27
|
+
* Closed area path between per-index lower/upper PIXEL edges. `xs`, `low`,
|
|
28
|
+
* `high` are parallel pixel arrays; `smooth` uses a basis spline for the
|
|
29
|
+
* organic stream look.
|
|
30
|
+
*/
|
|
31
|
+
export declare function bandAreaPath(xs: readonly number[], low: readonly number[], high: readonly number[], smooth?: boolean): string | null;
|
|
32
|
+
//# sourceMappingURL=streamStack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamStack.d.ts","sourceRoot":"","sources":["../../src/geometry/streamStack.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,MAAM,GAAE,YAAY,GAAG,MAAqB,GAC3C,WAAW,CA4Bb;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,SAAS,MAAM,EAAE,EACrB,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,UAAO,GACZ,MAAM,GAAG,IAAI,CAOf"}
|