@carto/ps-react-ui 6.1.1 → 6.3.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.
- package/dist/{actions-BWlS5tpP.js → actions-CkxKe8-J.js} +57 -47
- package/dist/actions-CkxKe8-J.js.map +1 -0
- package/dist/category-DULnoxvk.js +933 -0
- package/dist/category-DULnoxvk.js.map +1 -0
- package/dist/chat.js +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/components-CTUpdQuj.js.map +1 -1
- package/dist/download-B4sykOFm.js.map +1 -1
- package/dist/{echart-CV8F50gn.js → echart-ysYFLa_T.js} +2 -2
- package/dist/echart-ysYFLa_T.js.map +1 -0
- package/dist/formula-DJS-JLpk.js.map +1 -1
- package/dist/{option-builders-DQivvvbb.js → item-color-B7xkIpDZ.js} +26 -9
- package/dist/item-color-B7xkIpDZ.js.map +1 -0
- package/dist/legend.js.map +1 -1
- package/dist/{markdown-Bn2nKCbn.js → markdown-DmvLi3vx.js} +2 -2
- package/dist/markdown-DmvLi3vx.js.map +1 -0
- package/dist/{markdown-content-kYNLOX0T.js → markdown-content-BpAH_V6S.js} +5 -4
- package/dist/markdown-content-BpAH_V6S.js.map +1 -0
- package/dist/range-BpU2zBmq.js.map +1 -1
- package/dist/smart-tooltip-Dc_rHhyI.js.map +1 -1
- package/dist/spread-CQwFIwuz.js.map +1 -1
- package/dist/stores-DcBOZlU3.js.map +1 -1
- package/dist/stores-mad-9yFn.js.map +1 -1
- package/dist/styles-BCEBm7xh.js.map +1 -1
- package/dist/table-DEdW_fDj.js.map +1 -1
- package/dist/types/widgets/bar/types.d.ts +6 -0
- package/dist/types/widgets/category/category-ui.d.ts +10 -2
- package/dist/types/widgets/category/category.d.ts +7 -1
- package/dist/types/widgets/category/components/category-bar-stacked.d.ts +8 -1
- package/dist/types/widgets/category/components/category-bar.d.ts +24 -2
- package/dist/types/widgets/category/components/category-row-multi.d.ts +3 -1
- package/dist/types/widgets/category/components/category-row-single.d.ts +3 -1
- package/dist/types/widgets/category/components/category-row-stacked.d.ts +10 -1
- package/dist/types/widgets/category/style.d.ts +33 -6
- package/dist/types/widgets/histogram/bins.d.ts +7 -0
- package/dist/types/widgets/histogram/download.d.ts +2 -2
- package/dist/types/widgets/histogram/index.d.ts +2 -1
- package/dist/types/widgets/histogram/options.d.ts +1 -1
- package/dist/types/widgets/histogram/transforms.d.ts +5 -4
- package/dist/types/widgets/histogram/types.d.ts +22 -5
- package/dist/types/widgets/index.d.ts +1 -1
- package/dist/types/widgets/pie/types.d.ts +6 -0
- package/dist/types/widgets/scatterplot/download.d.ts +2 -2
- package/dist/types/widgets/scatterplot/index.d.ts +2 -1
- package/dist/types/widgets/scatterplot/points.d.ts +7 -0
- package/dist/types/widgets/scatterplot/transforms.d.ts +4 -3
- package/dist/types/widgets/scatterplot/types.d.ts +21 -2
- package/dist/types/widgets/timeseries/types.d.ts +6 -0
- package/dist/types/widgets/utils/item-color.d.ts +26 -0
- package/dist/utils-C-eWwOhE.js.map +1 -1
- package/dist/widgets/actions.js +1 -1
- package/dist/widgets/bar.js +78 -70
- package/dist/widgets/bar.js.map +1 -1
- package/dist/widgets/category.js +1 -1
- package/dist/widgets/echart.js +1 -1
- package/dist/widgets/histogram.js +116 -85
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/markdown.js +2 -2
- package/dist/widgets/pie.js +109 -90
- package/dist/widgets/pie.js.map +1 -1
- package/dist/widgets/scatterplot.js +131 -92
- package/dist/widgets/scatterplot.js.map +1 -1
- package/dist/widgets/timeseries.js +82 -74
- package/dist/widgets/timeseries.js.map +1 -1
- package/dist/widgets.js +5 -5
- package/dist/widgets.js.map +1 -1
- package/dist/zoom-toggle-MmLjMTEw.js.map +1 -1
- package/package.json +2 -2
- package/src/widgets/actions/fullscreen/fullscreen-store.test.tsx +77 -13
- package/src/widgets/actions/fullscreen/fullscreen.tsx +48 -5
- package/src/widgets/bar/options.test.ts +106 -0
- package/src/widgets/bar/options.ts +25 -14
- package/src/widgets/bar/types.ts +6 -0
- package/src/widgets/category/category-ui.test.tsx +139 -0
- package/src/widgets/category/category-ui.tsx +64 -11
- package/src/widgets/category/category.test.tsx +67 -0
- package/src/widgets/category/category.tsx +32 -15
- package/src/widgets/category/components/category-bar-stacked.test.tsx +18 -0
- package/src/widgets/category/components/category-bar-stacked.tsx +42 -29
- package/src/widgets/category/components/category-bar.test.tsx +66 -0
- package/src/widgets/category/components/category-bar.tsx +65 -9
- package/src/widgets/category/components/category-row-multi.tsx +21 -0
- package/src/widgets/category/components/category-row-single.tsx +4 -0
- package/src/widgets/category/components/category-row-stacked.tsx +24 -1
- package/src/widgets/category/style.ts +58 -14
- package/src/widgets/histogram/bins.ts +22 -0
- package/src/widgets/histogram/download.test.ts +10 -0
- package/src/widgets/histogram/download.ts +7 -3
- package/src/widgets/histogram/index.ts +3 -0
- package/src/widgets/histogram/options.test.ts +62 -0
- package/src/widgets/histogram/options.ts +38 -17
- package/src/widgets/histogram/transforms.test.ts +21 -7
- package/src/widgets/histogram/transforms.ts +26 -12
- package/src/widgets/histogram/types.ts +25 -5
- package/src/widgets/index.ts +6 -1
- package/src/widgets/pie/options.test.ts +91 -0
- package/src/widgets/pie/options.ts +50 -20
- package/src/widgets/pie/types.ts +6 -0
- package/src/widgets/scatterplot/download.test.ts +9 -0
- package/src/widgets/scatterplot/download.ts +5 -3
- package/src/widgets/scatterplot/index.ts +3 -0
- package/src/widgets/scatterplot/options.test.ts +78 -0
- package/src/widgets/scatterplot/options.ts +35 -11
- package/src/widgets/scatterplot/points.ts +35 -0
- package/src/widgets/scatterplot/transforms.test.ts +19 -0
- package/src/widgets/scatterplot/transforms.ts +23 -18
- package/src/widgets/scatterplot/types.ts +25 -2
- package/src/widgets/timeseries/options.test.ts +65 -0
- package/src/widgets/timeseries/options.ts +31 -14
- package/src/widgets/timeseries/types.ts +6 -0
- package/src/widgets/utils/item-color.test.ts +74 -0
- package/src/widgets/utils/item-color.ts +55 -0
- package/dist/actions-BWlS5tpP.js.map +0 -1
- package/dist/category-alzjKzvQ.js +0 -839
- package/dist/category-alzjKzvQ.js.map +0 -1
- package/dist/echart-CV8F50gn.js.map +0 -1
- package/dist/markdown-Bn2nKCbn.js.map +0 -1
- package/dist/markdown-content-kYNLOX0T.js.map +0 -1
- package/dist/option-builders-DQivvvbb.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"category-alzjKzvQ.js","names":["{\n value,\n maxValue,\n color,\n size = 'small',\n}: CategoryBarProps","size = 'small'","{ width: `${pct}%`, backgroundColor: color }","`${pct}%`","<Box\n sx={styles.barFill[size]}\n style={{ width: `${pct}%`, backgroundColor: color }}\n // Tag the fill so an ancestor row's `:hover` selector can target\n // it (`&:hover [data-bar-fill]`) — the brightening affordance now\n // fires from anywhere in the row, not only the bar track.\n data-bar-fill='true'\n />","styles.barFill[size]","<Box sx={styles.bar[size]} data-size={size}>\n <Box\n sx={styles.barFill[size]}\n style={{ width: `${pct}%`, backgroundColor: color }}\n // Tag the fill so an ancestor row's `:hover` selector can target\n // it (`&:hover [data-bar-fill]`) — the brightening affordance now\n // fires from anywhere in the row, not only the bar track.\n data-bar-fill='true'\n />\n </Box>","styles.bar[size]","{\n name,\n displayName,\n value,\n maxValue,\n color,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowSingleProps","(e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }","() => onToggle(name)","<Typography variant='body2' sx={styles.name}>\n {displayName ?? name}\n </Typography>","displayName ?? name","formatter(value)","<Typography variant='body2' sx={styles.value}>\n {formatter(value)}\n </Typography>","<Box sx={styles.rowHeader}>\n <Typography variant='body2' sx={styles.name}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.value}>\n {formatter(value)}\n </Typography>\n </Box>","<CategoryBar\n value={value}\n maxValue={maxValue}\n color={color}\n size={size}\n />","<Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowSingle}\n >\n <Box sx={styles.rowHeader}>\n <Typography variant='body2' sx={styles.name}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.value}>\n {formatter(value)}\n </Typography>\n </Box>\n <CategoryBar\n value={value}\n maxValue={maxValue}\n color={color}\n size={size}\n />\n </Box>","{\n name,\n displayName,\n values,\n colors,\n maxValue,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowMultiProps","(e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }","() => onToggle(name)","<Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>","displayName ?? name","values.map((v, i) => (\n // Multi-bar rows iterate per series — the bar belongs to series `i`,\n // and the row is keyed on the parent's `name`, so a composite of\n // `name` + the series index is stable across reorders.\n <Box key={`${name}-series-${i}`} sx={styles.multiBarRow}>\n <Box sx={styles.multiBarContainer}>\n <CategoryBar\n value={v}\n maxValue={maxValue}\n color={colors[i] ?? ''}\n size={size}\n />\n </Box>\n <Typography variant='body2' sx={styles.value}>\n {formatter(v)}\n </Typography>\n </Box>\n ))","(v, i) => (\n // Multi-bar rows iterate per series — the bar belongs to series `i`,\n // and the row is keyed on the parent's `name`, so a composite of\n // `name` + the series index is stable across reorders.\n <Box key={`${name}-series-${i}`} sx={styles.multiBarRow}>\n <Box sx={styles.multiBarContainer}>\n <CategoryBar\n value={v}\n maxValue={maxValue}\n color={colors[i] ?? ''}\n size={size}\n />\n </Box>\n <Typography variant='body2' sx={styles.value}>\n {formatter(v)}\n </Typography>\n </Box>\n )","<Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowMulti}\n >\n <Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>\n {values.map((v, i) => (\n // Multi-bar rows iterate per series — the bar belongs to series `i`,\n // and the row is keyed on the parent's `name`, so a composite of\n // `name` + the series index is stable across reorders.\n <Box key={`${name}-series-${i}`} sx={styles.multiBarRow}>\n <Box sx={styles.multiBarContainer}>\n <CategoryBar\n value={v}\n maxValue={maxValue}\n color={colors[i] ?? ''}\n size={size}\n />\n </Box>\n <Typography variant='body2' sx={styles.value}>\n {formatter(v)}\n </Typography>\n </Box>\n ))}\n </Box>","{\n values,\n colors,\n maxValue,\n size = 'small',\n}: CategoryBarStackedProps","size = 'small'","values.map((v, i) => {\n if (v <= 0) return null\n const leftPct = maxValue > 0 ? (offsets[i]! / maxValue) * 100 : 0\n const pct = maxValue > 0 ? (v / maxValue) * 100 : 0\n return (\n <Box\n // Segments are keyed by per-series colour (the only stable\n // distinguishing field on this side) plus the segment index for\n // when callers reuse palette colours across series.\n key={`seg-${colors[i] ?? 'none'}-${i}`}\n sx={styles.stackedSegment[size]}\n style={{\n left: `${leftPct}%`,\n width: `${pct}%`,\n backgroundColor: colors[i] ?? '',\n }}\n data-bar-fill='true'\n />\n )\n })","(v, i) => {\n if (v <= 0) return null\n const leftPct = maxValue > 0 ? (offsets[i]! / maxValue) * 100 : 0\n const pct = maxValue > 0 ? (v / maxValue) * 100 : 0\n return (\n <Box\n // Segments are keyed by per-series colour (the only stable\n // distinguishing field on this side) plus the segment index for\n // when callers reuse palette colours across series.\n key={`seg-${colors[i] ?? 'none'}-${i}`}\n sx={styles.stackedSegment[size]}\n style={{\n left: `${leftPct}%`,\n width: `${pct}%`,\n backgroundColor: colors[i] ?? '',\n }}\n data-bar-fill='true'\n />\n )\n }","v","<Box sx={styles.bar[size]} data-size={size}>\n {values.map((v, i) => {\n if (v <= 0) return null\n const leftPct = maxValue > 0 ? (offsets[i]! / maxValue) * 100 : 0\n const pct = maxValue > 0 ? (v / maxValue) * 100 : 0\n return (\n <Box\n // Segments are keyed by per-series colour (the only stable\n // distinguishing field on this side) plus the segment index for\n // when callers reuse palette colours across series.\n key={`seg-${colors[i] ?? 'none'}-${i}`}\n sx={styles.stackedSegment[size]}\n style={{\n left: `${leftPct}%`,\n width: `${pct}%`,\n backgroundColor: colors[i] ?? '',\n }}\n data-bar-fill='true'\n />\n )\n })}\n </Box>","styles.bar[size]","{\n name,\n displayName,\n values,\n colors,\n seriesNames,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowStackedProps","(e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }","Typography","Box","() => onToggle(name)","handleKey","styles.rowStacked","'body2'","styles.stackedTotal","formatter(total)","styles.stackedHeader","<Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>","'button'","selected","0","(a, b) => a + b","displayName ?? name","<Typography variant='body2' sx={styles.stackedTotal}>\n {formatter(total)}\n </Typography>","<Box sx={styles.stackedHeader}>\n <Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.stackedTotal}>\n {formatter(total)}\n </Typography>\n </Box>","<CategoryBarStacked\n values={values}\n colors={colors}\n maxValue={total}\n size={size}\n />","values.map((v, i) => (\n // Series name is the stable identity for the breakdown line.\n <Typography\n key={`breakdown-${seriesNames[i]}`}\n variant='body2'\n sx={styles.stackedBreakdownItem}\n >\n {`${seriesNames[i]}: ${formatter(v)}`}\n </Typography>\n ))","(v, i) => (\n // Series name is the stable identity for the breakdown line.\n <Typography\n key={`breakdown-${seriesNames[i]}`}\n variant='body2'\n sx={styles.stackedBreakdownItem}\n >\n {`${seriesNames[i]}: ${formatter(v)}`}\n </Typography>\n )","<Box sx={styles.stackedBreakdown}>\n {values.map((v, i) => (\n // Series name is the stable identity for the breakdown line.\n <Typography\n key={`breakdown-${seriesNames[i]}`}\n variant='body2'\n sx={styles.stackedBreakdownItem}\n >\n {`${seriesNames[i]}: ${formatter(v)}`}\n </Typography>\n ))}\n </Box>","<Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowStacked}\n >\n <Box sx={styles.stackedHeader}>\n <Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.stackedTotal}>\n {formatter(total)}\n </Typography>\n </Box>\n <CategoryBarStacked\n values={values}\n colors={colors}\n maxValue={total}\n size={size}\n />\n <Box sx={styles.stackedBreakdown}>\n {values.map((v, i) => (\n // Series name is the stable identity for the breakdown line.\n <Typography\n key={`breakdown-${seriesNames[i]}`}\n variant='body2'\n sx={styles.stackedBreakdownItem}\n >\n {`${seriesNames[i]}: ${formatter(v)}`}\n </Typography>\n ))}\n </Box>\n </Box>","{\n hiddenCount,\n otherLabel = 'Others',\n otherCountLabel = '{count}',\n onShowAll,\n showAllLabel = 'Show all',\n}: CategoryRowOtherProps","otherLabel = 'Others'","otherCountLabel = '{count}'","showAllLabel = 'Show all'","otherCountLabel.replace('{count}', String(hiddenCount))","<Typography variant='body2' sx={styles.otherCount}>\n {countText}\n </Typography>","<Typography variant='body2' sx={styles.otherLabel}>\n {otherLabel}\n </Typography>","<Box sx={styles.otherRow}>\n <Typography variant='body2' sx={styles.otherLabel}>\n {otherLabel}\n </Typography>\n {count}\n </Box>","(e: KeyboardEvent<HTMLSpanElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onShowAll()\n }\n }","{ ...styles.otherLabel, ...styles.otherLabelButton }","<Typography\n variant='body2'\n component='span'\n role='button'\n tabIndex={0}\n onClick={onShowAll}\n onKeyDown={handleKey}\n sx={{ ...styles.otherLabel, ...styles.otherLabelButton }}\n >\n {otherLabel}\n </Typography>","<Tooltip title={showAllLabel}>\n <Typography\n variant='body2'\n component='span'\n role='button'\n tabIndex={0}\n onClick={onShowAll}\n onKeyDown={handleKey}\n sx={{ ...styles.otherLabel, ...styles.otherLabelButton }}\n >\n {otherLabel}\n </Typography>\n </Tooltip>","<Box sx={styles.otherRow}>\n <Tooltip title={showAllLabel}>\n <Typography\n variant='body2'\n component='span'\n role='button'\n tabIndex={0}\n onClick={onShowAll}\n onKeyDown={handleKey}\n sx={{ ...styles.otherLabel, ...styles.otherLabelButton }}\n >\n {otherLabel}\n </Typography>\n </Tooltip>\n {count}\n </Box>","{\n series,\n colorAt,\n hiddenSeries = NO_HIDDEN_SERIES,\n onToggle,\n}: CategoryLegendProps","hiddenSeries = NO_HIDDEN_SERIES","series.map((s, i) => {\n const hidden = hiddenSeries.has(i)\n return (\n <Box\n key={s.name}\n // `checkbox`/`aria-checked`, not `button`/`aria-pressed` — this\n // toggles a persistent visibility state (closer to a checkbox)\n // AND keeps it out of `getByRole('button')` queries that count\n // category ROWS throughout this widget's test suite.\n role='checkbox'\n aria-checked={!hidden}\n tabIndex={0}\n onClick={() => onToggle?.(i)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle?.(i)\n }\n }}\n sx={hidden ? styles.legendItemHidden : styles.legendItem}\n >\n <Box\n sx={styles.legendDot}\n style={{ backgroundColor: colorAt(i) }}\n />\n <Typography variant='caption' sx={styles.legendLabel}>\n {s.name}\n </Typography>\n </Box>\n )\n })","(s, i) => {\n const hidden = hiddenSeries.has(i)\n return (\n <Box\n key={s.name}\n // `checkbox`/`aria-checked`, not `button`/`aria-pressed` — this\n // toggles a persistent visibility state (closer to a checkbox)\n // AND keeps it out of `getByRole('button')` queries that count\n // category ROWS throughout this widget's test suite.\n role='checkbox'\n aria-checked={!hidden}\n tabIndex={0}\n onClick={() => onToggle?.(i)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle?.(i)\n }\n }}\n sx={hidden ? styles.legendItemHidden : styles.legendItem}\n >\n <Box\n sx={styles.legendDot}\n style={{ backgroundColor: colorAt(i) }}\n />\n <Typography variant='caption' sx={styles.legendLabel}>\n {s.name}\n </Typography>\n </Box>\n )\n }","<Box sx={styles.legend}>\n {series.map((s, i) => {\n const hidden = hiddenSeries.has(i)\n return (\n <Box\n key={s.name}\n // `checkbox`/`aria-checked`, not `button`/`aria-pressed` — this\n // toggles a persistent visibility state (closer to a checkbox)\n // AND keeps it out of `getByRole('button')` queries that count\n // category ROWS throughout this widget's test suite.\n role='checkbox'\n aria-checked={!hidden}\n tabIndex={0}\n onClick={() => onToggle?.(i)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle?.(i)\n }\n }}\n sx={hidden ? styles.legendItemHidden : styles.legendItem}\n >\n <Box\n sx={styles.legendDot}\n style={{ backgroundColor: colorAt(i) }}\n />\n <Typography variant='caption' sx={styles.legendLabel}>\n {s.name}\n </Typography>\n </Box>\n )\n })}\n </Box>","{\n data,\n selection,\n onSelectionChange,\n formatter,\n labelFormatter,\n series,\n maxItems = DEFAULT_MAX_ITEMS,\n labels,\n onShowAll,\n maxOverride,\n size = 'small',\n stacked = false,\n hiddenSeries: hiddenSeriesProp,\n onHiddenSeriesChange,\n}: CategoryUIProps","maxItems = DEFAULT_MAX_ITEMS","size = 'small'","stacked = false","(n: number) => String(n)","selection ?? []","new Set<CategoryKey>(selection ?? [])","useMemo(\n () => new Set<CategoryKey>(selection ?? []),\n [selection],\n )","(name: CategoryKey) => {\n if (!onSelectionChange) return\n const cur = selection ?? []\n const next = cur.includes(name)\n ? cur.filter((n) => n !== name)\n : [...cur, name]\n onSelectionChange(next)\n }","n","useCallback(\n (name: CategoryKey) => {\n if (!onSelectionChange) return\n const cur = selection ?? []\n const next = cur.includes(name)\n ? cur.filter((n) => n !== name)\n : [...cur, name]\n onSelectionChange(next)\n },\n [onSelectionChange, selection],\n )","Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n )","palette","[\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ]","main","theme.palette.secondary.main","useMemo<readonly string[]>(\n () => [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n [theme],\n )","(seriesIndex: number, rowColor?: string): string => {\n const override = rowColor ?? series?.[seriesIndex]?.color\n if (override) return resolveThemeColor(theme, override) ?? override\n return (\n paletteColors[seriesIndex % paletteColors.length] ??\n theme.palette.secondary.main\n )\n }","paletteColors.length","useCallback(\n (seriesIndex: number, rowColor?: string): string => {\n const override = rowColor ?? series?.[seriesIndex]?.color\n if (override) return resolveThemeColor(theme, override) ?? override\n return (\n paletteColors[seriesIndex % paletteColors.length] ??\n theme.palette.secondary.main\n )\n },\n [series, paletteColors, theme],\n )","generateGroupedData(data)","useMemo(() => generateGroupedData(data), [data])","useMemo(() => {\n if (typeof maxOverride === 'number' && maxOverride > 0) return maxOverride\n let m = 0\n for (const row of grouped) {\n for (const v of row.values) if (v > m) m = v\n }\n return m\n }, [grouped, maxOverride])","(node: HTMLDivElement | null) => {\n if (!node) return\n if (measuredRef.current) return\n if (scrollMode) return\n const h = node.clientHeight\n if (h > 0) {\n measuredRef.current = true\n setNaturalHeight(h)\n }\n }","useCallback(\n (node: HTMLDivElement | null) => {\n if (!node) return\n if (measuredRef.current) return\n if (scrollMode) return\n const h = node.clientHeight\n if (h > 0) {\n measuredRef.current = true\n setNaturalHeight(h)\n }\n },\n [scrollMode],\n )","() => new Set()","categorySeriesKey(series)","(index: number) => {\n // Start from what the user sees, not the stored set: `data` can\n // shrink while the `series` names hold (the reset only watches the\n // names), and a stale index would otherwise count toward the guard\n // below — or make unchecking one series hide a different one.\n const next = effectiveHidden(hiddenSeries, seriesCount)\n if (next.has(index)) next.delete(index)\n else next.add(index)\n ;(onHiddenSeriesChange ?? setLocalHidden)(\n next.size >= seriesCount ? new Set() : next,\n )\n }","next","useCallback(\n (index: number) => {\n // Start from what the user sees, not the stored set: `data` can\n // shrink while the `series` names hold (the reset only watches the\n // names), and a stale index would otherwise count toward the guard\n // below — or make unchecking one series hide a different one.\n const next = effectiveHidden(hiddenSeries, seriesCount)\n if (next.has(index)) next.delete(index)\n else next.add(index)\n ;(onHiddenSeriesChange ?? setLocalHidden)(\n next.size >= seriesCount ? new Set() : next,\n )\n },\n [hiddenSeries, onHiddenSeriesChange, seriesCount],\n )","typeof maxItems === 'number' && Number.isFinite(maxItems) && maxItems > 0","hasCap ? grouped.slice(0, maxItems) : grouped","Box","listRefCallback","scrollMode ? { ...styles.list, ...styles.listScroll } : styles.list","scrollMaxHeight !== undefined\n ? { maxHeight: scrollMaxHeight, overflowY: 'auto' }\n : undefined","visible.map((row) => {\n const selected = selectionSet.has(row.name)\n const dimmed = hasSelection && !selected\n const displayName = labelFormatter\n ? labelFormatter(row.name)\n : undefined\n // When the row is dimmed, every series' bar fill is replaced\n // with `dimColor`. Text + track are untouched. This is the\n // ONLY visual difference between selected and non-selected\n // rows — no row-level opacity, no `rowSelected` bg tint.\n const fill = (i: number): string =>\n dimmed ? dimColor : colorAt(i, row.rowColors[i])\n // Series hidden via the legend are dropped before reaching the\n // row components — they render whatever arrays they're given\n // with no notion of \"hidden\" themselves. `visibleIdx` is hoisted\n // above this loop (same for every row).\n if (isMulti) {\n if (stacked) {\n // Stacked-mode branch: one segmented bar per row. Toggling\n // `stacked` after the natural-height measurement does NOT\n // re-trigger measurement (see the `naturalHeight` comment\n // above) — accepted trade-off: stacked rows have a slightly\n // different height, so the scroll viewport may show extra\n // whitespace after the toggle. Visual only, not functional.\n // Stacked rows are per-row normalized — they don't take\n // the global `maxValue`. Each row's bar fills 100% of the\n // track and the segments split it by `value_i / sum`. The\n // numeric total in the header preserves the per-row\n // magnitude information.\n return (\n <CategoryRowStacked\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n seriesNames={visibleIdx.map(\n (i) => series?.[i]?.name ?? `Series ${i + 1}`,\n )}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowMulti\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n maxValue={maxValue}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowSingle\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n value={row.values[0] ?? 0}\n maxValue={maxValue}\n color={fill(0)}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n })","styles.root","(_, i) => i","i","row","hiddenCount > 0 && (\n <CategoryRowOther\n hiddenCount={hiddenCount}\n otherLabel={labels?.other}\n otherCountLabel={labels?.otherCount}\n showAllLabel={labels?.showAll}\n onShowAll={onShowAll}\n />\n )","other","otherCount","showAll","<Box\n ref={listRefCallback}\n sx={scrollMode ? { ...styles.list, ...styles.listScroll } : styles.list}\n style={\n scrollMaxHeight !== undefined\n ? { maxHeight: scrollMaxHeight, overflowY: 'auto' }\n : undefined\n }\n >\n {visible.map((row) => {\n const selected = selectionSet.has(row.name)\n const dimmed = hasSelection && !selected\n const displayName = labelFormatter\n ? labelFormatter(row.name)\n : undefined\n // When the row is dimmed, every series' bar fill is replaced\n // with `dimColor`. Text + track are untouched. This is the\n // ONLY visual difference between selected and non-selected\n // rows — no row-level opacity, no `rowSelected` bg tint.\n const fill = (i: number): string =>\n dimmed ? dimColor : colorAt(i, row.rowColors[i])\n // Series hidden via the legend are dropped before reaching the\n // row components — they render whatever arrays they're given\n // with no notion of \"hidden\" themselves. `visibleIdx` is hoisted\n // above this loop (same for every row).\n if (isMulti) {\n if (stacked) {\n // Stacked-mode branch: one segmented bar per row. Toggling\n // `stacked` after the natural-height measurement does NOT\n // re-trigger measurement (see the `naturalHeight` comment\n // above) — accepted trade-off: stacked rows have a slightly\n // different height, so the scroll viewport may show extra\n // whitespace after the toggle. Visual only, not functional.\n // Stacked rows are per-row normalized — they don't take\n // the global `maxValue`. Each row's bar fills 100% of the\n // track and the segments split it by `value_i / sum`. The\n // numeric total in the header preserves the per-row\n // magnitude information.\n return (\n <CategoryRowStacked\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n seriesNames={visibleIdx.map(\n (i) => series?.[i]?.name ?? `Series ${i + 1}`,\n )}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowMulti\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n maxValue={maxValue}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowSingle\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n value={row.values[0] ?? 0}\n maxValue={maxValue}\n color={fill(0)}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n })}\n {hiddenCount > 0 && (\n <CategoryRowOther\n hiddenCount={hiddenCount}\n otherLabel={labels?.other}\n otherCountLabel={labels?.otherCount}\n showAllLabel={labels?.showAll}\n onShowAll={onShowAll}\n />\n )}\n </Box>","hasLegend && series && (\n <CategoryLegend\n // Trimmed: `series` is caller metadata and may outrun `data`,\n // and an entry with no data behind it has nothing to hide.\n series={series.slice(0, seriesCount)}\n colorAt={(i) => colorAt(i)}\n hiddenSeries={activeHidden}\n onToggle={toggleSeries}\n />\n )","<Box sx={styles.root}>\n <Box\n ref={listRefCallback}\n sx={scrollMode ? { ...styles.list, ...styles.listScroll } : styles.list}\n style={\n scrollMaxHeight !== undefined\n ? { maxHeight: scrollMaxHeight, overflowY: 'auto' }\n : undefined\n }\n >\n {visible.map((row) => {\n const selected = selectionSet.has(row.name)\n const dimmed = hasSelection && !selected\n const displayName = labelFormatter\n ? labelFormatter(row.name)\n : undefined\n // When the row is dimmed, every series' bar fill is replaced\n // with `dimColor`. Text + track are untouched. This is the\n // ONLY visual difference between selected and non-selected\n // rows — no row-level opacity, no `rowSelected` bg tint.\n const fill = (i: number): string =>\n dimmed ? dimColor : colorAt(i, row.rowColors[i])\n // Series hidden via the legend are dropped before reaching the\n // row components — they render whatever arrays they're given\n // with no notion of \"hidden\" themselves. `visibleIdx` is hoisted\n // above this loop (same for every row).\n if (isMulti) {\n if (stacked) {\n // Stacked-mode branch: one segmented bar per row. Toggling\n // `stacked` after the natural-height measurement does NOT\n // re-trigger measurement (see the `naturalHeight` comment\n // above) — accepted trade-off: stacked rows have a slightly\n // different height, so the scroll viewport may show extra\n // whitespace after the toggle. Visual only, not functional.\n // Stacked rows are per-row normalized — they don't take\n // the global `maxValue`. Each row's bar fills 100% of the\n // track and the segments split it by `value_i / sum`. The\n // numeric total in the header preserves the per-row\n // magnitude information.\n return (\n <CategoryRowStacked\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n seriesNames={visibleIdx.map(\n (i) => series?.[i]?.name ?? `Series ${i + 1}`,\n )}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowMulti\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n maxValue={maxValue}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowSingle\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n value={row.values[0] ?? 0}\n maxValue={maxValue}\n color={fill(0)}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n })}\n {hiddenCount > 0 && (\n <CategoryRowOther\n hiddenCount={hiddenCount}\n otherLabel={labels?.other}\n otherCountLabel={labels?.otherCount}\n showAllLabel={labels?.showAll}\n onShowAll={onShowAll}\n />\n )}\n </Box>\n {hasLegend && series && (\n <CategoryLegend\n // Trimmed: `series` is caller metadata and may outrun `data`,\n // and an entry with no data behind it has nothing to hide.\n series={series.slice(0, seriesCount)}\n colorAt={(i) => colorAt(i)}\n hiddenSeries={activeHidden}\n onToggle={toggleSeries}\n />\n )}\n </Box>","{\n selection,\n onSelectionChange,\n series,\n maxItems,\n labels,\n onShowAll,\n maxOverride,\n size,\n stacked,\n}: CategoryProps","categorySeriesKey(series)","(s) => s.categoryHiddenSeries","(indices: ReadonlySet<number>) =>\n getWidgetStore(id).setState({\n categoryHiddenSeries: { key: seriesKey, indices },\n } as Partial<CategoryWidgetState>)","useCallback(\n (indices: ReadonlySet<number>) =>\n getWidgetStore(id).setState({\n categoryHiddenSeries: { key: seriesKey, indices },\n } as Partial<CategoryWidgetState>),\n [id, seriesKey],\n )","<CategoryUI\n data={slice.data}\n formatter={slice.formatter}\n labelFormatter={slice.labelFormatter}\n selection={selection}\n onSelectionChange={onSelectionChange}\n series={series}\n maxItems={maxItems}\n labels={labels}\n onShowAll={onShowAll}\n maxOverride={effectiveMaxOverride}\n size={size}\n stacked={stacked}\n hiddenSeries={hiddenSeries}\n onHiddenSeriesChange={onHiddenSeriesChange}\n />","data","formatter","labelFormatter","{\n rows = 5,\n size = 'small',\n}: CategorySkeletonProps","rows = 5","size = 'small'","Array.from({ length: rows })","<Box sx={styles.root}>\n {Array.from({ length: rows }).map((_, i) => (\n <Box key={`row-${i}`} sx={styles.row}>\n <Box sx={styles.rowHeader}>\n <Skeleton variant='rectangular' sx={styles.name} />\n <Skeleton variant='rectangular' sx={styles.value} />\n </Box>\n <Skeleton\n variant='rectangular'\n sx={{\n height: BAR_HEIGHT[size],\n width: WIDTH_PATTERNS[i % WIDTH_PATTERNS.length],\n }}\n />\n </Box>\n ))}\n </Box>"],"sources":["../src/widgets/category/style.ts","../src/widgets/category/components/category-bar.tsx","../src/widgets/category/components/category-row-single.tsx","../src/widgets/category/components/category-row-multi.tsx","../src/widgets/category/components/category-bar-stacked.tsx","../src/widgets/category/components/category-row-stacked.tsx","../src/widgets/category/components/category-row-other.tsx","../src/widgets/category/helpers.ts","../src/widgets/category/components/category-legend.tsx","../src/widgets/category/category-ui.tsx","../src/widgets/category/category.tsx","../src/widgets/category/skeleton.tsx","../src/widgets/category/download.ts"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material'\nimport type { CategorySize } from './types'\n\n// Size-keyed bar styles — scoped strictly to the bar primitive\n// (track + fill height & corner radius). `small` preserves the\n// historical look (4px-tall pill); `medium` renders a 12px-tall track\n// with a 2px corner radius. Every other token (gap, padding, hover,\n// transitions, label/value typography) is size-invariant.\nconst bar: Record<CategorySize, SxProps<Theme>> = {\n small: {\n position: 'relative',\n height: 4,\n bgcolor: 'action.hover',\n borderRadius: 1,\n overflow: 'hidden',\n transition: 'background-color 150ms ease',\n },\n medium: {\n position: 'relative',\n height: 12,\n bgcolor: 'action.hover',\n borderRadius: 0.25,\n overflow: 'hidden',\n transition: 'background-color 150ms ease',\n },\n}\n\nconst barFill: Record<CategorySize, SxProps<Theme>> = {\n small: {\n position: 'absolute',\n inset: '0 auto 0 0',\n borderRadius: 1,\n transition:\n 'width 300ms ease-in-out, background-color 150ms ease, filter 150ms ease',\n },\n medium: {\n position: 'absolute',\n inset: '0 auto 0 0',\n borderRadius: 0.25,\n transition:\n 'width 300ms ease-in-out, background-color 150ms ease, filter 150ms ease',\n },\n}\n\n// Square-cornered segment for the stacked bar. Adjacent segments meet\n// cleanly; the parent `bar[size]` track clips the outer ends via its\n// own borderRadius + overflow:hidden, so the stacked bar still has the\n// same rounded silhouette as a single `CategoryBar`.\nconst stackedSegment: Record<CategorySize, SxProps<Theme>> = {\n small: {\n position: 'absolute',\n top: 0,\n bottom: 0,\n transition:\n 'left 300ms ease-in-out, width 300ms ease-in-out, background-color 150ms ease, filter 150ms ease',\n },\n medium: {\n position: 'absolute',\n top: 0,\n bottom: 0,\n transition:\n 'left 300ms ease-in-out, width 300ms ease-in-out, background-color 150ms ease, filter 150ms ease',\n },\n}\n\nexport const styles = {\n // ── Top-level container ────────────────────────────────────────────\n root: {\n display: 'flex',\n flexDirection: 'column',\n position: 'relative',\n width: '100%',\n },\n // Flat list of category rows. Holds both single + multi rows.\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: 1,\n py: 0.5,\n },\n // Layered on `list` only in scroll mode (`maxItems === 0`): right padding\n // so the bars + right-aligned values clear the scrollbar. Right side only\n // — the left edge stays at 0 so content stays aligned with the capped\n // view. `overflowY` + the dynamic `maxHeight` remain on the inline style.\n listScroll: {\n pr: 1,\n },\n\n // ── Single-series row ─────────────────────────────────────────────\n // v1 layout: label + value share a top flex row (justify-between); the\n // bar fills the row width below. Mirrors `rowMulti` for visual\n // consistency.\n rowSingle: {\n display: 'flex',\n flexDirection: 'column',\n gap: 0.5,\n // py: 0.5,\n // px: 0.5,\n cursor: 'pointer',\n userSelect: 'none',\n transition: 'background-color 80ms ease',\n // Hovering anywhere in the row also brightens the bar fill, matching\n // what the bar got from `bar:hover` before. `data-bar-fill` is set\n // on `barFill` by <CategoryBar>; descendant selector reaches it\n // through `bar` in single rows and through `multiBarRow > bar` in\n // multi rows.\n '&:hover [data-bar-fill=\"true\"]': {\n filter: 'brightness(1.2)',\n },\n '&:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n outlineOffset: -2,\n },\n },\n // Top row of `rowSingle`: label on the left, value on the right.\n rowHeader: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n gap: 1,\n },\n // ── Multi-series row ──────────────────────────────────────────────\n // Flex column: label fills the top row; bar+value pairs stack below.\n // Bars get full track width — better legibility for N>=2 series than a\n // shared grid would give.\n rowMulti: {\n display: 'flex',\n flexDirection: 'column',\n gap: 0.5,\n // py: 0.5,\n // px: 0.5,\n cursor: 'pointer',\n userSelect: 'none',\n transition: 'background-color 80ms ease',\n // Row-hover brightens every bar fill inside the multi-row.\n '&:hover [data-bar-fill=\"true\"]': {\n filter: 'brightness(1.2)',\n },\n '&:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n outlineOffset: -2,\n },\n },\n // ── Row labels / values ───────────────────────────────────────────\n // Matches v1: name uses medium-weight body2 in primary text color,\n // value uses regular body2 in secondary text color. Same font size,\n // visual hierarchy via weight + color.\n name: {\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n textOverflow: 'ellipsis',\n color: 'text.primary',\n fontWeight: 'medium',\n },\n multiName: {\n overflow: 'hidden',\n whiteSpace: 'nowrap',\n textOverflow: 'ellipsis',\n color: 'text.primary',\n fontWeight: 'medium',\n },\n value: {\n color: 'text.secondary',\n fontVariantNumeric: 'tabular-nums',\n // Right-align + minWidth keeps `rowMulti`'s per-bar value column\n // tidy (where the bar grows on the left and the number sits at a\n // fixed right edge). `rowSingle` uses `rowHeader`'s flex\n // justify-between to push the value to the trailing edge, so the\n // minWidth here is also harmless in that context.\n minWidth: 48,\n textAlign: 'right',\n },\n\n // ── Bar (track + fill) ────────────────────────────────────────────\n // Size-keyed maps live above the `styles` object; `<CategoryBar>` picks\n // the variant via its `size` prop. Hover brightening still fires from\n // the parent row (`&:hover [data-bar-fill]`), so it works regardless\n // of which size variant the bar uses.\n bar,\n barFill,\n stackedSegment,\n\n // ── Multi-series internals ────────────────────────────────────────\n // One [bar | value] flex row per series, inside `rowMulti`'s column.\n multiBarRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n },\n multiBarContainer: {\n flex: 1,\n },\n\n // ── Stacked-mode row ──────────────────────────────────────────────\n // Mirror `rowMulti`'s outer shape (click target + hover brightening +\n // focus ring) but stack three children vertically: header line, bar,\n // breakdown line.\n rowStacked: {\n display: 'flex',\n flexDirection: 'column',\n gap: 0.5,\n cursor: 'pointer',\n userSelect: 'none',\n transition: 'background-color 80ms ease',\n '&:hover [data-bar-fill=\"true\"]': {\n filter: 'brightness(1.2)',\n },\n '&:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n outlineOffset: -2,\n },\n },\n stackedHeader: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n gap: 1,\n },\n // Right-aligned total in the header. Tabular-nums keeps totals lined\n // up vertically across rows; minWidth reuses the per-series-value\n // column width so the right edge sits at the same spot as `rowMulti`'s\n // value column when both modes co-exist in the same widget card.\n stackedTotal: {\n color: 'text.primary',\n fontWeight: 'medium',\n fontVariantNumeric: 'tabular-nums',\n minWidth: 48,\n textAlign: 'right',\n },\n // Flex row of \"{seriesName}: {value}\" pairs. Wraps gracefully when\n // series names are long or there are many series.\n stackedBreakdown: {\n display: 'flex',\n flexWrap: 'wrap',\n columnGap: 2,\n rowGap: 0.25,\n },\n stackedBreakdownItem: {\n color: 'text.secondary',\n fontVariantNumeric: 'tabular-nums',\n },\n\n // ── Legend (sticky bottom) ────────────────────────────────────────\n legend: {\n display: 'flex',\n alignItems: 'center',\n gap: 2,\n flexWrap: 'wrap',\n pt: 2,\n mt: 2,\n position: 'sticky',\n bottom: 0,\n bgcolor: 'background.paper',\n borderTop: (theme: Theme) => `1px solid ${theme.palette.divider}`,\n },\n legendItem: {\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n cursor: 'pointer',\n },\n legendItemHidden: {\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n cursor: 'pointer',\n opacity: 0.5,\n },\n legendDot: {\n width: 14,\n height: 14,\n borderRadius: '50%',\n },\n legendLabel: {\n color: 'text.secondary',\n textTransform: 'uppercase',\n fontWeight: 'medium',\n },\n\n // ── \"Other\" overflow row ──────────────────────────────────────────\n // Left-aligned `Others <count>` — the label sits next to a muted count\n // (no parentheses). Only the label is the click target when interactive;\n // the count is always static text beside it.\n otherRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 1,\n py: 0.5,\n px: 0.5,\n },\n // Interactive affordances layered onto the \"Others\" label only (cursor,\n // hover tint, focus ring). A negative LEFT margin + matching padding keep\n // the hover pill breathing without shifting the label's text edge (it\n // stays aligned with the category rows above). No right margin, so the\n // row `gap` is preserved between the pill and the count.\n otherLabelButton: {\n cursor: 'pointer',\n userSelect: 'none',\n borderRadius: 1,\n px: 0.5,\n ml: -0.5,\n transition: 'background-color 80ms ease',\n '&:hover': {\n bgcolor: 'action.hover',\n },\n '&:focus-visible': {\n outline: '2px solid',\n outlineColor: 'primary.main',\n outlineOffset: 2,\n },\n },\n otherLabel: {\n color: 'text.primary',\n fontWeight: 'medium',\n },\n otherCount: {\n color: 'text.secondary',\n fontVariantNumeric: 'tabular-nums',\n },\n // `bar` / `barFill` are size-keyed (`Record<CategorySize, SxProps>`);\n // every other entry is a single `SxProps<Theme>`. The mixed shape means\n // we can't apply the file-wide `satisfies Record<string, SxProps<Theme>>`\n // constraint that the other widget style files use — consumers of\n // individual tokens still get full type-checking at the call site via\n // the typed `Box`/`Typography` `sx` prop.\n}\n","import type { CssColor } from '../../../components/types'\nimport { Box } from '@mui/material'\nimport type { CategorySize } from '../types'\nimport { styles } from '../style'\n\nexport interface CategoryBarProps {\n /** Numeric value driving the fill width. */\n value: number\n /** Denominator for the percentage fill. `0` → empty bar (no division by zero). */\n maxValue: number\n /** Fill color, already resolved by `CategoryUI.colorAt`. */\n color: CssColor\n /**\n * Visual density. `'small'` (default) keeps the historical 4px pill;\n * `'medium'` renders a 12px-tall track with a 2px corner radius. Only\n * the bar track + fill change — every other token in the parent row is\n * size-invariant.\n */\n size?: CategorySize\n}\n\n/**\n * Single horizontal bar (track + proportional fill). Pure presentation.\n * Selection / dimming live at the row level; this component is unaware of\n * both — bars stay vivid regardless of selection state (per the v2\n * \"row-bg + row-dim, never muted bar\" rule).\n */\nexport function CategoryBar({\n value,\n maxValue,\n color,\n size = 'small',\n}: CategoryBarProps) {\n const pct = maxValue > 0 ? (value / maxValue) * 100 : 0\n // Width + color rendered as inline `style` (not `sx`) to avoid\n // generating a new emotion class for every distinct value/color pair\n // — also makes the rendered DOM testable without computed-style math.\n return (\n <Box sx={styles.bar[size]} data-size={size}>\n <Box\n sx={styles.barFill[size]}\n style={{ width: `${pct}%`, backgroundColor: color }}\n // Tag the fill so an ancestor row's `:hover` selector can target\n // it (`&:hover [data-bar-fill]`) — the brightening affordance now\n // fires from anywhere in the row, not only the bar track.\n data-bar-fill='true'\n />\n </Box>\n )\n}\n","import type { CssColor } from '../../../components/types'\nimport type { KeyboardEvent } from 'react'\nimport { Box, Typography } from '@mui/material'\nimport { CategoryBar } from './category-bar'\nimport type { CategoryKey, CategorySize } from '../types'\nimport { styles } from '../style'\n\nexport interface CategoryRowSingleProps {\n /** Raw category key — what gets emitted to `onToggle` regardless of formatter. */\n name: CategoryKey\n /** Display label (post-`labelFormatter`). Falls back to `name` when omitted. */\n displayName?: string | number\n value: number\n maxValue: number\n color: CssColor\n formatter: (n: number) => string\n selected: boolean\n onToggle: (name: CategoryKey) => void\n /** Bar visual density. Forwarded to {@link CategoryBar}. */\n size?: CategorySize\n}\n\n/**\n * Single-series category row: `[label] [bar] [value]` grid. Click /\n * Enter / Space toggles `name` in/out of the selection set via\n * `onToggle`. Selection key is always the raw `name` — `displayName` is\n * presentation only.\n *\n * Visual selection / dim signal lives ENTIRELY in the bar color\n * (`color` prop) — computed upstream by `CategoryUI` (real palette\n * color when selected or no-selection; `theme.palette.action.disabled`\n * when a selection exists and this row isn't in it). The row itself\n * has no bg tint, no opacity rule.\n */\nexport function CategoryRowSingle({\n name,\n displayName,\n value,\n maxValue,\n color,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowSingleProps) {\n const handleKey = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }\n return (\n <Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowSingle}\n >\n <Box sx={styles.rowHeader}>\n <Typography variant='body2' sx={styles.name}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.value}>\n {formatter(value)}\n </Typography>\n </Box>\n <CategoryBar\n value={value}\n maxValue={maxValue}\n color={color}\n size={size}\n />\n </Box>\n )\n}\n","import type { CssColor } from '../../../components/types'\nimport type { KeyboardEvent } from 'react'\nimport { Box, Typography } from '@mui/material'\nimport { CategoryBar } from './category-bar'\nimport type { CategoryKey, CategorySize } from '../types'\nimport { styles } from '../style'\n\nexport interface CategoryRowMultiProps {\n /** Raw category key — what gets emitted to `onToggle`. */\n name: CategoryKey\n /** Display label (post-`labelFormatter`). Falls back to `name`. */\n displayName?: string | number\n /** One value per series. `0` is rendered as an empty bar (track only). */\n values: readonly number[]\n /** One color per series (palette + per-series overrides resolved upstream). */\n colors: readonly CssColor[]\n /** Shared max across all series + rows (or `maxOverride`). */\n maxValue: number\n formatter: (n: number) => string\n selected: boolean\n onToggle: (name: CategoryKey) => void\n /** Bar visual density. Forwarded to every {@link CategoryBar} in the row. */\n size?: CategorySize\n}\n\n/**\n * Multi-series category row: label spans the top; bar+value pairs stack\n * vertically below, one per series. Each bar uses the full row width\n * track (no shared grid) for legibility at N>=2 series. Click anywhere\n * in the row (label OR any bar/value) toggles selection by `name`.\n *\n * Visual selection / dim signal lives ENTIRELY in the bar colors\n * (`colors` prop) — every series in a dimmed row receives\n * `theme.palette.action.disabled`; selected / unselected-no-selection\n * rows receive their real palette colors. Computed upstream by\n * `CategoryUI`. The row itself has no bg tint, no opacity rule.\n */\nexport function CategoryRowMulti({\n name,\n displayName,\n values,\n colors,\n maxValue,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowMultiProps) {\n const handleKey = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }\n return (\n <Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowMulti}\n >\n <Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>\n {values.map((v, i) => (\n // Multi-bar rows iterate per series — the bar belongs to series `i`,\n // and the row is keyed on the parent's `name`, so a composite of\n // `name` + the series index is stable across reorders.\n <Box key={`${name}-series-${i}`} sx={styles.multiBarRow}>\n <Box sx={styles.multiBarContainer}>\n <CategoryBar\n value={v}\n maxValue={maxValue}\n color={colors[i] ?? ''}\n size={size}\n />\n </Box>\n <Typography variant='body2' sx={styles.value}>\n {formatter(v)}\n </Typography>\n </Box>\n ))}\n </Box>\n )\n}\n","import type { CssColor } from '../../../components/types'\nimport { Box } from '@mui/material'\nimport type { CategorySize } from '../types'\nimport { styles } from '../style'\n\nexport interface CategoryBarStackedProps {\n /** One value per series. Zero values render no segment (visually + in the DOM). */\n values: readonly number[]\n /** One color per series. Resolved upstream by `CategoryUI.colorAt`. */\n colors: readonly CssColor[]\n /**\n * Shared denominator across all rows + series (or `maxOverride`). Same\n * value `CategoryBar` uses — segments scale identically to single-bar rows\n * so cross-row comparison is preserved.\n */\n maxValue: number\n /** Visual density. Forwarded to the track styling; segments inherit height. */\n size?: CategorySize\n}\n\n/**\n * Stacked horizontal bar: one rounded track containing N square segments\n * placed side-by-side. Each segment's width is `value_i / maxValue * 100%`\n * and its left offset is the cumulative sum of preceding values. Square\n * interior edges + the parent track's `overflow: hidden` + rounded radius\n * mean the outer ends of the stacked bar match a single `CategoryBar` while\n * interior segment boundaries meet cleanly.\n *\n * Pure presentation — selection / dimming is resolved upstream by the row\n * via the `colors` prop. Each segment carries `data-bar-fill='true'` so the\n * existing row-hover brightening selector still fires on every segment.\n */\nexport function CategoryBarStacked({\n values,\n colors,\n maxValue,\n size = 'small',\n}: CategoryBarStackedProps) {\n // Cumulative offsets pre-computed up front so the render map function\n // stays pure (no mutable closure variable — keeps the React Compiler\n // immutability check happy).\n const offsets: number[] = []\n {\n let acc = 0\n for (const v of values) {\n offsets.push(acc)\n acc += v\n }\n }\n return (\n <Box sx={styles.bar[size]} data-size={size}>\n {values.map((v, i) => {\n if (v <= 0) return null\n const leftPct = maxValue > 0 ? (offsets[i]! / maxValue) * 100 : 0\n const pct = maxValue > 0 ? (v / maxValue) * 100 : 0\n return (\n <Box\n // Segments are keyed by per-series colour (the only stable\n // distinguishing field on this side) plus the segment index for\n // when callers reuse palette colours across series.\n key={`seg-${colors[i] ?? 'none'}-${i}`}\n sx={styles.stackedSegment[size]}\n style={{\n left: `${leftPct}%`,\n width: `${pct}%`,\n backgroundColor: colors[i] ?? '',\n }}\n data-bar-fill='true'\n />\n )\n })}\n </Box>\n )\n}\n","import type { CssColor } from '../../../components/types'\nimport type { KeyboardEvent } from 'react'\nimport { Box, Typography } from '@mui/material'\nimport { CategoryBarStacked } from './category-bar-stacked'\nimport type { CategoryKey, CategorySize } from '../types'\nimport { styles } from '../style'\n\nexport interface CategoryRowStackedProps {\n /** Raw category key — what gets emitted to `onToggle`. */\n name: CategoryKey\n /** Display label (post-`labelFormatter`). Falls back to `name`. */\n displayName?: string | number\n /** One value per series. Zero values still appear in the breakdown row. */\n values: readonly number[]\n /** One color per series (palette + per-series overrides resolved upstream). */\n colors: readonly CssColor[]\n /**\n * Series names for the breakdown row, one per visible series, already\n * resolved by the caller (unnamed series fall back to `Series N` using\n * their ORIGINAL series index — this component has no notion of which\n * series were hidden, so it must not recompute a positional fallback).\n */\n seriesNames: readonly string[]\n formatter: (n: number) => string\n selected: boolean\n onToggle: (name: CategoryKey) => void\n /** Bar visual density. Forwarded to the inner {@link CategoryBarStacked}. */\n size?: CategorySize\n}\n\n/**\n * Stacked-mode category row: label + total share a header line; a single\n * segmented bar sits below; an inline breakdown lists each series with its\n * formatted value. Click anywhere in the row toggles selection by `name`.\n *\n * Per-row normalization: the stacked bar always fills 100% of the track,\n * with each segment sized as `value_i / sum(values)`. This is intentionally\n * different from `CategoryRowMulti` (which scales bars against the global\n * `maxValue` for cross-row comparison) — in stacked mode the comparable\n * axis is the proportion of each series within the row, not the row's\n * total relative to other rows. The total numeric value is still shown in\n * the header so the per-row magnitude isn't lost.\n *\n * Visual selection / dim signal lives entirely in the segment colors\n * (`colors` prop) — every series in a dimmed row receives\n * `theme.palette.action.disabled`. The bar visually collapses to a single\n * grey shape when dimmed, but the breakdown text stays at full color so the\n * per-series values remain legible.\n */\nexport function CategoryRowStacked({\n name,\n displayName,\n values,\n colors,\n seriesNames,\n formatter,\n selected,\n onToggle,\n size,\n}: CategoryRowStackedProps) {\n const handleKey = (e: KeyboardEvent<HTMLDivElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle(name)\n }\n }\n const total = values.reduce((a, b) => a + b, 0)\n return (\n <Box\n role='button'\n aria-pressed={selected}\n tabIndex={0}\n onClick={() => onToggle(name)}\n onKeyDown={handleKey}\n sx={styles.rowStacked}\n >\n <Box sx={styles.stackedHeader}>\n <Typography variant='body2' sx={styles.multiName}>\n {displayName ?? name}\n </Typography>\n <Typography variant='body2' sx={styles.stackedTotal}>\n {formatter(total)}\n </Typography>\n </Box>\n <CategoryBarStacked\n values={values}\n colors={colors}\n maxValue={total}\n size={size}\n />\n <Box sx={styles.stackedBreakdown}>\n {values.map((v, i) => (\n // Series name is the stable identity for the breakdown line.\n <Typography\n key={`breakdown-${seriesNames[i]}`}\n variant='body2'\n sx={styles.stackedBreakdownItem}\n >\n {`${seriesNames[i]}: ${formatter(v)}`}\n </Typography>\n ))}\n </Box>\n </Box>\n )\n}\n","import type { KeyboardEvent } from 'react'\nimport { Box, Typography } from '@mui/material'\nimport { Tooltip } from '../../../components'\nimport { styles } from '../style'\n\nexport interface CategoryRowOtherProps {\n hiddenCount: number\n /** Label for the overflow row. Defaults to `'Others'`. */\n otherLabel?: string\n /** Count text with `{count}` placeholder. Defaults to `'{count}'`. */\n otherCountLabel?: string\n /**\n * When provided, the row becomes a button: clicking it (or Enter/Space)\n * fires `onShowAll`, expanding the widget to reveal every category. When\n * omitted the row renders as a static, non-interactive summary.\n */\n onShowAll?: () => void\n /** Tooltip shown over the button form. Defaults to `'Show all'`. */\n showAllLabel?: string\n}\n\n/**\n * Overflow summary row rendered after the last visible category row when\n * `data.length > maxItems`. Shows `Others <count>` (count muted, no\n * parentheses).\n *\n * When `onShowAll` is supplied the row is an interactive button — clicking\n * it (or Enter/Space) expands the widget to show all categories, mirroring\n * the Searcher's full-list view. Without `onShowAll` it stays a static,\n * non-interactive summary (backward-compatible default).\n */\nexport function CategoryRowOther({\n hiddenCount,\n otherLabel = 'Others',\n otherCountLabel = '{count}',\n onShowAll,\n showAllLabel = 'Show all',\n}: CategoryRowOtherProps) {\n const countText = otherCountLabel.replace('{count}', String(hiddenCount))\n\n const count = (\n <Typography variant='body2' sx={styles.otherCount}>\n {countText}\n </Typography>\n )\n\n if (!onShowAll) {\n return (\n <Box sx={styles.otherRow}>\n <Typography variant='body2' sx={styles.otherLabel}>\n {otherLabel}\n </Typography>\n {count}\n </Box>\n )\n }\n\n const handleKey = (e: KeyboardEvent<HTMLSpanElement>): void => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onShowAll()\n }\n }\n\n // Only the label is the click target; the count stays static beside it.\n return (\n <Box sx={styles.otherRow}>\n <Tooltip title={showAllLabel}>\n <Typography\n variant='body2'\n component='span'\n role='button'\n tabIndex={0}\n onClick={onShowAll}\n onKeyDown={handleKey}\n sx={{ ...styles.otherLabel, ...styles.otherLabelButton }}\n >\n {otherLabel}\n </Typography>\n </Tooltip>\n {count}\n </Box>\n )\n}\n","import type { CategorySeriesConfig } from './types'\n\n/** Shared empty set: \"no series hidden\" without a new identity per render. */\nexport const NO_HIDDEN_SERIES: ReadonlySet<number> = new Set()\n\n/**\n * Identifies a series breakdown by its names. Hidden-series indices are only\n * valid for the breakdown they were made for, and both the store copy (in\n * `Category`) and the local one (in `CategoryUI`) are tagged with this key —\n * so they must compute it the same way.\n */\nexport function categorySeriesKey(\n series: readonly CategorySeriesConfig[] | undefined,\n): string {\n return JSON.stringify(series?.map((s) => s.name) ?? [])\n}\n","import type { CssColor } from '../../../components/types'\nimport { Box, Typography } from '@mui/material'\nimport type { CategorySeriesConfig } from '../types'\nimport { styles } from '../style'\nimport { NO_HIDDEN_SERIES } from '../helpers'\n\nexport interface CategoryLegendProps {\n /** Series metadata. Empty array → renders nothing. */\n series: readonly CategorySeriesConfig[]\n /**\n * Per-series colour resolver. Receives the series index and returns the\n * palette colour (or `undefined` to fall back to the MUI default). The\n * legend derives its dot colour from this on the fly — the caller does\n * NOT pre-compute a `colors` array (avoids passing two views of the\n * same source).\n */\n colorAt: (index: number) => CssColor | undefined\n /**\n * Series indices currently hidden from the chart. Renders dimmed.\n * Optional — defaults to none hidden. Added after this component's first\n * release; kept optional (not required) so it stays a non-breaking\n * addition for any direct consumer of `CategoryLegend` (a public\n * subpath export) that predates the hide feature.\n */\n hiddenSeries?: ReadonlySet<number>\n /**\n * Fires with the series index when its legend item is clicked. Optional\n * — omitting it renders a legend with no hide affordance (same\n * reasoning as `hiddenSeries`).\n */\n onToggle?: (index: number) => void\n}\n\n/**\n * Sticky color legend rendered below a multi-series Category list.\n * Per-series `color` overrides the matching `colorAt(i)` result. Clicking an\n * item toggles that series' visibility in the rows above (mirrors ECharts'\n * native legend click-to-hide).\n */\nexport function CategoryLegend({\n series,\n colorAt,\n hiddenSeries = NO_HIDDEN_SERIES,\n onToggle,\n}: CategoryLegendProps) {\n if (series.length === 0) return null\n return (\n <Box sx={styles.legend}>\n {series.map((s, i) => {\n const hidden = hiddenSeries.has(i)\n return (\n <Box\n key={s.name}\n // `checkbox`/`aria-checked`, not `button`/`aria-pressed` — this\n // toggles a persistent visibility state (closer to a checkbox)\n // AND keeps it out of `getByRole('button')` queries that count\n // category ROWS throughout this widget's test suite.\n role='checkbox'\n aria-checked={!hidden}\n tabIndex={0}\n onClick={() => onToggle?.(i)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onToggle?.(i)\n }\n }}\n sx={hidden ? styles.legendItemHidden : styles.legendItem}\n >\n <Box\n sx={styles.legendDot}\n style={{ backgroundColor: colorAt(i) }}\n />\n <Typography variant='caption' sx={styles.legendLabel}>\n {s.name}\n </Typography>\n </Box>\n )\n })}\n </Box>\n )\n}\n","import { useCallback, useMemo, useRef, useState } from 'react'\nimport { Box, useTheme } from '@mui/material'\nimport { CategoryRowSingle } from './components/category-row-single'\nimport { CategoryRowMulti } from './components/category-row-multi'\nimport { CategoryRowStacked } from './components/category-row-stacked'\nimport { CategoryRowOther } from './components/category-row-other'\nimport { CategoryLegend } from './components/category-legend'\nimport type {\n CategoryKey,\n CategoryLabels,\n CategorySeriesConfig,\n CategorySize,\n CategoryWidgetData,\n} from './types'\nimport { styles } from './style'\nimport { categorySeriesKey } from './helpers'\nimport { resolveThemeColor } from '../utils'\n\nconst DEFAULT_MAX_ITEMS = 20\n\n// Fallback viewport height used in scroll mode ONLY when no natural\n// measurement is available yet — i.e., the consumer mounts directly into\n// `maxItems === 0` (e.g., SearcherToggle pre-enabled), so the list has\n// never rendered in capped form for us to measure.\n//\n// small: label(~20) + gap(4) + bar(4) + list-gap(8) ≈ 36\n// medium: label(~20) + gap(4) + bar(12) + list-gap(8) ≈ 44\nconst ROW_HEIGHT_PX: Record<CategorySize, number> = { small: 36, medium: 44 }\nconst SCROLL_VIEWPORT_FALLBACK_ROWS = 8\n\nexport interface CategoryUIProps {\n data: CategoryWidgetData\n /** Currently-selected category names (destination-owned). */\n selection?: readonly CategoryKey[]\n /** Fires with the next selection set when a row is clicked. */\n onSelectionChange?: (next: readonly CategoryKey[]) => void\n /** Number formatter applied to each row's value. */\n formatter?: (value: number) => string\n /**\n * Display-only transform for category names. Selection callbacks always\n * receive the raw `item.name` regardless of this formatter.\n */\n labelFormatter?: (value: string | number) => string | number\n /**\n * Per-series metadata. Enables the legend (for multi-series) and\n * overrides palette colors per series index. Length need not match\n * `data.length`; mismatches degrade gracefully (extras ignored, gaps\n * fall back to palette).\n */\n series?: readonly CategorySeriesConfig[]\n /**\n * Caps the number of visible category rows.\n *\n * - `undefined` (omitted) — caps at {@link DEFAULT_MAX_ITEMS} (20).\n * Surplus rows fold into a single \"Others <count>\" footer row.\n * `undefined` cannot mean \"no cap\" because it's consumed by the\n * default-parameter syntax — pass `null` instead.\n * - positive finite N — same as the default but with a custom cap.\n * - `0` — **no cap WITH scroll**: every row renders inside a\n * fixed-height viewport that scrolls internally. The viewport's\n * max-height is the list's natural `clientHeight` measured on the\n * first render where the list was NOT in scroll mode (i.e., the\n * capped view) — frozen for the component's lifetime. If the\n * consumer mounts directly into scroll mode and we never see a\n * capped render, the viewport falls back to `8 × ROW_HEIGHT_PX`.\n * Composers flip into this mode while the user is searching\n * (`maxItems = searcherOpen ? 0 : userMaxItems`) so the widget\n * card height stays stable.\n * - `null` / `Infinity` / negative — **no cap, no scroll**: render\n * every row; the list grows with content. Matches v1's \"undefined\"\n * behavior. Use `null` as the canonical explicit form.\n */\n maxItems?: number | null\n /** Labels for the \"Other\" overflow row. `{count}` placeholder is replaced. */\n labels?: CategoryLabels\n /**\n * When provided, the \"Other\" overflow row becomes a button — clicking it\n * fires `onShowAll`, which a composer typically wires to expand the widget\n * (drop the row cap) so every category becomes reachable. When omitted the\n * overflow row stays a static summary.\n */\n onShowAll?: () => void\n /** Manual override for the bar-width denominator. */\n maxOverride?: number\n /**\n * Visual density of the bar primitive. `'small'` (default) keeps the\n * historical 4px-tall pill; `'medium'` switches to a 12px-tall track\n * with a 2px corner radius. Only the bar track + fill change.\n */\n size?: CategorySize\n /**\n * Multi-series stacked mode. When `true` and `data.length > 1`, each\n * category renders as a single segmented bar (one segment per series)\n * with a `formatter(sum)` total in the header and a per-series\n * breakdown line below. Composers typically thread this from\n * `useTransformEnabled(id, 'stack-toggle')`.\n *\n * No-op for single-series data — same convention as the legend.\n */\n stacked?: boolean\n /**\n * Series indices hidden via the legend. Leave both this and\n * `onHiddenSeriesChange` unset and CategoryUI keeps the set itself; pass\n * them to own it — `Category` does, so the card and the fullscreen copy\n * share it through the widget store.\n */\n hiddenSeries?: ReadonlySet<number>\n onHiddenSeriesChange?: (next: ReadonlySet<number>) => void\n}\n\ninterface GroupedRow {\n name: CategoryKey\n values: number[]\n /** Per-series per-row color override (first non-undefined wins). */\n rowColors: (string | undefined)[]\n}\n\n/**\n * The hidden set as the chart applies it: indices past the current series\n * count are ignored, and an all-hidden result falls back to all visible.\n * The stored set is never rewritten from here, so hidden indices survive a\n * data shrink and come back if the data regrows.\n */\nfunction effectiveHidden(\n hidden: ReadonlySet<number>,\n seriesCount: number,\n): Set<number> {\n const clamped = new Set([...hidden].filter((i) => i < seriesCount))\n return clamped.size >= seriesCount ? new Set<number>() : clamped\n}\n\n/**\n * Group items by `name` across series so each unique name produces one\n * row containing N values (zero-filled where a series has no entry for\n * that name). First-seen order across series is preserved.\n *\n * Ported from v1\n * (`packages/react-ui/src/widgets/category/category-ui.tsx:137-166`)\n * with readonly typing + per-row color capture.\n */\nfunction generateGroupedData(data: CategoryWidgetData): GroupedRow[] {\n if (!data || data.length === 0) return []\n const seriesCount = Math.max(data.length, 1)\n const grouped = new Map<CategoryKey, GroupedRow>()\n const order: CategoryKey[] = []\n for (let s = 0; s < data.length; s++) {\n const series = data[s] ?? []\n for (const item of series) {\n let row = grouped.get(item.name)\n if (!row) {\n row = {\n name: item.name,\n values: new Array<number>(seriesCount).fill(0),\n rowColors: new Array<string | undefined>(seriesCount).fill(undefined),\n }\n grouped.set(item.name, row)\n order.push(item.name)\n }\n row.values[s] = item.value\n row.rowColors[s] = item.color\n }\n }\n return order.map((k) => grouped.get(k)!)\n}\n\n/**\n * Pure presentational component for the Category widget. Renders\n * single-series rows (`data.length === 1`) as a compact grid or\n * multi-series rows (`data.length > 1`) as stacked-bar groups, capped at\n * `maxItems` with an overflow summary, and an optional sticky color\n * legend when `series` metadata is supplied.\n *\n * Selection is destination-owned: clicking a row fires\n * `onSelectionChange(next)` with the toggled set; the consumer keeps the\n * list in their own store. `aria-pressed` + Enter/Space keyboard\n * activation give full a11y parity with the mouse path.\n *\n * Returns `null` when there's nothing to render — `Widget.State` is the\n * empty-state authority in the canonical compositor.\n */\nexport function CategoryUI({\n data,\n selection,\n onSelectionChange,\n formatter,\n labelFormatter,\n series,\n maxItems = DEFAULT_MAX_ITEMS,\n labels,\n onShowAll,\n maxOverride,\n size = 'small',\n stacked = false,\n hiddenSeries: hiddenSeriesProp,\n onHiddenSeriesChange,\n}: CategoryUIProps) {\n const theme = useTheme()\n const fmt = formatter ?? ((n: number) => String(n))\n\n const hasSelection = !!selection && selection.length > 0\n const selectionSet = useMemo(\n () => new Set<CategoryKey>(selection ?? []),\n [selection],\n )\n\n const toggle = useCallback(\n (name: CategoryKey) => {\n if (!onSelectionChange) return\n const cur = selection ?? []\n const next = cur.includes(name)\n ? cur.filter((n) => n !== name)\n : [...cur, name]\n onSelectionChange(next)\n },\n [onSelectionChange, selection],\n )\n\n // Palette: same convention as Bar/Pie/Histogram/Scatterplot/Timeseries v2.\n const paletteColors = useMemo<readonly string[]>(\n () => [\n theme.palette.secondary.main,\n ...Object.values(\n (theme.palette as { qualitative?: { bold?: Record<string, string> } })\n .qualitative?.bold ?? {},\n ),\n ],\n [theme],\n )\n\n // Resolved here, not at the paint site: the bars and the legend dot use\n // inline `style` to keep emotion from minting a class per value/colour\n // pair, and inline style has no theme resolver behind it — a caller's\n // `'primary.main'` would reach the DOM verbatim and paint nothing.\n const colorAt = useCallback(\n (seriesIndex: number, rowColor?: string): string => {\n const override = rowColor ?? series?.[seriesIndex]?.color\n if (override) return resolveThemeColor(theme, override) ?? override\n return (\n paletteColors[seriesIndex % paletteColors.length] ??\n theme.palette.secondary.main\n )\n },\n [series, paletteColors, theme],\n )\n\n // Dim color for non-selected rows when a selection exists. Swapped in\n // at the bar-fill level only — text + track stay at their real colors.\n // Theme-driven so it survives palette swaps.\n const dimColor = theme.palette.action.disabled\n\n const grouped = useMemo(() => generateGroupedData(data), [data])\n\n const maxValue = useMemo(() => {\n if (typeof maxOverride === 'number' && maxOverride > 0) return maxOverride\n let m = 0\n for (const row of grouped) {\n for (const v of row.values) if (v > m) m = v\n }\n return m\n }, [grouped, maxOverride])\n\n // Three modes:\n // * positive finite N (default `DEFAULT_MAX_ITEMS = 20` when prop\n // omitted): cap at N rows; surplus folds into \"Others <count>\".\n // * `0`: no cap WITH scroll — every row renders, the list locks to a\n // fixed viewport and scrolls internally. Composers use this to\n // bypass pagination while the user is searching:\n // `maxItems = searcherOpen ? 0 : userMaxItems`\n // * `null` / `Infinity` / negative: no cap, no scroll — every row\n // renders and the list grows with content. `null` is the canonical\n // explicit form (`undefined` is consumed by the default param above).\n const scrollMode = maxItems === 0\n\n // Natural-height measurement. In scroll mode (`maxItems === 0`,\n // set by the composer when the SearcherToggle is open) every row\n // renders inside a scrollable viewport. The viewport's max-height\n // must match the pre-overflow content area — i.e., whatever the\n // list was rendering BEFORE the composer dropped the cap.\n //\n // We measure the list's `clientHeight` exactly once, the first time\n // it attaches while NOT in scroll mode (otherwise we'd capture the\n // wrong layout). Once set, `naturalHeight` is the lock anchor\n // forever — matches v1's frozen-at-mount semantics, just with a\n // real DOM measurement instead of a hard-coded constant.\n //\n // Implemented via a callback ref instead of `useLayoutEffect` so\n // setState fires during commit, not inside an effect — satisfies\n // the `react-hooks/set-state-in-effect` rule. React invokes\n // callback refs on attach (and re-invokes them when the callback\n // identity changes, which it does when `scrollMode` flips), so\n // toggling the searcher off after a scroll-mode mount triggers the\n // first measurement at that point.\n //\n // Fallback: if the consumer never lands a non-scroll render\n // (e.g., searcher pre-enabled and never closed), `naturalHeight`\n // stays `null` → viewport falls back to `8 × ROW_HEIGHT_PX`.\n const measuredRef = useRef(false)\n const [naturalHeight, setNaturalHeight] = useState<number | null>(null)\n const listRefCallback = useCallback(\n (node: HTMLDivElement | null) => {\n if (!node) return\n if (measuredRef.current) return\n if (scrollMode) return\n const h = node.clientHeight\n if (h > 0) {\n measuredRef.current = true\n setNaturalHeight(h)\n }\n },\n [scrollMode],\n )\n\n // Which series are hidden via a legend click. Local UI-only state — not\n // synced to `selection`/`onSelectionChange` (that's row-level filter\n // selection, unrelated). Never allowed to reach \"every series hidden\":\n // toggling the last visible one instead resets to \"all visible\" rather\n // than leaving the widget blank.\n const [localHidden, setLocalHidden] = useState<ReadonlySet<number>>(\n () => new Set(),\n )\n const hiddenSeries = hiddenSeriesProp ?? localHidden\n // A reload with a different series breakdown invalidates hidden indices —\n // index 2 may now point at an entirely different series. Reset rather\n // than keep hiding whatever now occupies a stale index. Adjusted directly\n // during render (React's documented \"adjusting state when a prop changes\"\n // pattern) instead of in a `useEffect`, which would commit the stale\n // hidden-set for one extra frame before the effect fires. Compared by a\n // content-derived key, not the raw `series` reference — an inline\n // `series={[...]}` literal (no useMemo) gets a new reference on every\n // unrelated parent re-render, which would otherwise reset hiddenSeries\n // right after the user hides one.\n const seriesKey = categorySeriesKey(series)\n const [prevSeriesKey, setPrevSeriesKey] = useState(seriesKey)\n if (seriesKey !== prevSeriesKey) {\n setPrevSeriesKey(seriesKey)\n setLocalHidden(new Set())\n }\n // Real number of data series — NOT `series?.length`, which is legend\n // metadata and may legitimately be shorter than the actual series count\n // (unnamed trailing series fall back to `Series N`). Using the metadata\n // length here would trip the \"never fully hidden\" guard early whenever\n // `series` is shorter than `data`.\n const seriesCount = Math.max(data.length, 1)\n const toggleSeries = useCallback(\n (index: number) => {\n // Start from what the user sees, not the stored set: `data` can\n // shrink while the `series` names hold (the reset only watches the\n // names), and a stale index would otherwise count toward the guard\n // below — or make unchecking one series hide a different one.\n const next = effectiveHidden(hiddenSeries, seriesCount)\n if (next.has(index)) next.delete(index)\n else next.add(index)\n ;(onHiddenSeriesChange ?? setLocalHidden)(\n next.size >= seriesCount ? new Set() : next,\n )\n },\n [hiddenSeries, onHiddenSeriesChange, seriesCount],\n )\n\n if (grouped.length === 0) return null\n\n const hasCap =\n typeof maxItems === 'number' && Number.isFinite(maxItems) && maxItems > 0\n const visible = hasCap ? grouped.slice(0, maxItems) : grouped\n const hiddenCount = hasCap ? grouped.length - visible.length : 0\n\n const isMulti = data.length > 1\n const hasLegend = isMulti && !!series && series.length > 0\n // Invariant across every row in this render — only depends on\n // `hiddenSeries`/`seriesCount`, not on any particular row's data — so\n // it's computed once here instead of per row inside the render loop.\n const activeHidden = effectiveHidden(hiddenSeries, seriesCount)\n const visibleIdx = Array.from({ length: seriesCount }, (_, i) => i).filter(\n (i) => !activeHidden.has(i),\n )\n\n // Fallback viewport height matches the documented \"8 × ROW_HEIGHT_PX\"\n // (see the constants at the top of the file). Earlier code multiplied\n // this by `seriesCount`, making a 3-series widget render 3× the\n // intended viewport on the first frame before `naturalHeight` was\n // measured — a visible layout jump.\n const scrollMaxHeight = scrollMode\n ? (naturalHeight ?? ROW_HEIGHT_PX[size] * SCROLL_VIEWPORT_FALLBACK_ROWS)\n : undefined\n\n return (\n <Box sx={styles.root}>\n <Box\n ref={listRefCallback}\n sx={scrollMode ? { ...styles.list, ...styles.listScroll } : styles.list}\n style={\n scrollMaxHeight !== undefined\n ? { maxHeight: scrollMaxHeight, overflowY: 'auto' }\n : undefined\n }\n >\n {visible.map((row) => {\n const selected = selectionSet.has(row.name)\n const dimmed = hasSelection && !selected\n const displayName = labelFormatter\n ? labelFormatter(row.name)\n : undefined\n // When the row is dimmed, every series' bar fill is replaced\n // with `dimColor`. Text + track are untouched. This is the\n // ONLY visual difference between selected and non-selected\n // rows — no row-level opacity, no `rowSelected` bg tint.\n const fill = (i: number): string =>\n dimmed ? dimColor : colorAt(i, row.rowColors[i])\n // Series hidden via the legend are dropped before reaching the\n // row components — they render whatever arrays they're given\n // with no notion of \"hidden\" themselves. `visibleIdx` is hoisted\n // above this loop (same for every row).\n if (isMulti) {\n if (stacked) {\n // Stacked-mode branch: one segmented bar per row. Toggling\n // `stacked` after the natural-height measurement does NOT\n // re-trigger measurement (see the `naturalHeight` comment\n // above) — accepted trade-off: stacked rows have a slightly\n // different height, so the scroll viewport may show extra\n // whitespace after the toggle. Visual only, not functional.\n // Stacked rows are per-row normalized — they don't take\n // the global `maxValue`. Each row's bar fills 100% of the\n // track and the segments split it by `value_i / sum`. The\n // numeric total in the header preserves the per-row\n // magnitude information.\n return (\n <CategoryRowStacked\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n seriesNames={visibleIdx.map(\n (i) => series?.[i]?.name ?? `Series ${i + 1}`,\n )}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowMulti\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n values={visibleIdx.map((i) => row.values[i]!)}\n colors={visibleIdx.map((i) => fill(i))}\n maxValue={maxValue}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n }\n return (\n <CategoryRowSingle\n key={String(row.name)}\n name={row.name}\n displayName={displayName}\n value={row.values[0] ?? 0}\n maxValue={maxValue}\n color={fill(0)}\n formatter={fmt}\n selected={selected}\n onToggle={toggle}\n size={size}\n />\n )\n })}\n {hiddenCount > 0 && (\n <CategoryRowOther\n hiddenCount={hiddenCount}\n otherLabel={labels?.other}\n otherCountLabel={labels?.otherCount}\n showAllLabel={labels?.showAll}\n onShowAll={onShowAll}\n />\n )}\n </Box>\n {hasLegend && series && (\n <CategoryLegend\n // Trimmed: `series` is caller metadata and may outrun `data`,\n // and an entry with no data behind it has nothing to hide.\n series={series.slice(0, seriesCount)}\n colorAt={(i) => colorAt(i)}\n hiddenSeries={activeHidden}\n onToggle={toggleSeries}\n />\n )}\n </Box>\n )\n}\n","import { useCallback, useMemo } from 'react'\nimport {\n getWidgetStore,\n useWidget,\n useWidgetId,\n useWidgetShallow,\n type WidgetState,\n} from '../stores'\nimport { CategoryUI } from './category-ui'\nimport { categorySeriesKey, NO_HIDDEN_SERIES } from './helpers'\nimport type {\n CategoryKey,\n CategoryLabels,\n CategorySeriesConfig,\n CategorySize,\n CategoryWidgetData,\n} from './types'\n\ninterface CategorySlice {\n data: CategoryWidgetData\n rawData: CategoryWidgetData | undefined\n formatter?: (value: number) => string\n labelFormatter?: (value: string | number) => string | number\n}\n\nconst categorySelector = (s: WidgetState): CategorySlice => ({\n data: (s.data ?? []) as CategoryWidgetData,\n rawData: (s.rawData ?? undefined) as CategoryWidgetData | undefined,\n formatter: s.formatter,\n labelFormatter: s.labelFormatter,\n})\n\n/**\n * Legend-hidden series, kept on the widget store so every mounted Category\n * of the widget — the card and its fullscreen copy — shows the same set.\n * Tagged with the series breakdown it was made for: after a reload with\n * different series the indices point elsewhere, so a stale set reads as\n * empty instead of being reset with a write.\n */\ninterface CategoryWidgetState extends WidgetState {\n categoryHiddenSeries?: { key: string; indices: ReadonlySet<number> }\n}\n\nfunction maxFromCategoryData(d: CategoryWidgetData | undefined): number {\n if (!d) return 0\n let m = 0\n for (const series of d) {\n for (const item of series) if (item.value > m) m = item.value\n }\n return m\n}\n\nexport interface CategoryProps {\n /** Currently-selected category names. Destination-owned. */\n selection?: readonly CategoryKey[]\n /** Fires when a row is clicked. Consumer updates the destination's store. */\n onSelectionChange?: (next: readonly CategoryKey[]) => void\n /** Per-series metadata. Enables the legend + overrides palette per index. */\n series?: readonly CategorySeriesConfig[]\n /**\n * Cap visible rows; overflow folds into an \"Others <count>\" row. Default 20\n * (when omitted). Pass `0` to swap the cap for a scrollable viewport\n * (composers use this when the user opens the SearcherToggle —\n * `maxItems = searcherOpen ? 0 : userMaxItems`). Pass `null` to disable\n * the cap entirely without adding a scroll viewport.\n */\n maxItems?: number | null\n /** Labels for the \"Other\" overflow row. */\n labels?: CategoryLabels\n /**\n * When provided, the \"Other\" overflow row becomes a button that fires this\n * callback. Composers wire it to expand the widget (e.g. flip the\n * `show-all` flag) so every category is shown. Forwarded to\n * {@link CategoryUI}.\n */\n onShowAll?: () => void\n /**\n * Manual override for the bar-width denominator. When omitted, the\n * bridge auto-fills from the widget store's `rawData` so bar widths\n * stay coherent across data transforms (e.g., the Searcher filtering\n * rows in and out — bars don't rescale just because the larger rows\n * got hidden). Pass an explicit number to fix the denominator\n * regardless of the data.\n */\n maxOverride?: number\n /**\n * Visual density of the bar primitive. `'small'` (default) keeps the\n * historical 4px-tall pill; `'medium'` switches to a 12px-tall track\n * with a 2px corner radius. Forwarded as-is to {@link CategoryUI}.\n */\n size?: CategorySize\n /**\n * Multi-series stacked mode. Forwarded as-is to {@link CategoryUI}.\n * Composers typically wire this from\n * `useTransformEnabled(id, 'stack-toggle')`. No-op for single-series.\n */\n stacked?: boolean\n}\n\n/**\n * Stateful Category bridge — reads `data`, `formatter`, and\n * `labelFormatter` (post-pipeline) from the per-widget store and\n * forwards them to the pure {@link CategoryUI}. `selection` and\n * `onSelectionChange` follow the destination-owned principle: the\n * consumer keeps the list in their own store and passes it through.\n */\nexport function Category({\n selection,\n onSelectionChange,\n series,\n maxItems,\n labels,\n onShowAll,\n maxOverride,\n size,\n stacked,\n}: CategoryProps) {\n const id = useWidgetId()\n const slice = useWidgetShallow(id, categorySelector)\n // Auto-fill `maxOverride` from `rawData` so bar widths stay coherent\n // across data transforms (e.g., the Searcher filtering rows in/out).\n // Consumer's explicit value always wins; we only fill when omitted\n // AND `rawData` has at least one positive value (CategoryUI's\n // `maxOverride > 0` guard would reject 0 anyway, but skipping is\n // cleaner — it lets the live fallback path handle the empty case).\n const naturalMax = useMemo(\n () => maxFromCategoryData(slice.rawData),\n [slice.rawData],\n )\n const effectiveMaxOverride =\n maxOverride ?? (naturalMax > 0 ? naturalMax : undefined)\n\n const seriesKey = categorySeriesKey(series)\n const stored = useWidget<\n CategoryWidgetState['categoryHiddenSeries'],\n CategoryWidgetState\n >(id, (s) => s.categoryHiddenSeries)\n const hiddenSeries =\n stored?.key === seriesKey ? stored.indices : NO_HIDDEN_SERIES\n const onHiddenSeriesChange = useCallback(\n (indices: ReadonlySet<number>) =>\n getWidgetStore(id).setState({\n categoryHiddenSeries: { key: seriesKey, indices },\n } as Partial<CategoryWidgetState>),\n [id, seriesKey],\n )\n return (\n <CategoryUI\n data={slice.data}\n formatter={slice.formatter}\n labelFormatter={slice.labelFormatter}\n selection={selection}\n onSelectionChange={onSelectionChange}\n series={series}\n maxItems={maxItems}\n labels={labels}\n onShowAll={onShowAll}\n maxOverride={effectiveMaxOverride}\n size={size}\n stacked={stacked}\n hiddenSeries={hiddenSeries}\n onHiddenSeriesChange={onHiddenSeriesChange}\n />\n )\n}\n","import { Box, Skeleton } from '@mui/material'\nimport type { SxProps, Theme } from '@mui/material'\nimport type { CategorySize } from './types'\n\nconst styles = {\n root: {\n display: 'flex',\n flexDirection: 'column',\n gap: 1,\n py: 0.5,\n },\n // Mirror the real row layout: label + value on top, bar below.\n row: {\n display: 'flex',\n flexDirection: 'column',\n gap: 0.5,\n },\n rowHeader: {\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n gap: 1,\n },\n // Small fixed-width shimmer pills — matches the visual density of the\n // real label / value cells without claiming the full row width.\n name: { height: 14, width: 80 },\n value: { height: 14, width: 48 },\n} satisfies Record<string, SxProps<Theme>>\n\n// Bar shimmer height matches the live bar track for each size variant so\n// the skeleton doesn't visibly resize the row on data settle.\nconst BAR_HEIGHT: Record<CategorySize, number> = {\n small: 4,\n medium: 12,\n}\n\n// Varied widths give the bar shimmer a more realistic, less uniform feel.\n// Cycles when `rows > WIDTH_PATTERNS.length`.\nconst WIDTH_PATTERNS = ['95%', '80%', '55%', '75%', '60%']\n\nexport interface CategorySkeletonProps {\n rows?: number\n /**\n * Visual density. Mirrors the live widget's `size` prop so the loading\n * shimmer's bar height matches what's about to render — no row jump\n * on data settle.\n */\n size?: CategorySize\n}\n\nexport function CategorySkeleton({\n rows = 5,\n size = 'small',\n}: CategorySkeletonProps) {\n return (\n <Box sx={styles.root}>\n {Array.from({ length: rows }).map((_, i) => (\n <Box key={`row-${i}`} sx={styles.row}>\n <Box sx={styles.rowHeader}>\n <Skeleton variant='rectangular' sx={styles.name} />\n <Skeleton variant='rectangular' sx={styles.value} />\n </Box>\n <Skeleton\n variant='rectangular'\n sx={{\n height: BAR_HEIGHT[size],\n width: WIDTH_PATTERNS[i % WIDTH_PATTERNS.length],\n }}\n />\n </Box>\n ))}\n </Box>\n )\n}\n","import {\n buildCsvDownloadItem,\n withPngItem,\n type DownloadItem,\n type WidgetDownloadArgs,\n} from '../actions/download'\nimport type { CategoryWidgetData } from './types'\n\n/**\n * Download menu items for the Category widget. Always includes a CSV item\n * with `series, name, value` columns (one row per item). When `getCaptureEl`\n * is given, prepends a PNG item that rasterises the captured element via\n * `html2canvas`.\n */\nexport function createCategoryDownloadConfig(\n args: WidgetDownloadArgs<CategoryWidgetData> & {\n seriesNames?: readonly string[]\n },\n): DownloadItem[] {\n return withPngItem(\n args,\n buildCsvDownloadItem({\n filename: args.filename,\n getRows: (ctx) => {\n const data = args.getData(ctx)\n const rows: unknown[][] = [['series', 'name', 'value']]\n for (const [i, series] of data.entries()) {\n const seriesName = args.seriesNames?.[i] ?? `series_${i + 1}`\n for (const item of series) {\n rows.push([seriesName, item.name, item.value])\n }\n }\n return rows\n },\n }),\n )\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"echart-CV8F50gn.js","names":["{\n option,\n replaceMerge,\n onEvents,\n init,\n onInstance,\n className,\n sx,\n}: EchartUIProps","(chart: echarts.ECharts | null): void => {\n onInstance?.(chart)\n }","() => {\n if (!containerRef.current) return undefined\n const chart = echarts.init(containerRef.current, null, {\n ...DEFAULT_INIT_OPTS,\n ...init,\n })\n chartRef.current = chart\n notifyInstance(chart)\n // Edge-label clamp: after each layout settles (option change OR resize both\n // end in a render → `finished`), measure the category x-axis edge labels\n // and anchor them inward only if they'd clip. `finished` (not rAF) so\n // `convertToPixel` sees the flushed `lazyUpdate` layout. Loop-safe: the\n // verdict is anchor-independent, so our own clamp setOption recomputes the\n // same result and the ref guard short-circuits.\n const onFinished = (): void => {\n edgeAlignRef.current = clampEdgeLabels(\n chart,\n optionRef.current,\n edgeAlignRef.current,\n )\n }\n chart.on('finished', onFinished)\n // Never let the legend end up with every item hidden — a user can click\n // series off one by one until the chart goes blank. `selected` on this\n // event is always the FULL name→boolean map (not just the clicked item),\n // so \"every value false\" reliably means \"nothing left visible\". Restore\n // all of them instead. `legendAllSelect` fires its own `legendselectall`\n // event, not `legendselectchanged`, so this can't re-trigger itself.\n // Always-on and independent of the consumer's `onEvents` — applies to\n // every ECharts widget in the library for free. A widget that disables\n // legend clicks entirely (e.g. Pie's `selectedMode: false`) never fires\n // `legendselectchanged`, so this is a no-op there.\n const onLegendSelectChanged = (params: unknown): void => {\n const { selected } = params as { selected?: Record<string, boolean> }\n if (!selected) return\n const names = Object.keys(selected)\n if (names.length > 0 && names.every((name) => !selected[name])) {\n chart.dispatchAction({ type: 'legendAllSelect' })\n }\n }\n chart.on('legendselectchanged', onLegendSelectChanged)\n return () => {\n chart.off('finished', onFinished)\n chart.off('legendselectchanged', onLegendSelectChanged)\n // Notify observers *before* disposal so any queued imperative\n // dispatches (e.g. ZoomToggle's `setOption` cleanup) see the\n // instance disappear before its DOM is torn down.\n notifyInstance(null)\n chart.dispose()\n chartRef.current = null\n }\n }","chart","[init]","() => {\n const seriesFp = computeSeriesFingerprint(option)\n const datasetFp = computeDatasetFingerprint(option)\n const augmented = new Set<string>(replaceMerge ?? [])\n if (seriesFp !== seriesFingerprintRef.current) augmented.add('series')\n if (datasetFp !== datasetFingerprintRef.current) augmented.add('dataset')\n seriesFingerprintRef.current = seriesFp\n datasetFingerprintRef.current = datasetFp\n // Expose the latest option to the `finished` clamp listener (bound once).\n optionRef.current = option\n chartRef.current?.setOption(option, {\n notMerge: false,\n lazyUpdate: true,\n replaceMerge: augmented.size > 0 ? Array.from(augmented) : undefined,\n })\n }","[option, replaceMerge]","() => {\n const node = containerRef.current\n if (!node) return undefined\n return observeResize(node, () => {\n chartRef.current?.resize()\n })\n }","[]","() => {\n const chart = chartRef.current\n if (!chart || !onEvents) return undefined\n for (const [event, handler] of Object.entries(onEvents)) {\n chart.on(event, handler)\n }\n return () => {\n for (const [event, handler] of Object.entries(onEvents)) {\n chart.off(event, handler)\n }\n }\n }","[onEvents]","event","handler","{ ...styles.root, ...sx }","<Box\n ref={containerRef}\n className={className}\n sx={{ ...styles.root, ...sx }}\n />","{\n optionFactory,\n onEvents,\n init,\n className,\n sx,\n}: EchartProps","(chart: echarts.ECharts | null) => setEchartInstance(id, chart)","useCallback(\n (chart: echarts.ECharts | null) => setEchartInstance(id, chart),\n [id],\n )","optionFactory(undefined, undefined)","useMemo(\n () => optionFactory(undefined, undefined),\n [optionFactory],\n )","applyTransforms(structural, configTransforms)","Array.from(keys).sort()","useMemo(() => {\n const keys = new Set<string>(DEFAULT_REPLACE_MERGE)\n for (const xf of configTransforms) {\n if (xf.enabled && xf.replaceMergeKeys?.length) {\n for (const k of xf.replaceMergeKeys) keys.add(k)\n }\n }\n return Array.from(keys).sort()\n }, [configTransforms])","optionFactory(transformed, sliceData, {\n formatter: sliceFormatter,\n labelFormatter: sliceLabelFormatter,\n })","useMemo(\n () =>\n optionFactory(transformed, sliceData, {\n formatter: sliceFormatter,\n labelFormatter: sliceLabelFormatter,\n }),\n [\n optionFactory,\n transformed,\n sliceData,\n sliceFormatter,\n sliceLabelFormatter,\n ],\n )","<EchartUI\n option={option}\n replaceMerge={replaceMerge}\n onEvents={onEvents}\n init={init}\n onInstance={onInstance}\n className={className}\n sx={sx}\n />","{\n selection,\n onSelectionChange,\n keyFromClick,\n keysFromBrush,\n}: UseChartSelectionOptions<K>","(event: unknown) => {\n if (!onSelectionChange) return\n const params = event as CallbackDataParams\n const key = keyFromClick(params)\n if (key == null) return\n const current = selection ?? []\n const idx = current.indexOf(key)\n const next =\n idx === -1\n ? [...current, key]\n : [...current.slice(0, idx), ...current.slice(idx + 1)]\n onSelectionChange(next)\n }","useCallback(\n (event: unknown) => {\n if (!onSelectionChange) return\n const params = event as CallbackDataParams\n const key = keyFromClick(params)\n if (key == null) return\n const current = selection ?? []\n const idx = current.indexOf(key)\n const next =\n idx === -1\n ? [...current, key]\n : [...current.slice(0, idx), ...current.slice(idx + 1)]\n onSelectionChange(next)\n },\n [selection, onSelectionChange, keyFromClick],\n )","[]","(event: unknown) => {\n const params = event as {\n batch?: {\n selected?: { seriesIndex: number; dataIndex: number[] }[]\n }[]\n }\n latestBrushedRef.current = params.batch?.[0]?.selected ?? []\n }","params","event","useCallback((event: unknown) => {\n const params = event as {\n batch?: {\n selected?: { seriesIndex: number; dataIndex: number[] }[]\n }[]\n }\n latestBrushedRef.current = params.batch?.[0]?.selected ?? []\n }, [])","() => {\n if (!onSelectionChange) return\n onSelectionChange(keysFromBrush(latestBrushedRef.current))\n }","useCallback(() => {\n if (!onSelectionChange) return\n onSelectionChange(keysFromBrush(latestBrushedRef.current))\n }, [onSelectionChange, keysFromBrush])","{\n click: handleClick,\n brushSelected: handleBrushSelected,\n brushEnd: handleBrushEnd,\n }","useMemo<Record<string, EchartsEventHandler>>(\n () => ({\n click: handleClick,\n brushSelected: handleBrushSelected,\n brushEnd: handleBrushEnd,\n }),\n [handleClick, handleBrushSelected, handleBrushEnd],\n )","{ onEvents, selection: wiringSelection }"],"sources":["../src/widgets/echart/edge-label-clamp.ts","../src/widgets/echart/shared-resize-observer.ts","../src/widgets/echart/style.ts","../src/widgets/echart/echart-ui.tsx","../src/widgets/echart/echart.tsx","../src/widgets/echart/use-chart-selection.ts"],"sourcesContent":["import * as echarts from 'echarts'\nimport type { ECharts, EChartsOption } from 'echarts'\n\n/**\n * Render-time clamp that stops the first/last **category x-axis** labels from\n * clipping at the chart edges — *only when they actually overflow*.\n *\n * ECharts centers each category label on its tick; the first/last ticks sit at\n * the plot boundary, so a wide edge label spills past the chart and is cut off.\n * `grid.containLabel` does not contain this horizontal overflow and the v6\n * `outerBounds` shrink is unreliable, so we measure at render time and anchor\n * the overflowing edge label inward (`alignMinLabel: 'left'` /\n * `alignMaxLabel: 'right'`). Labels that fit stay centered.\n *\n * Lives in the generic bridge because the verdict needs the laid-out chart\n * (`convertToPixel`, `getWidth`); everything else is read from the `option`.\n * Auto-targets bar + histogram (category x-axis); pie's horizontal-bar fallback\n * (category *y*-axis), scatterplot (value) and timeseries (time) are skipped.\n */\n\nexport interface EdgeAlignment {\n alignMinLabel: 'left' | null\n alignMaxLabel: 'right' | null\n}\n\nexport const CENTERED: EdgeAlignment = {\n alignMinLabel: null,\n alignMaxLabel: null,\n}\n\n// Small cushion biasing toward anchoring when borderline: clipping is worse\n// than a hair of off-centering, and the measured width can differ slightly\n// from the final render (mainly when the web font isn't loaded yet at measure\n// time). Kept to 1px — a wider margin anchored labels that only just clear the\n// edge, and an anchored edge label can then be dropped by the axis\n// `hideOverlap`, leaving a gap. 1px guards measurement error without that.\nconst SAFETY_MARGIN_PX = 1\n\n/**\n * Pure overflow decision. Inputs are anchor-independent (tick centers and text\n * width don't depend on the label's current `text-anchor`), so feeding the\n * result back via `setOption` doesn't change them — the next pass yields the\n * same verdict, keeping the clamp loop-stable.\n */\nexport function decideEdgeAlignment(args: {\n firstLabel: string\n lastLabel: string\n font: string\n firstTickX: number\n lastTickX: number\n width: number\n}): EdgeAlignment {\n const halfFirst =\n echarts.format.getTextRect(args.firstLabel, args.font).width / 2\n const halfLast =\n echarts.format.getTextRect(args.lastLabel, args.font).width / 2\n return {\n alignMinLabel:\n halfFirst + SAFETY_MARGIN_PX > args.firstTickX ? 'left' : null,\n alignMaxLabel:\n halfLast + SAFETY_MARGIN_PX > args.width - args.lastTickX\n ? 'right'\n : null,\n }\n}\n\ninterface AxisLabel {\n formatter?: (value: string | number) => string | number\n fontSize?: number | string\n fontFamily?: string\n fontWeight?: number | string\n}\n\ninterface CategoryXAxis {\n type?: string\n axisLabel?: AxisLabel\n}\n\ninterface SeriesLike {\n encode?: { x?: string | number }\n datasetIndex?: number\n}\n\ntype Cell = string | number | null | undefined\n\nfunction firstOf<T>(value: T | T[] | undefined): T | undefined {\n if (Array.isArray(value)) return value[0]\n return value\n}\n\nfunction firstXAxis(option: EChartsOption): CategoryXAxis | undefined {\n const axis = firstOf(\n option.xAxis as CategoryXAxis | CategoryXAxis[] | undefined,\n )\n return axis && typeof axis === 'object' ? axis : undefined\n}\n\nfunction hasDataZoom(option: EChartsOption): boolean {\n const dz = option.dataZoom\n return Array.isArray(dz) ? dz.length > 0 : dz != null\n}\n\nfunction cellToText(value: Cell, fmt: AxisLabel['formatter']): string {\n if (typeof fmt === 'function') return String(fmt(value ?? ''))\n return value == null ? '' : String(value)\n}\n\n/**\n * Extracts the displayed first/last category strings and the label font from\n * the option, reading categories generically via `series[0].encode.x` into the\n * referenced dataset (`'name'` for bar, `0` for histogram). Returns `null` when\n * the option isn't a measurable category x-axis (fewer than 2 categories, no\n * dataset, etc.).\n */\nexport function resolveEdgeLabels(option: EChartsOption): {\n firstLabel: string\n lastLabel: string\n font: string\n count: number\n} | null {\n const xAxis = firstXAxis(option)\n if (!xAxis) return null\n if (xAxis.type !== 'category') return null\n\n const series0 = firstOf(\n option.series as SeriesLike | SeriesLike[] | undefined,\n )\n const encodeX = series0?.encode?.x\n if (encodeX == null) return null\n\n const datasetIndex = series0?.datasetIndex ?? 0\n const allDatasets = option.dataset as\n { source?: unknown } | { source?: unknown }[] | undefined\n const dataset = Array.isArray(allDatasets)\n ? allDatasets[datasetIndex]\n : allDatasets\n const source = dataset?.source\n if (!Array.isArray(source) || source.length < 2) return null\n\n const rows = source as Record<string | number, Cell>[]\n const fmt = xAxis.axisLabel?.formatter\n const axisLabel = xAxis.axisLabel ?? {}\n const size =\n typeof axisLabel.fontSize === 'number'\n ? `${axisLabel.fontSize}px`\n : (axisLabel.fontSize ?? '12px')\n const family = axisLabel.fontFamily ?? 'sans-serif'\n const weight = axisLabel.fontWeight != null ? `${axisLabel.fontWeight} ` : ''\n\n return {\n firstLabel: cellToText(rows[0]?.[encodeX], fmt),\n lastLabel: cellToText(rows[rows.length - 1]?.[encodeX], fmt),\n font: `${weight}${size} ${family}`,\n count: source.length,\n }\n}\n\n/**\n * Measures the rendered chart and, when the edge labels would clip, applies\n * `alignMinLabel`/`alignMaxLabel` (or clears them) via an imperative merge\n * `setOption`. Returns the alignment now in effect so the caller can keep a\n * `prev` ref and skip redundant `setOption`s. Should be invoked from the\n * chart's `finished` event (layout is settled, so `convertToPixel` is valid).\n */\nexport function clampEdgeLabels(\n chart: ECharts,\n option: EChartsOption,\n prev: EdgeAlignment,\n): EdgeAlignment {\n const next = computeAlignment(chart, option)\n if (\n next.alignMinLabel === prev.alignMinLabel &&\n next.alignMaxLabel === prev.alignMaxLabel\n ) {\n return prev\n }\n chart.setOption({ xAxis: { axisLabel: { ...next } } } as EChartsOption, {\n lazyUpdate: true,\n })\n return next\n}\n\nfunction computeAlignment(\n chart: ECharts,\n option: EChartsOption,\n): EdgeAlignment {\n // Under dataZoom, convertToPixel on the absolute first/last index returns\n // off-plot pixels, so the overflow math is invalid — reset to centered.\n if (hasDataZoom(option)) return CENTERED\n\n const labels = resolveEdgeLabels(option)\n if (!labels) return CENTERED\n\n const firstTickX = chart.convertToPixel({ xAxisIndex: 0 }, 0)\n const lastTickX = chart.convertToPixel({ xAxisIndex: 0 }, labels.count - 1)\n const width = chart.getWidth()\n // Layout not measurable yet (defensive — `finished` normally fires after\n // layout). Fall back to centered; a later `finished` re-measures.\n if (\n typeof firstTickX !== 'number' ||\n typeof lastTickX !== 'number' ||\n !Number.isFinite(firstTickX) ||\n !Number.isFinite(lastTickX) ||\n !width\n ) {\n return CENTERED\n }\n\n return decideEdgeAlignment({\n firstLabel: labels.firstLabel,\n lastLabel: labels.lastLabel,\n font: labels.font,\n firstTickX,\n lastTickX,\n width,\n })\n}\n","type ResizeCallback = () => void\n\nconst callbacks = new Map<Element, ResizeCallback>()\nlet observer: ResizeObserver | null = null\n\n/**\n * Returns the singleton ResizeObserver, lazily constructing it on first use.\n * Returns `null` when `ResizeObserver` is not available in the global scope\n * (SSR, very old browsers, or tests that intentionally remove it).\n */\nfunction getObserver(): ResizeObserver | null {\n if (typeof ResizeObserver === 'undefined') return null\n observer ??= new ResizeObserver((entries) => {\n for (const entry of entries) {\n const callback = callbacks.get(entry.target)\n callback?.()\n }\n })\n return observer\n}\n\nconst NOOP_CLEANUP = (): void => undefined\n\nexport function observeResize(\n element: Element,\n callback: ResizeCallback,\n): () => void {\n const ro = getObserver()\n // Gracefully degrade when no ResizeObserver is available — consumers\n // still see one initial measure() pass via their own effect; we just\n // skip the subsequent resize-driven re-measures.\n if (!ro) return NOOP_CLEANUP\n callbacks.set(element, callback)\n ro.observe(element)\n return () => {\n callbacks.delete(element)\n if (observer) {\n observer.unobserve(element)\n // Disconnect the singleton once the last subscriber leaves so the\n // observer doesn't outlive its consumers in SSR teardown / micro-\n // frontend unmount scenarios. A subsequent observeResize() will\n // lazily re-create it via getObserver().\n if (callbacks.size === 0) {\n observer.disconnect()\n observer = null\n }\n }\n }\n}\n\n/** @internal — for tests only. */\nexport function __resetSharedResizeObserver(): void {\n if (observer) observer.disconnect()\n callbacks.clear()\n observer = null\n}\n","import type { SxProps, Theme } from '@mui/material'\n\nexport const styles = {\n root: {\n width: '100%',\n // `flex: 1` lets the chart fill a parent that has a definite height —\n // the fullscreen dialog. Inline it has none (Widget.Content is an\n // auto-height flex column), so flex-basis resolves to the content box,\n // which is empty until ECharts measures it: at `min-height: 0` the\n // container is 0px tall, ECharts draws nothing and the widget renders\n // blank. 304 is not a new magic number — it's the height `init.height`\n // used to pin, kept so cards look exactly as before. Override per widget\n // with `<Widget.Echart sx={{ minHeight }} />`.\n flex: 1,\n minHeight: 304,\n // ECharts puts its tooltip inside this box at `z-index: 9999999`. With\n // no stacking context of its own, that number competes page-wide and\n // beats MUI's modal and popover layers, so a tooltip left showing\n // paints over any dialog or menu opened above the chart. Isolating\n // keeps the tooltip above its own chart only.\n isolation: 'isolate',\n },\n} satisfies Record<string, SxProps<Theme>>\n","import { useEffect, useEffectEvent, useRef } from 'react'\nimport { Box, type SxProps, type Theme } from '@mui/material'\nimport * as echarts from 'echarts'\nimport {\n clampEdgeLabels,\n CENTERED,\n type EdgeAlignment,\n} from './edge-label-clamp'\nimport { observeResize } from './shared-resize-observer'\nimport { styles } from './style'\n\n// No `height`: a numeric height here is stored in zrender's init opts, and\n// every later `resize()` resolves back to it — the chart would stay that tall\n// inside a fullscreen dialog. Height comes from the container instead\n// (`styles.root`'s `min-height` inline, `flex: 1` when the parent has one).\nexport const DEFAULT_INIT_OPTS = {\n renderer: 'svg',\n} satisfies echarts.EChartsInitOpts\n\nexport type EchartsEventHandler = (event: unknown) => void\n\nexport interface EchartUIProps {\n option: echarts.EChartsOption\n /**\n * Keys to merge as arrays (replace by index) instead of by id. The middleware\n * memoizes this content-stably, so identical sets don't re-trigger setOption.\n */\n replaceMerge?: readonly string[]\n /**\n * Opaque ECharts event passthrough — handlers fire untransformed.\n *\n * **Must be referentially stable** (memoize it with `useMemo` /\n * `useCallback`, or hoist to module scope). The binding effect depends on\n * the `onEvents` object identity; an inline `{ click: handler }` literal\n * recreates the object on every render and causes every listener to be\n * detached and re-attached each commit.\n */\n onEvents?: Record<string, EchartsEventHandler>\n /**\n * Init options forwarded to echarts.init. Captured at mount only — to change\n * `renderer` or `height` after mount, unmount and remount the chart.\n * Defaults: `{ renderer: 'svg' }` — height is container-driven, so prefer\n * `sx={{ minHeight }}` over pinning `height` here.\n */\n init?: echarts.EChartsInitOpts\n /**\n * Optional callback fired once after the ECharts instance is created\n * (`onInstance(chart)`) and once when it's about to be disposed\n * (`onInstance(null)`). Used by `Widget.Echart` to publish the live\n * instance to the per-widget registry so other actions (e.g.\n * `ZoomToggle`'s disable handler) can reach it without DOM lookups.\n * No need to memoize — the bridge wraps it in `useEffectEvent` so an\n * unstable reference does not re-init the chart.\n */\n onInstance?: (chart: echarts.ECharts | null) => void\n className?: string\n sx?: SxProps<Theme>\n}\n\nexport function EchartUI({\n option,\n replaceMerge,\n onEvents,\n init,\n onInstance,\n className,\n sx,\n}: EchartUIProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const chartRef = useRef<echarts.ECharts | null>(null)\n\n // Structural fingerprint of the last applied series / dataset arrays.\n // Used to decide whether the next `setOption` should *replace* those\n // components (when their shape changed) or *merge* them (when only\n // styling-level fields differ — e.g. a new `itemStyle.color` callback\n // from a selection update). Replacing wipes ECharts' per-series runtime\n // state (hover, animation cache, brush areas), so we want to do it only\n // when the structure actually moved.\n const seriesFingerprintRef = useRef<string | null>(null)\n const datasetFingerprintRef = useRef<string | null>(null)\n\n // Latest applied option (read by the `finished` clamp listener, which is\n // bound once and must see the current option) + the edge-label alignment\n // currently applied to the chart (so the clamp skips redundant setOptions).\n const optionRef = useRef<echarts.EChartsOption>(option)\n const edgeAlignRef = useRef<EdgeAlignment>(CENTERED)\n\n // Stable notify wrapper — always reads the latest `onInstance` prop\n // without forcing the init effect below to re-run when the parent\n // passes a fresh function reference. The init effect now only fires\n // when `init` actually changes (documented as mount-only anyway).\n const notifyInstance = useEffectEvent(\n (chart: echarts.ECharts | null): void => {\n onInstance?.(chart)\n },\n )\n\n // Init / dispose. `init` is captured once at mount.\n useEffect(() => {\n if (!containerRef.current) return undefined\n const chart = echarts.init(containerRef.current, null, {\n ...DEFAULT_INIT_OPTS,\n ...init,\n })\n chartRef.current = chart\n notifyInstance(chart)\n // Edge-label clamp: after each layout settles (option change OR resize both\n // end in a render → `finished`), measure the category x-axis edge labels\n // and anchor them inward only if they'd clip. `finished` (not rAF) so\n // `convertToPixel` sees the flushed `lazyUpdate` layout. Loop-safe: the\n // verdict is anchor-independent, so our own clamp setOption recomputes the\n // same result and the ref guard short-circuits.\n const onFinished = (): void => {\n edgeAlignRef.current = clampEdgeLabels(\n chart,\n optionRef.current,\n edgeAlignRef.current,\n )\n }\n chart.on('finished', onFinished)\n // Never let the legend end up with every item hidden — a user can click\n // series off one by one until the chart goes blank. `selected` on this\n // event is always the FULL name→boolean map (not just the clicked item),\n // so \"every value false\" reliably means \"nothing left visible\". Restore\n // all of them instead. `legendAllSelect` fires its own `legendselectall`\n // event, not `legendselectchanged`, so this can't re-trigger itself.\n // Always-on and independent of the consumer's `onEvents` — applies to\n // every ECharts widget in the library for free. A widget that disables\n // legend clicks entirely (e.g. Pie's `selectedMode: false`) never fires\n // `legendselectchanged`, so this is a no-op there.\n const onLegendSelectChanged = (params: unknown): void => {\n const { selected } = params as { selected?: Record<string, boolean> }\n if (!selected) return\n const names = Object.keys(selected)\n if (names.length > 0 && names.every((name) => !selected[name])) {\n chart.dispatchAction({ type: 'legendAllSelect' })\n }\n }\n chart.on('legendselectchanged', onLegendSelectChanged)\n return () => {\n chart.off('finished', onFinished)\n chart.off('legendselectchanged', onLegendSelectChanged)\n // Notify observers *before* disposal so any queued imperative\n // dispatches (e.g. ZoomToggle's `setOption` cleanup) see the\n // instance disappear before its DOM is torn down.\n notifyInstance(null)\n chart.dispose()\n chartRef.current = null\n }\n }, [init])\n\n // Apply option / replaceMerge changes via merge mode (no remount required).\n useEffect(() => {\n const seriesFp = computeSeriesFingerprint(option)\n const datasetFp = computeDatasetFingerprint(option)\n const augmented = new Set<string>(replaceMerge ?? [])\n if (seriesFp !== seriesFingerprintRef.current) augmented.add('series')\n if (datasetFp !== datasetFingerprintRef.current) augmented.add('dataset')\n seriesFingerprintRef.current = seriesFp\n datasetFingerprintRef.current = datasetFp\n // Expose the latest option to the `finished` clamp listener (bound once).\n optionRef.current = option\n chartRef.current?.setOption(option, {\n notMerge: false,\n lazyUpdate: true,\n replaceMerge: augmented.size > 0 ? Array.from(augmented) : undefined,\n })\n }, [option, replaceMerge])\n\n // Resize via shared singleton observer.\n useEffect(() => {\n const node = containerRef.current\n if (!node) return undefined\n return observeResize(node, () => {\n chartRef.current?.resize()\n })\n }, [])\n\n // Bind / unbind opaque event handlers.\n useEffect(() => {\n const chart = chartRef.current\n if (!chart || !onEvents) return undefined\n for (const [event, handler] of Object.entries(onEvents)) {\n chart.on(event, handler)\n }\n return () => {\n for (const [event, handler] of Object.entries(onEvents)) {\n chart.off(event, handler)\n }\n }\n }, [onEvents])\n\n return (\n <Box\n ref={containerRef}\n className={className}\n sx={{ ...styles.root, ...sx }}\n />\n )\n}\n\n/**\n * Cheap structural digest of the option's `series` array — covers the fields\n * that actually demand a `replaceMerge` (length, type, datasetIndex, name,\n * stack, encode). Excludes runtime-style fields like `itemStyle` (callbacks\n * have unstable identity but don't change structure).\n */\nfunction computeSeriesFingerprint(option: echarts.EChartsOption): string {\n const series = option.series\n if (!Array.isArray(series)) return series ? '1' : ''\n return series\n .map((s) => {\n if (s == null || typeof s !== 'object') return String(s)\n const o = s as Record<string, unknown>\n return [\n o.type,\n o.datasetIndex,\n o.name,\n o.stack,\n // `encode` is small; stringify is cheap and stable for plain objects.\n JSON.stringify(o.encode ?? null),\n ].join('|')\n })\n .join('||')\n}\n\n/**\n * Structural digest of the option's `dataset` array — count is the only\n * thing that needs `replaceMerge` here. Row-level changes are picked up by\n * ECharts' default merge.\n */\nfunction computeDatasetFingerprint(option: echarts.EChartsOption): string {\n const dataset = option.dataset\n if (!Array.isArray(dataset)) return dataset ? '1' : ''\n return String(dataset.length)\n}\n","import { useCallback, useMemo } from 'react'\nimport type { SxProps, Theme } from '@mui/material'\nimport type * as echarts from 'echarts'\nimport {\n applyTransforms,\n setEchartInstance,\n useWidgetId,\n useWidgetShallow,\n type Transform,\n} from '../stores'\nimport { EchartUI, type EchartsEventHandler } from './echart-ui'\n\n/**\n * Default ECharts `replaceMerge` keys for every widget. `dataZoom` and\n * `brush` are omitted so ZoomToggle / BrushToggle's user-driven runtime\n * state (slider range, brushed areas in `multiple` mode) survives unrelated\n * re-renders. `series` and `dataset` are also omitted: EchartUI fingerprints\n * them per-render and adds them to `replaceMerge` only when their shape\n * actually changes, keeping ECharts' per-series runtime state alive when\n * only callback-style fields differ.\n *\n * Lives in this module (not the generic store) because it's an ECharts\n * concept. Module-private — callers should not need it.\n */\nconst DEFAULT_REPLACE_MERGE: readonly string[] = ['toolbox']\n\n/**\n * Reactive context passed to every {@link OptionFactory} call so the\n * factory can rebuild render-time pieces (axis label / tooltip formatters)\n * from the live store. Drives RelativeData / consumer-formatter changes\n * through to the chart without rebuilding the structural option.\n */\nexport interface OptionFactoryContext {\n formatter?: (value: number) => string\n labelFormatter?: (value: string | number) => string | number\n}\n\n/**\n * The per-widget option factory — a single callable that owns BOTH phases\n * of option construction:\n *\n * - **Structural phase** — when `option == null`, return the theme-aware\n * structural option (tooltip / legend / color palette / series\n * template, optionally merged with a consumer-supplied `optionsOverride`).\n * No data is read. `<Widget.Echart>` calls the factory with\n * `(undefined, undefined)` synchronously during render to derive the\n * structural base; `configTransforms` (Stack/Zoom/Brush) then mutate it\n * in the same render pass.\n * - **Merge phase** — when `option` is defined, fuse `data` into the\n * post-configTransforms option at fusion time. `<Widget.Echart>` calls\n * the factory with `(transformed, data, ctx)` on every render.\n *\n * The two phases share a closure (the factory creator captures `theme`,\n * `formatter`, `labelFormatter`, `seriesNames`, `selection`, `optionsOverride`,\n * …), so structural and merge agree on the same widget configuration.\n *\n * The third arg `ctx` carries the **live** store-side formatters at the\n * call site — distinct from the closure-time formatters because actions\n * like RelativeData can install a percent formatter on the store after\n * the factory was constructed. The merge phase reads from `ctx`; the\n * structural phase typically uses the closure-time values.\n */\nexport type OptionFactory = (\n option: echarts.EChartsOption | undefined,\n data: unknown,\n ctx?: OptionFactoryContext,\n) => echarts.EChartsOption\n\nexport interface EchartProps {\n /**\n * The per-widget {@link OptionFactory}. Required — `<Widget.Echart>`\n * derives the structural option from it (so configTransforms have a base\n * to mutate) and fuses `state.data` into the post-pipeline option at\n * render time. Wrap the factory creator in `useMemo` so its identity is\n * stable across renders.\n */\n optionFactory: OptionFactory\n onEvents?: Record<string, EchartsEventHandler>\n init?: echarts.EChartsInitOpts\n className?: string\n /**\n * Styles for the chart container — this is where its height comes from.\n * `min-height` defaults to the card height (304px) and `flex: 1` lets the\n * chart fill a taller parent; override either here, e.g.\n * `sx={{ minHeight: 400 }}` for a taller card.\n */\n sx?: SxProps<Theme>\n}\n\ninterface EchartSlice {\n data: unknown\n configTransforms: readonly Transform[]\n formatter?: (value: number) => string\n labelFormatter?: (value: string | number) => string | number\n}\n\nconst echartSelector = (s: {\n data: unknown\n configTransforms: readonly Transform[]\n formatter?: (value: number) => string\n labelFormatter?: (value: string | number) => string | number\n}): EchartSlice => ({\n data: s.data,\n configTransforms: s.configTransforms,\n formatter: s.formatter,\n labelFormatter: s.labelFormatter,\n})\n\n/**\n * Stateful Echart bridge — owns the entire ECharts coupling. The whole\n * option pipeline lives here, not in the store:\n *\n * 1. **Structural** — `optionFactory(undefined, undefined)` produces the\n * theme-aware base. Memoized on the factory identity, so the consumer's\n * `useMemo` ID gates the rebuild.\n * 2. **Transformed** — `applyTransforms(structural, configTransforms)`\n * applies any registered configTransforms (Stack/Zoom/Brush) over the\n * structural base. Memoized on `[structural, configTransforms]`.\n * 3. **`replaceMerge`** — derived from the enabled configTransforms'\n * `replaceMergeKeys`, deduped and sorted, seeded with\n * {@link DEFAULT_REPLACE_MERGE}. Memoized on `[configTransforms]` so\n * ECharts sees a stable array reference across non-transform changes.\n * 4. **Merge** — `optionFactory(transformed, data, ctx)` fuses post-\n * pipeline data into the option. Reactive `ctx` carries the live store\n * formatters so RelativeData's percent formatter flows through without\n * a structural rebuild.\n *\n * The `<Widget.Provider>` doesn't know about the factory at all: it stays\n * a renderer-agnostic shell. The `ProviderProps` surface has no ECharts\n * coupling, so non-Echart widgets don't transitively import the type.\n */\nexport function Echart({\n optionFactory,\n onEvents,\n init,\n className,\n sx,\n}: EchartProps) {\n const id = useWidgetId()\n const slice = useWidgetShallow(id, echartSelector)\n\n // Publish the live ECharts instance to the per-id registry so actions\n // (e.g. `ZoomToggle`'s disable handler) can reach it imperatively. The\n // callback identity is stable across renders (only `id` is in deps), so\n // EchartUI's init effect doesn't see a fresh `onInstance` and re-init.\n const onInstance = useCallback(\n (chart: echarts.ECharts | null) => setEchartInstance(id, chart),\n [id],\n )\n\n // Destructure so React Compiler sees specific deps instead of inferring\n // the whole `slice` object. `useWidgetShallow` already shallow-compares\n // the slice, so per-field deps drive each memo exactly when its inputs\n // change.\n const {\n data: sliceData,\n configTransforms,\n formatter: sliceFormatter,\n labelFormatter: sliceLabelFormatter,\n } = slice\n\n const structural = useMemo(\n () => optionFactory(undefined, undefined),\n [optionFactory],\n )\n\n const transformed = useMemo(\n () =>\n applyTransforms(structural, configTransforms) as echarts.EChartsOption,\n [structural, configTransforms],\n )\n\n const replaceMerge = useMemo(() => {\n const keys = new Set<string>(DEFAULT_REPLACE_MERGE)\n for (const xf of configTransforms) {\n if (xf.enabled && xf.replaceMergeKeys?.length) {\n for (const k of xf.replaceMergeKeys) keys.add(k)\n }\n }\n return Array.from(keys).sort()\n }, [configTransforms])\n\n const option = useMemo(\n () =>\n optionFactory(transformed, sliceData, {\n formatter: sliceFormatter,\n labelFormatter: sliceLabelFormatter,\n }),\n [\n optionFactory,\n transformed,\n sliceData,\n sliceFormatter,\n sliceLabelFormatter,\n ],\n )\n\n return (\n <EchartUI\n option={option}\n replaceMerge={replaceMerge}\n onEvents={onEvents}\n init={init}\n onInstance={onInstance}\n className={className}\n sx={sx}\n />\n )\n}\n","import { useCallback, useMemo, useRef } from 'react'\nimport type { CallbackDataParams } from 'echarts/types/dist/shared'\nimport type { EchartsEventHandler } from './echart-ui'\n\nexport type SelectionKey = string | number\n\nexport interface UseChartSelectionOptions<K extends SelectionKey> {\n /**\n * Controlled selection. `undefined`/`null`/an explicit `[]` all mean \"no\n * selection interaction\" — every current merger already treats an empty\n * selection as \"nothing excluded,\" so this hook collapses all three to\n * `null` rather than promising per-merger `[]` semantics it doesn't\n * implement.\n */\n selection?: readonly K[]\n /** Receives the next selection. When omitted the wiring is a no-op. */\n onSelectionChange?: (next: readonly K[]) => void\n /**\n * Pull a selection key out of an ECharts `click` event. Return `null` to\n * skip (e.g. a click on empty plot area). For Bar/Pie/Timeseries this is\n * usually `(p) => p.name`; for Histogram/Scatterplot it's the dataIndex.\n */\n keyFromClick: (params: CallbackDataParams) => K | null\n /**\n * Map a `brushSelected` payload's `selected[*]` entries (each carrying a\n * `seriesIndex` + `dataIndex[]`) into the full set of selection keys for\n * the brushed area. Called once per brush event.\n */\n keysFromBrush: (\n selected: readonly { seriesIndex: number; dataIndex: number[] }[],\n ) => K[]\n}\n\nexport interface ChartSelectionWiring<K extends SelectionKey> {\n /**\n * Pass straight to `<Widget.Echart onEvents={…}>`. Wires `click` and\n * `brushSelected` so the user's interactions reach `onSelectionChange`.\n */\n onEvents: Record<string, EchartsEventHandler>\n /**\n * The current selection — `null` for \"no selection interaction\"\n * (`undefined`/`null`/an explicit `[]`), or a non-empty array meaning\n * \"render only these as fully opaque, dim the rest\".\n */\n selection: readonly K[] | null\n}\n\n/**\n * Translate ECharts' click + brush events into a consumer-owned selection\n * with the expected dashboard semantics:\n *\n * - **Click toggles** — clicking an item adds it to the selection if\n * absent, removes it if present.\n * - **Brush replaces** — finishing a brush stroke overwrites the selection\n * with whatever the brush covered (drag-to-empty clears).\n *\n * Wire the returned `onEvents` into `<Widget.Echart>` and feed\n * `wiring.selection` to the widget's option factory so the merger can dim\n * non-selected rows at fusion time.\n */\nexport function useChartSelection<K extends SelectionKey>({\n selection,\n onSelectionChange,\n keyFromClick,\n keysFromBrush,\n}: UseChartSelectionOptions<K>): ChartSelectionWiring<K> {\n const handleClick = useCallback(\n (event: unknown) => {\n if (!onSelectionChange) return\n const params = event as CallbackDataParams\n const key = keyFromClick(params)\n if (key == null) return\n const current = selection ?? []\n const idx = current.indexOf(key)\n const next =\n idx === -1\n ? [...current, key]\n : [...current.slice(0, idx), ...current.slice(idx + 1)]\n onSelectionChange(next)\n },\n [selection, onSelectionChange, keyFromClick],\n )\n\n // ECharts fires `brushSelected` on every pointer-move while the user is\n // drawing a brush, but we only want to push to `onSelectionChange` once\n // the gesture ends. Cache the latest `brushSelected` payload here, then\n // commit on `brushEnd`.\n const latestBrushedRef = useRef<\n readonly { seriesIndex: number; dataIndex: number[] }[]\n >([])\n\n const handleBrushSelected = useCallback((event: unknown) => {\n const params = event as {\n batch?: {\n selected?: { seriesIndex: number; dataIndex: number[] }[]\n }[]\n }\n latestBrushedRef.current = params.batch?.[0]?.selected ?? []\n }, [])\n\n const handleBrushEnd = useCallback(() => {\n if (!onSelectionChange) return\n onSelectionChange(keysFromBrush(latestBrushedRef.current))\n }, [onSelectionChange, keysFromBrush])\n\n const onEvents = useMemo<Record<string, EchartsEventHandler>>(\n () => ({\n click: handleClick,\n brushSelected: handleBrushSelected,\n brushEnd: handleBrushEnd,\n }),\n [handleClick, handleBrushSelected, handleBrushEnd],\n )\n\n const wiringSelection = useMemo(\n () => (selection && selection.length > 0 ? selection : null),\n [selection],\n )\n\n return { onEvents, selection: wiringSelection }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-Bn2nKCbn.js","names":["{ content }: MarkdownUIProps","<Box sx={styles.root}>\n <MarkdownContent\n content={content}\n components={components}\n allowHtml\n allowImages\n />\n </Box>","(s) => {\n const data = s.data as MarkdownWidgetData | undefined\n return data?.content ?? ''\n }","<MarkdownUI content={content} />","{ lines = 5 }: MarkdownSkeletonProps","lines = 5","<Box sx={styles.root}>\n {Array.from({ length: lines }).map((_, i) => (\n <Skeleton\n key={`line-${i}`}\n variant='rectangular'\n sx={styles.line(widths[i % widths.length] ?? '85%')}\n />\n ))}\n </Box>"],"sources":["../src/widgets/markdown/style.ts","../src/widgets/markdown/markdown-ui.tsx","../src/widgets/markdown/markdown.tsx","../src/widgets/markdown/skeleton.tsx","../src/widgets/markdown/download.tsx"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material'\n\nexport const styles = {\n root: {\n color: 'text.primary',\n '& a': { color: 'primary.main' },\n '& code': {\n bgcolor: 'action.hover',\n px: 0.5,\n borderRadius: 0.5,\n fontFamily: 'monospace',\n fontSize: '0.875em',\n },\n '& pre': {\n bgcolor: 'action.hover',\n p: 1.5,\n borderRadius: 1,\n overflowX: 'auto',\n },\n '& blockquote': {\n borderLeft: '3px solid',\n borderColor: 'divider',\n pl: 1.5,\n ml: 0,\n color: 'text.secondary',\n fontStyle: 'italic',\n },\n '& ul, & ol': { pl: 3, my: 1 },\n '& li': { my: 0.25 },\n '& hr': {\n border: 'none',\n borderTop: '1px solid',\n borderColor: 'divider',\n my: 1.5,\n },\n '& table': { borderCollapse: 'collapse', width: '100%' },\n '& th, & td': {\n border: '1px solid',\n borderColor: 'divider',\n px: 1,\n py: 0.5,\n textAlign: 'left',\n },\n },\n paragraph: {\n my: 1,\n },\n heading: {\n mt: 2,\n mb: 1,\n fontWeight: 600,\n },\n} satisfies Record<string, SxProps<Theme>>\n","import { Box, Typography, type TypographyProps } from '@mui/material'\nimport type { Components } from 'react-markdown'\nimport { MarkdownContent } from './markdown-content'\nimport { styles } from './style'\n\nexport interface MarkdownUIProps {\n content: string\n}\n\n/** MUI-styled element mapping for `react-markdown`. */\nconst components: Components = {\n h1: (props) => mdHeading(props, 'h4'),\n h2: (props) => mdHeading(props, 'h5'),\n h3: (props) => mdHeading(props, 'h6'),\n h4: (props) => mdHeading(props, 'subtitle1'),\n h5: (props) => mdHeading(props, 'subtitle2'),\n h6: (props) => mdHeading(props, 'subtitle2'),\n p: ({ children }) => (\n <Typography variant='body2' component='p' sx={styles.paragraph}>\n {children}\n </Typography>\n ),\n}\n\nfunction mdHeading(\n props: { children?: React.ReactNode },\n variant: TypographyProps['variant'],\n) {\n return (\n <Typography variant={variant} component='div' sx={styles.heading}>\n {props.children}\n </Typography>\n )\n}\n\n/**\n * Pure presentational component for the Markdown widget. Delegates the\n * `react-markdown` engine to {@link MarkdownContent} and supplies a\n * document-style MUI element mapping (body2 paragraphs, real heading\n * typography). Opts INTO raw HTML and inline images — the Markdown widget is\n * the one slot in the library where authors can ship rich content. Caption\n * slots like `Note` keep the conservative defaults.\n */\nexport function MarkdownUI({ content }: MarkdownUIProps) {\n return (\n <Box sx={styles.root}>\n <MarkdownContent\n content={content}\n components={components}\n allowHtml\n allowImages\n />\n </Box>\n )\n}\n","import { useWidgetId, useWidget } from '../stores'\nimport { MarkdownUI } from './markdown-ui'\nimport type { MarkdownWidgetData } from './types'\n\n/**\n * Stateful Markdown bridge — reads `data.content` (post-pipeline) from the\n * per-widget store and forwards it to the pure {@link MarkdownUI}. Sits\n * inside `<Widget.Provider>`.\n */\nexport function Markdown() {\n const id = useWidgetId()\n const content = useWidget(id, (s) => {\n const data = s.data as MarkdownWidgetData | undefined\n return data?.content ?? ''\n })\n return <MarkdownUI content={content} />\n}\n","import { Box, Skeleton } from '@mui/material'\nimport type { SxProps, Theme } from '@mui/material'\n\nconst styles = {\n root: {\n display: 'flex',\n flexDirection: 'column',\n gap: 1,\n py: 1,\n },\n line: (width: string): SxProps<Theme> => ({ width, height: 18 }),\n} as const\n\nexport interface MarkdownSkeletonProps {\n lines?: number\n}\n\nexport function MarkdownSkeleton({ lines = 5 }: MarkdownSkeletonProps) {\n // Vary widths to mimic prose.\n const widths = ['100%', '95%', '90%', '85%', '70%']\n return (\n <Box sx={styles.root}>\n {Array.from({ length: lines }).map((_, i) => (\n <Skeleton\n key={`line-${i}`}\n variant='rectangular'\n sx={styles.line(widths[i % widths.length] ?? '85%')}\n />\n ))}\n </Box>\n )\n}\n","import {\n DOWNLOAD_ITEM_IDS,\n MarkdownIcon,\n withPngItem,\n toCsvString,\n triggerLinkDownload,\n type DownloadItem,\n type WidgetDownloadArgs,\n} from '../actions/download'\nimport type { MarkdownWidgetData } from './types'\n\n/**\n * Download menu items for the Markdown widget. Always includes a `.md` item\n * that saves the markdown source verbatim. When `getCaptureEl` is given,\n * prepends a PNG item that rasterises the captured element via\n * `html2canvas`.\n */\nexport function createMarkdownDownloadConfig(\n args: WidgetDownloadArgs<MarkdownWidgetData>,\n): DownloadItem[] {\n return withPngItem(args, {\n id: DOWNLOAD_ITEM_IDS.markdown,\n label: 'Markdown',\n icon: <MarkdownIcon fontSize='small' />,\n resolve: (ctx) => {\n const data = args.getData(ctx)\n const blob = new Blob([data.content ?? ''], {\n type: 'text/markdown;charset=utf-8;',\n })\n const url = URL.createObjectURL(blob)\n return Promise.resolve({\n url,\n filename: `${args.filename}.md`,\n revoke: () => URL.revokeObjectURL(url),\n })\n },\n })\n}\n\n// Re-export pure helpers consumers may want when building a custom Download item.\nexport { toCsvString, triggerLinkDownload }\n"],"mappings":"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"}
|