@carto/ps-react-ui 5.4.0 → 5.5.0

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 (67) hide show
  1. package/dist/{actions-Dhlz_adK.js → actions-CwjqEm8Y.js} +56 -46
  2. package/dist/actions-CwjqEm8Y.js.map +1 -0
  3. package/dist/item-color-ZYxH3NM5.js +20 -0
  4. package/dist/item-color-ZYxH3NM5.js.map +1 -0
  5. package/dist/types/widgets-v2/bar/types.d.ts +6 -0
  6. package/dist/types/widgets-v2/histogram/bins.d.ts +7 -0
  7. package/dist/types/widgets-v2/histogram/download.d.ts +2 -2
  8. package/dist/types/widgets-v2/histogram/index.d.ts +2 -1
  9. package/dist/types/widgets-v2/histogram/options.d.ts +1 -1
  10. package/dist/types/widgets-v2/histogram/transforms.d.ts +5 -4
  11. package/dist/types/widgets-v2/histogram/types.d.ts +22 -5
  12. package/dist/types/widgets-v2/index.d.ts +1 -1
  13. package/dist/types/widgets-v2/pie/types.d.ts +6 -0
  14. package/dist/types/widgets-v2/scatterplot/download.d.ts +2 -2
  15. package/dist/types/widgets-v2/scatterplot/index.d.ts +2 -1
  16. package/dist/types/widgets-v2/scatterplot/points.d.ts +7 -0
  17. package/dist/types/widgets-v2/scatterplot/transforms.d.ts +4 -3
  18. package/dist/types/widgets-v2/scatterplot/types.d.ts +21 -2
  19. package/dist/types/widgets-v2/timeseries/types.d.ts +6 -0
  20. package/dist/types/widgets-v2/utils/item-color.d.ts +26 -0
  21. package/dist/widgets-v2/actions.js +1 -1
  22. package/dist/widgets-v2/bar.js +66 -57
  23. package/dist/widgets-v2/bar.js.map +1 -1
  24. package/dist/widgets-v2/histogram.js +103 -71
  25. package/dist/widgets-v2/histogram.js.map +1 -1
  26. package/dist/widgets-v2/pie.js +98 -78
  27. package/dist/widgets-v2/pie.js.map +1 -1
  28. package/dist/widgets-v2/scatterplot.js +119 -79
  29. package/dist/widgets-v2/scatterplot.js.map +1 -1
  30. package/dist/widgets-v2/timeseries.js +70 -61
  31. package/dist/widgets-v2/timeseries.js.map +1 -1
  32. package/dist/widgets-v2.js +1 -1
  33. package/dist/widgets-v2.js.map +1 -1
  34. package/package.json +3 -3
  35. package/src/widgets-v2/actions/fullscreen/fullscreen-store.test.tsx +77 -13
  36. package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +48 -5
  37. package/src/widgets-v2/bar/options.test.ts +106 -0
  38. package/src/widgets-v2/bar/options.ts +25 -14
  39. package/src/widgets-v2/bar/types.ts +6 -0
  40. package/src/widgets-v2/histogram/bins.ts +22 -0
  41. package/src/widgets-v2/histogram/download.test.ts +10 -0
  42. package/src/widgets-v2/histogram/download.ts +7 -3
  43. package/src/widgets-v2/histogram/index.ts +3 -0
  44. package/src/widgets-v2/histogram/options.test.ts +62 -0
  45. package/src/widgets-v2/histogram/options.ts +38 -17
  46. package/src/widgets-v2/histogram/transforms.test.ts +21 -7
  47. package/src/widgets-v2/histogram/transforms.ts +26 -12
  48. package/src/widgets-v2/histogram/types.ts +25 -5
  49. package/src/widgets-v2/index.ts +6 -1
  50. package/src/widgets-v2/pie/options.test.ts +91 -0
  51. package/src/widgets-v2/pie/options.ts +50 -20
  52. package/src/widgets-v2/pie/types.ts +6 -0
  53. package/src/widgets-v2/scatterplot/download.test.ts +9 -0
  54. package/src/widgets-v2/scatterplot/download.ts +5 -3
  55. package/src/widgets-v2/scatterplot/index.ts +3 -0
  56. package/src/widgets-v2/scatterplot/options.test.ts +78 -0
  57. package/src/widgets-v2/scatterplot/options.ts +35 -11
  58. package/src/widgets-v2/scatterplot/points.ts +35 -0
  59. package/src/widgets-v2/scatterplot/transforms.test.ts +19 -0
  60. package/src/widgets-v2/scatterplot/transforms.ts +23 -18
  61. package/src/widgets-v2/scatterplot/types.ts +25 -2
  62. package/src/widgets-v2/timeseries/options.test.ts +65 -0
  63. package/src/widgets-v2/timeseries/options.ts +31 -14
  64. package/src/widgets-v2/timeseries/types.ts +6 -0
  65. package/src/widgets-v2/utils/item-color.test.ts +74 -0
  66. package/src/widgets-v2/utils/item-color.ts +55 -0
  67. package/dist/actions-Dhlz_adK.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bar.js","names":["<Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>","(height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n )","<Box sx={styles.container}>\n <Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>","(i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n )","i"],"sources":["../../src/widgets-v2/bar/options.ts","../../src/widgets-v2/bar/skeleton.tsx","../../src/widgets-v2/bar/download.tsx"],"sourcesContent":["import type { EChartsOption } from 'echarts'\nimport * as echarts from 'echarts'\nimport type { CallbackDataParams } from 'echarts/types/dist/shared'\nimport {\n buildAxisLabelStyle,\n buildGridConfig,\n buildLegendConfig,\n createTooltipFormatter,\n createTooltipPositioner,\n niceNum,\n} from '../../widgets/utils/chart-config'\nimport { ZOOM_LAYOUT } from '../actions/zoom-toggle'\nimport type { OptionFactory } from '../echart'\nimport { mergeOptions, resolveThemeColor } from '../utils'\nimport { positionDataZoomForLegend } from '../utils/data-zoom-layout'\nimport type {\n BarEChartsOption,\n BarOptionFactoryInput,\n BarOptionsInput,\n BarWidgetData,\n} from './types'\n\n/**\n * Builds the **structural** ECharts option for a bar widget — axes, grid,\n * tooltip styling, themed legend. Intentionally data-agnostic: no series,\n * no dataset, no `legend.show` (those depend on data and are added by the\n * option factory's merge phase). This separation is what lets data-side\n * pipeline transforms (Searcher, RelativeData) drive the rendered chart —\n * the merge happens at render time inside the Echart bridge.\n *\n * Styling matches the v1 `barConfig` look-and-feel: minimal axes (only\n * min/max y-labels rendered inside the plot via `niceNum`), themed tooltip,\n * scroll legend, and CARTO color palette. The y-axis min/max + label\n * formatter and the tooltip formatter are wired here for the no-data case;\n * {@link createBarOptionFactory} re-derives them at fusion time so reactive\n * formatter changes (RelativeData) and stack templates (StackToggle) flow\n * through to the chart.\n */\nexport function barOptions({\n theme,\n formatter,\n labelFormatter,\n}: BarOptionsInput): BarEChartsOption {\n // Closure shared between yAxis min/max callbacks and the label formatter,\n // so only the rounded extents are labelled (matches v1).\n let niceMin = 0\n let niceMax = 1\n\n return {\n grid: {\n left: parseInt(theme.spacing(1)),\n top: parseInt(theme.spacing(3)),\n right: parseInt(theme.spacing(1)),\n // Default: no legend. Merger bumps this when there are >1 series.\n ...buildGridConfig(false, theme),\n containLabel: true,\n },\n tooltip: {\n trigger: 'axis',\n backgroundColor: theme.palette.grey[900],\n borderWidth: 0,\n padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],\n textStyle: {\n color: theme.palette.common.white,\n fontSize: 11,\n fontFamily: theme.typography.caption.fontFamily,\n },\n axisPointer: { type: 'line' },\n position: createTooltipPositioner(theme),\n formatter: buildBarTooltipFormatter(formatter, labelFormatter),\n },\n // Legend styling baked here; `show` is toggled by the merger based on\n // series count.\n legend: {\n ...buildLegendConfig({ hasLegend: false, labelFormatter }),\n },\n axisPointer: { lineStyle: { color: theme.palette.grey[400] } },\n color: [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n xAxis: {\n type: 'category',\n axisLine: { show: false },\n axisTick: { show: false },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n padding: [parseInt(theme.spacing(0.5)), 0, 0, 0],\n margin: 0,\n // `interval: 0` makes every category label a candidate so `hideOverlap`\n // can pack the maximum number that physically fit; the category-axis\n // default (`'auto'`) sub-samples first and shows fewer than fit.\n interval: 0,\n hideOverlap: true,\n ...(labelFormatter && {\n formatter: (v: string | number) => String(labelFormatter(v)),\n }),\n },\n },\n yAxis: {\n type: 'value',\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLine: { show: false },\n axisTick: { show: false },\n splitLine: {\n show: true,\n lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },\n },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n margin: parseInt(theme.spacing(1)),\n show: true,\n showMaxLabel: true,\n showMinLabel: true,\n verticalAlign: 'bottom',\n inside: true,\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return formatter ? formatter(value) : String(value)\n },\n },\n },\n } as BarEChartsOption\n}\n\n/**\n * Returns the bar widget's {@link OptionFactory} — a single closure that\n * handles BOTH option-construction phases:\n *\n * - **Structural phase** (`option == null`) — builds the theme-aware\n * structural option via {@link barOptions}, optionally merging the\n * consumer-supplied `optionsOverride` on top. Called once by Provider\n * to seed `rawOptions` in the store; configTransforms (StackToggle /\n * ZoomToggle / BrushToggle) then mutate it via the pipeline middleware.\n * - **Merge phase** (`option != null`) — fuses post-pipeline `state.data`\n * (`BarWidgetData`) into the option via the dataset API: one dataset\n * per series, each series referencing its dataset by index, encoded\n * by `name` (x) and `value` (y). Spreads any series-template fields\n * already on the incoming option (e.g. `{ stack: 'total' }` from\n * `addStack`) into every emitted series so configTransforms compose\n * end-to-end. Reactive `ctx.formatter` / `ctx.labelFormatter` drive\n * the y-axis min/max-only label and the tooltip formatter at fusion\n * time so RelativeData's percent formatter flows through without a\n * structural rebuild.\n *\n * Stable identity when the inputs don't change (consumers should wrap the\n * call in `useMemo` keyed on the same inputs).\n */\nexport function createBarOptionFactory(\n options: BarOptionFactoryInput,\n): OptionFactory {\n const { theme, formatter, labelFormatter, optionsOverride } = options\n const series = options.series\n const selection = options.selection\n const selectionSet =\n selection && selection.length > 0\n ? new Set<string | number>(selection)\n : null\n return (option, data, ctx) => {\n // Structural phase: Provider seeds rawOptions with this branch. No data\n // is read; we just emit the theme-aware base (optionally with override).\n if (option == null) {\n const structural = barOptions({ theme, formatter, labelFormatter })\n return optionsOverride\n ? (mergeOptions(\n structural as unknown as Record<string, unknown>,\n optionsOverride as Partial<Record<string, unknown>>,\n ) as EChartsOption)\n : structural\n }\n\n const seriesArr = Array.isArray(data) ? (data as BarWidgetData) : []\n if (seriesArr.length === 0) {\n return { ...option, dataset: [], series: [] }\n }\n const hasLegend = seriesArr.length > 1\n const baseLegend =\n typeof option.legend === 'object' && !Array.isArray(option.legend)\n ? option.legend\n : {}\n const baseGrid =\n typeof option.grid === 'object' && !Array.isArray(option.grid)\n ? option.grid\n : {}\n const baseTooltip =\n typeof option.tooltip === 'object' && !Array.isArray(option.tooltip)\n ? option.tooltip\n : {}\n const baseYAxis =\n typeof option.yAxis === 'object' && !Array.isArray(option.yAxis)\n ? option.yAxis\n : {}\n\n const seriesTemplates = Array.isArray(option.series) ? option.series : []\n const broadcastTemplate = seriesTemplates[0] ?? {}\n\n // Reactive (live store) formatters from ctx — distinct from the\n // closure-time `formatter` / `labelFormatter` captured for the\n // structural-build branch above. RelativeData can install a percent\n // formatter on the store after the factory was constructed; the merge\n // phase reads `ctx` to pick that up.\n const liveFormatter = ctx?.formatter\n const liveLabelFormatter = ctx?.labelFormatter\n\n // Closure shared between the yAxis min/max callbacks and the label\n // formatter, so only the rounded extents are labelled (matches v1).\n // Delegating the extent to ECharts (rather than precomputing scalars\n // from the raw data) is what keeps stacked bars inside the plot: when\n // StackToggle marks the series, ECharts feeds the *post-stack* extent\n // to these callbacks, so `niceNum` rounds the stacked total instead of\n // a single series.\n let niceMin = 0\n let niceMax = 1\n\n // Zoom slider layout: when ZoomToggle has installed `dataZoom`, push the\n // slider above the legend (if any) and reserve room in the grid below.\n const dataZoomLayout = positionDataZoomForLegend(option.dataZoom, hasLegend)\n const fallbackBottom =\n typeof baseGrid.bottom === 'number' ? baseGrid.bottom : 24\n const baseBottom = hasLegend ? 56 : fallbackBottom\n const gridBottom = dataZoomLayout\n ? baseBottom + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap\n : baseBottom\n\n // When a selection is active, dim non-selected bars by routing the\n // resolved palette color through `modifyAlpha`. Per-row `itemStyle` on\n // dataset object-rows is silently ignored when `series.encode` is in\n // play — the callback approach is the standard ECharts pattern for\n // per-data styling derived from a dataset.\n //\n // We *always* emit `itemStyle.color` (a passthrough when nothing is\n // selected), not conditionally — dropping the key between renders\n // would let ECharts' default merge keep the previous callback alive\n // and bars would stay dimmed forever after an external clear. Always\n // emitting lets normal merge swap the callback in place, no\n // `replaceMerge` and no entry-animation flash on selection on/off.\n const dimItemStyle = {\n color: (params: CallbackDataParams) => {\n const base = params.color as string\n if (!selectionSet) return base\n const datum = params.value as { name?: string | number } | undefined\n const name = datum?.name ?? params.name\n return name != null && selectionSet.has(name)\n ? base\n : echarts.color.modifyAlpha(base, 0.15)\n },\n }\n\n return {\n ...option,\n dataset: seriesArr.map((s) => ({ source: s as readonly object[] })),\n series: seriesArr.map((_, i) => {\n const template =\n (seriesTemplates[i] as object | undefined) ??\n (broadcastTemplate as object)\n // Per-series `color` override: ECharts sets `params.color` from\n // `series[i].color` when resolving styles, so the dim callback\n // above keeps working — it just dims the user's colour rather\n // than the palette default.\n const overrideColor = resolveThemeColor(theme, series?.[i]?.color)\n return {\n ...(typeof template === 'object' ? template : {}),\n type: 'bar' as const,\n datasetIndex: i,\n name: series?.[i]?.name ?? `Series ${i + 1}`,\n encode: { x: 'name', y: 'value' },\n barMaxWidth: 100,\n emphasis: { focus: 'series' },\n itemStyle: dimItemStyle,\n ...(overrideColor ? { color: overrideColor } : {}),\n }\n }),\n legend: { ...baseLegend, show: hasLegend },\n grid: {\n ...baseGrid,\n bottom: gridBottom,\n },\n ...(dataZoomLayout ? { dataZoom: dataZoomLayout } : {}),\n yAxis: {\n ...baseYAxis,\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLabel: {\n ...((baseYAxis as { axisLabel?: object }).axisLabel ?? {}),\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return liveFormatter ? liveFormatter(value) : String(value)\n },\n },\n } as EChartsOption['yAxis'],\n tooltip: {\n ...baseTooltip,\n formatter: buildBarTooltipFormatter(liveFormatter, liveLabelFormatter),\n },\n } as EChartsOption\n }\n}\n\nfunction buildBarTooltipFormatter(\n formatter: ((value: number) => string) | undefined,\n labelFormatter: ((value: string | number) => string | number) | undefined,\n) {\n return createTooltipFormatter((item) => {\n const row = item.value as { name?: string | number; value?: number }\n const raw = row?.value\n const formattedValue =\n typeof raw === 'number' && formatter ? formatter(raw) : (raw ?? '')\n const marker = typeof item.marker === 'string' ? item.marker : ''\n const seriesName = item.seriesName ? `${item.seriesName}: ` : ''\n const name = labelFormatter\n ? String(labelFormatter(item.name ?? ''))\n : (item.name ?? '')\n return { name: String(name), seriesName, marker, value: formattedValue }\n })\n}\n","import { Box, Skeleton, type SxProps, type Theme } from '@mui/material'\n\nconst styles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexDirection: 'column',\n gap: ({ spacing }) => spacing(1),\n height: ({ spacing }) => spacing(38),\n },\n grid: {\n display: 'flex',\n justifyContent: 'space-between',\n flex: '1 1 auto',\n alignItems: 'flex-end',\n width: '100%',\n },\n bar: {\n flex: 1,\n maxWidth: ({ spacing }) => spacing(12),\n '& + &': {\n marginLeft: ({ spacing }) => spacing(1),\n },\n },\n legend: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(2),\n height: ({ spacing }) => spacing(5),\n },\n legendItem: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(1.5),\n },\n} satisfies Record<string, SxProps<Theme>>\n\nconst BAR_HEIGHTS = ['20%', '40%', '60%', '20%', '80%'] as const\n\n/**\n * Loading state for the Bar widget. Mirrors a bar chart's silhouette — five\n * vertical bars at staggered heights anchored to the bottom, plus a legend\n * stub — so the skeleton reads as \"a bar chart\" rather than a generic list.\n */\nexport function BarSkeleton() {\n return (\n <Box sx={styles.container}>\n <Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>\n )\n}\n","import {\n buildCsvDownloadItem,\n buildPngDownloadItem,\n type DownloadContext,\n type DownloadItem,\n} from '../actions/download'\nimport type { BarWidgetData } from './types'\n\nexport interface BarDownloadConfigArgs {\n filename: string\n getData: (ctx: DownloadContext) => BarWidgetData\n /**\n * Optional getter for the widget's capture element (registered by\n * `Widget.State` on its success path). When provided, a PNG item is\n * prepended to the returned list. Wire it to\n * `() => getCaptureEl(id)`.\n */\n getCaptureEl?: (ctx: DownloadContext) => HTMLElement | null\n /** PNG `pixelRatio` (default 2). */\n pngPixelRatio?: number\n /** PNG `backgroundColor` (default transparent). */\n pngBackgroundColor?: string | null\n}\n\n/**\n * Builds download items for the Bar widget. Always includes a CSV item\n * with one `name,value` block per series, separated by an empty row when\n * there are multiple. When `getCaptureEl` is supplied, also prepends a PNG\n * item that rasterises the captured element via `html2canvas`.\n */\nexport function createBarDownloadConfig(\n args: BarDownloadConfigArgs,\n): DownloadItem[] {\n const items: DownloadItem[] = []\n if (args.getCaptureEl) {\n items.push(\n buildPngDownloadItem({\n filename: args.filename,\n getCaptureEl: args.getCaptureEl,\n pixelRatio: args.pngPixelRatio,\n backgroundColor: args.pngBackgroundColor,\n }),\n )\n }\n items.push(\n buildCsvDownloadItem({\n filename: args.filename,\n getRows: (ctx) => {\n const data = args.getData(ctx)\n const rows: unknown[][] = []\n for (const [i, series] of data.entries()) {\n if (i > 0) rows.push([])\n rows.push(['name', 'value'])\n for (const d of series) rows.push([d.name, d.value])\n }\n return rows\n },\n }),\n )\n return items\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bar.js","names":["<Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>","(height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n )","<Box sx={styles.container}>\n <Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>","(i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n )","i"],"sources":["../../src/widgets-v2/bar/options.ts","../../src/widgets-v2/bar/skeleton.tsx","../../src/widgets-v2/bar/download.tsx"],"sourcesContent":["import type { EChartsOption } from 'echarts'\nimport * as echarts from 'echarts'\nimport type { CallbackDataParams } from 'echarts/types/dist/shared'\nimport {\n buildAxisLabelStyle,\n buildGridConfig,\n buildLegendConfig,\n createTooltipFormatter,\n createTooltipPositioner,\n niceNum,\n} from '../../widgets/utils/chart-config'\nimport { ZOOM_LAYOUT } from '../actions/zoom-toggle'\nimport type { OptionFactory } from '../echart'\nimport { mergeOptions, resolveThemeColor } from '../utils'\nimport { createItemColorResolver } from '../utils/item-color'\nimport { positionDataZoomForLegend } from '../utils/data-zoom-layout'\nimport type {\n BarEChartsOption,\n BarOptionFactoryInput,\n BarOptionsInput,\n BarWidgetData,\n} from './types'\n\n/**\n * Builds the **structural** ECharts option for a bar widget — axes, grid,\n * tooltip styling, themed legend. Intentionally data-agnostic: no series,\n * no dataset, no `legend.show` (those depend on data and are added by the\n * option factory's merge phase). This separation is what lets data-side\n * pipeline transforms (Searcher, RelativeData) drive the rendered chart —\n * the merge happens at render time inside the Echart bridge.\n *\n * Styling matches the v1 `barConfig` look-and-feel: minimal axes (only\n * min/max y-labels rendered inside the plot via `niceNum`), themed tooltip,\n * scroll legend, and CARTO color palette. The y-axis min/max + label\n * formatter and the tooltip formatter are wired here for the no-data case;\n * {@link createBarOptionFactory} re-derives them at fusion time so reactive\n * formatter changes (RelativeData) and stack templates (StackToggle) flow\n * through to the chart.\n */\nexport function barOptions({\n theme,\n formatter,\n labelFormatter,\n}: BarOptionsInput): BarEChartsOption {\n // Closure shared between yAxis min/max callbacks and the label formatter,\n // so only the rounded extents are labelled (matches v1).\n let niceMin = 0\n let niceMax = 1\n\n return {\n grid: {\n left: parseInt(theme.spacing(1)),\n top: parseInt(theme.spacing(3)),\n right: parseInt(theme.spacing(1)),\n // Default: no legend. Merger bumps this when there are >1 series.\n ...buildGridConfig(false, theme),\n containLabel: true,\n },\n tooltip: {\n trigger: 'axis',\n backgroundColor: theme.palette.grey[900],\n borderWidth: 0,\n padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],\n textStyle: {\n color: theme.palette.common.white,\n fontSize: 11,\n fontFamily: theme.typography.caption.fontFamily,\n },\n axisPointer: { type: 'line' },\n position: createTooltipPositioner(theme),\n formatter: buildBarTooltipFormatter(formatter, labelFormatter),\n },\n // Legend styling baked here; `show` is toggled by the merger based on\n // series count.\n legend: {\n ...buildLegendConfig({ hasLegend: false, labelFormatter }),\n },\n axisPointer: { lineStyle: { color: theme.palette.grey[400] } },\n color: [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n xAxis: {\n type: 'category',\n axisLine: { show: false },\n axisTick: { show: false },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n padding: [parseInt(theme.spacing(0.5)), 0, 0, 0],\n margin: 0,\n // `interval: 0` makes every category label a candidate so `hideOverlap`\n // can pack the maximum number that physically fit; the category-axis\n // default (`'auto'`) sub-samples first and shows fewer than fit.\n interval: 0,\n hideOverlap: true,\n ...(labelFormatter && {\n formatter: (v: string | number) => String(labelFormatter(v)),\n }),\n },\n },\n yAxis: {\n type: 'value',\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLine: { show: false },\n axisTick: { show: false },\n splitLine: {\n show: true,\n lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },\n },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n margin: parseInt(theme.spacing(1)),\n show: true,\n showMaxLabel: true,\n showMinLabel: true,\n verticalAlign: 'bottom',\n inside: true,\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return formatter ? formatter(value) : String(value)\n },\n },\n },\n } as BarEChartsOption\n}\n\n/**\n * Returns the bar widget's {@link OptionFactory} — a single closure that\n * handles BOTH option-construction phases:\n *\n * - **Structural phase** (`option == null`) — builds the theme-aware\n * structural option via {@link barOptions}, optionally merging the\n * consumer-supplied `optionsOverride` on top. Called once by Provider\n * to seed `rawOptions` in the store; configTransforms (StackToggle /\n * ZoomToggle / BrushToggle) then mutate it via the pipeline middleware.\n * - **Merge phase** (`option != null`) — fuses post-pipeline `state.data`\n * (`BarWidgetData`) into the option via the dataset API: one dataset\n * per series, each series referencing its dataset by index, encoded\n * by `name` (x) and `value` (y). Spreads any series-template fields\n * already on the incoming option (e.g. `{ stack: 'total' }` from\n * `addStack`) into every emitted series so configTransforms compose\n * end-to-end. Reactive `ctx.formatter` / `ctx.labelFormatter` drive\n * the y-axis min/max-only label and the tooltip formatter at fusion\n * time so RelativeData's percent formatter flows through without a\n * structural rebuild.\n *\n * Stable identity when the inputs don't change (consumers should wrap the\n * call in `useMemo` keyed on the same inputs).\n */\nexport function createBarOptionFactory(\n options: BarOptionFactoryInput,\n): OptionFactory {\n const { theme, formatter, labelFormatter, optionsOverride } = options\n const series = options.series\n const selection = options.selection\n const selectionSet =\n selection && selection.length > 0\n ? new Set<string | number>(selection)\n : null\n return (option, data, ctx) => {\n // Structural phase: Provider seeds rawOptions with this branch. No data\n // is read; we just emit the theme-aware base (optionally with override).\n if (option == null) {\n const structural = barOptions({ theme, formatter, labelFormatter })\n return optionsOverride\n ? (mergeOptions(\n structural as unknown as Record<string, unknown>,\n optionsOverride as Partial<Record<string, unknown>>,\n ) as EChartsOption)\n : structural\n }\n\n const seriesArr = Array.isArray(data) ? (data as BarWidgetData) : []\n if (seriesArr.length === 0) {\n return { ...option, dataset: [], series: [] }\n }\n const hasLegend = seriesArr.length > 1\n const baseLegend =\n typeof option.legend === 'object' && !Array.isArray(option.legend)\n ? option.legend\n : {}\n const baseGrid =\n typeof option.grid === 'object' && !Array.isArray(option.grid)\n ? option.grid\n : {}\n const baseTooltip =\n typeof option.tooltip === 'object' && !Array.isArray(option.tooltip)\n ? option.tooltip\n : {}\n const baseYAxis =\n typeof option.yAxis === 'object' && !Array.isArray(option.yAxis)\n ? option.yAxis\n : {}\n\n const seriesTemplates = Array.isArray(option.series) ? option.series : []\n const broadcastTemplate = seriesTemplates[0] ?? {}\n\n // Reactive (live store) formatters from ctx — distinct from the\n // closure-time `formatter` / `labelFormatter` captured for the\n // structural-build branch above. RelativeData can install a percent\n // formatter on the store after the factory was constructed; the merge\n // phase reads `ctx` to pick that up.\n const liveFormatter = ctx?.formatter\n const liveLabelFormatter = ctx?.labelFormatter\n\n // Closure shared between the yAxis min/max callbacks and the label\n // formatter, so only the rounded extents are labelled (matches v1).\n // Delegating the extent to ECharts (rather than precomputing scalars\n // from the raw data) is what keeps stacked bars inside the plot: when\n // StackToggle marks the series, ECharts feeds the *post-stack* extent\n // to these callbacks, so `niceNum` rounds the stacked total instead of\n // a single series.\n let niceMin = 0\n let niceMax = 1\n\n // Zoom slider layout: when ZoomToggle has installed `dataZoom`, push the\n // slider above the legend (if any) and reserve room in the grid below.\n const dataZoomLayout = positionDataZoomForLegend(option.dataZoom, hasLegend)\n const fallbackBottom =\n typeof baseGrid.bottom === 'number' ? baseGrid.bottom : 24\n const baseBottom = hasLegend ? 56 : fallbackBottom\n const gridBottom = dataZoomLayout\n ? baseBottom + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap\n : baseBottom\n\n // When a selection is active, dim non-selected bars by routing the\n // item's base colour through `modifyAlpha`. Per-row `itemStyle` on\n // dataset object-rows is silently ignored when `series.encode` is in\n // play — the callback approach is the standard ECharts pattern for\n // per-data styling derived from a dataset. The base colour is the\n // datum's `color`, else the template's `itemStyle.color`, else the\n // series colour / palette (`params.color`).\n //\n // We *always* emit `itemStyle.color` (a passthrough when nothing is\n // selected), not conditionally — dropping the key between renders\n // would let ECharts' default merge keep the previous callback alive\n // and bars would stay dimmed forever after an external clear. Always\n // emitting lets normal merge swap the callback in place, no\n // `replaceMerge` and no entry-animation flash on selection on/off.\n const makeDimItemStyle = (datums: readonly object[], template: unknown) => {\n const baseColor = createItemColorResolver({\n theme,\n datums,\n template,\n fallback: (params) => params.color as string,\n })\n return {\n color: (params: CallbackDataParams) => {\n const base = baseColor(params)\n if (!selectionSet) return base\n const datum = params.value as { name?: string | number } | undefined\n const name = datum?.name ?? params.name\n return name != null && selectionSet.has(name)\n ? base\n : echarts.color.modifyAlpha(base, 0.15)\n },\n }\n }\n\n return {\n ...option,\n dataset: seriesArr.map((s) => ({ source: s as readonly object[] })),\n series: seriesArr.map((s, i) => {\n const template =\n (seriesTemplates[i] as object | undefined) ??\n (broadcastTemplate as object)\n // Per-series `color` override: ECharts sets `params.color` from\n // `series[i].color` when resolving styles, so the dim callback\n // above keeps working — it just dims the user's colour rather\n // than the palette default.\n const overrideColor = resolveThemeColor(theme, series?.[i]?.color)\n return {\n ...(typeof template === 'object' ? template : {}),\n type: 'bar' as const,\n datasetIndex: i,\n name: series?.[i]?.name ?? `Series ${i + 1}`,\n encode: { x: 'name', y: 'value' },\n barMaxWidth: 100,\n emphasis: { focus: 'series' },\n itemStyle: makeDimItemStyle(s, template),\n ...(overrideColor ? { color: overrideColor } : {}),\n }\n }),\n legend: { ...baseLegend, show: hasLegend },\n grid: {\n ...baseGrid,\n bottom: gridBottom,\n },\n ...(dataZoomLayout ? { dataZoom: dataZoomLayout } : {}),\n yAxis: {\n ...baseYAxis,\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLabel: {\n ...((baseYAxis as { axisLabel?: object }).axisLabel ?? {}),\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return liveFormatter ? liveFormatter(value) : String(value)\n },\n },\n } as EChartsOption['yAxis'],\n tooltip: {\n ...baseTooltip,\n formatter: buildBarTooltipFormatter(liveFormatter, liveLabelFormatter),\n },\n } as EChartsOption\n }\n}\n\nfunction buildBarTooltipFormatter(\n formatter: ((value: number) => string) | undefined,\n labelFormatter: ((value: string | number) => string | number) | undefined,\n) {\n return createTooltipFormatter((item) => {\n const row = item.value as { name?: string | number; value?: number }\n const raw = row?.value\n const formattedValue =\n typeof raw === 'number' && formatter ? formatter(raw) : (raw ?? '')\n const marker = typeof item.marker === 'string' ? item.marker : ''\n const seriesName = item.seriesName ? `${item.seriesName}: ` : ''\n const name = labelFormatter\n ? String(labelFormatter(item.name ?? ''))\n : (item.name ?? '')\n return { name: String(name), seriesName, marker, value: formattedValue }\n })\n}\n","import { Box, Skeleton, type SxProps, type Theme } from '@mui/material'\n\nconst styles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexDirection: 'column',\n gap: ({ spacing }) => spacing(1),\n height: ({ spacing }) => spacing(38),\n },\n grid: {\n display: 'flex',\n justifyContent: 'space-between',\n flex: '1 1 auto',\n alignItems: 'flex-end',\n width: '100%',\n },\n bar: {\n flex: 1,\n maxWidth: ({ spacing }) => spacing(12),\n '& + &': {\n marginLeft: ({ spacing }) => spacing(1),\n },\n },\n legend: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(2),\n height: ({ spacing }) => spacing(5),\n },\n legendItem: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(1.5),\n },\n} satisfies Record<string, SxProps<Theme>>\n\nconst BAR_HEIGHTS = ['20%', '40%', '60%', '20%', '80%'] as const\n\n/**\n * Loading state for the Bar widget. Mirrors a bar chart's silhouette — five\n * vertical bars at staggered heights anchored to the bottom, plus a legend\n * stub — so the skeleton reads as \"a bar chart\" rather than a generic list.\n */\nexport function BarSkeleton() {\n return (\n <Box sx={styles.container}>\n <Box sx={styles.grid}>\n {BAR_HEIGHTS.map((height, i) => (\n <Skeleton\n key={`bar-${i}`}\n sx={styles.bar}\n variant='rectangular'\n height={height}\n />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>\n )\n}\n","import {\n buildCsvDownloadItem,\n buildPngDownloadItem,\n type DownloadContext,\n type DownloadItem,\n} from '../actions/download'\nimport type { BarWidgetData } from './types'\n\nexport interface BarDownloadConfigArgs {\n filename: string\n getData: (ctx: DownloadContext) => BarWidgetData\n /**\n * Optional getter for the widget's capture element (registered by\n * `Widget.State` on its success path). When provided, a PNG item is\n * prepended to the returned list. Wire it to\n * `() => getCaptureEl(id)`.\n */\n getCaptureEl?: (ctx: DownloadContext) => HTMLElement | null\n /** PNG `pixelRatio` (default 2). */\n pngPixelRatio?: number\n /** PNG `backgroundColor` (default transparent). */\n pngBackgroundColor?: string | null\n}\n\n/**\n * Builds download items for the Bar widget. Always includes a CSV item\n * with one `name,value` block per series, separated by an empty row when\n * there are multiple. When `getCaptureEl` is supplied, also prepends a PNG\n * item that rasterises the captured element via `html2canvas`.\n */\nexport function createBarDownloadConfig(\n args: BarDownloadConfigArgs,\n): DownloadItem[] {\n const items: DownloadItem[] = []\n if (args.getCaptureEl) {\n items.push(\n buildPngDownloadItem({\n filename: args.filename,\n getCaptureEl: args.getCaptureEl,\n pixelRatio: args.pngPixelRatio,\n backgroundColor: args.pngBackgroundColor,\n }),\n )\n }\n items.push(\n buildCsvDownloadItem({\n filename: args.filename,\n getRows: (ctx) => {\n const data = args.getData(ctx)\n const rows: unknown[][] = []\n for (const [i, series] of data.entries()) {\n if (i > 0) rows.push([])\n rows.push(['name', 'value'])\n for (const d of series) rows.push([d.name, d.value])\n }\n return rows\n },\n }),\n )\n return items\n}\n"],"mappings":"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"}
@@ -2,12 +2,30 @@ import { r as e } from "../zoom-toggle-IsF8BnAp.js";
2
2
  import { n as t, t as n } from "../download-DGgFOaXj.js";
3
3
  import { n as r, r as i, t as a } from "../utils-Dr6gf3vD.js";
4
4
  import { a as o, d as s, f as c, i as l, s as u, u as d } from "../chart-config-B_fdIcD3.js";
5
- import { c as f } from "react/compiler-runtime";
6
- import { Box as p, Skeleton as m } from "@mui/material";
7
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
8
- import * as _ from "echarts";
5
+ import { t as f } from "../item-color-ZYxH3NM5.js";
6
+ import { c as p } from "react/compiler-runtime";
7
+ import { Box as m, Skeleton as h } from "@mui/material";
8
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
9
+ import * as v from "echarts";
10
+ //#region src/widgets-v2/histogram/bins.ts
11
+ function y(e) {
12
+ return typeof e == "number" ? e : e.value;
13
+ }
14
+ function b(e) {
15
+ if (typeof e == "number") return Number.isFinite(e);
16
+ if (typeof e != "object" || !e) return !1;
17
+ let t = e.value;
18
+ return typeof t == "number" && Number.isFinite(t);
19
+ }
20
+ function x(e, t) {
21
+ return typeof e == "number" ? t : {
22
+ ...e,
23
+ value: t
24
+ };
25
+ }
26
+ //#endregion
9
27
  //#region src/widgets-v2/histogram/options.ts
10
- function v({ theme: e, formatter: t }) {
28
+ function S({ theme: e, formatter: t }) {
11
29
  let n = 0, r = 1;
12
30
  return {
13
31
  grid: {
@@ -29,7 +47,7 @@ function v({ theme: e, formatter: t }) {
29
47
  },
30
48
  axisPointer: { type: "line" },
31
49
  position: s(e),
32
- formatter: b(t)
50
+ formatter: w(t)
33
51
  },
34
52
  legend: { ...u({ hasLegend: !1 }) },
35
53
  axisPointer: { lineStyle: { color: e.palette.grey[400] } },
@@ -74,14 +92,14 @@ function v({ theme: e, formatter: t }) {
74
92
  }
75
93
  };
76
94
  }
77
- function y(t) {
78
- let { theme: n, formatter: o, ticks: s, series: l, labelFormatter: u, selection: d } = t, f = t.optionsOverride, p = t.lockedItems, m = d && d.length > 0 ? new Set(d) : null, h = [];
95
+ function C(t) {
96
+ let { theme: n, formatter: o, ticks: s, series: l, labelFormatter: u, selection: d } = t, p = t.optionsOverride, m = t.lockedItems, h = d && d.length > 0 ? new Set(d) : null, g = [];
79
97
  for (let e = 0; e < s.length - 1; e++) {
80
- let t = s[e], n = s[e + 1], r = `${x(t)}–${x(n)}`;
81
- h.push(u ? u(r) : r);
98
+ let t = s[e], n = s[e + 1], r = `${T(t)}–${T(n)}`;
99
+ g.push(u ? u(r) : r);
82
100
  }
83
- let g = p && p.length > 0 ? (() => {
84
- let e = p.map(Number), t = h[Math.min(...e)], n = h[Math.max(...e)];
101
+ let _ = m && m.length > 0 ? (() => {
102
+ let e = m.map(Number), t = g[Math.min(...e)], n = g[Math.max(...e)];
85
103
  return t != null && n != null ? {
86
104
  min: t,
87
105
  max: n
@@ -89,37 +107,48 @@ function y(t) {
89
107
  })() : null;
90
108
  return (t, s, u) => {
91
109
  if (t == null) {
92
- let e = v({
110
+ let e = S({
93
111
  theme: n,
94
112
  formatter: o
95
113
  });
96
- return f ? i(e, f) : e;
114
+ return p ? i(e, p) : e;
97
115
  }
98
116
  let d = Array.isArray(s) ? s : [];
99
- if (d.length === 0 || h.length === 0) return {
117
+ if (d.length === 0 || g.length === 0) return {
100
118
  ...t,
101
119
  dataset: [],
102
120
  series: []
103
121
  };
104
- let p = d.length > 1, y = Array.isArray(t.series) ? t.series : [], x = y[0] ?? {}, S = typeof t.yAxis == "object" && !Array.isArray(t.yAxis) ? t.yAxis : {}, C = typeof t.grid == "object" && !Array.isArray(t.grid) ? t.grid : {}, w = typeof t.tooltip == "object" && !Array.isArray(t.tooltip) ? t.tooltip : {}, T = typeof t.legend == "object" && !Array.isArray(t.legend) ? t.legend : {}, E = u?.formatter, D = 0, O = 1, k = r(t.dataZoom, p), A = typeof C.bottom == "number" ? C.bottom : 24, j = p ? 56 : A, M = k ? j + e.sliderHeight + e.sliderGap : j, N = { color: (e) => {
105
- let t = e.color;
106
- return m ? m.has(e.dataIndex) ? t : _.color.modifyAlpha(t, .15) : t;
107
- } }, P = Array.isArray(t.xAxis) ? t.xAxis[0] : t.xAxis, F = {
108
- min: g?.min ?? P?.min ?? null,
109
- max: g?.max ?? P?.max ?? null
110
- }, I = Array.isArray(t.xAxis) ? [{
122
+ let m = d.length > 1, b = Array.isArray(t.series) ? t.series : [], x = b[0] ?? {}, C = typeof t.yAxis == "object" && !Array.isArray(t.yAxis) ? t.yAxis : {}, T = typeof t.grid == "object" && !Array.isArray(t.grid) ? t.grid : {}, E = typeof t.tooltip == "object" && !Array.isArray(t.tooltip) ? t.tooltip : {}, D = typeof t.legend == "object" && !Array.isArray(t.legend) ? t.legend : {}, O = u?.formatter, k = 0, A = 1, j = r(t.dataZoom, m), M = typeof T.bottom == "number" ? T.bottom : 24, N = m ? 56 : M, P = j ? N + e.sliderHeight + e.sliderGap : N, F = (e, t) => {
123
+ let r = f({
124
+ theme: n,
125
+ datums: e,
126
+ template: t,
127
+ fallback: (e) => e.color
128
+ });
129
+ return { color: (e) => {
130
+ let t = r(e);
131
+ return h ? h.has(e.dataIndex) ? t : v.color.modifyAlpha(t, .15) : t;
132
+ } };
133
+ }, I = Array.isArray(t.xAxis) ? t.xAxis[0] : t.xAxis, L = {
134
+ min: _?.min ?? I?.min ?? null,
135
+ max: _?.max ?? I?.max ?? null
136
+ }, R = Array.isArray(t.xAxis) ? [{
111
137
  ...t.xAxis[0],
112
- ...F
138
+ ...L
113
139
  }, ...t.xAxis.slice(1)] : {
114
140
  ...typeof t.xAxis == "object" ? t.xAxis : {},
115
- ...F
141
+ ...L
116
142
  };
117
143
  return {
118
144
  ...t,
119
- xAxis: I,
120
- dataset: d.map((e) => ({ source: h.map((t, n) => [t, e[n] ?? 0]) })),
145
+ xAxis: R,
146
+ dataset: d.map((e) => ({ source: g.map((t, n) => {
147
+ let r = e[n];
148
+ return [t, r == null ? 0 : y(r)];
149
+ }) })),
121
150
  series: d.map((e, t) => {
122
- let r = y[t] ?? x, i = a(n, l?.[t]?.color);
151
+ let r = b[t] ?? x, i = a(n, l?.[t]?.color);
123
152
  return {
124
153
  ...typeof r == "object" ? r : {},
125
154
  type: "bar",
@@ -131,36 +160,36 @@ function y(t) {
131
160
  },
132
161
  barCategoryGap: "0%",
133
162
  emphasis: { focus: "series" },
134
- itemStyle: N,
163
+ itemStyle: F(e, r),
135
164
  ...i ? { color: i } : {}
136
165
  };
137
166
  }),
138
167
  legend: {
139
- ...T,
140
- show: p
168
+ ...D,
169
+ show: m
141
170
  },
142
171
  grid: {
143
- ...C,
144
- bottom: M
172
+ ...T,
173
+ bottom: P
145
174
  },
146
- ...k ? { dataZoom: k } : {},
175
+ ...j ? { dataZoom: j } : {},
147
176
  yAxis: {
148
- ...S,
149
- min: (e) => (D = e.min < 0 ? c(e.min) : 0, D),
150
- max: (e) => (O = e.max <= 0 ? 1 : c(e.max), O),
177
+ ...C,
178
+ min: (e) => (k = e.min < 0 ? c(e.min) : 0, k),
179
+ max: (e) => (A = e.max <= 0 ? 1 : c(e.max), A),
151
180
  axisLabel: {
152
- ...S.axisLabel ?? {},
153
- formatter: (e) => e !== O && e !== D || e === 0 ? "" : E ? E(e) : String(e)
181
+ ...C.axisLabel ?? {},
182
+ formatter: (e) => e !== A && e !== k || e === 0 ? "" : O ? O(e) : String(e)
154
183
  }
155
184
  },
156
185
  tooltip: {
157
- ...w,
158
- formatter: b(E)
186
+ ...E,
187
+ formatter: w(O)
159
188
  }
160
189
  };
161
190
  };
162
191
  }
163
- function b(e) {
192
+ function w(e) {
164
193
  return d((t) => {
165
194
  let n = t.value?.[1], r = typeof n == "number" && e ? e(n) : n ?? "", i = typeof t.marker == "string" ? t.marker : "", a = t.seriesName ? `${t.seriesName}: ` : "";
166
195
  return {
@@ -171,12 +200,12 @@ function b(e) {
171
200
  };
172
201
  });
173
202
  }
174
- function x(e) {
203
+ function T(e) {
175
204
  return Number.isInteger(e) ? String(e) : Number(e.toFixed(2)).toString();
176
205
  }
177
206
  //#endregion
178
207
  //#region src/widgets-v2/histogram/skeleton.tsx
179
- var S = {
208
+ var E = {
180
209
  container: {
181
210
  display: "flex",
182
211
  alignItems: "center",
@@ -203,58 +232,58 @@ var S = {
203
232
  alignItems: "center",
204
233
  gap: ({ spacing: e }) => e(1.5)
205
234
  }
206
- }, C = (e) => ({
235
+ }, D = (e) => ({
207
236
  flex: 1,
208
237
  height: `${e}%`,
209
238
  minWidth: 8
210
239
  });
211
- function w(e) {
212
- let t = f(15), { bins: n } = e, r = n === void 0 ? 12 : n, i, a, o, s, c;
240
+ function O(e) {
241
+ let t = p(15), { bins: n } = e, r = n === void 0 ? 12 : n, i, a, o, s, c;
213
242
  if (t[0] !== r) {
214
243
  let e = Array.from({ length: r }, (e, t) => {
215
244
  let n = t / Math.max(1, r - 1);
216
245
  return Math.round(20 + 80 * Math.exp(-((n - .5) ** 2) * 8));
217
246
  });
218
- a = p, c = S.container, i = p, o = S.grid, s = e.map(E), t[0] = r, t[1] = i, t[2] = a, t[3] = o, t[4] = s, t[5] = c;
247
+ a = m, c = E.container, i = m, o = E.grid, s = e.map(A), t[0] = r, t[1] = i, t[2] = a, t[3] = o, t[4] = s, t[5] = c;
219
248
  } else i = t[1], a = t[2], o = t[3], s = t[4], c = t[5];
220
249
  let l;
221
- t[6] !== i || t[7] !== o || t[8] !== s ? (l = /* @__PURE__ */ h(i, {
250
+ t[6] !== i || t[7] !== o || t[8] !== s ? (l = /* @__PURE__ */ g(i, {
222
251
  sx: o,
223
252
  children: s
224
253
  }), t[6] = i, t[7] = o, t[8] = s, t[9] = l) : l = t[9];
225
254
  let u;
226
- t[10] === Symbol.for("react.memo_cache_sentinel") ? (u = /* @__PURE__ */ h(p, {
227
- sx: S.legend,
228
- children: [0, 1].map(T)
255
+ t[10] === Symbol.for("react.memo_cache_sentinel") ? (u = /* @__PURE__ */ g(m, {
256
+ sx: E.legend,
257
+ children: [0, 1].map(k)
229
258
  }), t[10] = u) : u = t[10];
230
259
  let d;
231
- return t[11] !== a || t[12] !== c || t[13] !== l ? (d = /* @__PURE__ */ g(a, {
260
+ return t[11] !== a || t[12] !== c || t[13] !== l ? (d = /* @__PURE__ */ _(a, {
232
261
  sx: c,
233
262
  children: [l, u]
234
263
  }), t[11] = a, t[12] = c, t[13] = l, t[14] = d) : d = t[14], d;
235
264
  }
236
- function T(e) {
237
- return /* @__PURE__ */ g(p, {
238
- sx: S.legendItem,
239
- children: [/* @__PURE__ */ h(m, {
265
+ function k(e) {
266
+ return /* @__PURE__ */ _(m, {
267
+ sx: E.legendItem,
268
+ children: [/* @__PURE__ */ g(h, {
240
269
  variant: "circular",
241
270
  width: 8,
242
271
  height: 8
243
- }), /* @__PURE__ */ h(m, {
272
+ }), /* @__PURE__ */ g(h, {
244
273
  width: 48,
245
274
  height: 8
246
275
  })]
247
276
  }, `legend-${e}`);
248
277
  }
249
- function E(e, t) {
250
- return /* @__PURE__ */ h(m, {
278
+ function A(e, t) {
279
+ return /* @__PURE__ */ g(h, {
251
280
  variant: "rectangular",
252
- sx: C(e)
281
+ sx: D(e)
253
282
  }, `bar-${t}`);
254
283
  }
255
284
  //#endregion
256
285
  //#region src/widgets-v2/histogram/download.ts
257
- function D(e) {
286
+ function j(e) {
258
287
  let r = [];
259
288
  return e.getCaptureEl && r.push(t({
260
289
  filename: e.filename,
@@ -269,7 +298,10 @@ function D(e) {
269
298
  let o = [a];
270
299
  for (let e = 0; e < Math.max(0, r.length - 1); e++) {
271
300
  let t = [r[e], r[e + 1]];
272
- for (let r = 0; r < i; r++) t.push(n[r]?.[e] ?? 0);
301
+ for (let r = 0; r < i; r++) {
302
+ let i = n[r]?.[e];
303
+ t.push(i == null ? 0 : y(i));
304
+ }
273
305
  o.push(t);
274
306
  }
275
307
  return o;
@@ -278,20 +310,20 @@ function D(e) {
278
310
  }
279
311
  //#endregion
280
312
  //#region src/widgets-v2/histogram/transforms.ts
281
- var O = (e) => Array.isArray(e) ? e.map((e) => {
282
- if (!k(e)) return e;
283
- let t = e.reduce((e, t) => e + Math.abs(t), 0);
284
- return t <= 0 ? e : e.map((e) => e / t * 100);
313
+ var M = (e) => Array.isArray(e) ? e.map((e) => {
314
+ if (!N(e)) return e;
315
+ let t = e.reduce((e, t) => e + Math.abs(y(t)), 0);
316
+ return t <= 0 ? e : e.map((e) => x(e, y(e) / t * 100));
285
317
  }) : e;
286
- function k(e) {
287
- return Array.isArray(e) ? e.every((e) => typeof e == "number" && Number.isFinite(e)) : !1;
318
+ function N(e) {
319
+ return Array.isArray(e) && e.every(b);
288
320
  }
289
- function A(e, t) {
321
+ function P(e, t) {
290
322
  if (t.length === 0 || !Array.isArray(e)) return e;
291
323
  let n = new Set(t.map(Number));
292
- return e.map((e) => k(e) ? e.map((e, t) => n.has(t) ? e : 0) : e);
324
+ return e.map((e) => N(e) ? e.map((e, t) => n.has(t) ? e : x(e, 0)) : e);
293
325
  }
294
326
  //#endregion
295
- export { w as HistogramSkeleton, D as createHistogramDownloadConfig, y as createHistogramOptionFactory, A as filterHistogramByLockedBins, v as histogramOptions, O as toRelativeHistogramData };
327
+ export { O as HistogramSkeleton, j as createHistogramDownloadConfig, C as createHistogramOptionFactory, P as filterHistogramByLockedBins, y as getHistogramBinValue, S as histogramOptions, M as toRelativeHistogramData };
296
328
 
297
329
  //# sourceMappingURL=histogram.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"histogram.js","names":["{ bins = 12 }: HistogramSkeletonProps","bins = 12","Box","styles.grid","heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))","styles.container","(h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n )","<Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>","<Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>","(i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n )","<Box sx={styles.container}>\n <Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>","i"],"sources":["../../src/widgets-v2/histogram/options.ts","../../src/widgets-v2/histogram/skeleton.tsx","../../src/widgets-v2/histogram/download.ts","../../src/widgets-v2/histogram/transforms.ts"],"sourcesContent":["import type { EChartsOption } from 'echarts'\nimport * as echarts from 'echarts'\nimport type { CallbackDataParams } from 'echarts/types/dist/shared'\nimport {\n buildAxisLabelStyle,\n buildGridConfig,\n buildLegendConfig,\n createTooltipFormatter,\n createTooltipPositioner,\n niceNum,\n} from '../../widgets/utils/chart-config'\nimport { ZOOM_LAYOUT } from '../actions/zoom-toggle'\nimport type { OptionFactory } from '../echart'\nimport { mergeOptions, resolveThemeColor } from '../utils'\nimport { positionDataZoomForLegend } from '../utils/data-zoom-layout'\nimport type {\n HistogramEChartsOption,\n HistogramOptionFactoryInput,\n HistogramOptionsInput,\n HistogramWidgetData,\n} from './types'\n\n/**\n * Builds the **structural** ECharts option for a histogram widget — axes,\n * grid, tooltip, value-axis label formatter, themed legend. Mirrors\n * {@link import('../bar/options').barOptions} so the two widgets share v1\n * look-and-feel: themed dark tooltip with positioner, CARTO color palette,\n * structural legend (`show` toggled by the merger based on series count),\n * y-axis labels rendered inside the plot via the `niceNum` closure pattern.\n *\n * Intentional deviations from bar (histogram-specific):\n * - **No `xAxis.axisLabel.formatter`.** Bin labels are pre-formatted in\n * the merger via `formatNumber(lo)–formatNumber(hi)` and optionally\n * the consumer's `labelFormatter`. The strings on the axis are\n * already display-ready, so there's nothing to post-format here.\n * - **Tooltip formatter reads the array-tuple row shape.** The dataset\n * uses positional `encode: { x: 0, y: 1 }`, so `item.value` comes in\n * as `[binLabel, count]` rather than bar's `{ name, value }` object.\n *\n * Intentionally data-agnostic: no series, no dataset, no x-axis category\n * data (those depend on data + ticks and are added by the option factory's\n * merge phase via {@link createHistogramOptionFactory}).\n */\nexport function histogramOptions({\n theme,\n formatter,\n}: HistogramOptionsInput): HistogramEChartsOption {\n // Closure shared between yAxis min/max callbacks and the label formatter,\n // so only the rounded extents are labelled (matches v1 + bar).\n let niceMin = 0\n let niceMax = 1\n\n return {\n grid: {\n left: parseInt(theme.spacing(1)),\n top: parseInt(theme.spacing(3)),\n right: parseInt(theme.spacing(1)),\n // Default: no legend. Merger bumps this when there are >1 series.\n ...buildGridConfig(false, theme),\n containLabel: true,\n },\n tooltip: {\n trigger: 'axis',\n backgroundColor: theme.palette.grey[900],\n borderWidth: 0,\n padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],\n textStyle: {\n color: theme.palette.common.white,\n fontSize: 11,\n fontFamily: theme.typography.caption.fontFamily,\n },\n axisPointer: { type: 'line' },\n position: createTooltipPositioner(theme),\n formatter: buildHistogramTooltipFormatter(formatter),\n },\n // Legend styling baked here; `show` is toggled by the merger based on\n // series count. Histogram doesn't accept `labelFormatter` at the\n // options layer (bin labels are pre-formatted in the merger), so we\n // skip the legend `labelFormatter` argument too.\n legend: {\n ...buildLegendConfig({ hasLegend: false }),\n },\n axisPointer: { lineStyle: { color: theme.palette.grey[400] } },\n color: [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n xAxis: {\n type: 'category',\n axisLine: { show: false },\n axisTick: { show: false },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n padding: [parseInt(theme.spacing(0.5)), 0, 0, 0],\n margin: 0,\n // `interval: 0` makes every bin label a candidate so `hideOverlap`\n // can pack the maximum number that physically fit. The category-axis\n // default (`'auto'`) sub-samples first and leaves only one/two labels\n // even when there's room for more.\n interval: 0,\n hideOverlap: true,\n },\n },\n yAxis: {\n type: 'value',\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLine: { show: false },\n axisTick: { show: false },\n splitLine: {\n show: true,\n lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },\n },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n margin: parseInt(theme.spacing(1)),\n show: true,\n showMaxLabel: true,\n showMinLabel: true,\n verticalAlign: 'bottom',\n inside: true,\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return formatter ? formatter(value) : String(value)\n },\n },\n },\n } as HistogramEChartsOption\n}\n\n/**\n * Returns the histogram widget's {@link OptionFactory} — one closure that\n * owns BOTH phases of option construction:\n *\n * - **Structural phase** (`option == null`) — builds the theme-aware\n * structural option via {@link histogramOptions}, optionally merging\n * the consumer-supplied `optionsOverride`. Called once by Provider to\n * seed `rawOptions` in the store.\n * - **Merge phase** (`option != null`) — fuses post-pipeline `state.data`\n * (`HistogramWidgetData` — `number[][]`) into the option via the\n * dataset API. Each series gets its own dataset of `[binLabel, count]`\n * pairs derived from `ticks`; series reference their dataset by\n * `datasetIndex` and are encoded by column position. Reactive\n * `ctx.formatter` (driven by RelativeData → `%`) plus `niceMin`/`niceMax`\n * are re-derived here so the y-axis label formatter and the tooltip\n * pick up live store values, mirroring bar.\n */\nexport function createHistogramOptionFactory(\n options: HistogramOptionFactoryInput,\n): OptionFactory {\n const { theme, formatter, ticks, series, labelFormatter, selection } = options\n const optionsOverride = options.optionsOverride\n const lockedItems = options.lockedItems\n const selectionSet =\n selection && selection.length > 0 ? new Set<number>(selection) : null\n\n const binLabels: string[] = []\n for (let i = 0; i < ticks.length - 1; i++) {\n const lo = ticks[i]!\n const hi = ticks[i + 1]!\n const raw = `${formatNumber(lo)}–${formatNumber(hi)}`\n binLabels.push(labelFormatter ? labelFormatter(raw) : raw)\n }\n\n // Locked range → x-axis crop. `min`/`max` on a category axis accept a\n // category value (the bin label string), bounding the visible window\n // without touching `dataset`/`series` — the zeroed-out bins from\n // `filterHistogramByLockedBins` stay in the data, just scrolled out of\n // view. `null` (not omitted) on unlock, since ECharts' option merge skips\n // `undefined` keys and would otherwise leave a stale crop in place.\n const lockedRange =\n lockedItems && lockedItems.length > 0\n ? (() => {\n const indices = lockedItems.map(Number)\n const min = binLabels[Math.min(...indices)]\n const max = binLabels[Math.max(...indices)]\n return min != null && max != null ? { min, max } : null\n })()\n : null\n\n return (option, data, ctx) => {\n if (option == null) {\n const structural = histogramOptions({ theme, formatter })\n return optionsOverride\n ? (mergeOptions(\n structural as unknown as Record<string, unknown>,\n optionsOverride as Partial<Record<string, unknown>>,\n ) as EChartsOption)\n : structural\n }\n\n const seriesArr: HistogramWidgetData = Array.isArray(data)\n ? (data as HistogramWidgetData)\n : []\n if (seriesArr.length === 0 || binLabels.length === 0) {\n return { ...option, dataset: [], series: [] }\n }\n\n const hasLegend = seriesArr.length > 1\n const seriesTemplates = Array.isArray(option.series) ? option.series : []\n const broadcastTemplate = seriesTemplates[0] ?? {}\n const baseYAxis =\n typeof option.yAxis === 'object' && !Array.isArray(option.yAxis)\n ? option.yAxis\n : {}\n const baseGrid =\n typeof option.grid === 'object' && !Array.isArray(option.grid)\n ? option.grid\n : {}\n const baseTooltip =\n typeof option.tooltip === 'object' && !Array.isArray(option.tooltip)\n ? option.tooltip\n : {}\n const baseLegend =\n typeof option.legend === 'object' && !Array.isArray(option.legend)\n ? option.legend\n : {}\n // Reactive (live store) formatter from ctx — distinct from the\n // closure-time `formatter` captured for the structural-build branch\n // above. RelativeData can install a percent formatter on the store\n // after the factory was constructed; the merge phase reads `ctx` to\n // pick that up.\n const liveFormatter = ctx?.formatter\n\n // Closure shared between the yAxis min/max callbacks and the label\n // formatter, so only the rounded extents are labelled (matches v1 +\n // bar). Delegating the extent to ECharts (rather than precomputing\n // scalars from the raw counts) keeps stacked bins inside the plot:\n // when StackToggle marks the series, ECharts feeds the *post-stack*\n // extent to these callbacks, so `niceNum` rounds the stacked total.\n let niceMin = 0\n let niceMax = 1\n\n // Zoom slider layout: when ZoomToggle has installed `dataZoom`, push\n // the slider above the legend (if any) and reserve room in the grid.\n const dataZoomLayout = positionDataZoomForLegend(option.dataZoom, hasLegend)\n const fallbackBottom =\n typeof baseGrid.bottom === 'number' ? baseGrid.bottom : 24\n const baseBottom = hasLegend ? 56 : fallbackBottom\n const gridBottom = dataZoomLayout\n ? baseBottom + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap\n : baseBottom\n\n // Dim non-selected bins via `series.itemStyle.color`. Per-row\n // `itemStyle` on dataset sources is silently ignored when\n // `series.encode` is in play.\n //\n // We *always* emit `itemStyle.color` (a passthrough when nothing is\n // selected), not conditionally — dropping the key between renders\n // would let ECharts' default merge keep the previous callback alive\n // and bins would stay dimmed forever after an external clear. Always\n // emitting lets normal merge swap the callback in place, no\n // `replaceMerge` and no entry-animation flash on selection on/off.\n const dimItemStyle = {\n color: (params: CallbackDataParams) => {\n const base = params.color as string\n if (!selectionSet) return base\n return selectionSet.has(params.dataIndex)\n ? base\n : echarts.color.modifyAlpha(base, 0.15)\n },\n }\n\n // A consumer's `optionsOverride.xAxis` may legitimately be an array\n // (ECharts supports multiple x-axes on one grid). Merge the locked-range\n // crop into the primary (first) axis instead of discarding the rest via\n // an object fallback — `min`/`max` here bound a category axis by label\n // string, which is only ambiguous if `labelFormatter` produces duplicate\n // labels across bins (a structural limitation of label-string bounds,\n // not fixed here).\n //\n // `min`/`max` are ALWAYS written explicitly below (never omitted) —\n // ECharts' merge silently keeps whatever value it last received for an\n // omitted key, so omitting them while unlocked would leave a stale crop\n // in place from a previous locked render. But the fallback when there's\n // no active crop must be the CONSUMER's own `optionsOverride.xAxis.min/max`\n // (if they set one), not an unconditional `null` — `option.xAxis` here\n // is the post-structural-phase axis, which only ever carries the\n // consumer's static override (never our own prior crop — the structural\n // phase is rebuilt from scratch on every factory recreation, so it can't\n // have picked up a merge-phase write from a previous render).\n const currentAxis = (\n Array.isArray(option.xAxis) ? option.xAxis[0] : option.xAxis\n ) as { min?: unknown; max?: unknown } | undefined\n const lockedRangeAxis = {\n min: lockedRange?.min ?? currentAxis?.min ?? null,\n max: lockedRange?.max ?? currentAxis?.max ?? null,\n }\n const nextXAxis = Array.isArray(option.xAxis)\n ? [{ ...option.xAxis[0], ...lockedRangeAxis }, ...option.xAxis.slice(1)]\n : {\n ...(typeof option.xAxis === 'object' ? option.xAxis : {}),\n ...lockedRangeAxis,\n }\n\n return {\n ...option,\n xAxis: nextXAxis,\n dataset: seriesArr.map((counts) => ({\n source: binLabels.map(\n (label, i) => [label, counts[i] ?? 0] as [string, number],\n ),\n })),\n series: seriesArr.map((_, i) => {\n const template =\n (seriesTemplates[i] as object | undefined) ??\n (broadcastTemplate as object)\n const overrideColor = resolveThemeColor(theme, series?.[i]?.color)\n return {\n ...(typeof template === 'object' ? template : {}),\n type: 'bar' as const,\n datasetIndex: i,\n name: series?.[i]?.name ?? `Series ${i + 1}`,\n encode: { x: 0, y: 1 },\n barCategoryGap: '0%',\n emphasis: { focus: 'series' },\n itemStyle: dimItemStyle,\n ...(overrideColor ? { color: overrideColor } : {}),\n }\n }),\n legend: { ...baseLegend, show: hasLegend },\n grid: { ...baseGrid, bottom: gridBottom },\n ...(dataZoomLayout ? { dataZoom: dataZoomLayout } : {}),\n yAxis: {\n ...baseYAxis,\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLabel: {\n ...((baseYAxis as { axisLabel?: object }).axisLabel ?? {}),\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return liveFormatter ? liveFormatter(value) : String(value)\n },\n },\n } as EChartsOption['yAxis'],\n tooltip: {\n ...baseTooltip,\n formatter: buildHistogramTooltipFormatter(liveFormatter),\n },\n } as EChartsOption\n }\n}\n\n/**\n * Tooltip formatter for the histogram's positional `[binLabel, count]`\n * row shape. ECharts surfaces the x-axis category through `item.name`\n * (already pre-formatted by the bin-label generator), and `item.value`\n * comes in as the array tuple — distinct from bar's `{ name, value }`\n * object row.\n */\nfunction buildHistogramTooltipFormatter(\n formatter: ((value: number) => string) | undefined,\n) {\n return createTooltipFormatter((item) => {\n const row = item.value as [string, number] | undefined\n const raw = row?.[1]\n const formattedValue =\n typeof raw === 'number' && formatter ? formatter(raw) : (raw ?? '')\n const marker = typeof item.marker === 'string' ? item.marker : ''\n const seriesName = item.seriesName ? `${item.seriesName}: ` : ''\n return {\n name: String(item.name ?? ''),\n seriesName,\n marker,\n value: formattedValue,\n }\n })\n}\n\nfunction formatNumber(n: number): string {\n if (Number.isInteger(n)) return String(n)\n return Number(n.toFixed(2)).toString()\n}\n","import { Box, Skeleton, type SxProps, type Theme } from '@mui/material'\n\nconst styles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexDirection: 'column',\n gap: ({ spacing }) => spacing(1),\n height: ({ spacing }) => spacing(38),\n },\n grid: {\n display: 'flex',\n alignItems: 'flex-end',\n flex: '1 1 auto',\n gap: 0.5,\n width: '100%',\n },\n legend: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(2),\n height: ({ spacing }) => spacing(5),\n },\n legendItem: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(1.5),\n },\n} satisfies Record<string, SxProps<Theme>>\n\nconst barSx = (height: number): SxProps<Theme> => ({\n flex: 1,\n height: `${height}%`,\n minWidth: 8,\n})\n\nexport interface HistogramSkeletonProps {\n bins?: number\n}\n\n/**\n * Loading state for the Histogram widget. Mirrors Bar's skeleton structure\n * — a grid of bars (bin-shaped silhouette, anchored to the bottom) plus a\n * legend stub — so the placeholder reads consistently with Bar.\n */\nexport function HistogramSkeleton({ bins = 12 }: HistogramSkeletonProps) {\n // Sample heights mimicking a typical bell-shaped distribution.\n const heights = Array.from({ length: bins }, (_, i) => {\n const t = i / Math.max(1, bins - 1)\n return Math.round(20 + 80 * Math.exp(-((t - 0.5) ** 2) * 8))\n })\n return (\n <Box sx={styles.container}>\n <Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>\n )\n}\n","import {\n buildCsvDownloadItem,\n buildPngDownloadItem,\n type DownloadContext,\n type DownloadItem,\n} from '../actions/download'\nimport type { HistogramWidgetData } from './types'\n\n/**\n * Download menu items for histograms. Always includes a CSV item with\n * `bin_low, bin_high, series_1_count, series_2_count, …` columns (one row\n * per bin). When `getCaptureEl` is supplied, prepends a PNG item that\n * rasterises the captured element via `html2canvas`.\n */\nexport function createHistogramDownloadConfig(args: {\n filename: string\n getData: (ctx: DownloadContext) => HistogramWidgetData\n getTicks: () => readonly number[]\n seriesNames?: readonly string[]\n getCaptureEl?: (ctx: DownloadContext) => HTMLElement | null\n pngPixelRatio?: number\n pngBackgroundColor?: string | null\n}): DownloadItem[] {\n const items: DownloadItem[] = []\n if (args.getCaptureEl) {\n items.push(\n buildPngDownloadItem({\n filename: args.filename,\n getCaptureEl: args.getCaptureEl,\n pixelRatio: args.pngPixelRatio,\n backgroundColor: args.pngBackgroundColor,\n }),\n )\n }\n items.push(\n buildCsvDownloadItem({\n filename: args.filename,\n getRows: (ctx) => {\n const data = args.getData(ctx)\n const ticks = args.getTicks()\n const seriesCount = data.length\n const header: unknown[] = ['bin_low', 'bin_high']\n for (let i = 0; i < seriesCount; i++) {\n header.push(args.seriesNames?.[i] ?? `series_${i + 1}`)\n }\n const rows: unknown[][] = [header]\n for (let bin = 0; bin < Math.max(0, ticks.length - 1); bin++) {\n const row: unknown[] = [ticks[bin], ticks[bin + 1]]\n for (let s = 0; s < seriesCount; s++) row.push(data[s]?.[bin] ?? 0)\n rows.push(row)\n }\n return rows\n },\n }),\n )\n return items\n}\n","/**\n * Histogram-specific `RelativeData` transform. Histogram data is\n * `number[][]` — each series is a flat list of bin counts. This\n * rewrites each count to its share of the series total (0–100), so\n * pairing with `createPercentFormatter` renders the y-axis as\n * percentages.\n *\n * Pass to `<Widget.RelativeData transform={toRelativeHistogramData} />`.\n *\n * The denominator is the sum of |value| across the series so mixed-sign\n * inputs produce sane signed shares-of-magnitude. A series whose total\n * magnitude is zero (all-zero or empty input) is returned unchanged so\n * a stalled or empty data set doesn't show misleading 0% values. Inputs\n * whose shape isn't `number[][]` fall through untouched (same defensive\n * shape-check pattern as the default transform).\n */\nexport const toRelativeHistogramData = (input: unknown): unknown => {\n if (!Array.isArray(input)) return input\n return input.map((series: unknown): unknown => {\n if (!isFlatNumberArray(series)) return series\n const total = series.reduce((acc, v) => acc + Math.abs(v), 0)\n if (total <= 0) return series\n return series.map((v) => (v / total) * 100)\n })\n}\n\nfunction isFlatNumberArray(v: unknown): v is number[] {\n if (!Array.isArray(v)) return false\n return v.every((item) => typeof item === 'number' && Number.isFinite(item))\n}\n\n/**\n * `LockSelection` filter for Histogram. Data is `number[][]` — a flat list\n * of bin counts per series, positionally paired with `ticks` (bin `i` covers\n * `[ticks[i], ticks[i+1])`). Unlike name-keyed widgets, locked-out bins can't\n * be REMOVED — that would shift every later bin's index and desync it from\n * its `ticks` range. Zeroing them out instead preserves array length/position,\n * so locked bins just render as empty (height 0) rather than disappearing.\n *\n * `lockedItems` holds bin indices (numbers, from Histogram's `selection`).\n */\nexport function filterHistogramByLockedBins(\n input: unknown,\n lockedItems: readonly (string | number)[],\n): unknown {\n if (lockedItems.length === 0) return input\n if (!Array.isArray(input)) return input\n const locked = new Set<number>(lockedItems.map(Number))\n return input.map((series: unknown): unknown => {\n if (!isFlatNumberArray(series)) return series\n return series.map((count, i) => (locked.has(i) ? count : 0))\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"histogram.js","names":["{ bins = 12 }: HistogramSkeletonProps","bins = 12","Box","styles.grid","heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))","styles.container","(h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n )","<Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>","<Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>","(i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n )","<Box sx={styles.container}>\n <Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>","i"],"sources":["../../src/widgets-v2/histogram/bins.ts","../../src/widgets-v2/histogram/options.ts","../../src/widgets-v2/histogram/skeleton.tsx","../../src/widgets-v2/histogram/download.ts","../../src/widgets-v2/histogram/transforms.ts"],"sourcesContent":["import type { HistogramBin } from './types'\n\n/** Count of a {@link HistogramBin}, whichever form it takes. */\nexport function getHistogramBinValue(bin: HistogramBin): number {\n return typeof bin === 'number' ? bin : bin.value\n}\n\n/** Whether `v` is a valid {@link HistogramBin} with a finite count. */\nexport function isHistogramBin(v: unknown): v is HistogramBin {\n if (typeof v === 'number') return Number.isFinite(v)\n if (v == null || typeof v !== 'object') return false\n const value = (v as { value?: unknown }).value\n return typeof value === 'number' && Number.isFinite(value)\n}\n\n/** Same bin with its count replaced; keeps the object form (and `color`). */\nexport function withHistogramBinValue(\n bin: HistogramBin,\n value: number,\n): HistogramBin {\n return typeof bin === 'number' ? value : { ...bin, value }\n}\n","import type { EChartsOption } from 'echarts'\nimport * as echarts from 'echarts'\nimport type { CallbackDataParams } from 'echarts/types/dist/shared'\nimport {\n buildAxisLabelStyle,\n buildGridConfig,\n buildLegendConfig,\n createTooltipFormatter,\n createTooltipPositioner,\n niceNum,\n} from '../../widgets/utils/chart-config'\nimport { ZOOM_LAYOUT } from '../actions/zoom-toggle'\nimport type { OptionFactory } from '../echart'\nimport { mergeOptions, resolveThemeColor } from '../utils'\nimport { createItemColorResolver } from '../utils/item-color'\nimport { getHistogramBinValue } from './bins'\nimport { positionDataZoomForLegend } from '../utils/data-zoom-layout'\nimport type {\n HistogramEChartsOption,\n HistogramOptionFactoryInput,\n HistogramOptionsInput,\n HistogramBin,\n HistogramWidgetData,\n} from './types'\n\n/**\n * Builds the **structural** ECharts option for a histogram widget — axes,\n * grid, tooltip, value-axis label formatter, themed legend. Mirrors\n * {@link import('../bar/options').barOptions} so the two widgets share v1\n * look-and-feel: themed dark tooltip with positioner, CARTO color palette,\n * structural legend (`show` toggled by the merger based on series count),\n * y-axis labels rendered inside the plot via the `niceNum` closure pattern.\n *\n * Intentional deviations from bar (histogram-specific):\n * - **No `xAxis.axisLabel.formatter`.** Bin labels are pre-formatted in\n * the merger via `formatNumber(lo)–formatNumber(hi)` and optionally\n * the consumer's `labelFormatter`. The strings on the axis are\n * already display-ready, so there's nothing to post-format here.\n * - **Tooltip formatter reads the array-tuple row shape.** The dataset\n * uses positional `encode: { x: 0, y: 1 }`, so `item.value` comes in\n * as `[binLabel, count]` rather than bar's `{ name, value }` object.\n *\n * Intentionally data-agnostic: no series, no dataset, no x-axis category\n * data (those depend on data + ticks and are added by the option factory's\n * merge phase via {@link createHistogramOptionFactory}).\n */\nexport function histogramOptions({\n theme,\n formatter,\n}: HistogramOptionsInput): HistogramEChartsOption {\n // Closure shared between yAxis min/max callbacks and the label formatter,\n // so only the rounded extents are labelled (matches v1 + bar).\n let niceMin = 0\n let niceMax = 1\n\n return {\n grid: {\n left: parseInt(theme.spacing(1)),\n top: parseInt(theme.spacing(3)),\n right: parseInt(theme.spacing(1)),\n // Default: no legend. Merger bumps this when there are >1 series.\n ...buildGridConfig(false, theme),\n containLabel: true,\n },\n tooltip: {\n trigger: 'axis',\n backgroundColor: theme.palette.grey[900],\n borderWidth: 0,\n padding: [parseInt(theme.spacing(1)), parseInt(theme.spacing(1))],\n textStyle: {\n color: theme.palette.common.white,\n fontSize: 11,\n fontFamily: theme.typography.caption.fontFamily,\n },\n axisPointer: { type: 'line' },\n position: createTooltipPositioner(theme),\n formatter: buildHistogramTooltipFormatter(formatter),\n },\n // Legend styling baked here; `show` is toggled by the merger based on\n // series count. Histogram doesn't accept `labelFormatter` at the\n // options layer (bin labels are pre-formatted in the merger), so we\n // skip the legend `labelFormatter` argument too.\n legend: {\n ...buildLegendConfig({ hasLegend: false }),\n },\n axisPointer: { lineStyle: { color: theme.palette.grey[400] } },\n color: [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n xAxis: {\n type: 'category',\n axisLine: { show: false },\n axisTick: { show: false },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n padding: [parseInt(theme.spacing(0.5)), 0, 0, 0],\n margin: 0,\n // `interval: 0` makes every bin label a candidate so `hideOverlap`\n // can pack the maximum number that physically fit. The category-axis\n // default (`'auto'`) sub-samples first and leaves only one/two labels\n // even when there's room for more.\n interval: 0,\n hideOverlap: true,\n },\n },\n yAxis: {\n type: 'value',\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLine: { show: false },\n axisTick: { show: false },\n splitLine: {\n show: true,\n lineStyle: { color: theme.palette.black?.[4] ?? theme.palette.divider },\n },\n axisLabel: {\n ...buildAxisLabelStyle(theme),\n margin: parseInt(theme.spacing(1)),\n show: true,\n showMaxLabel: true,\n showMinLabel: true,\n verticalAlign: 'bottom',\n inside: true,\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return formatter ? formatter(value) : String(value)\n },\n },\n },\n } as HistogramEChartsOption\n}\n\n/**\n * Returns the histogram widget's {@link OptionFactory} — one closure that\n * owns BOTH phases of option construction:\n *\n * - **Structural phase** (`option == null`) — builds the theme-aware\n * structural option via {@link histogramOptions}, optionally merging\n * the consumer-supplied `optionsOverride`. Called once by Provider to\n * seed `rawOptions` in the store.\n * - **Merge phase** (`option != null`) — fuses post-pipeline `state.data`\n * (`HistogramWidgetData` — `HistogramBin[][]`) into the option via the\n * dataset API. Each series gets its own dataset of `[binLabel, count]`\n * pairs derived from `ticks`; series reference their dataset by\n * `datasetIndex` and are encoded by column position. Reactive\n * `ctx.formatter` (driven by RelativeData → `%`) plus `niceMin`/`niceMax`\n * are re-derived here so the y-axis label formatter and the tooltip\n * pick up live store values, mirroring bar.\n */\nexport function createHistogramOptionFactory(\n options: HistogramOptionFactoryInput,\n): OptionFactory {\n const { theme, formatter, ticks, series, labelFormatter, selection } = options\n const optionsOverride = options.optionsOverride\n const lockedItems = options.lockedItems\n const selectionSet =\n selection && selection.length > 0 ? new Set<number>(selection) : null\n\n const binLabels: string[] = []\n for (let i = 0; i < ticks.length - 1; i++) {\n const lo = ticks[i]!\n const hi = ticks[i + 1]!\n const raw = `${formatNumber(lo)}–${formatNumber(hi)}`\n binLabels.push(labelFormatter ? labelFormatter(raw) : raw)\n }\n\n // Locked range → x-axis crop. `min`/`max` on a category axis accept a\n // category value (the bin label string), bounding the visible window\n // without touching `dataset`/`series` — the zeroed-out bins from\n // `filterHistogramByLockedBins` stay in the data, just scrolled out of\n // view. `null` (not omitted) on unlock, since ECharts' option merge skips\n // `undefined` keys and would otherwise leave a stale crop in place.\n const lockedRange =\n lockedItems && lockedItems.length > 0\n ? (() => {\n const indices = lockedItems.map(Number)\n const min = binLabels[Math.min(...indices)]\n const max = binLabels[Math.max(...indices)]\n return min != null && max != null ? { min, max } : null\n })()\n : null\n\n return (option, data, ctx) => {\n if (option == null) {\n const structural = histogramOptions({ theme, formatter })\n return optionsOverride\n ? (mergeOptions(\n structural as unknown as Record<string, unknown>,\n optionsOverride as Partial<Record<string, unknown>>,\n ) as EChartsOption)\n : structural\n }\n\n const seriesArr: HistogramWidgetData<HistogramBin> = Array.isArray(data)\n ? (data as HistogramWidgetData<HistogramBin>)\n : []\n if (seriesArr.length === 0 || binLabels.length === 0) {\n return { ...option, dataset: [], series: [] }\n }\n\n const hasLegend = seriesArr.length > 1\n const seriesTemplates = Array.isArray(option.series) ? option.series : []\n const broadcastTemplate = seriesTemplates[0] ?? {}\n const baseYAxis =\n typeof option.yAxis === 'object' && !Array.isArray(option.yAxis)\n ? option.yAxis\n : {}\n const baseGrid =\n typeof option.grid === 'object' && !Array.isArray(option.grid)\n ? option.grid\n : {}\n const baseTooltip =\n typeof option.tooltip === 'object' && !Array.isArray(option.tooltip)\n ? option.tooltip\n : {}\n const baseLegend =\n typeof option.legend === 'object' && !Array.isArray(option.legend)\n ? option.legend\n : {}\n // Reactive (live store) formatter from ctx — distinct from the\n // closure-time `formatter` captured for the structural-build branch\n // above. RelativeData can install a percent formatter on the store\n // after the factory was constructed; the merge phase reads `ctx` to\n // pick that up.\n const liveFormatter = ctx?.formatter\n\n // Closure shared between the yAxis min/max callbacks and the label\n // formatter, so only the rounded extents are labelled (matches v1 +\n // bar). Delegating the extent to ECharts (rather than precomputing\n // scalars from the raw counts) keeps stacked bins inside the plot:\n // when StackToggle marks the series, ECharts feeds the *post-stack*\n // extent to these callbacks, so `niceNum` rounds the stacked total.\n let niceMin = 0\n let niceMax = 1\n\n // Zoom slider layout: when ZoomToggle has installed `dataZoom`, push\n // the slider above the legend (if any) and reserve room in the grid.\n const dataZoomLayout = positionDataZoomForLegend(option.dataZoom, hasLegend)\n const fallbackBottom =\n typeof baseGrid.bottom === 'number' ? baseGrid.bottom : 24\n const baseBottom = hasLegend ? 56 : fallbackBottom\n const gridBottom = dataZoomLayout\n ? baseBottom + ZOOM_LAYOUT.sliderHeight + ZOOM_LAYOUT.sliderGap\n : baseBottom\n\n // Dim non-selected bins via `series.itemStyle.color`. Per-row\n // `itemStyle` on dataset sources is silently ignored when\n // `series.encode` is in play.\n //\n // We *always* emit `itemStyle.color` (a passthrough when nothing is\n // selected), not conditionally — dropping the key between renders\n // would let ECharts' default merge keep the previous callback alive\n // and bins would stay dimmed forever after an external clear. Always\n // emitting lets normal merge swap the callback in place, no\n // `replaceMerge` and no entry-animation flash on selection on/off.\n //\n // The base colour is the bin's own `color` (object bins), else the\n // template's `itemStyle.color`, else the series colour / palette.\n const makeDimItemStyle = (\n datums: readonly unknown[],\n template: unknown,\n ) => {\n const baseColor = createItemColorResolver({\n theme,\n datums,\n template,\n fallback: (params) => params.color as string,\n })\n return {\n color: (params: CallbackDataParams) => {\n const base = baseColor(params)\n if (!selectionSet) return base\n return selectionSet.has(params.dataIndex)\n ? base\n : echarts.color.modifyAlpha(base, 0.15)\n },\n }\n }\n\n // A consumer's `optionsOverride.xAxis` may legitimately be an array\n // (ECharts supports multiple x-axes on one grid). Merge the locked-range\n // crop into the primary (first) axis instead of discarding the rest via\n // an object fallback — `min`/`max` here bound a category axis by label\n // string, which is only ambiguous if `labelFormatter` produces duplicate\n // labels across bins (a structural limitation of label-string bounds,\n // not fixed here).\n //\n // `min`/`max` are ALWAYS written explicitly below (never omitted) —\n // ECharts' merge silently keeps whatever value it last received for an\n // omitted key, so omitting them while unlocked would leave a stale crop\n // in place from a previous locked render. But the fallback when there's\n // no active crop must be the CONSUMER's own `optionsOverride.xAxis.min/max`\n // (if they set one), not an unconditional `null` — `option.xAxis` here\n // is the post-structural-phase axis, which only ever carries the\n // consumer's static override (never our own prior crop — the structural\n // phase is rebuilt from scratch on every factory recreation, so it can't\n // have picked up a merge-phase write from a previous render).\n const currentAxis = (\n Array.isArray(option.xAxis) ? option.xAxis[0] : option.xAxis\n ) as { min?: unknown; max?: unknown } | undefined\n const lockedRangeAxis = {\n min: lockedRange?.min ?? currentAxis?.min ?? null,\n max: lockedRange?.max ?? currentAxis?.max ?? null,\n }\n const nextXAxis = Array.isArray(option.xAxis)\n ? [{ ...option.xAxis[0], ...lockedRangeAxis }, ...option.xAxis.slice(1)]\n : {\n ...(typeof option.xAxis === 'object' ? option.xAxis : {}),\n ...lockedRangeAxis,\n }\n\n return {\n ...option,\n xAxis: nextXAxis,\n dataset: seriesArr.map((bins) => ({\n source: binLabels.map((label, i) => {\n const bin = bins[i]\n return [label, bin == null ? 0 : getHistogramBinValue(bin)] as [\n string,\n number,\n ]\n }),\n })),\n series: seriesArr.map((bins, i) => {\n const template =\n (seriesTemplates[i] as object | undefined) ??\n (broadcastTemplate as object)\n const overrideColor = resolveThemeColor(theme, series?.[i]?.color)\n return {\n ...(typeof template === 'object' ? template : {}),\n type: 'bar' as const,\n datasetIndex: i,\n name: series?.[i]?.name ?? `Series ${i + 1}`,\n encode: { x: 0, y: 1 },\n barCategoryGap: '0%',\n emphasis: { focus: 'series' },\n itemStyle: makeDimItemStyle(bins, template),\n ...(overrideColor ? { color: overrideColor } : {}),\n }\n }),\n legend: { ...baseLegend, show: hasLegend },\n grid: { ...baseGrid, bottom: gridBottom },\n ...(dataZoomLayout ? { dataZoom: dataZoomLayout } : {}),\n yAxis: {\n ...baseYAxis,\n min: (extent: { min: number }) => {\n niceMin = extent.min < 0 ? niceNum(extent.min) : 0\n return niceMin\n },\n max: (extent: { min: number; max: number }) => {\n niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)\n return niceMax\n },\n axisLabel: {\n ...((baseYAxis as { axisLabel?: object }).axisLabel ?? {}),\n formatter: (value: number) => {\n if (value !== niceMax && value !== niceMin) return ''\n if (value === 0) return ''\n return liveFormatter ? liveFormatter(value) : String(value)\n },\n },\n } as EChartsOption['yAxis'],\n tooltip: {\n ...baseTooltip,\n formatter: buildHistogramTooltipFormatter(liveFormatter),\n },\n } as EChartsOption\n }\n}\n\n/**\n * Tooltip formatter for the histogram's positional `[binLabel, count]`\n * row shape. ECharts surfaces the x-axis category through `item.name`\n * (already pre-formatted by the bin-label generator), and `item.value`\n * comes in as the array tuple — distinct from bar's `{ name, value }`\n * object row.\n */\nfunction buildHistogramTooltipFormatter(\n formatter: ((value: number) => string) | undefined,\n) {\n return createTooltipFormatter((item) => {\n const row = item.value as [string, number] | undefined\n const raw = row?.[1]\n const formattedValue =\n typeof raw === 'number' && formatter ? formatter(raw) : (raw ?? '')\n const marker = typeof item.marker === 'string' ? item.marker : ''\n const seriesName = item.seriesName ? `${item.seriesName}: ` : ''\n return {\n name: String(item.name ?? ''),\n seriesName,\n marker,\n value: formattedValue,\n }\n })\n}\n\nfunction formatNumber(n: number): string {\n if (Number.isInteger(n)) return String(n)\n return Number(n.toFixed(2)).toString()\n}\n","import { Box, Skeleton, type SxProps, type Theme } from '@mui/material'\n\nconst styles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n flexDirection: 'column',\n gap: ({ spacing }) => spacing(1),\n height: ({ spacing }) => spacing(38),\n },\n grid: {\n display: 'flex',\n alignItems: 'flex-end',\n flex: '1 1 auto',\n gap: 0.5,\n width: '100%',\n },\n legend: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(2),\n height: ({ spacing }) => spacing(5),\n },\n legendItem: {\n display: 'flex',\n alignItems: 'center',\n gap: ({ spacing }) => spacing(1.5),\n },\n} satisfies Record<string, SxProps<Theme>>\n\nconst barSx = (height: number): SxProps<Theme> => ({\n flex: 1,\n height: `${height}%`,\n minWidth: 8,\n})\n\nexport interface HistogramSkeletonProps {\n bins?: number\n}\n\n/**\n * Loading state for the Histogram widget. Mirrors Bar's skeleton structure\n * — a grid of bars (bin-shaped silhouette, anchored to the bottom) plus a\n * legend stub — so the placeholder reads consistently with Bar.\n */\nexport function HistogramSkeleton({ bins = 12 }: HistogramSkeletonProps) {\n // Sample heights mimicking a typical bell-shaped distribution.\n const heights = Array.from({ length: bins }, (_, i) => {\n const t = i / Math.max(1, bins - 1)\n return Math.round(20 + 80 * Math.exp(-((t - 0.5) ** 2) * 8))\n })\n return (\n <Box sx={styles.container}>\n <Box sx={styles.grid}>\n {heights.map((h, i) => (\n <Skeleton key={`bar-${i}`} variant='rectangular' sx={barSx(h)} />\n ))}\n </Box>\n <Box sx={styles.legend}>\n {[0, 1].map((i) => (\n <Box key={`legend-${i}`} sx={styles.legendItem}>\n <Skeleton variant='circular' width={8} height={8} />\n <Skeleton width={48} height={8} />\n </Box>\n ))}\n </Box>\n </Box>\n )\n}\n","import {\n buildCsvDownloadItem,\n buildPngDownloadItem,\n type DownloadContext,\n type DownloadItem,\n} from '../actions/download'\nimport { getHistogramBinValue } from './bins'\nimport type { HistogramBin, HistogramWidgetData } from './types'\n\n/**\n * Download menu items for histograms. Always includes a CSV item with\n * `bin_low, bin_high, series_1_count, series_2_count, …` columns (one row\n * per bin). When `getCaptureEl` is supplied, prepends a PNG item that\n * rasterises the captured element via `html2canvas`.\n */\nexport function createHistogramDownloadConfig(args: {\n filename: string\n getData: (ctx: DownloadContext) => HistogramWidgetData<HistogramBin>\n getTicks: () => readonly number[]\n seriesNames?: readonly string[]\n getCaptureEl?: (ctx: DownloadContext) => HTMLElement | null\n pngPixelRatio?: number\n pngBackgroundColor?: string | null\n}): DownloadItem[] {\n const items: DownloadItem[] = []\n if (args.getCaptureEl) {\n items.push(\n buildPngDownloadItem({\n filename: args.filename,\n getCaptureEl: args.getCaptureEl,\n pixelRatio: args.pngPixelRatio,\n backgroundColor: args.pngBackgroundColor,\n }),\n )\n }\n items.push(\n buildCsvDownloadItem({\n filename: args.filename,\n getRows: (ctx) => {\n const data = args.getData(ctx)\n const ticks = args.getTicks()\n const seriesCount = data.length\n const header: unknown[] = ['bin_low', 'bin_high']\n for (let i = 0; i < seriesCount; i++) {\n header.push(args.seriesNames?.[i] ?? `series_${i + 1}`)\n }\n const rows: unknown[][] = [header]\n for (let bin = 0; bin < Math.max(0, ticks.length - 1); bin++) {\n const row: unknown[] = [ticks[bin], ticks[bin + 1]]\n for (let s = 0; s < seriesCount; s++) {\n const value = data[s]?.[bin]\n row.push(value == null ? 0 : getHistogramBinValue(value))\n }\n rows.push(row)\n }\n return rows\n },\n }),\n )\n return items\n}\n","import {\n getHistogramBinValue,\n isHistogramBin,\n withHistogramBinValue,\n} from './bins'\nimport type { HistogramBin } from './types'\n\n/**\n * Histogram-specific `RelativeData` transform. Histogram data is\n * `HistogramBin[][]` — each series is a flat list of bin counts (plain\n * numbers or `{ value, color? }`, whose shape is kept). This\n * rewrites each count to its share of the series total (0–100), so\n * pairing with `createPercentFormatter` renders the y-axis as\n * percentages.\n *\n * Pass to `<Widget.RelativeData transform={toRelativeHistogramData} />`.\n *\n * The denominator is the sum of |value| across the series so mixed-sign\n * inputs produce sane signed shares-of-magnitude. A series whose total\n * magnitude is zero (all-zero or empty input) is returned unchanged so\n * a stalled or empty data set doesn't show misleading 0% values. Inputs\n * whose shape isn't `HistogramBin[][]` fall through untouched (same defensive\n * shape-check pattern as the default transform).\n */\nexport const toRelativeHistogramData = (input: unknown): unknown => {\n if (!Array.isArray(input)) return input\n return input.map((series: unknown): unknown => {\n if (!isBinArray(series)) return series\n const total = series.reduce<number>(\n (acc, b) => acc + Math.abs(getHistogramBinValue(b)),\n 0,\n )\n if (total <= 0) return series\n return series.map((b) =>\n withHistogramBinValue(b, (getHistogramBinValue(b) / total) * 100),\n )\n })\n}\n\nfunction isBinArray(v: unknown): v is HistogramBin[] {\n return Array.isArray(v) && v.every(isHistogramBin)\n}\n\n/**\n * `LockSelection` filter for Histogram. Data is `HistogramBin[][]` — a flat\n * list of bin counts per series, positionally paired with `ticks` (bin `i` covers\n * `[ticks[i], ticks[i+1])`). Unlike name-keyed widgets, locked-out bins can't\n * be REMOVED — that would shift every later bin's index and desync it from\n * its `ticks` range. Zeroing them out instead preserves array length/position,\n * so locked bins just render as empty (height 0) rather than disappearing.\n *\n * `lockedItems` holds bin indices (numbers, from Histogram's `selection`).\n */\nexport function filterHistogramByLockedBins(\n input: unknown,\n lockedItems: readonly (string | number)[],\n): unknown {\n if (lockedItems.length === 0) return input\n if (!Array.isArray(input)) return input\n const locked = new Set<number>(lockedItems.map(Number))\n return input.map((series: unknown): unknown => {\n if (!isBinArray(series)) return series\n return series.map((bin, i) =>\n locked.has(i) ? bin : withHistogramBinValue(bin, 0),\n )\n })\n}\n"],"mappings":"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"}