@misoto22/design 0.5.0 → 0.6.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/README.md +124 -0
- package/bin/misoto22-design.mjs +205 -0
- package/dist/agent/Accordion.md +41 -0
- package/dist/agent/Alert.md +42 -0
- package/dist/agent/AppShell.md +40 -0
- package/dist/agent/AreaChart.md +68 -0
- package/dist/agent/Article.md +41 -0
- package/dist/agent/Avatar.md +32 -0
- package/dist/agent/Badge.md +40 -0
- package/dist/agent/BarChart.md +65 -0
- package/dist/agent/BarList.md +40 -0
- package/dist/agent/BigNumber.md +45 -0
- package/dist/agent/BoxPlot.md +58 -0
- package/dist/agent/Breadcrumb.md +32 -0
- package/dist/agent/BulletChart.md +48 -0
- package/dist/agent/Button.md +56 -0
- package/dist/agent/Calendar.md +42 -0
- package/dist/agent/Card.md +49 -0
- package/dist/agent/Checkbox.md +35 -0
- package/dist/agent/Collapsible.md +46 -0
- package/dist/agent/Combobox.md +50 -0
- package/dist/agent/Command.md +93 -0
- package/dist/agent/ComposedChart.md +57 -0
- package/dist/agent/ContextMenu.md +59 -0
- package/dist/agent/DatePicker.md +75 -0
- package/dist/agent/Diagram.md +46 -0
- package/dist/agent/Dialog.md +52 -0
- package/dist/agent/DropdownMenu.md +70 -0
- package/dist/agent/EmptyState.md +37 -0
- package/dist/agent/ErrorState.md +41 -0
- package/dist/agent/Facet.md +82 -0
- package/dist/agent/Field.md +37 -0
- package/dist/agent/FigureBand.md +37 -0
- package/dist/agent/FloatingIconButton.md +38 -0
- package/dist/agent/FunnelChart.md +52 -0
- package/dist/agent/Heatmap.md +53 -0
- package/dist/agent/Histogram.md +63 -0
- package/dist/agent/Input.md +30 -0
- package/dist/agent/Kbd.md +29 -0
- package/dist/agent/LineChart.md +60 -0
- package/dist/agent/LinkArrow.md +29 -0
- package/dist/agent/NativeSelect.md +40 -0
- package/dist/agent/NavItem.md +35 -0
- package/dist/agent/Pagination.md +43 -0
- package/dist/agent/PieChart.md +55 -0
- package/dist/agent/Popover.md +57 -0
- package/dist/agent/Progress.md +33 -0
- package/dist/agent/RadarChart.md +54 -0
- package/dist/agent/RadialChart.md +57 -0
- package/dist/agent/RadioGroup.md +44 -0
- package/dist/agent/SankeyChart.md +61 -0
- package/dist/agent/ScatterChart.md +54 -0
- package/dist/agent/ScrollArea.md +40 -0
- package/dist/agent/SearchableMenu.md +46 -0
- package/dist/agent/Select.md +75 -0
- package/dist/agent/Separator.md +35 -0
- package/dist/agent/Sheet.md +57 -0
- package/dist/agent/Skeleton.md +73 -0
- package/dist/agent/Slider.md +39 -0
- package/dist/agent/Sparkline.md +45 -0
- package/dist/agent/Spinner.md +39 -0
- package/dist/agent/StatusDot.md +36 -0
- package/dist/agent/StatusPill.md +28 -0
- package/dist/agent/Steps.md +44 -0
- package/dist/agent/Switch.md +29 -0
- package/dist/agent/Table.md +85 -0
- package/dist/agent/Tabs.md +48 -0
- package/dist/agent/Tag.md +35 -0
- package/dist/agent/Textarea.md +26 -0
- package/dist/agent/Toast.md +26 -0
- package/dist/agent/ToggleGroup.md +44 -0
- package/dist/agent/Tooltip.md +51 -0
- package/dist/agent/TreemapChart.md +52 -0
- package/dist/agent/WaterfallChart.md +62 -0
- package/dist/agent/catalog.json +1791 -0
- package/dist/agent/index.md +131 -0
- package/dist/charts/AreaChart/AreaChart.d.ts +191 -0
- package/dist/charts/AreaChart/AreaChart.js +480 -0
- package/dist/charts/AreaChart/AreaChart.js.map +1 -0
- package/dist/charts/BarChart/BarChart.d.ts +179 -0
- package/dist/charts/BarChart/BarChart.js +591 -0
- package/dist/charts/BarChart/BarChart.js.map +1 -0
- package/dist/charts/BarList/BarList.d.ts +57 -0
- package/dist/charts/BarList/BarList.js +71 -0
- package/dist/charts/BarList/BarList.js.map +1 -0
- package/dist/charts/BigNumber/BigNumber.d.ts +56 -0
- package/dist/charts/BigNumber/BigNumber.js +31 -0
- package/dist/charts/BigNumber/BigNumber.js.map +1 -0
- package/dist/charts/BoxPlot/BoxPlot.d.ts +177 -0
- package/dist/charts/BoxPlot/BoxPlot.js +495 -0
- package/dist/charts/BoxPlot/BoxPlot.js.map +1 -0
- package/dist/charts/BulletChart/BulletChart.d.ts +100 -0
- package/dist/charts/BulletChart/BulletChart.js +162 -0
- package/dist/charts/BulletChart/BulletChart.js.map +1 -0
- package/dist/charts/ComposedChart/ComposedChart.d.ts +171 -0
- package/dist/charts/ComposedChart/ComposedChart.js +667 -0
- package/dist/charts/ComposedChart/ComposedChart.js.map +1 -0
- package/dist/charts/Facet/Facet.d.ts +240 -0
- package/dist/charts/Facet/Facet.js +133 -0
- package/dist/charts/Facet/Facet.js.map +1 -0
- package/dist/charts/Facet/panels.d.ts +187 -0
- package/dist/charts/Facet/panels.js +203 -0
- package/dist/charts/Facet/panels.js.map +1 -0
- package/dist/charts/FunnelChart/FunnelChart.d.ts +101 -0
- package/dist/charts/FunnelChart/FunnelChart.js +147 -0
- package/dist/charts/FunnelChart/FunnelChart.js.map +1 -0
- package/dist/charts/Heatmap/Heatmap.d.ts +78 -0
- package/dist/charts/Heatmap/Heatmap.js +108 -0
- package/dist/charts/Heatmap/Heatmap.js.map +1 -0
- package/dist/charts/Histogram/Histogram.d.ts +165 -0
- package/dist/charts/Histogram/Histogram.js +350 -0
- package/dist/charts/Histogram/Histogram.js.map +1 -0
- package/dist/charts/LineChart/LineChart.d.ts +167 -0
- package/dist/charts/LineChart/LineChart.js +486 -0
- package/dist/charts/LineChart/LineChart.js.map +1 -0
- package/dist/charts/PieChart/PieChart.d.ts +128 -0
- package/dist/charts/PieChart/PieChart.js +309 -0
- package/dist/charts/PieChart/PieChart.js.map +1 -0
- package/dist/charts/RadarChart/RadarChart.d.ts +123 -0
- package/dist/charts/RadarChart/RadarChart.js +299 -0
- package/dist/charts/RadarChart/RadarChart.js.map +1 -0
- package/dist/charts/RadialChart/RadialChart.d.ts +124 -0
- package/dist/charts/RadialChart/RadialChart.js +257 -0
- package/dist/charts/RadialChart/RadialChart.js.map +1 -0
- package/dist/charts/SankeyChart/SankeyChart.d.ts +137 -0
- package/dist/charts/SankeyChart/SankeyChart.js +501 -0
- package/dist/charts/SankeyChart/SankeyChart.js.map +1 -0
- package/dist/charts/ScatterChart/ScatterChart.d.ts +128 -0
- package/dist/charts/ScatterChart/ScatterChart.js +269 -0
- package/dist/charts/ScatterChart/ScatterChart.js.map +1 -0
- package/dist/charts/Sparkline/Sparkline.d.ts +53 -0
- package/dist/charts/Sparkline/Sparkline.js +93 -0
- package/dist/charts/Sparkline/Sparkline.js.map +1 -0
- package/dist/charts/TreemapChart/TreemapChart.d.ts +93 -0
- package/dist/charts/TreemapChart/TreemapChart.js +118 -0
- package/dist/charts/TreemapChart/TreemapChart.js.map +1 -0
- package/dist/charts/WaterfallChart/WaterfallChart.d.ts +164 -0
- package/dist/charts/WaterfallChart/WaterfallChart.js +319 -0
- package/dist/charts/WaterfallChart/WaterfallChart.js.map +1 -0
- package/dist/charts/index.d.ts +41 -0
- package/dist/charts/index.js +128 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/lib/annotations.d.ts +103 -0
- package/dist/charts/lib/annotations.js +99 -0
- package/dist/charts/lib/annotations.js.map +1 -0
- package/dist/charts/lib/axis.d.ts +18 -0
- package/dist/charts/lib/axis.js +21 -0
- package/dist/charts/lib/axis.js.map +1 -0
- package/dist/charts/lib/background.d.ts +33 -0
- package/dist/charts/lib/background.js +173 -0
- package/dist/charts/lib/background.js.map +1 -0
- package/dist/charts/lib/brush.d.ts +94 -0
- package/dist/charts/lib/brush.js +535 -0
- package/dist/charts/lib/brush.js.map +1 -0
- package/dist/charts/lib/chart.d.ts +94 -0
- package/dist/charts/lib/chart.js +134 -0
- package/dist/charts/lib/chart.js.map +1 -0
- package/dist/charts/lib/dot.d.ts +29 -0
- package/dist/charts/lib/dot.js +78 -0
- package/dist/charts/lib/dot.js.map +1 -0
- package/dist/charts/lib/empty.d.ts +31 -0
- package/dist/charts/lib/empty.js +13 -0
- package/dist/charts/lib/empty.js.map +1 -0
- package/dist/charts/lib/export.d.ts +103 -0
- package/dist/charts/lib/export.js +204 -0
- package/dist/charts/lib/export.js.map +1 -0
- package/dist/charts/lib/figure.d.ts +61 -0
- package/dist/charts/lib/figure.js +59 -0
- package/dist/charts/lib/figure.js.map +1 -0
- package/dist/charts/lib/format.d.ts +46 -0
- package/dist/charts/lib/format.js +56 -0
- package/dist/charts/lib/format.js.map +1 -0
- package/dist/charts/lib/legend.d.ts +45 -0
- package/dist/charts/lib/legend.js +117 -0
- package/dist/charts/lib/legend.js.map +1 -0
- package/dist/charts/lib/loading.d.ts +48 -0
- package/dist/charts/lib/loading.js +105 -0
- package/dist/charts/lib/loading.js.map +1 -0
- package/dist/charts/lib/paint.d.ts +154 -0
- package/dist/charts/lib/paint.js +551 -0
- package/dist/charts/lib/paint.js.map +1 -0
- package/dist/charts/lib/sonify-control.d.ts +163 -0
- package/dist/charts/lib/sonify-control.js +172 -0
- package/dist/charts/lib/sonify-control.js.map +1 -0
- package/dist/charts/lib/sonify.d.ts +248 -0
- package/dist/charts/lib/sonify.js +262 -0
- package/dist/charts/lib/sonify.js.map +1 -0
- package/dist/charts/lib/toolbar.d.ts +143 -0
- package/dist/charts/lib/toolbar.js +156 -0
- package/dist/charts/lib/toolbar.js.map +1 -0
- package/dist/charts/lib/tooltip.d.ts +59 -0
- package/dist/charts/lib/tooltip.js +135 -0
- package/dist/charts/lib/tooltip.js.map +1 -0
- package/dist/charts/lib/values.d.ts +56 -0
- package/dist/charts/lib/values.js +71 -0
- package/dist/charts/lib/values.js.map +1 -0
- package/dist/charts/lib/zoom.d.ts +139 -0
- package/dist/charts/lib/zoom.js +314 -0
- package/dist/charts/lib/zoom.js.map +1 -0
- package/dist/semantic.css +19 -0
- package/dist/styles.css +1 -1
- package/dist/themes.css +50 -0
- package/dist/tokens.css +118 -0
- package/dist/tokens.d.ts +17 -0
- package/dist/tokens.js +101 -0
- package/dist/tokens.json +101 -0
- package/package.json +26 -4
- package/skills/misoto22-design/SKILL.md +171 -0
- package/skills/misoto22-design/evals/claims.json +95 -0
- package/skills/misoto22-design/rules/a11y.md +115 -0
- package/skills/misoto22-design/rules/composition.md +108 -0
- package/skills/misoto22-design/rules/forms.md +128 -0
- package/skills/misoto22-design/rules/naming.md +155 -0
- package/skills/misoto22-design/rules/tokens.md +131 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/charts/ComposedChart/ComposedChart.tsx"],"sourcesContent":["'use client'\n\nimport {\n Children,\n createContext,\n isValidElement,\n useCallback,\n useContext,\n useId,\n useMemo,\n useState,\n type ComponentProps,\n type FC,\n type ReactElement,\n type ReactNode,\n} from 'react'\nimport {\n Bar as RechartsBar,\n CartesianGrid,\n ComposedChart as RechartsComposedChart,\n Line as RechartsLine,\n XAxis as RechartsXAxis,\n YAxis as RechartsYAxis,\n} from 'recharts'\nimport { motion, useReducedMotion } from 'motion/react'\nimport { ChartContainer, type ChartConfig } from '../lib/chart'\nimport { ChartFigure } from '../lib/figure'\nimport { ChartEmpty, type ChartEmptyProps } from '../lib/empty'\nimport { axisLabel } from '../lib/axis'\nimport { defaultTick } from '../lib/format'\nimport { Annotation, ReferenceBand, ReferenceLine } from '../lib/annotations'\nimport { Values, findSlot, resolveValues, type ValuesProps } from '../lib/values'\n\n/**\n * The axis props, with `label` narrowed to a string.\n *\n * Recharts' own `label` accepts a render function and a config object, neither\n * of which a call site should be reaching for to name an axis — and both of\n * which would let a consumer opt out of the system's own axis type styling\n * without noticing.\n */\nexport type ChartXAxisProps = Omit<ComponentProps<typeof RechartsXAxis>, 'label'> & {\n /** What this axis measures. Say the unit here or in the series label, once. */\n label?: string\n}\n\nexport type ChartYAxisProps = Omit<ComponentProps<typeof RechartsYAxis>, 'label'> & {\n /** What this axis measures. Say the unit here or in the series label, once. */\n label?: string\n}\n\nimport { ChartBackground, type ChartBackgroundVariant } from '../lib/background'\nimport { ChartDot, type ChartDotVariant } from '../lib/dot'\nimport { ChartLegend, ChartLegendContent } from '../lib/legend'\nimport { ChartTooltip, ChartTooltipContent } from '../lib/tooltip'\nimport { Brush, ChartBrush, type BrushProps, type ChartBrushRange } from '../lib/brush'\nimport { ChartControls, Toolbar, type ToolbarProps } from '../lib/toolbar'\nimport { useChartZoom } from '../lib/zoom'\nimport { LoadingIndicator, LoadingShimmer, LOADING_KEY, useLoadingRows } from '../lib/loading'\nimport {\n AnimatedDashedStroke,\n BarDuotoneFill,\n BarGradientFill,\n BarHatchedFill,\n BarStrippedFill,\n GlowFilter,\n REVEAL_EASE,\n RevealMask,\n SeriesGradient,\n type ChartRevealType,\n} from '../lib/paint'\nimport type { BarVariant } from '../BarChart/BarChart'\nimport type {\n AreaStrokeVariant,\n ChartCurveType,\n ChartLegendSlotProps,\n ChartTooltipSlotProps,\n} from '../AreaChart/AreaChart'\n\nconst STROKE_WIDTH = 2\nconst BAR_RADIUS = 4\nconst GROW_DURATION = 0.5\nconst GROW_STAGGER = 0.05\n\n/**\n * Room around the plot, so the first and last tick labels are not sliced by the\n * container's edge. Recharts' own default is 5px on every side, which is not\n * enough for a label centred on the axis's first point.\n */\nconst CHART_MARGIN = { top: 8, right: 12, bottom: 0, left: 12 }\n\ninterface ComposedChartContextValue {\n config: ChartConfig\n /** The rows on screen, so a mark can label its own extremes. */\n rows: Record<string, unknown>[]\n curveType: ChartCurveType\n animationType: ChartRevealType\n introStartedAt: number\n dataLength: number\n isLoading: boolean\n hoveredIndex: number | null\n selectedDataKey: string | null\n selectDataKey: (dataKey: string | null) => void\n}\n\nconst ComposedChartContext = createContext<ComposedChartContextValue | null>(null)\n\nfunction useComposedChart(): ComposedChartContextValue {\n const context = useContext(ComposedChartContext)\n if (!context) throw new Error('A composed chart part must be rendered inside <ComposedChart>')\n return context\n}\n\ntype ValidateKeys<TData, TConfig> = {\n [K in keyof TConfig]: K extends keyof TData ? ChartConfig[string] : never\n}\n\nexport interface ComposedChartProps<\n TData extends Record<string, unknown>,\n TConfig extends Record<string, ChartConfig[string]>,\n> {\n config: TConfig & ValidateKeys<TData, TConfig>\n /** The rows the chart draws. One entry per point, bar or category. */\n data: TData[]\n /**\n * What the chart shows, in a sentence a reader could act on. Required, and\n * announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the plot instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /**\n * The composed parts — axes, grid, tooltip, legend, and the marks\n * themselves.\n */\n children: ReactNode\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n /** Escape hatch onto the raw Recharts chart element. */\n chartProps?: ComponentProps<typeof RechartsComposedChart>\n /**\n * How the line between two points is interpolated. Every mark inherits it\n * unless it says otherwise.\n */\n curveType?: ChartCurveType\n animationType?: ChartRevealType\n barGap?: number\n barCategoryGap?: number\n /** The series lit on first render. Selection dims every other series. */\n defaultSelectedDataKey?: string | null\n /** Fires when the selection changes, and with null when it is cleared. */\n onSelectionChange?: (selectedDataKey: string | null) => void\n /**\n * Swaps the marks for an animated skeleton, keeping the measured height so\n * the page does not jump when the data lands.\n */\n isLoading?: boolean\n /** How many bars the skeleton draws. */\n loadingBars?: number\n /**\n * The row field on the category axis. Needed by the brush and by the table\n * view.\n */\n xDataKey?: keyof TData & string\n /**\n * Drops the hidden table view. Only correct when the page prints the data\n * itself.\n */\n hideDataTable?: boolean\n /**\n * What the chart shows when it has nothing to draw.\n *\n * Rendered instead of the plot whenever `data` is empty and the chart is not\n * loading — the state a real dashboard reaches within a week, and the one an\n * empty pair of axes is indistinguishable from a failed load. `false` keeps\n * the axes, for a chart whose emptiness is itself the reading.\n */\n empty?: ChartEmptyProps | false\n}\n\n/**\n * Bars and lines over one axis — the shape for \"the volume, and the rate it\n * moved at\".\n *\n * One axis, always. Two measures at different scales belong in two charts or\n * indexed to a common base; a second y-scale lets the author choose where the\n * lines cross, which is the single most misleading thing a chart can do.\n *\n * @example\n * <ComposedChart title=\"Revenue and profit\" config={config} data={data}>\n * <ComposedChart.Grid />\n * <ComposedChart.XAxis dataKey=\"month\" />\n * <ComposedChart.Tooltip />\n * <ComposedChart.Bar dataKey=\"revenue\" variant=\"duotone\" />\n * <ComposedChart.Line dataKey=\"profit\" />\n * </ComposedChart>\n */\nexport function ComposedChart<\n TData extends Record<string, unknown>,\n TConfig extends Record<string, ChartConfig[string]>,\n>({\n config,\n data,\n title,\n showTitle,\n description,\n children,\n className,\n chartProps,\n curveType = 'linear',\n animationType = 'forward',\n barGap,\n barCategoryGap,\n defaultSelectedDataKey = null,\n onSelectionChange,\n isLoading = false,\n loadingBars,\n xDataKey,\n hideDataTable = false,\n empty,\n}: ComposedChartProps<TData, TConfig>) {\n const chartId = useId().replace(/:/g, '')\n const [introStartedAt] = useState(() => Date.now())\n const [selectedDataKey, setSelectedDataKey] = useState<string | null>(defaultSelectedDataKey)\n const [hoveredIndex, setHoveredIndex] = useState<number | null>(null)\n const { rows: loadingData, onShimmerExit } = useLoadingRows(isLoading, loadingBars ?? 12)\n // One window, two views of it: the brush strip below the plot and the\n // toolbar above it both read and write this. See `useChartZoom` for why they\n // are not two separate windows.\n const zoom = useChartZoom({ data, xDataKey })\n\n // The brush and the toolbar are declared as children but never rendered into\n // the Recharts tree — one is a footer under the plot and the other a row\n // above it, and both live outside the SVG.\n const { brushSlot, toolbarSlot, chartChildren } = useMemo(() => {\n const parts = Children.toArray(children)\n const element = parts.find((child) => isValidElement(child) && child.type === Brush)\n const props = (isValidElement(element) ? element.props : {}) as BrushProps\n return {\n brushSlot: { present: isValidElement(element), ...props },\n toolbarSlot: findSlot<ToolbarProps>(parts, Toolbar),\n chartChildren: parts.filter(\n (child) => !(isValidElement(child) && (child.type === Brush || child.type === Toolbar)),\n ),\n }\n }, [children])\n\n const columns = useMemo(\n () => Object.entries(config).map(([key, series]) => ({ key, label: series.label })),\n [config],\n )\n\n const showBrush = brushSlot.present && !isLoading\n const showToolbar = toolbarSlot !== null && !isLoading\n // `empty === false` keeps the axes, for a chart whose emptiness is the\n // reading — a monitoring panel that should show a flat, silent range rather\n // than a message.\n const isEmpty = !isLoading && empty !== false && data.length === 0\n const displayData = showBrush || showToolbar ? zoom.visibleData : data\n\n const selectDataKey = useCallback(\n (next: string | null) => {\n setSelectedDataKey(next)\n onSelectionChange?.(next)\n },\n [onSelectionChange],\n )\n\n const context = useMemo<ComposedChartContextValue>(\n () => ({\n config,\n rows: displayData as Record<string, unknown>[],\n curveType,\n animationType,\n introStartedAt,\n dataLength: displayData.length,\n isLoading,\n hoveredIndex,\n selectedDataKey,\n selectDataKey,\n }),\n [\n animationType,\n config,\n curveType,\n displayData,\n hoveredIndex,\n introStartedAt,\n isLoading,\n selectDataKey,\n selectedDataKey,\n ],\n )\n\n return (\n <ComposedChartContext.Provider value={context}>\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : { rows: data, rowKey: xDataKey, columns }\n }\n >\n {isEmpty ? (\n <ChartEmpty {...(empty || {})} />\n ) : (\n <ChartControls\n toolbar={showToolbar ? toolbarSlot : null}\n zoom={zoom}\n title={title}\n rows={data}\n columns={columns}\n rowKey={xDataKey}\n >\n <ChartContainer\n config={config}\n footer={\n showBrush && (\n <ChartBrush\n data={data}\n config={config}\n xDataKey={xDataKey}\n variant=\"area\"\n curveType={curveType}\n height={brushSlot.height}\n formatLabel={brushSlot.formatLabel}\n className=\"mt-1\"\n {...zoom.brushProps}\n onChange={(range: ChartBrushRange) => {\n zoom.brushProps.onChange(range)\n brushSlot.onChange?.(range)\n }}\n />\n )\n }\n >\n <RechartsComposedChart\n id={chartId}\n accessibilityLayer\n margin={CHART_MARGIN}\n data={isLoading ? loadingData : displayData}\n barGap={barGap}\n barCategoryGap={barCategoryGap}\n // The hovered column, read from Recharts' own tooltip cursor. This\n // was the missing half of `enableHoverHighlight`: the state existed\n // and was cleared on leave, but nothing ever set it, so the\n // highlight never fired.\n onMouseMove={(state) => {\n const index = (state as { activeTooltipIndex?: number } | undefined)\n ?.activeTooltipIndex\n setHoveredIndex(typeof index === 'number' ? index : null)\n }}\n onMouseLeave={() => setHoveredIndex(null)}\n {...chartProps}\n >\n {chartChildren}\n {isLoading && <LoadingBar chartId={chartId} onShimmerExit={onShimmerExit} />}\n </RechartsComposedChart>\n </ChartContainer>\n </ChartControls>\n )}\n <LoadingIndicator isLoading={isLoading} />\n </ChartFigure>\n </ComposedChartContext.Provider>\n )\n}\n\nexport interface ComposedBarProps {\n dataKey: string\n variant?: BarVariant\n radius?: number\n /** A halo behind the bars. */\n glowing?: boolean\n animationType?: ChartRevealType\n isClickable?: boolean\n /** Dims this series while another COLUMN is hovered. */\n enableHoverHighlight?: boolean\n /** `<ComposedChart.Values>`. */\n children?: ReactNode\n barProps?: ComponentProps<typeof RechartsBar>\n}\n\n/** One bar series inside a composed chart. */\nfunction Bar({\n dataKey,\n variant = 'default',\n radius = BAR_RADIUS,\n glowing = false,\n animationType,\n isClickable = false,\n enableHoverHighlight = false,\n children,\n barProps,\n}: ComposedBarProps) {\n const {\n config,\n animationType: defaultAnimation,\n introStartedAt,\n dataLength,\n isLoading,\n rows,\n hoveredIndex,\n selectedDataKey,\n selectDataKey,\n } = useComposedChart()\n const id = useId().replace(/:/g, '')\n const reduceMotion = useReducedMotion()\n\n if (isLoading) return null\n\n const isSelected = selectedDataKey === null || selectedDataKey === dataKey\n const reveal: ChartRevealType = reduceMotion ? 'none' : (animationType ?? defaultAnimation)\n\n return (\n <>\n <RechartsBar\n dataKey={dataKey}\n fill={`url(#${id}-colors-${dataKey})`}\n radius={radius}\n isAnimationActive={false}\n style={isClickable || enableHoverHighlight ? { cursor: 'pointer' } : undefined}\n shape={(props: unknown) => {\n const geometry = props as BarGeometry\n const index = typeof geometry.index === 'number' ? geometry.index : -1\n\n return (\n <ComposedBarShape\n {...geometry}\n id={id}\n dataKey={dataKey}\n variant={variant}\n barRadius={radius}\n filter={glowing ? `url(#${id}-glow-${dataKey})` : undefined}\n fillOpacity={barOpacity({\n isClickable,\n isSelected,\n selectedDataKey,\n enableHoverHighlight,\n hoveredIndex,\n index,\n })}\n isClickable={isClickable}\n enableHoverHighlight={enableHoverHighlight}\n animationType={reveal}\n dataLength={dataLength}\n introStartedAt={introStartedAt}\n onSelect={() => {\n if (!isClickable) return\n selectDataKey(selectedDataKey === dataKey ? null : dataKey)\n }}\n />\n )\n }}\n {...barProps}\n >\n {resolveValues(\n findSlot<ValuesProps>(children, Values),\n rows.map((row) => Number(row[dataKey])).filter(Number.isFinite),\n )}\n </RechartsBar>\n <defs>\n <SeriesGradient id={id} dataKey={dataKey} config={config} direction=\"vertical\" />\n {variant === 'hatched' && <BarHatchedFill id={id} dataKey={dataKey} />}\n {variant === 'duotone' && <BarDuotoneFill id={id} dataKey={dataKey} config={config} />}\n {variant === 'duotone-reverse' && (\n <BarDuotoneFill id={id} dataKey={dataKey} config={config} reverse />\n )}\n {variant === 'gradient' && <BarGradientFill id={id} dataKey={dataKey} />}\n {variant === 'stripped' && <BarStrippedFill id={id} dataKey={dataKey} />}\n {glowing && <GlowFilter id={id} dataKey={dataKey} />}\n </defs>\n </>\n )\n}\n\ninterface BarGeometry {\n x?: number\n y?: number\n width?: number\n height?: number\n index?: number\n background?: { x?: number; y?: number; width?: number; height?: number }\n [key: string]: unknown\n}\n\n/** One drawn bar, with an invisible full-column rect keeping it hoverable. */\nfunction ComposedBarShape({\n x = 0,\n y = 0,\n width = 0,\n height = 0,\n background,\n index = -1,\n id,\n dataKey,\n variant,\n barRadius,\n filter,\n fillOpacity,\n isClickable,\n enableHoverHighlight,\n animationType,\n dataLength,\n introStartedAt,\n onSelect,\n}: BarGeometry & {\n id: string\n dataKey: string\n variant: BarVariant\n barRadius: number\n filter?: string\n fillOpacity: number\n isClickable: boolean\n enableHoverHighlight: boolean\n animationType: ChartRevealType\n dataLength: number\n introStartedAt: number\n onSelect: () => void\n}) {\n const cursor = isClickable || enableHoverHighlight ? { cursor: 'pointer' } : undefined\n const grow = growAnimation(animationType, index, dataLength, introStartedAt) ?? {}\n const fill =\n variant === 'default'\n ? `url(#${id}-colors-${dataKey})`\n : `url(#${id}-${variant}-${dataKey})`\n\n const hitArea = (\n <rect\n x={background?.x ?? x}\n y={background?.y ?? y}\n width={background?.width ?? width}\n height={background?.height ?? height}\n fill=\"transparent\"\n />\n )\n\n if (variant === 'stripped') {\n return (\n <g style={cursor} onClick={onSelect}>\n <motion.g\n {...grow}\n filter={filter}\n opacity={fillOpacity}\n className=\"transition-opacity duration-(--duration-base)\"\n >\n <rect x={x} y={y} width={width} height={height} fill={fill} />\n <rect x={x} y={y} width={width} height={2} fill={`url(#${id}-colors-${dataKey})`} />\n </motion.g>\n {hitArea}\n </g>\n )\n }\n\n return (\n <g style={cursor} onClick={onSelect}>\n <motion.g {...grow}>\n <rect\n x={x}\n y={y}\n width={width}\n height={height}\n rx={barRadius}\n ry={barRadius}\n fill={fill}\n opacity={fillOpacity}\n filter={filter}\n className=\"transition-opacity duration-(--duration-base)\"\n />\n </motion.g>\n {hitArea}\n </g>\n )\n}\n\n/** The staggered grow-in, anchored to the chart rather than to the bar's mount. */\nfunction growAnimation(\n animationType: ChartRevealType,\n index: number,\n dataLength: number,\n introStartedAt: number,\n) {\n if (animationType === 'none' || index < 0 || dataLength <= 0) return null\n\n const lastIndex = dataLength - 1\n const centre = lastIndex / 2\n\n let step: number\n switch (animationType) {\n case 'reverse':\n step = lastIndex - index\n break\n case 'center-out':\n step = Math.abs(index - centre)\n break\n case 'edges-in':\n step = centre - Math.abs(index - centre)\n break\n default:\n step = index\n }\n\n const startMs = step * GROW_STAGGER * 1000\n const endMs = startMs + GROW_DURATION * 1000\n const elapsed = Date.now() - introStartedAt\n if (elapsed >= endMs) return null\n\n const from = elapsed <= startMs ? 0 : (elapsed - startMs) / (GROW_DURATION * 1000)\n\n return {\n initial: { scaleY: from },\n animate: { scaleY: 1 },\n transition: {\n duration: (endMs - Math.max(elapsed, startMs)) / 1000,\n ease: REVEAL_EASE,\n delay: Math.max(0, startMs - elapsed) / 1000,\n },\n style: { originY: 1 },\n }\n}\n\n/** How lit a bar is, given the selection and the hovered column. */\nfunction barOpacity({\n isClickable,\n isSelected,\n selectedDataKey,\n enableHoverHighlight,\n hoveredIndex,\n index,\n}: {\n isClickable: boolean\n isSelected: boolean\n selectedDataKey: string | null\n enableHoverHighlight: boolean\n hoveredIndex: number | null\n index: number\n}) {\n const selectionOpacity = isClickable && selectedDataKey !== null ? (isSelected ? 1 : 0.15) : 1\n if (enableHoverHighlight && hoveredIndex !== null) {\n return hoveredIndex === index ? selectionOpacity : selectionOpacity * 0.3\n }\n return selectionOpacity\n}\n\nexport interface ComposedLineProps {\n dataKey: string\n strokeVariant?: AreaStrokeVariant\n curveType?: ChartCurveType\n animationType?: ChartRevealType\n connectNulls?: boolean\n glowing?: boolean\n isClickable?: boolean\n children?: ReactNode\n lineProps?: ComponentProps<typeof RechartsLine>\n}\n\n/** One line series inside a composed chart. */\nfunction Line({\n dataKey,\n strokeVariant = 'solid',\n curveType,\n animationType,\n connectNulls = false,\n glowing = false,\n isClickable = false,\n children,\n lineProps,\n}: ComposedLineProps) {\n const {\n config,\n curveType: defaultCurve,\n animationType: defaultAnimation,\n isLoading,\n rows,\n selectedDataKey,\n selectDataKey,\n } = useComposedChart()\n const id = useId().replace(/:/g, '')\n const reduceMotion = useReducedMotion()\n\n if (isLoading) return null\n\n const resolvedCurve = curveType ?? defaultCurve\n const reveal: ChartRevealType = reduceMotion ? 'none' : (animationType ?? defaultAnimation)\n const maskId = reveal === 'none' ? undefined : `${id}-reveal-mask`\n\n const hasSelection = selectedDataKey !== null\n const dimmed = hasSelection && selectedDataKey !== dataKey\n const opacity = dimmed ? { stroke: 0.3, dot: 0.3 } : { stroke: 1, dot: 1 }\n\n const { dot, activeDot } = resolveDots(children, id, dataKey, opacity.dot, maskId)\n const labels = resolveValues(\n findSlot<ValuesProps>(children, Values),\n rows.map((row) => Number(row[dataKey])).filter(Number.isFinite),\n )\n const animateDash = strokeVariant === 'animated-dashed' && !hasSelection && !reduceMotion\n const isDashed = strokeVariant !== 'solid'\n\n const select = () => {\n if (!isClickable) return\n selectDataKey(selectedDataKey === dataKey ? null : dataKey)\n }\n\n return (\n <>\n {isClickable && (\n // A 20px transparent line under the visible one, giving a 2px stroke a\n // pointer target a finger can hit.\n <RechartsLine\n type={resolvedCurve}\n dataKey={dataKey}\n connectNulls={connectNulls}\n stroke=\"transparent\"\n strokeWidth={20}\n dot={false}\n activeDot={false}\n isAnimationActive={false}\n legendType=\"none\"\n tooltipType=\"none\"\n style={{ cursor: 'pointer' }}\n onClick={select}\n />\n )}\n <RechartsLine\n type={resolvedCurve}\n dataKey={dataKey}\n connectNulls={connectNulls}\n strokeOpacity={opacity.stroke}\n stroke={`url(#${id}-colors-${dataKey})`}\n filter={glowing ? `url(#${id}-glow-${dataKey})` : undefined}\n dot={dot}\n activeDot={activeDot}\n strokeWidth={STROKE_WIDTH}\n strokeDasharray={isDashed ? '5 5' : undefined}\n isAnimationActive={false}\n style={{\n ...(maskId ? { mask: `url(#${maskId})` } : {}),\n // The hit line underneath owns the pointer; without this the visible\n // line swallows the click it is sitting on top of.\n ...(isClickable ? { cursor: 'pointer', pointerEvents: 'none' } : {}),\n }}\n {...lineProps}\n >\n {animateDash && <AnimatedDashedStroke dash={5} />}\n {labels}\n </RechartsLine>\n <defs>\n {reveal !== 'none' && <RevealMask id={id} type={reveal} />}\n <SeriesGradient id={id} dataKey={dataKey} config={config} />\n {glowing && <GlowFilter id={id} dataKey={dataKey} spread={10} intensity={2} />}\n </defs>\n </>\n )\n}\n\nexport interface DotProps {\n variant?: ChartDotVariant\n}\n\n/** The resting point marker for the line it is composed inside. A slot. */\nconst Dot: FC<DotProps> = () => null\n\n/** The marker under the pointer. */\nconst ActiveDot: FC<DotProps> = () => null\n\nfunction resolveDots(\n children: ReactNode,\n id: string,\n dataKey: string,\n dotOpacity: number,\n maskId: string | undefined,\n) {\n let dot: ComponentProps<typeof RechartsLine>['dot'] = false\n let activeDot: ComponentProps<typeof RechartsLine>['activeDot'] = false\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) return\n const { variant } = (child as ReactElement<DotProps>).props\n\n if (child.type === Dot) {\n dot = (\n <ChartDot\n variant={variant}\n dataKey={dataKey}\n chartId={id}\n fillOpacity={dotOpacity}\n maskId={maskId}\n />\n )\n }\n if (child.type === ActiveDot) {\n activeDot = (\n <ChartDot variant={variant} dataKey={dataKey} chartId={id} fillOpacity={dotOpacity} />\n )\n }\n })\n\n return { dot, activeDot }\n}\n\n/** The category axis. */\nfunction XAxis({\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n minTickGap = 8,\n label,\n ...rest\n}: ChartXAxisProps) {\n const { isLoading } = useComposedChart()\n if (isLoading) return null\n return (\n <RechartsXAxis\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n minTickGap={minTickGap}\n {...rest}\n >\n {axisLabel(label, 'x')}\n </RechartsXAxis>\n )\n}\n\n/** The value axis — the only one, by design. */\nfunction YAxis({\n tickLine = false,\n axisLine = false,\n tickMargin = 8,\n minTickGap = 8,\n width = 'auto',\n tickFormatter = defaultTick,\n label,\n ...rest\n}: ChartYAxisProps) {\n const { isLoading } = useComposedChart()\n if (isLoading) return null\n return (\n <RechartsYAxis\n tickLine={tickLine}\n axisLine={axisLine}\n tickMargin={tickMargin}\n minTickGap={minTickGap}\n width={width}\n tickFormatter={tickFormatter}\n {...rest}\n >\n {axisLabel(label, 'y')}\n </RechartsYAxis>\n )\n}\n\n/** Horizontal dashed rules. */\nfunction Grid({\n vertical = false,\n strokeDasharray = '3 3',\n ...rest\n}: ComponentProps<typeof CartesianGrid>) {\n return <CartesianGrid vertical={vertical} strokeDasharray={strokeDasharray} {...rest} />\n}\n\n/** The hover panel, with a crosshair along the axis. */\nfunction Tooltip({ variant, roundness, defaultIndex, cursor = true }: ChartTooltipSlotProps) {\n const { isLoading, selectedDataKey } = useComposedChart()\n if (isLoading) return null\n\n return (\n <ChartTooltip\n defaultIndex={defaultIndex}\n cursor={cursor ? { strokeDasharray: '3 3', strokeWidth: STROKE_WIDTH } : false}\n content={\n <ChartTooltipContent selected={selectedDataKey} roundness={roundness} variant={variant} />\n }\n />\n )\n}\n\n/** The key. Required above one series. */\nfunction Legend({\n variant,\n align = 'right',\n verticalAlign = 'top',\n isClickable = false,\n}: ChartLegendSlotProps) {\n const { selectedDataKey, selectDataKey } = useComposedChart()\n\n return (\n <ChartLegend\n verticalAlign={verticalAlign}\n align={align}\n content={\n <ChartLegendContent\n selected={selectedDataKey}\n onSelectChange={selectDataKey}\n isClickable={isClickable}\n variant={variant}\n />\n }\n />\n )\n}\n\n/** The decorative plate behind the plot. */\nfunction Background({ variant }: { variant: ChartBackgroundVariant }) {\n const { isLoading } = useComposedChart()\n if (isLoading) return null\n return <ChartBackground variant={variant} />\n}\n\n/** The skeleton drawn in place of the real marks. */\nfunction LoadingBar({ chartId, onShimmerExit }: { chartId: string; onShimmerExit: () => void }) {\n return (\n <>\n <RechartsBar\n dataKey={LOADING_KEY}\n fill=\"currentColor\"\n fillOpacity={0.15}\n radius={BAR_RADIUS}\n isAnimationActive={false}\n legendType=\"none\"\n className=\"text-(--ink)\"\n style={{ mask: `url(#${chartId}-loading-mask)` }}\n />\n <defs>\n <LoadingShimmer id={chartId} onShimmerExit={onShimmerExit} />\n </defs>\n </>\n )\n}\n\nComposedChart.Bar = Bar\nComposedChart.Line = Line\nComposedChart.Dot = Dot\nComposedChart.ActiveDot = ActiveDot\nComposedChart.XAxis = XAxis\nComposedChart.YAxis = YAxis\nComposedChart.Grid = Grid\nComposedChart.Tooltip = Tooltip\nComposedChart.Legend = Legend\nComposedChart.Background = Background\nComposedChart.ReferenceLine = ReferenceLine\nComposedChart.ReferenceBand = ReferenceBand\nComposedChart.Annotation = Annotation\nComposedChart.Values = Values\nComposedChart.Brush = Brush\nComposedChart.Toolbar = Toolbar\n\nexport default ComposedChart\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ChartEmptyProps } from '../lib/empty.js';
|
|
4
|
+
import { FacetScales, FacetSort, FacetOverflow, FacetPanel } from './panels.js';
|
|
5
|
+
|
|
6
|
+
/** What the overflow note is told, so a call site can write its own. */
|
|
7
|
+
interface FacetOverflowInfo {
|
|
8
|
+
/** How many panels are drawn, including the fold. */
|
|
9
|
+
shown: number;
|
|
10
|
+
/** How many groups the data held. */
|
|
11
|
+
total: number;
|
|
12
|
+
/** How many groups the cap left out. */
|
|
13
|
+
hidden: number;
|
|
14
|
+
/** What the folded panel is called. */
|
|
15
|
+
otherLabel: string;
|
|
16
|
+
overflow: FacetOverflow;
|
|
17
|
+
}
|
|
18
|
+
interface FacetProps<TRow extends Record<string, unknown>> {
|
|
19
|
+
/**
|
|
20
|
+
* What the whole grid shows, in a sentence a reader could act on. Required,
|
|
21
|
+
* and announced to a screen reader even when it is not printed.
|
|
22
|
+
*/
|
|
23
|
+
title: string;
|
|
24
|
+
/** Prints the title above the grid instead of hiding it from sight. */
|
|
25
|
+
showTitle?: boolean;
|
|
26
|
+
/** A line under the title — the unit, the window, the caveat. */
|
|
27
|
+
description?: ReactNode;
|
|
28
|
+
/** Every row, across every group. The split happens here, not at the call site. */
|
|
29
|
+
data: TRow[];
|
|
30
|
+
/** The row field the data is split on. One panel per distinct value. */
|
|
31
|
+
by: keyof TRow & string;
|
|
32
|
+
/**
|
|
33
|
+
* The row field, or fields, holding the numbers.
|
|
34
|
+
*
|
|
35
|
+
* This is what the shared domain is measured over and what a statistical sort
|
|
36
|
+
* reads, so it has to name every series a panel draws — a panel that plots a
|
|
37
|
+
* field this does not mention can still overflow its own axis.
|
|
38
|
+
*/
|
|
39
|
+
value: (keyof TRow & string) | (keyof TRow & string)[];
|
|
40
|
+
/**
|
|
41
|
+
* The shared category field — the x axis every panel has in common.
|
|
42
|
+
*
|
|
43
|
+
* Feeds the hidden table view, and is what lets `overflow="fold"` add the
|
|
44
|
+
* tail up at each category instead of concatenating it.
|
|
45
|
+
*/
|
|
46
|
+
xDataKey?: keyof TRow & string;
|
|
47
|
+
/**
|
|
48
|
+
* Whether every panel is drawn against one domain or against its own.
|
|
49
|
+
*
|
|
50
|
+
* `"shared"` — the default, and the entire reason this component exists. On
|
|
51
|
+
* independent scales every panel fills its own plot, so a group peaking at
|
|
52
|
+
* 40 and a group peaking at 4,000 come out the same shape, and the
|
|
53
|
+
* comparison the reader opened the grid to make is not merely hard but
|
|
54
|
+
* actively wrong. The shared domain is computed across all the panels that
|
|
55
|
+
* survive the cap and handed to each one as `panel.domain`; a panel that does
|
|
56
|
+
* not pass it to its value axis has opted back into the broken version.
|
|
57
|
+
*
|
|
58
|
+
* `"independent"` is the escape hatch for the case where the panels are not
|
|
59
|
+
* comparable — different units, different orders of magnitude that carry no
|
|
60
|
+
* relation — and where each panel's own shape is the only reading. It is
|
|
61
|
+
* never the safe answer, which is why it is not the default.
|
|
62
|
+
*/
|
|
63
|
+
scales?: FacetScales;
|
|
64
|
+
/** Pins the domain outright, ignoring the data and `scales` alike. */
|
|
65
|
+
domain?: [number, number];
|
|
66
|
+
/**
|
|
67
|
+
* Pulls the shared domain out to include zero.
|
|
68
|
+
*
|
|
69
|
+
* On by default, and the same default Recharts applies on its own: bars and
|
|
70
|
+
* areas read against a truncated baseline overstate every difference in the
|
|
71
|
+
* grid, and a grid exists to be compared. Turn it off when the values live
|
|
72
|
+
* far from zero and the differences between them ARE the reading —
|
|
73
|
+
* temperatures, latencies, ratings out of five.
|
|
74
|
+
*/
|
|
75
|
+
includeZero?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Rounds the domain out so the panels' ticks land on numbers a reader
|
|
78
|
+
* recognises. Off, a shared domain of `[0, 314]` gives ticks at 78.5.
|
|
79
|
+
*/
|
|
80
|
+
nice?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The panel order.
|
|
83
|
+
*
|
|
84
|
+
* Defaults to the largest peak first, because that is what makes a grid
|
|
85
|
+
* readable: eye order becomes rank order, and "which of these is the
|
|
86
|
+
* problem" is answered by position before a single axis is read. `"name"`
|
|
87
|
+
* for a grid a reader will look things up in, an explicit array of names for
|
|
88
|
+
* an order the data does not carry — stages, weekdays, severities — and a
|
|
89
|
+
* comparator over `{ name, rows, stats }` for anything else.
|
|
90
|
+
*/
|
|
91
|
+
sort?: FacetSort<TRow>;
|
|
92
|
+
/**
|
|
93
|
+
* The sort direction. Omit and it follows the sort: A→Z for a name or an
|
|
94
|
+
* explicit list, biggest first for a statistic.
|
|
95
|
+
*/
|
|
96
|
+
order?: 'asc' | 'desc';
|
|
97
|
+
/**
|
|
98
|
+
* How many panels are drawn before the rest are dealt with.
|
|
99
|
+
*
|
|
100
|
+
* Twelve by default rather than unlimited, because the failure this prevents
|
|
101
|
+
* is silent: a `by` field with forty values renders forty plots, each one
|
|
102
|
+
* roughly a postage stamp, and nothing on the page says the grid stopped
|
|
103
|
+
* being readable. `false` lifts the cap when the call site has genuinely
|
|
104
|
+
* decided to draw them all.
|
|
105
|
+
*/
|
|
106
|
+
limit?: number | false;
|
|
107
|
+
/**
|
|
108
|
+
* What happens to the groups past the cap.
|
|
109
|
+
*
|
|
110
|
+
* `"note"` — the default — draws the top N and prints a line under the grid
|
|
111
|
+
* saying how many are missing. `"fold"` adds one more panel holding the tail
|
|
112
|
+
* summed at each category, the same bargain `BarList`'s "Other" row makes:
|
|
113
|
+
* the total stays true, and one panel absorbs the long tail. Either way a
|
|
114
|
+
* capped grid says so; nothing is dropped in silence.
|
|
115
|
+
*/
|
|
116
|
+
overflow?: FacetOverflow;
|
|
117
|
+
/** What the folded panel is called. */
|
|
118
|
+
otherLabel?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Writes the line under a capped grid. The default sentence is English; this
|
|
121
|
+
* is where a translated call site replaces it.
|
|
122
|
+
*/
|
|
123
|
+
overflowNote?: (info: FacetOverflowInfo) => ReactNode;
|
|
124
|
+
/**
|
|
125
|
+
* How many columns the grid holds at its widest.
|
|
126
|
+
*
|
|
127
|
+
* `"auto"` — the default — fits as many panels as `minPanelWidth` allows and
|
|
128
|
+
* reflows on its own, which is the behaviour a card, a sidebar and a
|
|
129
|
+
* full-width page all need from the same call site. A number caps the count
|
|
130
|
+
* without pinning it: the grid still drops to fewer columns when the
|
|
131
|
+
* container is narrow, rather than shrinking twelve plots to nothing.
|
|
132
|
+
*/
|
|
133
|
+
columns?: 'auto' | number;
|
|
134
|
+
/** How narrow a panel may get before the grid drops a column, in pixels. */
|
|
135
|
+
minPanelWidth?: number;
|
|
136
|
+
/**
|
|
137
|
+
* One key for the whole grid, printed above it.
|
|
138
|
+
*
|
|
139
|
+
* A legend inside every panel is the same three swatches repeated twelve
|
|
140
|
+
* times, which is ink spent restating what the reader learned from the first
|
|
141
|
+
* panel. Put `<LineChart.Legend>` in one panel and this is unnecessary; put
|
|
142
|
+
* it here and take it out of the panels.
|
|
143
|
+
*/
|
|
144
|
+
legend?: ReactNode;
|
|
145
|
+
/** The value axis's name, printed once above the grid instead of per panel. */
|
|
146
|
+
yLabel?: ReactNode;
|
|
147
|
+
/** The category axis's name, printed once under the grid. */
|
|
148
|
+
xLabel?: ReactNode;
|
|
149
|
+
/**
|
|
150
|
+
* Prints each panel's group name above its plot.
|
|
151
|
+
*
|
|
152
|
+
* On by default. Turning it off only hides the name — it stays in the
|
|
153
|
+
* accessibility tree, because a grid whose panels a screen reader cannot tell
|
|
154
|
+
* apart is a grid with one figure and twelve anonymous plots in it.
|
|
155
|
+
*/
|
|
156
|
+
showPanelNames?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Drops the hidden table view of every row.
|
|
159
|
+
*
|
|
160
|
+
* Worth setting when the panels' own charts already ship theirs — each chart
|
|
161
|
+
* in the package renders its rows as a table too, so a twelve-panel grid can
|
|
162
|
+
* otherwise put thirteen tables in the accessibility tree.
|
|
163
|
+
*/
|
|
164
|
+
hideDataTable?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* What the grid shows when the data yields no groups at all.
|
|
167
|
+
*
|
|
168
|
+
* No `false` escape hatch, unlike a single chart's: an empty pair of axes is
|
|
169
|
+
* at least a chart, but an empty grid is nothing — there is no reading for
|
|
170
|
+
* the absence to be.
|
|
171
|
+
*/
|
|
172
|
+
empty?: ChartEmptyProps;
|
|
173
|
+
/** Merged onto the figure, last, so a call site can size or space it. */
|
|
174
|
+
className?: string;
|
|
175
|
+
/**
|
|
176
|
+
* One panel, called once per group.
|
|
177
|
+
*
|
|
178
|
+
* Hand `panel.domain` to the panel chart's value axis. That single line is
|
|
179
|
+
* what the component is for; without it the panels are on independent scales
|
|
180
|
+
* and the grid is decorative.
|
|
181
|
+
*/
|
|
182
|
+
children: (panel: FacetPanel<TRow>) => ReactNode;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The same chart repeated once per group, on one shared scale — small
|
|
186
|
+
* multiples.
|
|
187
|
+
*
|
|
188
|
+
* The answer to the question a multi-series chart stops being able to answer at
|
|
189
|
+
* around five series: eight lines in one plot is a hairball, and the reader
|
|
190
|
+
* spends their attention untangling strands instead of reading them. Splitting
|
|
191
|
+
* the same lines across eight small plots costs the direct overlay comparison
|
|
192
|
+
* and buys back every individual shape, which is almost always the better
|
|
193
|
+
* trade — this is Tufte's small multiple, and Observable Plot and Vega-Lite
|
|
194
|
+
* both treat it as a first-class operation for the same reason.
|
|
195
|
+
*
|
|
196
|
+
* Reach for a multi-series `<LineChart>` or `<AreaChart>` instead when the
|
|
197
|
+
* series really do have to be read AGAINST each other — crossovers, gaps,
|
|
198
|
+
* shares of one total — and when there are few enough of them to tell apart.
|
|
199
|
+
* Reach for this when each group has its own shape worth seeing, when the
|
|
200
|
+
* groups are many, or when the reader's question is "which of these is
|
|
201
|
+
* different".
|
|
202
|
+
*
|
|
203
|
+
* What it gets right that a hand-rolled `data.map()` does not: the panels share
|
|
204
|
+
* one domain by default. On independent scales every panel fills its own plot,
|
|
205
|
+
* so a group peaking at 40 and a group peaking at 4,000 come out the same
|
|
206
|
+
* shape — the comparison is not merely lost, it is inverted, and nothing on
|
|
207
|
+
* screen says so. `panel.domain` is that shared scale; the render function has
|
|
208
|
+
* to pass it to the panel's value axis. It also caps the grid, folds or counts
|
|
209
|
+
* the tail, and prints one legend and one axis label instead of twelve.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* <Facet title="Visitors by channel" data={rows} by="channel" value="visitors" xDataKey="month">
|
|
213
|
+
* {(panel) => (
|
|
214
|
+
* <LineChart title={`${panel.name} visitors`} config={config} data={panel.rows}>
|
|
215
|
+
* <LineChart.XAxis dataKey="month" />
|
|
216
|
+
* <LineChart.YAxis domain={panel.domain} />
|
|
217
|
+
* <LineChart.Line dataKey="visitors" />
|
|
218
|
+
* </LineChart>
|
|
219
|
+
* )}
|
|
220
|
+
* </Facet>
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* // Biggest first, six panels, the rest summed into one — and the grid says so.
|
|
224
|
+
* <Facet
|
|
225
|
+
* title="Requests by endpoint"
|
|
226
|
+
* data={rows}
|
|
227
|
+
* by="endpoint"
|
|
228
|
+
* value="requests"
|
|
229
|
+
* xDataKey="hour"
|
|
230
|
+
* sort="sum"
|
|
231
|
+
* limit={6}
|
|
232
|
+
* overflow="fold"
|
|
233
|
+
* columns={3}
|
|
234
|
+
* >
|
|
235
|
+
* {(panel) => <MiniBars rows={panel.rows} domain={panel.domain} />}
|
|
236
|
+
* </Facet>
|
|
237
|
+
*/
|
|
238
|
+
declare function Facet<TRow extends Record<string, unknown>>({ title, showTitle, description, data, by, value, xDataKey, scales, domain, includeZero, nice, sort, order, limit, overflow, otherLabel, overflowNote, columns, minPanelWidth, legend, yLabel, xLabel, showPanelNames, hideDataTable, empty, className, children, }: FacetProps<TRow>): react.JSX.Element;
|
|
239
|
+
|
|
240
|
+
export { Facet, type FacetOverflowInfo, type FacetProps, Facet as default };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../../lib/cn.js";
|
|
3
|
+
import { ChartFigure } from "../lib/figure.js";
|
|
4
|
+
import { ChartEmpty } from "../lib/empty.js";
|
|
5
|
+
import {
|
|
6
|
+
buildPanels
|
|
7
|
+
} from "./panels.js";
|
|
8
|
+
const GAP_REM = 1;
|
|
9
|
+
const MIN_PANEL_PX = 200;
|
|
10
|
+
function Facet({
|
|
11
|
+
title,
|
|
12
|
+
showTitle,
|
|
13
|
+
description,
|
|
14
|
+
data,
|
|
15
|
+
by,
|
|
16
|
+
value,
|
|
17
|
+
xDataKey,
|
|
18
|
+
scales = "shared",
|
|
19
|
+
domain,
|
|
20
|
+
includeZero = true,
|
|
21
|
+
nice = true,
|
|
22
|
+
sort = "max",
|
|
23
|
+
order,
|
|
24
|
+
limit = 12,
|
|
25
|
+
overflow = "note",
|
|
26
|
+
otherLabel = "Other",
|
|
27
|
+
overflowNote = defaultOverflowNote,
|
|
28
|
+
columns = "auto",
|
|
29
|
+
minPanelWidth = MIN_PANEL_PX,
|
|
30
|
+
legend,
|
|
31
|
+
yLabel,
|
|
32
|
+
xLabel,
|
|
33
|
+
showPanelNames = true,
|
|
34
|
+
hideDataTable = false,
|
|
35
|
+
empty,
|
|
36
|
+
className,
|
|
37
|
+
children
|
|
38
|
+
}) {
|
|
39
|
+
const { panels, total, hidden } = buildPanels(data, {
|
|
40
|
+
by,
|
|
41
|
+
value,
|
|
42
|
+
xDataKey,
|
|
43
|
+
scales,
|
|
44
|
+
domain,
|
|
45
|
+
includeZero,
|
|
46
|
+
nice,
|
|
47
|
+
sort,
|
|
48
|
+
order,
|
|
49
|
+
limit,
|
|
50
|
+
overflow,
|
|
51
|
+
otherLabel
|
|
52
|
+
});
|
|
53
|
+
const valueKeys = Array.isArray(value) ? value : [value];
|
|
54
|
+
const note = hidden > 0 ? overflowNote({ shown: panels.length, total, hidden, otherLabel, overflow }) : null;
|
|
55
|
+
return /* @__PURE__ */ jsx(
|
|
56
|
+
ChartFigure,
|
|
57
|
+
{
|
|
58
|
+
title,
|
|
59
|
+
showTitle,
|
|
60
|
+
description,
|
|
61
|
+
className,
|
|
62
|
+
table: hideDataTable ? false : {
|
|
63
|
+
rows: data,
|
|
64
|
+
rowKey: xDataKey,
|
|
65
|
+
columns: [
|
|
66
|
+
{ key: by, label: by },
|
|
67
|
+
...valueKeys.map((key) => ({ key, label: key }))
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
children: panels.length === 0 ? /* @__PURE__ */ jsx(ChartEmpty, { ...empty || {} }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
71
|
+
(legend || yLabel) && /* @__PURE__ */ jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
72
|
+
/* @__PURE__ */ jsx("span", { className: "mono-meta text-(--ink-3-aa)", children: yLabel }),
|
|
73
|
+
legend
|
|
74
|
+
] }),
|
|
75
|
+
/* @__PURE__ */ jsx(
|
|
76
|
+
"ul",
|
|
77
|
+
{
|
|
78
|
+
role: "list",
|
|
79
|
+
className: "m-0 grid list-none gap-4 p-0",
|
|
80
|
+
style: { gridTemplateColumns: track(columns, minPanelWidth) },
|
|
81
|
+
children: panels.map((panel) => (
|
|
82
|
+
// Keyed by name rather than by position, so re-sorting the grid
|
|
83
|
+
// moves the panels instead of remounting them — which would
|
|
84
|
+
// replay every panel's intro animation on every sort change.
|
|
85
|
+
/* @__PURE__ */ jsxs(
|
|
86
|
+
"li",
|
|
87
|
+
{
|
|
88
|
+
className: "flex min-w-0 flex-col gap-1",
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsx(
|
|
91
|
+
"p",
|
|
92
|
+
{
|
|
93
|
+
className: cn(
|
|
94
|
+
showPanelNames ? "truncate eyebrow text-(--ink-3-aa)" : "sr-only"
|
|
95
|
+
),
|
|
96
|
+
children: panel.name
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 flex-col", children: children(panel) })
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
panel.isOther ? `${panel.name} (fold)` : panel.name
|
|
103
|
+
)
|
|
104
|
+
))
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
(note || xLabel) && /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-col gap-1", children: [
|
|
108
|
+
xLabel && /* @__PURE__ */ jsx("span", { className: "mono-meta text-center text-(--ink-3-aa)", children: xLabel }),
|
|
109
|
+
note && /* @__PURE__ */ jsx("span", { className: "mono-meta text-(--ink-3-aa)", children: note })
|
|
110
|
+
] })
|
|
111
|
+
] })
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function track(columns, minPanelWidth) {
|
|
116
|
+
const floor = `min(${minPanelWidth}px, 100%)`;
|
|
117
|
+
if (columns === "auto") return `repeat(auto-fit, minmax(${floor}, 1fr))`;
|
|
118
|
+
const count = Math.max(1, Math.round(columns));
|
|
119
|
+
const share = `calc((100% - ${(count - 1) * GAP_REM}rem) / ${count})`;
|
|
120
|
+
return `repeat(auto-fit, minmax(clamp(${floor}, ${share}, 100%), 1fr))`;
|
|
121
|
+
}
|
|
122
|
+
function defaultOverflowNote({ shown, total, hidden, otherLabel, overflow }) {
|
|
123
|
+
if (overflow === "fold") {
|
|
124
|
+
return `${shown - 1} of ${total} shown; the other ${hidden} are summed into ${otherLabel}.`;
|
|
125
|
+
}
|
|
126
|
+
return `${shown} of ${total} shown; ${hidden} not drawn.`;
|
|
127
|
+
}
|
|
128
|
+
var Facet_default = Facet;
|
|
129
|
+
export {
|
|
130
|
+
Facet,
|
|
131
|
+
Facet_default as default
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=Facet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/charts/Facet/Facet.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { ChartFigure } from '../lib/figure'\nimport { ChartEmpty, type ChartEmptyProps } from '../lib/empty'\nimport {\n buildPanels,\n type FacetOverflow,\n type FacetPanel,\n type FacetScales,\n type FacetSort,\n} from './panels'\n\n/**\n * The gap between panels. Kept in step with `gap-4` on the grid, because the\n * column cap below has to subtract the gaps it cannot see.\n */\nconst GAP_REM = 1\n\n/**\n * How narrow a panel may get before the grid drops a column.\n *\n * Below roughly this width a plot has room for its axis labels or its marks,\n * not both, and a grid of unreadable panels is worse than a scroll.\n */\nconst MIN_PANEL_PX = 200\n\n/** What the overflow note is told, so a call site can write its own. */\nexport interface FacetOverflowInfo {\n /** How many panels are drawn, including the fold. */\n shown: number\n /** How many groups the data held. */\n total: number\n /** How many groups the cap left out. */\n hidden: number\n /** What the folded panel is called. */\n otherLabel: string\n overflow: FacetOverflow\n}\n\nexport interface FacetProps<TRow extends Record<string, unknown>> {\n /**\n * What the whole grid shows, in a sentence a reader could act on. Required,\n * and announced to a screen reader even when it is not printed.\n */\n title: string\n /** Prints the title above the grid instead of hiding it from sight. */\n showTitle?: boolean\n /** A line under the title — the unit, the window, the caveat. */\n description?: ReactNode\n /** Every row, across every group. The split happens here, not at the call site. */\n data: TRow[]\n /** The row field the data is split on. One panel per distinct value. */\n by: keyof TRow & string\n /**\n * The row field, or fields, holding the numbers.\n *\n * This is what the shared domain is measured over and what a statistical sort\n * reads, so it has to name every series a panel draws — a panel that plots a\n * field this does not mention can still overflow its own axis.\n */\n value: (keyof TRow & string) | (keyof TRow & string)[]\n /**\n * The shared category field — the x axis every panel has in common.\n *\n * Feeds the hidden table view, and is what lets `overflow=\"fold\"` add the\n * tail up at each category instead of concatenating it.\n */\n xDataKey?: keyof TRow & string\n /**\n * Whether every panel is drawn against one domain or against its own.\n *\n * `\"shared\"` — the default, and the entire reason this component exists. On\n * independent scales every panel fills its own plot, so a group peaking at\n * 40 and a group peaking at 4,000 come out the same shape, and the\n * comparison the reader opened the grid to make is not merely hard but\n * actively wrong. The shared domain is computed across all the panels that\n * survive the cap and handed to each one as `panel.domain`; a panel that does\n * not pass it to its value axis has opted back into the broken version.\n *\n * `\"independent\"` is the escape hatch for the case where the panels are not\n * comparable — different units, different orders of magnitude that carry no\n * relation — and where each panel's own shape is the only reading. It is\n * never the safe answer, which is why it is not the default.\n */\n scales?: FacetScales\n /** Pins the domain outright, ignoring the data and `scales` alike. */\n domain?: [number, number]\n /**\n * Pulls the shared domain out to include zero.\n *\n * On by default, and the same default Recharts applies on its own: bars and\n * areas read against a truncated baseline overstate every difference in the\n * grid, and a grid exists to be compared. Turn it off when the values live\n * far from zero and the differences between them ARE the reading —\n * temperatures, latencies, ratings out of five.\n */\n includeZero?: boolean\n /**\n * Rounds the domain out so the panels' ticks land on numbers a reader\n * recognises. Off, a shared domain of `[0, 314]` gives ticks at 78.5.\n */\n nice?: boolean\n /**\n * The panel order.\n *\n * Defaults to the largest peak first, because that is what makes a grid\n * readable: eye order becomes rank order, and \"which of these is the\n * problem\" is answered by position before a single axis is read. `\"name\"`\n * for a grid a reader will look things up in, an explicit array of names for\n * an order the data does not carry — stages, weekdays, severities — and a\n * comparator over `{ name, rows, stats }` for anything else.\n */\n sort?: FacetSort<TRow>\n /**\n * The sort direction. Omit and it follows the sort: A→Z for a name or an\n * explicit list, biggest first for a statistic.\n */\n order?: 'asc' | 'desc'\n /**\n * How many panels are drawn before the rest are dealt with.\n *\n * Twelve by default rather than unlimited, because the failure this prevents\n * is silent: a `by` field with forty values renders forty plots, each one\n * roughly a postage stamp, and nothing on the page says the grid stopped\n * being readable. `false` lifts the cap when the call site has genuinely\n * decided to draw them all.\n */\n limit?: number | false\n /**\n * What happens to the groups past the cap.\n *\n * `\"note\"` — the default — draws the top N and prints a line under the grid\n * saying how many are missing. `\"fold\"` adds one more panel holding the tail\n * summed at each category, the same bargain `BarList`'s \"Other\" row makes:\n * the total stays true, and one panel absorbs the long tail. Either way a\n * capped grid says so; nothing is dropped in silence.\n */\n overflow?: FacetOverflow\n /** What the folded panel is called. */\n otherLabel?: string\n /**\n * Writes the line under a capped grid. The default sentence is English; this\n * is where a translated call site replaces it.\n */\n overflowNote?: (info: FacetOverflowInfo) => ReactNode\n /**\n * How many columns the grid holds at its widest.\n *\n * `\"auto\"` — the default — fits as many panels as `minPanelWidth` allows and\n * reflows on its own, which is the behaviour a card, a sidebar and a\n * full-width page all need from the same call site. A number caps the count\n * without pinning it: the grid still drops to fewer columns when the\n * container is narrow, rather than shrinking twelve plots to nothing.\n */\n columns?: 'auto' | number\n /** How narrow a panel may get before the grid drops a column, in pixels. */\n minPanelWidth?: number\n /**\n * One key for the whole grid, printed above it.\n *\n * A legend inside every panel is the same three swatches repeated twelve\n * times, which is ink spent restating what the reader learned from the first\n * panel. Put `<LineChart.Legend>` in one panel and this is unnecessary; put\n * it here and take it out of the panels.\n */\n legend?: ReactNode\n /** The value axis's name, printed once above the grid instead of per panel. */\n yLabel?: ReactNode\n /** The category axis's name, printed once under the grid. */\n xLabel?: ReactNode\n /**\n * Prints each panel's group name above its plot.\n *\n * On by default. Turning it off only hides the name — it stays in the\n * accessibility tree, because a grid whose panels a screen reader cannot tell\n * apart is a grid with one figure and twelve anonymous plots in it.\n */\n showPanelNames?: boolean\n /**\n * Drops the hidden table view of every row.\n *\n * Worth setting when the panels' own charts already ship theirs — each chart\n * in the package renders its rows as a table too, so a twelve-panel grid can\n * otherwise put thirteen tables in the accessibility tree.\n */\n hideDataTable?: boolean\n /**\n * What the grid shows when the data yields no groups at all.\n *\n * No `false` escape hatch, unlike a single chart's: an empty pair of axes is\n * at least a chart, but an empty grid is nothing — there is no reading for\n * the absence to be.\n */\n empty?: ChartEmptyProps\n /** Merged onto the figure, last, so a call site can size or space it. */\n className?: string\n /**\n * One panel, called once per group.\n *\n * Hand `panel.domain` to the panel chart's value axis. That single line is\n * what the component is for; without it the panels are on independent scales\n * and the grid is decorative.\n */\n children: (panel: FacetPanel<TRow>) => ReactNode\n}\n\n/**\n * The same chart repeated once per group, on one shared scale — small\n * multiples.\n *\n * The answer to the question a multi-series chart stops being able to answer at\n * around five series: eight lines in one plot is a hairball, and the reader\n * spends their attention untangling strands instead of reading them. Splitting\n * the same lines across eight small plots costs the direct overlay comparison\n * and buys back every individual shape, which is almost always the better\n * trade — this is Tufte's small multiple, and Observable Plot and Vega-Lite\n * both treat it as a first-class operation for the same reason.\n *\n * Reach for a multi-series `<LineChart>` or `<AreaChart>` instead when the\n * series really do have to be read AGAINST each other — crossovers, gaps,\n * shares of one total — and when there are few enough of them to tell apart.\n * Reach for this when each group has its own shape worth seeing, when the\n * groups are many, or when the reader's question is \"which of these is\n * different\".\n *\n * What it gets right that a hand-rolled `data.map()` does not: the panels share\n * one domain by default. On independent scales every panel fills its own plot,\n * so a group peaking at 40 and a group peaking at 4,000 come out the same\n * shape — the comparison is not merely lost, it is inverted, and nothing on\n * screen says so. `panel.domain` is that shared scale; the render function has\n * to pass it to the panel's value axis. It also caps the grid, folds or counts\n * the tail, and prints one legend and one axis label instead of twelve.\n *\n * @example\n * <Facet title=\"Visitors by channel\" data={rows} by=\"channel\" value=\"visitors\" xDataKey=\"month\">\n * {(panel) => (\n * <LineChart title={`${panel.name} visitors`} config={config} data={panel.rows}>\n * <LineChart.XAxis dataKey=\"month\" />\n * <LineChart.YAxis domain={panel.domain} />\n * <LineChart.Line dataKey=\"visitors\" />\n * </LineChart>\n * )}\n * </Facet>\n *\n * @example\n * // Biggest first, six panels, the rest summed into one — and the grid says so.\n * <Facet\n * title=\"Requests by endpoint\"\n * data={rows}\n * by=\"endpoint\"\n * value=\"requests\"\n * xDataKey=\"hour\"\n * sort=\"sum\"\n * limit={6}\n * overflow=\"fold\"\n * columns={3}\n * >\n * {(panel) => <MiniBars rows={panel.rows} domain={panel.domain} />}\n * </Facet>\n */\nexport function Facet<TRow extends Record<string, unknown>>({\n title,\n showTitle,\n description,\n data,\n by,\n value,\n xDataKey,\n scales = 'shared',\n domain,\n includeZero = true,\n nice = true,\n sort = 'max',\n order,\n limit = 12,\n overflow = 'note',\n otherLabel = 'Other',\n overflowNote = defaultOverflowNote,\n columns = 'auto',\n minPanelWidth = MIN_PANEL_PX,\n legend,\n yLabel,\n xLabel,\n showPanelNames = true,\n hideDataTable = false,\n empty,\n className,\n children,\n}: FacetProps<TRow>) {\n const { panels, total, hidden } = buildPanels(data, {\n by,\n value,\n xDataKey,\n scales,\n domain,\n includeZero,\n nice,\n sort,\n order,\n limit,\n overflow,\n otherLabel,\n })\n\n const valueKeys = Array.isArray(value) ? value : [value]\n const note =\n hidden > 0\n ? overflowNote({ shown: panels.length, total, hidden, otherLabel, overflow })\n : null\n\n return (\n <ChartFigure\n title={title}\n showTitle={showTitle}\n description={description}\n className={className}\n table={\n hideDataTable\n ? false\n : {\n rows: data,\n rowKey: xDataKey,\n columns: [\n { key: by, label: by },\n ...valueKeys.map((key) => ({ key, label: key })),\n ],\n }\n }\n >\n {panels.length === 0 ? (\n <ChartEmpty {...(empty || {})} />\n ) : (\n <>\n {(legend || yLabel) && (\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n {/* Rendered even when empty: it is what holds the legend at the\n inline end when there is no axis label beside it. */}\n <span className=\"mono-meta text-(--ink-3-aa)\">{yLabel}</span>\n {legend}\n </div>\n )}\n <ul\n // `list-none` strips the list role in Safari, and a grid of panels\n // that does not announce \"list, 12 items\" loses the one cue a\n // screen reader user has for how far they have to go.\n role=\"list\"\n className=\"m-0 grid list-none gap-4 p-0\"\n style={{ gridTemplateColumns: track(columns, minPanelWidth) }}\n >\n {panels.map((panel) => (\n // Keyed by name rather than by position, so re-sorting the grid\n // moves the panels instead of remounting them — which would\n // replay every panel's intro animation on every sort change.\n <li\n key={panel.isOther ? `${panel.name} (fold)` : panel.name}\n className=\"flex min-w-0 flex-col gap-1\"\n >\n <p\n className={cn(\n showPanelNames ? 'truncate eyebrow text-(--ink-3-aa)' : 'sr-only',\n )}\n >\n {panel.name}\n </p>\n <div className=\"flex min-w-0 flex-1 flex-col\">{children(panel)}</div>\n </li>\n ))}\n </ul>\n {(note || xLabel) && (\n <div className=\"mt-3 flex flex-col gap-1\">\n {xLabel && <span className=\"mono-meta text-center text-(--ink-3-aa)\">{xLabel}</span>}\n {note && <span className=\"mono-meta text-(--ink-3-aa)\">{note}</span>}\n </div>\n )}\n </>\n )}\n </ChartFigure>\n )\n}\n\n/**\n * The grid's track list.\n *\n * `auto-fit` rather than a fixed count, so the same call site works in a card\n * and across a page. A numeric `columns` is a CAP, not a pin: the track's\n * minimum is the width that would give exactly that many columns, clamped so\n * it never falls below a readable panel — which is what makes the grid drop to\n * two columns in a sidebar instead of rendering twelve slivers.\n *\n * Written as a style rather than a class because the track is computed from\n * two numbers, and Tailwind only generates what it can read verbatim.\n */\nfunction track(columns: 'auto' | number, minPanelWidth: number): string {\n const floor = `min(${minPanelWidth}px, 100%)`\n if (columns === 'auto') return `repeat(auto-fit, minmax(${floor}, 1fr))`\n\n const count = Math.max(1, Math.round(columns))\n const share = `calc((100% - ${(count - 1) * GAP_REM}rem) / ${count})`\n return `repeat(auto-fit, minmax(clamp(${floor}, ${share}, 100%), 1fr))`\n}\n\n/** The default sentence under a capped grid. */\nfunction defaultOverflowNote({ shown, total, hidden, otherLabel, overflow }: FacetOverflowInfo) {\n if (overflow === 'fold') {\n return `${shown - 1} of ${total} shown; the other ${hidden} are summed into ${otherLabel}.`\n }\n return `${shown} of ${total} shown; ${hidden} not drawn.`\n}\n\nexport default Facet\n"],"mappings":"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","names":[]}
|