@acronis-platform/ui-react 2.0.0 → 4.0.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/components/ui/accordion-container/accordion-container.js +144 -0
- package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
- package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
- package/dist/components/ui/area-chart/area-chart.js +141 -139
- package/dist/components/ui/area-chart/area-chart.js.map +1 -1
- package/dist/components/ui/avatar/avatar.js +33 -22
- package/dist/components/ui/avatar/avatar.js.map +1 -1
- package/dist/components/ui/bar-chart/bar-chart.js +295 -293
- package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
- package/dist/components/ui/card/card.js +150 -74
- package/dist/components/ui/card/card.js.map +1 -1
- package/dist/components/ui/card-section/card-section.js +111 -0
- package/dist/components/ui/card-section/card-section.js.map +1 -0
- package/dist/components/ui/category-bar/category-bar.js +109 -93
- package/dist/components/ui/category-bar/category-bar.js.map +1 -1
- package/dist/components/ui/chart/chart-palette.js +113 -0
- package/dist/components/ui/chart/chart-palette.js.map +1 -0
- package/dist/components/ui/chart/chart.js +176 -129
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
- package/dist/components/ui/chart-state/chart-state.js +48 -34
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +87 -0
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
- package/dist/components/ui/composed-chart/composed-chart.js +117 -115
- package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
- package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
- package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
- package/dist/components/ui/data-table/data-table.js +4 -2
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
- package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
- package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
- package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
- package/dist/components/ui/histogram/histogram.js +94 -92
- package/dist/components/ui/histogram/histogram.js.map +1 -1
- package/dist/components/ui/input/input.js.map +1 -1
- package/dist/components/ui/label/label.js +2 -9
- package/dist/components/ui/label/label.js.map +1 -1
- package/dist/components/ui/line-chart/line-chart.js +143 -141
- package/dist/components/ui/line-chart/line-chart.js.map +1 -1
- package/dist/components/ui/metric/metric.js +74 -185
- package/dist/components/ui/metric/metric.js.map +1 -1
- package/dist/components/ui/page-content/page-content.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +201 -195
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/components/ui/radar-chart/radar-chart.js +133 -131
- package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
- package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
- package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
- package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
- package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
- package/dist/components/ui/section/section.js +157 -31
- package/dist/components/ui/section/section.js.map +1 -1
- package/dist/components/ui/stepper/stepper.js +24 -24
- package/dist/components/ui/stepper/stepper.js.map +1 -1
- package/dist/components/ui/stepper-item/stepper-item.js +43 -24
- package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
- package/dist/components/ui/table/table.js +27 -27
- package/dist/components/ui/table/table.js.map +1 -1
- package/dist/components/ui/treemap/treemap.js +133 -124
- package/dist/components/ui/treemap/treemap.js.map +1 -1
- package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
- package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
- package/dist/components/ui/wizard/wizard.js +59 -0
- package/dist/components/ui/wizard/wizard.js.map +1 -0
- package/dist/hooks/use-wizard.js +75 -0
- package/dist/hooks/use-wizard.js.map +1 -0
- package/dist/index.js +540 -525
- package/dist/index.js.map +1 -1
- package/dist/llms/accordion-container.md +181 -0
- package/dist/llms/accordion.md +124 -0
- package/dist/llms/alert.md +225 -0
- package/dist/llms/app-shell-chat.md +415 -0
- package/dist/llms/area-chart.md +355 -0
- package/dist/llms/avatar.md +208 -0
- package/dist/llms/bar-chart.md +406 -0
- package/dist/llms/breadcrumb.md +326 -0
- package/dist/llms/button-group.md +298 -0
- package/dist/llms/button-icon-input.md +142 -0
- package/dist/llms/button-icon-menu.md +209 -0
- package/dist/llms/button-icon.md +128 -0
- package/dist/llms/button-menu.md +172 -0
- package/dist/llms/button.md +185 -0
- package/dist/llms/calendar-panel.md +733 -0
- package/dist/llms/calendar.md +188 -0
- package/dist/llms/card-filter.md +214 -0
- package/dist/llms/card-section.md +293 -0
- package/dist/llms/card.md +447 -0
- package/dist/llms/carousel-dialog.md +156 -0
- package/dist/llms/category-bar.md +193 -0
- package/dist/llms/chart-state.md +190 -0
- package/dist/llms/chart-widget.md +209 -0
- package/dist/llms/chart.md +182 -0
- package/dist/llms/checkbox.md +257 -0
- package/dist/llms/chip.md +195 -0
- package/dist/llms/combobox.md +164 -0
- package/dist/llms/composed-chart.md +508 -0
- package/dist/llms/confidence-cone.md +277 -0
- package/dist/llms/data-table.md +474 -0
- package/dist/llms/date-range-picker.md +272 -0
- package/dist/llms/description-list.md +131 -0
- package/dist/llms/dialog-footer-carousel.md +147 -0
- package/dist/llms/dialog-footer-default.md +142 -0
- package/dist/llms/dialog-root.md +246 -0
- package/dist/llms/dialog-welcome.md +223 -0
- package/dist/llms/dialog.md +403 -0
- package/dist/llms/dropdown-menu.md +193 -0
- package/dist/llms/empty-overlay.md +146 -0
- package/dist/llms/empty.md +156 -0
- package/dist/llms/field.md +185 -0
- package/dist/llms/filter-cards.md +157 -0
- package/dist/llms/filter-chips.md +323 -0
- package/dist/llms/filter-search.md +512 -0
- package/dist/llms/form.md +154 -0
- package/dist/llms/funnel-chart.md +333 -0
- package/dist/llms/grid.md +80 -0
- package/dist/llms/histogram.md +190 -0
- package/dist/llms/input-date-picker.md +203 -0
- package/dist/llms/input-otp.md +173 -0
- package/dist/llms/input-password.md +197 -0
- package/dist/llms/input-search.md +170 -0
- package/dist/llms/input-select.md +278 -0
- package/dist/llms/input-text-area.md +271 -0
- package/dist/llms/input-text.md +192 -0
- package/dist/llms/line-chart.md +369 -0
- package/dist/llms/link.md +209 -0
- package/dist/llms/loading.md +130 -0
- package/dist/llms/meter.md +177 -0
- package/dist/llms/metric.md +204 -0
- package/dist/llms/number-field.md +122 -0
- package/dist/llms/page-content.md +69 -0
- package/dist/llms/page-header.md +182 -0
- package/dist/llms/pagination.md +133 -0
- package/dist/llms/pie-chart.md +307 -0
- package/dist/llms/popover.md +252 -0
- package/dist/llms/progress-circle.md +126 -0
- package/dist/llms/progress.md +111 -0
- package/dist/llms/radar-chart.md +316 -0
- package/dist/llms/radial-bar-chart.md +319 -0
- package/dist/llms/radio.md +255 -0
- package/dist/llms/resizable.md +195 -0
- package/dist/llms/sankey-chart.md +208 -0
- package/dist/llms/scatter-chart.md +239 -0
- package/dist/llms/scroll-area.md +183 -0
- package/dist/llms/section.md +401 -0
- package/dist/llms/select.md +210 -0
- package/dist/llms/separator.md +87 -0
- package/dist/llms/sheet.md +202 -0
- package/dist/llms/sidebar-primary.md +360 -0
- package/dist/llms/sidebar-secondary.md +417 -0
- package/dist/llms/skeleton.md +81 -0
- package/dist/llms/slider.md +97 -0
- package/dist/llms/spinner.md +100 -0
- package/dist/llms/stack.md +80 -0
- package/dist/llms/stepper-item.md +309 -0
- package/dist/llms/stepper.md +251 -0
- package/dist/llms/switch.md +140 -0
- package/dist/llms/table.md +436 -0
- package/dist/llms/tabs.md +197 -0
- package/dist/llms/tag.md +139 -0
- package/dist/llms/timeline.md +361 -0
- package/dist/llms/timer.md +259 -0
- package/dist/llms/toast.md +241 -0
- package/dist/llms/toggle-group.md +121 -0
- package/dist/llms/toolbar.md +453 -0
- package/dist/llms/tooltip.md +164 -0
- package/dist/llms/treemap.md +254 -0
- package/dist/llms/trend-indicator.md +132 -0
- package/dist/llms/widget-placeholder.md +169 -0
- package/dist/llms/wizard.md +412 -0
- package/dist/llms.txt +142 -0
- package/dist/react.js +540 -525
- package/dist/react.js.map +1 -1
- package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
- package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
- package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
- package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
- package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
- package/dist/src/components/ui/button/button.d.ts +1 -1
- package/dist/src/components/ui/card/card.d.ts +69 -8
- package/dist/src/components/ui/card/index.d.ts +1 -1
- package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
- package/dist/src/components/ui/card-section/index.d.ts +1 -0
- package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
- package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
- package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
- package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
- package/dist/src/components/ui/chart/chart.d.ts +66 -15
- package/dist/src/components/ui/chart/index.d.ts +4 -2
- package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
- package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
- package/dist/src/components/ui/chart-state/index.d.ts +1 -0
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
- package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
- package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
- package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
- package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
- package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
- package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
- package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
- package/dist/src/components/ui/grid/grid.d.ts +1 -1
- package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
- package/dist/src/components/ui/label/index.d.ts +1 -1
- package/dist/src/components/ui/label/label.d.ts +1 -5
- package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
- package/dist/src/components/ui/metric/metric.d.ts +13 -14
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
- package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
- package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
- package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
- package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
- package/dist/src/components/ui/section/index.d.ts +1 -1
- package/dist/src/components/ui/section/section.d.ts +75 -6
- package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
- package/dist/src/components/ui/stack/stack.d.ts +1 -1
- package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
- package/dist/src/components/ui/tag/tag.d.ts +1 -1
- package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
- package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
- package/dist/src/components/ui/wizard/index.d.ts +1 -0
- package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/use-wizard.d.ts +80 -0
- package/dist/src/index.d.ts +5 -3
- package/dist/ui-react.css +1 -1
- package/package.json +12 -6
- package/dist/components/ui/app-shell/app-shell.js +0 -75
- package/dist/components/ui/app-shell/app-shell.js.map +0 -1
- package/dist/components/ui/auth-layout/auth-layout.js +0 -56
- package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
- package/dist/components/ui/collapsible/collapsible.js +0 -22
- package/dist/components/ui/collapsible/collapsible.js.map +0 -1
- package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
- package/dist/src/components/ui/app-shell/index.d.ts +0 -1
- package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
- package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
- package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
- package/dist/src/components/ui/collapsible/index.d.ts +0 -1
- package/dist/src/components/ui/label/label.docs.d.ts +0 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"empty-overlay.js","sources":["../../../../src/components/ui/empty-overlay/empty-overlay.tsx"],"sourcesContent":["import * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\n\n// A full-bleed empty-state overlay (Figma: `EmptyOverlay`, node 7461:40875) —\n// distinct from the plain, compositional `Empty`: this one owns a solid fade\n// background meant to sit over other content (e.g. a frozen/blurred widget\n// preview) and a colored icon badge. The badge reuses `Avatar`'s 8-color\n// `--ui-avatar-color-*` / `--ui-avatar-label-color-*` tokens rather than a\n// dedicated tier — Figma's own variable names for this badge\n// (`components/Avatar/color/*`) confirm it's a deliberate reuse of the avatar\n// palette, not a component composition (the badge holds an arbitrary glyph,\n// not a user image/initials, so wrapping `Avatar` itself would pull in image\n// loading semantics this doesn't need).\n//\n// No `--ui-empty-overlay-*` token tier exists, so non-badge colors come from\n// the shared semantic vocabulary already bridged in `src/styles/index.css`:\n// title -> text-foreground, description -> text-muted-foreground, gradient\n// end-stop -> bg-muted (all `--ui-text-on-surface-*` / `--ui-background-surface-secondary`).\n//\n// The root has no fixed size — per design intent, the developer sizes the\n// overlay (e.g. `absolute inset-0` over a positioned parent); this component\n// only fills that box (`size-full`) and centers its content.\nconst emptyOverlayIconVariants = cva(\n 'flex size-16 shrink-0 items-center justify-center rounded-full [&>svg]:size-6',\n {\n variants: {\n color: {\n teal: 'bg-[var(--ui-avatar-color-teal)] text-[var(--ui-avatar-label-color-teal)]',\n violet:\n 'bg-[var(--ui-avatar-color-violet)] text-[var(--ui-avatar-label-color-violet)]',\n red: 'bg-[var(--ui-avatar-color-red)] text-[var(--ui-avatar-label-color-red)]',\n yellow:\n 'bg-[var(--ui-avatar-color-yellow)] text-[var(--ui-avatar-label-color-yellow)]',\n orange:\n 'bg-[var(--ui-avatar-color-orange)] text-[var(--ui-avatar-label-color-orange)]',\n blue: 'bg-[var(--ui-avatar-color-blue)] text-[var(--ui-avatar-label-color-blue)]',\n gray: 'bg-[var(--ui-avatar-color-gray)] text-[var(--ui-avatar-label-color-gray)]',\n green:\n 'bg-[var(--ui-avatar-color-green)] text-[var(--ui-avatar-label-color-green)]',\n },\n },\n defaultVariants: {\n color: 'green',\n },\n }\n);\n\nexport interface EmptyOverlayProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'title' | 'color' | 'children'>,\n VariantProps<typeof emptyOverlayIconVariants> {\n /** Icon rendered in the colored badge (e.g. an `icons-react` glyph at 24px). */\n icon?: React.ReactNode;\n /** The empty-state headline. */\n title: React.ReactNode;\n /** Supporting copy shown under the title. */\n description?: React.ReactNode;\n}\n\n/**\n * A full-bleed empty-state overlay: a colored icon badge over a centered\n * title/description, on a fade-to-solid backdrop. Size it via the parent\n * (e.g. `absolute inset-0`) — the component fills whatever box it's given.\n */\nconst EmptyOverlay = React.forwardRef<HTMLDivElement, EmptyOverlayProps>(\n ({ className, icon, title, description, color, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex size-full flex-col items-center justify-center overflow-hidden bg-linear-to-b from-transparent to-muted to-[51.481%] p-12 text-center',\n className\n )}\n {...props}\n >\n <div className=\"flex w-full max-w-lg flex-col items-center justify-center gap-3\">\n {icon ? (\n <div className={cn(emptyOverlayIconVariants({ color }))}>{icon}</div>\n ) : null}\n <div className=\"flex w-full flex-col items-center gap-1\">\n <h3 className=\"w-full text-2xl font-normal leading-8 text-foreground\">\n {title}\n </h3>\n {description ? (\n <p className=\"w-full text-sm leading-6 text-muted-foreground\">\n {description}\n </p>\n ) : null}\n </div>\n </div>\n </div>\n )\n);\nEmptyOverlay.displayName = 'EmptyOverlay';\n\nexport { EmptyOverlay, emptyOverlayIconVariants };\n"],"names":["emptyOverlayIconVariants","cva","EmptyOverlay","React","className","icon","title","description","color","props","ref","jsx","cn","jsxs"],"mappings":";;;;AAwBA,MAAMA,IAA2BC;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,MAAM;AAAA,QACN,QACE;AAAA,QACF,KAAK;AAAA,QACL,QACE;AAAA,QACF,QACE;AAAA,QACF,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OACE;AAAA,MAAA;AAAA,IACJ;AAAA,IAEF,iBAAiB;AAAA,MACf,OAAO;AAAA,IAAA;AAAA,EACT;AAEJ,GAmBMC,IAAeC,EAAM;AAAA,EACzB,CAAC,EAAE,WAAAC,GAAW,MAAAC,GAAM,OAAAC,GAAO,aAAAC,GAAa,OAAAC,GAAO,GAAGC,KAASC,MACzD,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,WAAWE;AAAA,QACT;AAAA,QACAR;AAAA,MAAA;AAAA,MAED,GAAGK;AAAA,MAEJ,UAAA,gBAAAI,EAAC,OAAA,EAAI,WAAU,mEACZ,UAAA;AAAA,QAAAR,IACC,gBAAAM,EAAC,OAAA,EAAI,WAAWC,EAAGZ,EAAyB,EAAE,OAAAQ,EAAA,CAAO,CAAC,GAAI,UAAAH,EAAA,CAAK,IAC7D;AAAA,QACJ,gBAAAQ,EAAC,OAAA,EAAI,WAAU,2CACb,UAAA;AAAA,UAAA,gBAAAF,EAAC,MAAA,EAAG,WAAU,yDACX,UAAAL,GACH;AAAA,UACCC,IACC,gBAAAI,EAAC,KAAA,EAAE,WAAU,kDACV,aACH,IACE;AAAA,QAAA,EAAA,CACN;AAAA,MAAA,EAAA,CACF;AAAA,IAAA;AAAA,EAAA;AAGN;AACAT,EAAa,cAAc;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx as c, jsxs as
|
|
2
|
-
import * as
|
|
3
|
-
import { cva as
|
|
4
|
-
import { FunnelChart as
|
|
5
|
-
import { cn as
|
|
6
|
-
import { resolveAnimation as
|
|
7
|
-
import { ChartContainer as
|
|
8
|
-
const
|
|
1
|
+
import { jsx as c, jsxs as V } from "react/jsx-runtime";
|
|
2
|
+
import * as N from "react";
|
|
3
|
+
import { cva as fr } from "../../../node_modules/.pnpm/class-variance-authority@0.7.1/node_modules/class-variance-authority/dist/index.js";
|
|
4
|
+
import { FunnelChart as dr, Funnel as mr, Cell as vr, LabelList as O } from "recharts";
|
|
5
|
+
import { cn as hr } from "../../../lib/utils.js";
|
|
6
|
+
import { resolveAnimation as pr, resolveLabelFillClass as R } from "../chart/chart-format.js";
|
|
7
|
+
import { ChartContainer as br, ChartTooltip as Lr, ChartTooltipContent as Cr, ChartLegend as Ar, ChartLegendContent as Nr } from "../chart/chart.js";
|
|
8
|
+
const _r = fr("", {
|
|
9
9
|
variants: {
|
|
10
10
|
lastShape: {
|
|
11
11
|
triangle: "",
|
|
@@ -15,12 +15,12 @@ const Nr = ur("", {
|
|
|
15
15
|
defaultVariants: {
|
|
16
16
|
lastShape: "triangle"
|
|
17
17
|
}
|
|
18
|
-
}),
|
|
18
|
+
}), D = {
|
|
19
19
|
right: "right",
|
|
20
20
|
left: "left",
|
|
21
21
|
inside: "center"
|
|
22
22
|
};
|
|
23
|
-
function
|
|
23
|
+
function Er(r) {
|
|
24
24
|
switch (r) {
|
|
25
25
|
case "right":
|
|
26
26
|
return "left";
|
|
@@ -30,152 +30,153 @@ function _r(r) {
|
|
|
30
30
|
return "right";
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
const
|
|
33
|
+
const G = "__stageLabel", j = "__stageValueLabel", Tr = 45, $r = {
|
|
34
34
|
stroke: "var(--ui-border-on-surface-border-active)",
|
|
35
35
|
strokeWidth: 2
|
|
36
|
-
},
|
|
36
|
+
}, W = 96, H = {
|
|
37
37
|
top: 8,
|
|
38
|
-
right:
|
|
38
|
+
right: W,
|
|
39
39
|
bottom: 8,
|
|
40
40
|
left: 24
|
|
41
|
-
},
|
|
42
|
-
function
|
|
41
|
+
}, Ir = "75%";
|
|
42
|
+
function gr(r) {
|
|
43
43
|
return r.includes("-");
|
|
44
44
|
}
|
|
45
|
-
function
|
|
45
|
+
function Fr(r) {
|
|
46
46
|
const { showLabels: e, labelPosition: t, labelFormat: o } = r;
|
|
47
|
-
return e && t === "right" &&
|
|
47
|
+
return e && t === "right" && gr(o) ? Ir : void 0;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
50
|
-
const { showLabels: e, labelPosition: t, showValueLabels: o, valuePosition:
|
|
49
|
+
function xr(r) {
|
|
50
|
+
const { showLabels: e, labelPosition: t, showValueLabels: o, valuePosition: u } = r;
|
|
51
51
|
return {
|
|
52
|
-
...
|
|
53
|
-
left: (e ? t : void 0) === "left" || (o ?
|
|
52
|
+
...H,
|
|
53
|
+
left: (e ? t : void 0) === "left" || (o ? u : void 0) === "left" ? W : H.left
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
const
|
|
56
|
+
const Mr = (r) => {
|
|
57
57
|
const e = typeof r == "number" ? r : Number(r);
|
|
58
58
|
return Number.isFinite(e) ? `${(e * 100).toFixed(1)}%` : String(r);
|
|
59
59
|
};
|
|
60
|
-
function
|
|
60
|
+
function U(r) {
|
|
61
61
|
const {
|
|
62
62
|
name: e,
|
|
63
63
|
value: t,
|
|
64
64
|
base: o,
|
|
65
|
-
format:
|
|
66
|
-
formatter:
|
|
67
|
-
percentFormatter:
|
|
68
|
-
} = r,
|
|
69
|
-
switch (
|
|
65
|
+
format: u,
|
|
66
|
+
formatter: n,
|
|
67
|
+
percentFormatter: m = Mr
|
|
68
|
+
} = r, v = n ? n(t) : String(t), h = typeof t == "number" ? t : Number(t), l = o > 0 && Number.isFinite(h) ? m(h / o) : "";
|
|
69
|
+
switch (u) {
|
|
70
70
|
case "name":
|
|
71
71
|
return e;
|
|
72
72
|
case "value":
|
|
73
|
-
return
|
|
73
|
+
return v;
|
|
74
74
|
case "percent":
|
|
75
|
-
return
|
|
75
|
+
return l;
|
|
76
76
|
case "name-value":
|
|
77
|
-
return `${e}: ${
|
|
77
|
+
return `${e}: ${v}`;
|
|
78
78
|
case "name-percent":
|
|
79
|
-
return
|
|
79
|
+
return l ? `${e}: ${l}` : e;
|
|
80
80
|
case "value-percent":
|
|
81
|
-
return
|
|
81
|
+
return l ? `${v} (${l})` : v;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function Pr(r, e, t) {
|
|
85
85
|
if (t <= 1) return r;
|
|
86
|
-
const o = 100 - (100 -
|
|
86
|
+
const o = 100 - (100 - Tr) * e / (t - 1);
|
|
87
87
|
return `color-mix(in oklab, ${r} ${o.toFixed(1)}%, var(--ui-background-surface-primary))`;
|
|
88
88
|
}
|
|
89
|
-
const
|
|
89
|
+
const kr = N.forwardRef(
|
|
90
90
|
({
|
|
91
91
|
className: r,
|
|
92
92
|
config: e,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
palette: t,
|
|
94
|
+
data: o,
|
|
95
|
+
dataKey: u,
|
|
95
96
|
nameKey: n,
|
|
96
|
-
lastShape:
|
|
97
|
-
reversed:
|
|
98
|
-
showLabels:
|
|
99
|
-
labelFormat:
|
|
100
|
-
labelPosition:
|
|
101
|
-
showValueLabels:
|
|
102
|
-
valuePosition:
|
|
103
|
-
labelFill:
|
|
104
|
-
labelFormatter:
|
|
105
|
-
percentFormatter:
|
|
106
|
-
showLegend:
|
|
107
|
-
legendPos:
|
|
108
|
-
colorMode:
|
|
109
|
-
gradientColor:
|
|
110
|
-
stageSettings:
|
|
111
|
-
showActiveShape:
|
|
112
|
-
stroke:
|
|
113
|
-
strokeWidth:
|
|
114
|
-
funnelWidth:
|
|
115
|
-
margin:
|
|
116
|
-
showTooltip:
|
|
117
|
-
tooltipContent:
|
|
118
|
-
animate:
|
|
119
|
-
animationDuration:
|
|
120
|
-
animationBegin:
|
|
121
|
-
animationEasing:
|
|
122
|
-
...
|
|
123
|
-
},
|
|
124
|
-
const
|
|
125
|
-
animate:
|
|
126
|
-
animationDuration:
|
|
127
|
-
animationBegin:
|
|
128
|
-
animationEasing:
|
|
129
|
-
}),
|
|
130
|
-
var
|
|
131
|
-
const
|
|
97
|
+
lastShape: m = "triangle",
|
|
98
|
+
reversed: v = !1,
|
|
99
|
+
showLabels: h = !0,
|
|
100
|
+
labelFormat: l = "name",
|
|
101
|
+
labelPosition: L = "right",
|
|
102
|
+
showValueLabels: _ = !1,
|
|
103
|
+
valuePosition: Y,
|
|
104
|
+
labelFill: C,
|
|
105
|
+
labelFormatter: E,
|
|
106
|
+
percentFormatter: T,
|
|
107
|
+
showLegend: z = !1,
|
|
108
|
+
legendPos: $ = "bottom",
|
|
109
|
+
colorMode: I = "palette",
|
|
110
|
+
gradientColor: g,
|
|
111
|
+
stageSettings: a,
|
|
112
|
+
showActiveShape: X = !1,
|
|
113
|
+
stroke: q,
|
|
114
|
+
strokeWidth: F,
|
|
115
|
+
funnelWidth: w,
|
|
116
|
+
margin: y,
|
|
117
|
+
showTooltip: J = !0,
|
|
118
|
+
tooltipContent: Q,
|
|
119
|
+
animate: Z,
|
|
120
|
+
animationDuration: K,
|
|
121
|
+
animationBegin: rr,
|
|
122
|
+
animationEasing: er,
|
|
123
|
+
...tr
|
|
124
|
+
}, nr) => {
|
|
125
|
+
const or = pr({
|
|
126
|
+
animate: Z,
|
|
127
|
+
animationDuration: K,
|
|
128
|
+
animationBegin: rr,
|
|
129
|
+
animationEasing: er
|
|
130
|
+
}), A = N.useMemo(() => {
|
|
131
|
+
var k;
|
|
132
|
+
const i = o.filter(
|
|
132
133
|
(f) => {
|
|
133
|
-
var
|
|
134
|
-
return !((
|
|
134
|
+
var b;
|
|
135
|
+
return !((b = a == null ? void 0 : a[String(f[n])]) != null && b.hidden);
|
|
135
136
|
}
|
|
136
|
-
), s =
|
|
137
|
-
const
|
|
138
|
-
return Number.isFinite(
|
|
137
|
+
), s = i.length ? String(i[0][n]) : void 0, p = g ?? (s === void 0 ? void 0 : ((k = a == null ? void 0 : a[s]) == null ? void 0 : k.color) ?? `var(--color-${s})`), cr = i.reduce((f, b) => {
|
|
138
|
+
const d = Number(b[u]);
|
|
139
|
+
return Number.isFinite(d) && d > f ? d : f;
|
|
139
140
|
}, 0);
|
|
140
|
-
return
|
|
141
|
-
var
|
|
142
|
-
const
|
|
143
|
-
name:
|
|
144
|
-
value: f[
|
|
145
|
-
base:
|
|
146
|
-
formatter:
|
|
147
|
-
percentFormatter:
|
|
141
|
+
return i.map((f, b) => {
|
|
142
|
+
var B;
|
|
143
|
+
const d = String(f[n]), ur = ((B = a == null ? void 0 : a[d]) == null ? void 0 : B.color) ?? (I === "gradient" && p ? Pr(p, b, i.length) : `var(--color-${d})`), S = {
|
|
144
|
+
name: d,
|
|
145
|
+
value: f[u],
|
|
146
|
+
base: cr,
|
|
147
|
+
formatter: E,
|
|
148
|
+
percentFormatter: T
|
|
148
149
|
};
|
|
149
150
|
return {
|
|
150
151
|
...f,
|
|
151
|
-
fill:
|
|
152
|
-
[
|
|
153
|
-
...
|
|
154
|
-
format:
|
|
152
|
+
fill: ur,
|
|
153
|
+
[G]: U({
|
|
154
|
+
...S,
|
|
155
|
+
format: l
|
|
155
156
|
}),
|
|
156
|
-
[
|
|
157
|
-
...
|
|
157
|
+
[j]: U({
|
|
158
|
+
...S,
|
|
158
159
|
format: "value"
|
|
159
160
|
})
|
|
160
161
|
};
|
|
161
162
|
});
|
|
162
163
|
}, [
|
|
163
|
-
$,
|
|
164
|
-
t,
|
|
165
|
-
o,
|
|
166
164
|
I,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
o,
|
|
166
|
+
u,
|
|
167
|
+
g,
|
|
168
|
+
l,
|
|
170
169
|
E,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
n,
|
|
171
|
+
T,
|
|
172
|
+
a
|
|
173
|
+
]), ar = N.useMemo(() => {
|
|
174
|
+
const i = /* @__PURE__ */ new Set();
|
|
175
|
+
return A.flatMap((s) => {
|
|
176
|
+
const p = String(s[n]);
|
|
177
|
+
return i.has(p) ? [] : (i.add(p), [
|
|
177
178
|
{
|
|
178
|
-
value:
|
|
179
|
+
value: p,
|
|
179
180
|
dataKey: n,
|
|
180
181
|
type: "rect",
|
|
181
182
|
color: String(s.fill),
|
|
@@ -183,80 +184,80 @@ const Pr = A.forwardRef(
|
|
|
183
184
|
}
|
|
184
185
|
]);
|
|
185
186
|
});
|
|
186
|
-
}, [n,
|
|
187
|
-
...
|
|
188
|
-
showLabels:
|
|
189
|
-
labelPosition:
|
|
190
|
-
showValueLabels:
|
|
191
|
-
valuePosition:
|
|
187
|
+
}, [n, A]), x = Y ?? Er(L), M = D[L], P = D[x], ir = {
|
|
188
|
+
...xr({
|
|
189
|
+
showLabels: h,
|
|
190
|
+
labelPosition: L,
|
|
191
|
+
showValueLabels: _,
|
|
192
|
+
valuePosition: x
|
|
192
193
|
}),
|
|
193
|
-
...
|
|
194
|
-
},
|
|
194
|
+
...y
|
|
195
|
+
}, lr = w ?? Fr({ showLabels: h, labelPosition: L, labelFormat: l }), sr = q ?? (F != null ? "var(--ui-border-on-surface-border)" : void 0);
|
|
195
196
|
return /* @__PURE__ */ c(
|
|
196
197
|
"div",
|
|
197
198
|
{
|
|
198
|
-
ref:
|
|
199
|
-
"data-last-shape":
|
|
200
|
-
className:
|
|
201
|
-
...
|
|
202
|
-
children: /* @__PURE__ */ c(
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
ref: nr,
|
|
200
|
+
"data-last-shape": m,
|
|
201
|
+
className: hr(_r({ lastShape: m }), r),
|
|
202
|
+
...tr,
|
|
203
|
+
children: /* @__PURE__ */ c(br, { config: e, palette: t, className: "size-full", children: /* @__PURE__ */ V(dr, { margin: ir, children: [
|
|
204
|
+
J && /* @__PURE__ */ c(
|
|
205
|
+
Lr,
|
|
205
206
|
{
|
|
206
|
-
content:
|
|
207
|
+
content: Q ?? /* @__PURE__ */ c(Cr, { nameKey: n, hideLabel: !0 })
|
|
207
208
|
}
|
|
208
209
|
),
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
z && /* @__PURE__ */ c(
|
|
211
|
+
Ar,
|
|
211
212
|
{
|
|
212
|
-
verticalAlign:
|
|
213
|
+
verticalAlign: $,
|
|
213
214
|
content: () => /* @__PURE__ */ c(
|
|
214
|
-
|
|
215
|
+
Nr,
|
|
215
216
|
{
|
|
216
|
-
payload:
|
|
217
|
-
verticalAlign:
|
|
217
|
+
payload: ar,
|
|
218
|
+
verticalAlign: $,
|
|
218
219
|
nameKey: n
|
|
219
220
|
}
|
|
220
221
|
)
|
|
221
222
|
}
|
|
222
223
|
),
|
|
223
|
-
/* @__PURE__ */
|
|
224
|
-
|
|
224
|
+
/* @__PURE__ */ V(
|
|
225
|
+
mr,
|
|
225
226
|
{
|
|
226
|
-
dataKey:
|
|
227
|
+
dataKey: u,
|
|
227
228
|
nameKey: n,
|
|
228
|
-
data:
|
|
229
|
-
lastShapeType:
|
|
230
|
-
reversed:
|
|
231
|
-
width:
|
|
232
|
-
stroke:
|
|
233
|
-
strokeWidth:
|
|
234
|
-
activeShape:
|
|
235
|
-
...
|
|
229
|
+
data: A,
|
|
230
|
+
lastShapeType: m ?? "triangle",
|
|
231
|
+
reversed: v,
|
|
232
|
+
width: lr,
|
|
233
|
+
stroke: sr,
|
|
234
|
+
strokeWidth: F,
|
|
235
|
+
activeShape: X ? $r : void 0,
|
|
236
|
+
...or,
|
|
236
237
|
children: [
|
|
237
|
-
|
|
238
|
+
A.map((i, s) => (
|
|
238
239
|
// Keyed by index, not the name: two stages could share a nameKey
|
|
239
240
|
// value, which would collide as a React key. Same-named stages
|
|
240
241
|
// intentionally share a color/config entry via `--color-<name>`.
|
|
241
|
-
/* @__PURE__ */ c(
|
|
242
|
+
/* @__PURE__ */ c(vr, { fill: String(i.fill) }, s)
|
|
242
243
|
)),
|
|
243
|
-
|
|
244
|
-
|
|
244
|
+
h && /* @__PURE__ */ c(
|
|
245
|
+
O,
|
|
245
246
|
{
|
|
246
|
-
position:
|
|
247
|
-
dataKey:
|
|
248
|
-
className:
|
|
249
|
-
fill:
|
|
247
|
+
position: M,
|
|
248
|
+
dataKey: G,
|
|
249
|
+
className: C ? void 0 : R(M),
|
|
250
|
+
fill: C,
|
|
250
251
|
stroke: "none"
|
|
251
252
|
}
|
|
252
253
|
),
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
_ && /* @__PURE__ */ c(
|
|
255
|
+
O,
|
|
255
256
|
{
|
|
256
|
-
position:
|
|
257
|
-
dataKey:
|
|
258
|
-
className:
|
|
259
|
-
fill:
|
|
257
|
+
position: P,
|
|
258
|
+
dataKey: j,
|
|
259
|
+
className: C ? void 0 : R(P),
|
|
260
|
+
fill: C,
|
|
260
261
|
stroke: "none"
|
|
261
262
|
}
|
|
262
263
|
)
|
|
@@ -268,14 +269,14 @@ const Pr = A.forwardRef(
|
|
|
268
269
|
);
|
|
269
270
|
}
|
|
270
271
|
);
|
|
271
|
-
|
|
272
|
+
kr.displayName = "FunnelChart";
|
|
272
273
|
export {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
274
|
+
kr as FunnelChart,
|
|
275
|
+
xr as funnelChartLabelMargin,
|
|
276
|
+
Fr as funnelChartLabelReserve,
|
|
277
|
+
U as funnelChartLabelText,
|
|
278
|
+
Er as funnelChartOppositeSide,
|
|
279
|
+
Mr as funnelChartPercent,
|
|
280
|
+
_r as funnelChartVariants
|
|
280
281
|
};
|
|
281
282
|
//# sourceMappingURL=funnel-chart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"funnel-chart.js","sources":["../../../../src/components/ui/funnel-chart/funnel-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Funnel,\n FunnelChart as RechartsFunnelChart,\n LabelList,\n} from 'recharts';\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveLabelFillClass,\n type ChartAnimationProps,\n type ChartConfig,\n type TickFormatter,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The single\n// CVA axis is the design's Funnel-chart variant set: `lastShape` (does the\n// funnel narrow to a point — `triangle` — or end flat — `rectangle`). The class\n// stays empty because recharts' SVG — not CSS — draws the funnel: `lastShape`\n// drives the `<Funnel>`'s `lastShapeType`. CVA is kept so the variant set is a\n// first-class, spec-conformant part of the API (matched against ui-spec's\n// api.yaml enums) and exposed via `VariantProps`; the resolved value is also\n// mirrored onto `data-last-shape`.\nconst funnelChartVariants = cva('', {\n variants: {\n lastShape: {\n triangle: '',\n rectangle: '',\n },\n },\n defaultVariants: {\n lastShape: 'triangle',\n },\n});\n\n/** Where a stage's label sits relative to its segment. */\nexport type FunnelChartLabelPosition = 'right' | 'left' | 'inside';\n\n/** What a stage's label says. */\nexport type FunnelChartLabelFormat =\n | 'name'\n | 'value'\n | 'percent'\n | 'name-value'\n | 'name-percent'\n | 'value-percent';\n\n/** How the stages are coloured: one `config` entry each, or one shared ramp. */\nexport type FunnelChartColorMode = 'palette' | 'gradient';\n\n/** Override for a single stage, keyed by its `nameKey` value. */\nexport interface FunnelChartStageSettings {\n /**\n * Paint this stage with a colour of its own, overriding both `config` and\n * `colorMode`. Reference an existing semantic `--ui-*` token.\n */\n color?: string;\n /**\n * Drop the stage from the funnel — and from the legend, labels and the\n * conversion percentages, which are computed over the visible stages only.\n */\n hidden?: boolean;\n}\n\n// recharts' `LabelList` positions that a funnel trapezoid understands. `inside`\n// is recharts' `center`: a trapezoid has no `insideLeft`/`insideTop` anchor, so\n// centring is the only on-segment placement. `outside` is not offered — for a\n// funnel it would mean \"beside the segment\", which is what `right`/`left`\n// already are, and recharts' polar `outside` doesn't apply to a trapezoid.\nconst LABEL_POSITION: Record<\n FunnelChartLabelPosition,\n 'right' | 'left' | 'center'\n> = {\n right: 'right',\n left: 'left',\n inside: 'center',\n};\n\n/**\n * Where the value labels sit when the caller doesn't say: opposite the names, so\n * the two lists never stack on the same edge. `inside` names sit on the segments\n * and leave both edges free, so the values take the default `right`.\n *\n * A static default can't do this — `valuePosition=\"left\"` beside\n * `labelPosition=\"left\"` draws both lists at the same anchor, overprinting the\n * name with its own value.\n */\nexport function funnelChartOppositeSide(\n labelPosition: FunnelChartLabelPosition\n): FunnelChartLabelPosition {\n switch (labelPosition) {\n case 'right':\n return 'left';\n case 'left':\n return 'right';\n case 'inside':\n return 'right';\n }\n}\n\n// Synthetic row fields the two label lists read. A `LabelList` resolves its text\n// from a `dataKey`, and its `formatter` only ever sees that one field's value —\n// so a composite label (\"Signups: 52.0%\") has to be composed onto the row before\n// recharts sees it, the same way `fill` already is.\nconst STAGE_LABEL_KEY = '__stageLabel';\nconst STAGE_VALUE_LABEL_KEY = '__stageValueLabel';\n\n// How far the gradient ramp fades by the last stage: the final segment is mixed\n// down to this share of the base colour, the first stays at full strength.\nconst GRADIENT_MIN_MIX = 45;\n\n// Hover highlight for `showActiveShape` — an outline rather than a fill change,\n// so the segment keeps the colour that ties it to its legend entry and label.\nconst ACTIVE_SHAPE = {\n stroke: 'var(--ui-border-on-surface-border-active)',\n strokeWidth: 2,\n} as const;\n\n// The inset reserved on whichever side a label list sits. It is *not* label\n// room — see `funnelChartLabelReserve` for why — but a label whose text can't\n// wrap (one long word) does overflow past the plot edge, and this is what keeps\n// that overflow inside the SVG.\nconst LABEL_SIDE_INSET = 96;\n\n// The plot-area margin a funnel has always been drawn with: the right side\n// reserved, the left not.\nconst BASE_MARGIN = {\n top: 8,\n right: LABEL_SIDE_INSET,\n bottom: 8,\n left: 24,\n};\n\n/**\n * Share of the plot area handed to the funnel when a composite label sits to its\n * right. The rest becomes real label room — see `funnelChartLabelReserve`.\n *\n * A percentage, not a pixel count: the plot area is responsive, and recharts\n * resolves `Funnel`'s `width` against it, so a ratio keeps the reserve\n * proportional at every chart size.\n */\nconst COMPOSITE_FUNNEL_WIDTH = '75%';\n\n/** A label format that pairs two fields, so its text is roughly twice as wide. */\nfunction isCompositeFormat(format: FunnelChartLabelFormat): boolean {\n return format.includes('-');\n}\n\n/**\n * Reserve room for a composite label beside the funnel, by narrowing the\n * **funnel** rather than the plot area.\n *\n * recharts word-wraps a label against the space between its trapezoid's\n * mid-height edge and the *plot area* edge (`getCartesianPosition` clamps\n * `width` to `parentViewBox`, which `Funnel` sets to the plot box). A margin\n * shrinks the plot area and the funnel inside it in lockstep, so widening\n * `margin.right` moves the clamp edge *inward* and leaves a composite label\n * with less room, not more — the reserved strip sits outside `parentViewBox`\n * where the text can never reach it.\n *\n * `Funnel`'s `width` is the lever that works: it scales the funnel within the\n * plot area while `parentViewBox` stays put, and because recharts anchors the\n * funnel at the plot area's left edge (`offsetX = offset.left`), everything it\n * frees lands on the right. Narrowing the funnel to\n * `COMPOSITE_FUNNEL_WIDTH` turns ~35px of wrap width into ~140px on a 460px\n * chart.\n *\n * Returns `undefined` — i.e. leave the funnel at its full width — for every\n * other case, so a funnel with plain labels, no labels, or `inside` labels keeps\n * exactly the geometry it has always had.\n *\n * There is no equivalent lever for `labelPosition=\"left\"`: the widest trapezoid\n * always starts flush at the plot area's left edge, so a left-hand label is\n * bounded by how sharply the funnel narrows and narrowing it further only makes\n * that worse. Composite left-hand labels wrap; that's a recharts limitation, not\n * a tuning choice.\n *\n * A caller-supplied `funnelWidth` always wins — this is only the default.\n */\nexport function funnelChartLabelReserve(options: {\n showLabels: boolean;\n labelPosition: FunnelChartLabelPosition;\n labelFormat: FunnelChartLabelFormat;\n}): string | undefined {\n const { showLabels, labelPosition, labelFormat } = options;\n return showLabels &&\n labelPosition === 'right' &&\n isCompositeFormat(labelFormat)\n ? COMPOSITE_FUNNEL_WIDTH\n : undefined;\n}\n\n/**\n * The plot-area margin. Reserves the left inset when a label list sits there, so\n * a left-hand label isn't clipped at the SVG edge; the right inset is always\n * reserved, since that is the geometry every existing funnel was drawn with.\n *\n * This is overflow protection, not label room — the wrap width comes from\n * `funnelChartLabelReserve`. A caller-supplied `margin` is merged over these\n * defaults per side, so `margin={{ right: 160 }}` keeps the default top, bottom\n * and left rather than collapsing them to zero.\n */\nexport function funnelChartLabelMargin(options: {\n showLabels: boolean;\n labelPosition: FunnelChartLabelPosition;\n showValueLabels: boolean;\n valuePosition: FunnelChartLabelPosition;\n}) {\n const { showLabels, labelPosition, showValueLabels, valuePosition } = options;\n const stageLabelAt = showLabels ? labelPosition : undefined;\n const valueLabelAt = showValueLabels ? valuePosition : undefined;\n\n return {\n ...BASE_MARGIN,\n left:\n stageLabelAt === 'left' || valueLabelAt === 'left'\n ? LABEL_SIDE_INSET\n : BASE_MARGIN.left,\n };\n}\n\n/**\n * Format a conversion share the way the default `percent` label reads it:\n * `0.52 → \"52.0%\"`. Exported so a `percentFormatter` can wrap or replace it.\n */\nexport const funnelChartPercent: TickFormatter = (value) => {\n const n = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(n) ? `${(n * 100).toFixed(1)}%` : String(value);\n};\n\n/**\n * Compose one stage's label text.\n *\n * `base` is the value the percentage is measured against — the funnel's widest\n * stage, not the sum of every stage. A funnel's stages are nested subsets of\n * each other rather than parts of a whole, so their values don't add up to\n * anything meaningful; the share that reads as information is the conversion\n * from the top of the funnel. (A pie's `percent` is the other way round — there\n * a slice really is a share of the total.)\n *\n * Only the numeric part goes through `formatter`, so a label and its tooltip can\n * share one formatter; the share goes through `percentFormatter`, so a locale\n * that doesn't write a bare `%` can replace it. The percent formats degrade to\n * the name (or to the value) rather than printing `NaN%` when there is nothing\n * to divide by.\n */\nexport function funnelChartLabelText(options: {\n name: string;\n value: number | string;\n base: number;\n format: FunnelChartLabelFormat;\n formatter?: TickFormatter;\n percentFormatter?: TickFormatter;\n}): string {\n const {\n name,\n value,\n base,\n format,\n formatter,\n percentFormatter = funnelChartPercent,\n } = options;\n const text = formatter ? formatter(value) : String(value);\n const numeric = typeof value === 'number' ? value : Number(value);\n const share =\n base > 0 && Number.isFinite(numeric)\n ? percentFormatter(numeric / base)\n : '';\n\n switch (format) {\n case 'name':\n return name;\n case 'value':\n return text;\n case 'percent':\n return share;\n case 'name-value':\n return `${name}: ${text}`;\n case 'name-percent':\n return share ? `${name}: ${share}` : name;\n case 'value-percent':\n return share ? `${text} (${share})` : text;\n }\n}\n\n/**\n * Mix one stage of the gradient ramp. Mixing toward the surface (rather than\n * lowering alpha) keeps every stage opaque, so an on-segment label keeps its\n * contrast and the ramp reads the same over any card background.\n */\nfunction gradientStageFill(base: string, index: number, count: number): string {\n if (count <= 1) return base;\n const strength = 100 - ((100 - GRADIENT_MIN_MIX) * index) / (count - 1);\n return `color-mix(in oklab, ${base} ${strength.toFixed(1)}%, var(--ui-background-surface-primary))`;\n}\n\nexport interface FunnelChartProps\n extends\n Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof funnelChartVariants>,\n ChartAnimationProps {\n /** Row-per-stage data. Each object holds the stage's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<Record<string, string | number>>;\n /**\n * Per-stage map of `label` / `color`, keyed by the stage's `nameKey` value\n * (imported from the shared `Chart` primitives). Turned into `--color-<name>`\n * custom properties. Colors are caller-supplied — reference an existing\n * semantic `--ui-*` token; there is no chart palette tier yet.\n */\n config: ChartConfig;\n /** Numeric field that sizes each stage (the funnel narrows as it drops). */\n dataKey: string;\n /**\n * Label field that names each stage (drives the legend, tooltip, on-chart\n * labels, and the `--color-<name>` lookup). Values should be unique per chart —\n * stages sharing a name share one `config`/color entry.\n */\n nameKey: string;\n /** Flip the funnel so it widens toward the bottom instead of narrowing. */\n reversed?: boolean;\n /** Render a label for each stage — see `labelFormat` and `labelPosition`. */\n showLabels?: boolean;\n /**\n * What each stage's label says. `percent` is the stage's share of the widest\n * stage — its conversion from the top of the funnel.\n */\n labelFormat?: FunnelChartLabelFormat;\n /**\n * Where each stage's label sits. Defaults to `right`, beside the segment.\n * `inside` centres it on the segment, which only works while every stage is\n * wide enough to hold the text — a funnel narrows, so its last stages often\n * are not. Keep the text short there (`percent`) or leave the labels beside\n * the funnel.\n */\n labelPosition?: FunnelChartLabelPosition;\n /**\n * Render a second label carrying the stage's value, so the name and the number\n * can sit on opposite sides of the funnel.\n */\n showValueLabels?: boolean;\n /**\n * Where the value label sits. Defaults to the side opposite `labelPosition`,\n * where every stage has room — so it follows the names rather than pinning\n * itself to one edge. See `labelPosition` before choosing `inside`.\n */\n valuePosition?: FunnelChartLabelPosition;\n /**\n * Colour for both label lists, overriding the contrast-matched default (the\n * on-surface token beside the funnel, the on-fill token on a segment). Pass a\n * `--ui-*` token; only use it when the default doesn't work on your surface.\n */\n labelFill?: string;\n /** Format the numeric part of a label — pass the same formatter used elsewhere. */\n labelFormatter?: TickFormatter;\n /**\n * Format the conversion share behind the `percent` formats. Receives the share\n * as a fraction (`0.52`); defaults to `funnelChartPercent` (`\"52.0%\"`). Pass\n * `createTickFormatter({ style: 'percent', minimumFractionDigits: 1 }, locale)`\n * for a locale that doesn't write a bare `%`.\n */\n percentFormatter?: TickFormatter;\n /** Render the legend. Off by default: a funnel labels its stages on the chart. */\n showLegend?: boolean;\n /** Which edge the legend sits on. */\n legendPos?: 'top' | 'bottom';\n /**\n * How the stages are coloured. `palette` (the default) gives each stage its own\n * `config` colour; `gradient` ramps one hue — `gradientColor` — from the widest\n * stage down to the narrowest.\n */\n colorMode?: FunnelChartColorMode;\n /**\n * The hue `colorMode=\"gradient\"` ramps. Defaults to the first visible stage's\n * own colour — including a `stageSettings` colour set on it — so a palette\n * config becomes a ramp without a second colour decision. Ignored when\n * `colorMode` is `palette`.\n */\n gradientColor?: string;\n /** Per-stage `color` / `hidden` overrides, keyed by the stage's `nameKey` value. */\n stageSettings?: Record<string, FunnelChartStageSettings>;\n /** Outline the hovered segment. */\n showActiveShape?: boolean;\n /**\n * Segment border colour. Reference an existing semantic `--ui-*` token.\n * Defaults to the border token when `strokeWidth` is set on its own.\n */\n stroke?: string;\n /** Segment border width, in px. Implies a default `stroke` when given alone. */\n strokeWidth?: number;\n /**\n * Funnel width — a number in px or a percentage of the plot area (`'70%'`).\n * Defaults to the full plot area, except that a composite `labelFormat` beside\n * the funnel narrows it to leave the label real room to wrap into.\n */\n funnelWidth?: number | string;\n /**\n * Plot-area margin, in px. Merged over the defaults per side, so passing one\n * side keeps the others.\n */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n showTooltip?: boolean;\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize formatting, per-series rows, or extra fields without composing\n * recharts yourself. Ignored when `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n}\n\nconst FunnelChart = React.forwardRef<HTMLDivElement, FunnelChartProps>(\n (\n {\n className,\n config,\n data,\n dataKey,\n nameKey,\n lastShape = 'triangle',\n reversed = false,\n showLabels = true,\n labelFormat = 'name',\n labelPosition = 'right',\n showValueLabels = false,\n valuePosition,\n labelFill,\n labelFormatter,\n percentFormatter,\n showLegend = false,\n legendPos = 'bottom',\n colorMode = 'palette',\n gradientColor,\n stageSettings,\n showActiveShape = false,\n stroke,\n strokeWidth,\n funnelWidth,\n margin,\n showTooltip = true,\n tooltipContent,\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n\n // Stamp each row with its `fill` and its label texts (the shadcn data-driven\n // pattern). A Funnel's default fill is grey (#808080) and recharts doesn't\n // carry a per-segment color on the tooltip/legend payload item, so putting the\n // color on the data row is what lets a real hover resolve each segment's\n // color. (The forced-open `defaultIndex` VR snapshot can still show a neutral\n // indicator — it synthesizes the open state without a pointer hover.)\n const seriesData = React.useMemo(() => {\n const visible = data.filter(\n (row) => !stageSettings?.[String(row[nameKey])]?.hidden\n );\n // The ramp's default hue is the first visible stage's own colour — its\n // `stageSettings` override if it has one, otherwise `--color-*` rather than\n // `config[name].color` so a per-theme `theme` entry resolves too. Reading\n // the override matters because that stage paints with it, so ramping from\n // the config colour would start the ramp on a hue nothing on screen shows.\n const firstName = visible.length ? String(visible[0][nameKey]) : undefined;\n const base =\n gradientColor ??\n (firstName === undefined\n ? undefined\n : (stageSettings?.[firstName]?.color ?? `var(--color-${firstName})`));\n // The percentages are conversions from the funnel's widest stage, and\n // recharts sizes the trapezoids off `Math.max` of the values — so the base\n // is the largest visible value, not the first row. Taking the first row\n // would read as a conversion above 100% whenever the data isn't sorted\n // descending.\n const conversionBase = visible.reduce((widest, row) => {\n const value = Number(row[dataKey]);\n return Number.isFinite(value) && value > widest ? value : widest;\n }, 0);\n\n return visible.map((row, index) => {\n const name = String(row[nameKey]);\n const override = stageSettings?.[name]?.color;\n const fill =\n override ??\n (colorMode === 'gradient' && base\n ? gradientStageFill(base, index, visible.length)\n : `var(--color-${name})`);\n const labelArgs = {\n name,\n value: row[dataKey],\n base: conversionBase,\n formatter: labelFormatter,\n percentFormatter,\n };\n\n return {\n ...row,\n fill,\n [STAGE_LABEL_KEY]: funnelChartLabelText({\n ...labelArgs,\n format: labelFormat,\n }),\n [STAGE_VALUE_LABEL_KEY]: funnelChartLabelText({\n ...labelArgs,\n format: 'value',\n }),\n } as Record<string, string | number>;\n });\n }, [\n colorMode,\n data,\n dataKey,\n gradientColor,\n labelFormat,\n labelFormatter,\n nameKey,\n percentFormatter,\n stageSettings,\n ]);\n\n // recharts 3 builds the legend payload from the graphical item, and `Funnel`\n // — unlike Bar/Line/Area/Pie/Radar/RadialBar/Scatter — never registers one,\n // so a `<Legend>` inside a `FunnelChart` renders empty. The payload is\n // synthesized from the visible stages instead and handed to the shared\n // `ChartLegendContent`, which keeps the funnel on the same legend markers,\n // labels and `config` lookup as every other chart. `payload` carries the row\n // so each entry resolves its own `config` entry via `nameKey`.\n //\n // One entry per *distinct* stage name: same-named stages deliberately share a\n // `--color-<name>`/`config` entry, so a second entry would repeat the first\n // verbatim — and `ChartLegendContent` keys its entries on `value`, so it would\n // also be a duplicate React key.\n const legendPayload = React.useMemo<LegendPayload[]>(() => {\n const seen = new Set<string>();\n return seriesData.flatMap((row) => {\n const value = String(row[nameKey]);\n if (seen.has(value)) return [];\n seen.add(value);\n return [\n {\n value,\n dataKey: nameKey,\n type: 'rect' as const,\n color: String(row.fill),\n payload: row,\n },\n ];\n });\n }, [nameKey, seriesData]);\n\n const resolvedValuePosition =\n valuePosition ?? funnelChartOppositeSide(labelPosition);\n const stagePosition = LABEL_POSITION[labelPosition];\n const valueLabelPosition = LABEL_POSITION[resolvedValuePosition];\n const plotMargin = {\n ...funnelChartLabelMargin({\n showLabels,\n labelPosition,\n showValueLabels,\n valuePosition: resolvedValuePosition,\n }),\n ...margin,\n };\n // A caller-supplied `funnelWidth` wins; otherwise a composite label beside the\n // funnel narrows it to leave itself room to wrap into.\n const resolvedFunnelWidth =\n funnelWidth ??\n funnelChartLabelReserve({ showLabels, labelPosition, labelFormat });\n // recharts defaults a Funnel's stroke to a hardcoded `#fff` that\n // `ChartContainer` neutralizes, so a bare `strokeWidth` would widen an\n // invisible border. Pair it with the border token instead.\n const resolvedStroke =\n stroke ??\n (strokeWidth != null ? 'var(--ui-border-on-surface-border)' : undefined);\n\n return (\n <div\n ref={ref}\n data-last-shape={lastShape}\n className={cn(funnelChartVariants({ lastShape }), className)}\n {...props}\n >\n <ChartContainer config={config} className=\"size-full\">\n <RechartsFunnelChart margin={plotMargin}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ?? (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n )\n }\n />\n )}\n {showLegend && (\n <ChartLegend\n verticalAlign={legendPos}\n content={() => (\n <ChartLegendContent\n payload={legendPayload}\n verticalAlign={legendPos}\n nameKey={nameKey}\n />\n )}\n />\n )}\n <Funnel\n dataKey={dataKey}\n nameKey={nameKey}\n data={seriesData}\n lastShapeType={lastShape ?? 'triangle'}\n reversed={reversed}\n width={resolvedFunnelWidth}\n stroke={resolvedStroke}\n strokeWidth={strokeWidth}\n activeShape={showActiveShape ? ACTIVE_SHAPE : undefined}\n {...animation}\n >\n {seriesData.map((entry, index) => (\n // Keyed by index, not the name: two stages could share a nameKey\n // value, which would collide as a React key. Same-named stages\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell key={index} fill={String(entry.fill)} />\n ))}\n {showLabels && (\n <LabelList\n position={stagePosition}\n dataKey={STAGE_LABEL_KEY}\n className={\n labelFill ? undefined : resolveLabelFillClass(stagePosition)\n }\n fill={labelFill}\n stroke=\"none\"\n />\n )}\n {showValueLabels && (\n <LabelList\n position={valueLabelPosition}\n dataKey={STAGE_VALUE_LABEL_KEY}\n className={\n labelFill\n ? undefined\n : resolveLabelFillClass(valueLabelPosition)\n }\n fill={labelFill}\n stroke=\"none\"\n />\n )}\n </Funnel>\n </RechartsFunnelChart>\n </ChartContainer>\n </div>\n );\n }\n);\nFunnelChart.displayName = 'FunnelChart';\n\nexport { FunnelChart, funnelChartVariants };\n"],"names":["funnelChartVariants","cva","LABEL_POSITION","funnelChartOppositeSide","labelPosition","STAGE_LABEL_KEY","STAGE_VALUE_LABEL_KEY","GRADIENT_MIN_MIX","ACTIVE_SHAPE","LABEL_SIDE_INSET","BASE_MARGIN","COMPOSITE_FUNNEL_WIDTH","isCompositeFormat","format","funnelChartLabelReserve","options","showLabels","labelFormat","funnelChartLabelMargin","showValueLabels","valuePosition","funnelChartPercent","value","n","funnelChartLabelText","name","base","formatter","percentFormatter","text","numeric","share","gradientStageFill","index","count","strength","FunnelChart","React","className","config","data","dataKey","nameKey","lastShape","reversed","labelFill","labelFormatter","showLegend","legendPos","colorMode","gradientColor","stageSettings","showActiveShape","stroke","strokeWidth","funnelWidth","margin","showTooltip","tooltipContent","animate","animationDuration","animationBegin","animationEasing","props","ref","animation","resolveAnimation","seriesData","visible","row","_a","firstName","conversionBase","widest","fill","labelArgs","legendPayload","seen","resolvedValuePosition","stagePosition","valueLabelPosition","plotMargin","resolvedFunnelWidth","resolvedStroke","jsx","cn","ChartContainer","jsxs","RechartsFunnelChart","ChartTooltip","ChartTooltipContent","ChartLegend","ChartLegendContent","Funnel","entry","Cell","LabelList","resolveLabelFillClass"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"funnel-chart.js","sources":["../../../../src/components/ui/funnel-chart/funnel-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Funnel,\n FunnelChart as RechartsFunnelChart,\n LabelList,\n} from 'recharts';\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveLabelFillClass,\n type ChartAnimationProps,\n type ChartConfig,\n type ChartPalette,\n type TickFormatter,\n} from '../chart';\n\n// A typed recharts composition over the shared `Chart` primitives. The single\n// CVA axis is the design's Funnel-chart variant set: `lastShape` (does the\n// funnel narrow to a point — `triangle` — or end flat — `rectangle`). The class\n// stays empty because recharts' SVG — not CSS — draws the funnel: `lastShape`\n// drives the `<Funnel>`'s `lastShapeType`. CVA is kept so the variant set is a\n// first-class, spec-conformant part of the API (matched against ui-spec's\n// api.yaml enums) and exposed via `VariantProps`; the resolved value is also\n// mirrored onto `data-last-shape`.\nconst funnelChartVariants = cva('', {\n variants: {\n lastShape: {\n triangle: '',\n rectangle: '',\n },\n },\n defaultVariants: {\n lastShape: 'triangle',\n },\n});\n\n/** Where a stage's label sits relative to its segment. */\nexport type FunnelChartLabelPosition = 'right' | 'left' | 'inside';\n\n/** What a stage's label says. */\nexport type FunnelChartLabelFormat =\n | 'name'\n | 'value'\n | 'percent'\n | 'name-value'\n | 'name-percent'\n | 'value-percent';\n\n/** How the stages are coloured: one `config` entry each, or one shared ramp. */\nexport type FunnelChartColorMode = 'palette' | 'gradient';\n\n/** Override for a single stage, keyed by its `nameKey` value. */\nexport interface FunnelChartStageSettings {\n /**\n * Paint this stage with a colour of its own, overriding both `config` and\n * `colorMode`. Reference an existing semantic `--ui-*` token.\n */\n color?: string;\n /**\n * Drop the stage from the funnel — and from the legend, labels and the\n * conversion percentages, which are computed over the visible stages only.\n */\n hidden?: boolean;\n}\n\n// recharts' `LabelList` positions that a funnel trapezoid understands. `inside`\n// is recharts' `center`: a trapezoid has no `insideLeft`/`insideTop` anchor, so\n// centring is the only on-segment placement. `outside` is not offered — for a\n// funnel it would mean \"beside the segment\", which is what `right`/`left`\n// already are, and recharts' polar `outside` doesn't apply to a trapezoid.\nconst LABEL_POSITION: Record<\n FunnelChartLabelPosition,\n 'right' | 'left' | 'center'\n> = {\n right: 'right',\n left: 'left',\n inside: 'center',\n};\n\n/**\n * Where the value labels sit when the caller doesn't say: opposite the names, so\n * the two lists never stack on the same edge. `inside` names sit on the segments\n * and leave both edges free, so the values take the default `right`.\n *\n * A static default can't do this — `valuePosition=\"left\"` beside\n * `labelPosition=\"left\"` draws both lists at the same anchor, overprinting the\n * name with its own value.\n */\nexport function funnelChartOppositeSide(\n labelPosition: FunnelChartLabelPosition\n): FunnelChartLabelPosition {\n switch (labelPosition) {\n case 'right':\n return 'left';\n case 'left':\n return 'right';\n case 'inside':\n return 'right';\n }\n}\n\n// Synthetic row fields the two label lists read. A `LabelList` resolves its text\n// from a `dataKey`, and its `formatter` only ever sees that one field's value —\n// so a composite label (\"Signups: 52.0%\") has to be composed onto the row before\n// recharts sees it, the same way `fill` already is.\nconst STAGE_LABEL_KEY = '__stageLabel';\nconst STAGE_VALUE_LABEL_KEY = '__stageValueLabel';\n\n// How far the gradient ramp fades by the last stage: the final segment is mixed\n// down to this share of the base colour, the first stays at full strength.\nconst GRADIENT_MIN_MIX = 45;\n\n// Hover highlight for `showActiveShape` — an outline rather than a fill change,\n// so the segment keeps the colour that ties it to its legend entry and label.\nconst ACTIVE_SHAPE = {\n stroke: 'var(--ui-border-on-surface-border-active)',\n strokeWidth: 2,\n} as const;\n\n// The inset reserved on whichever side a label list sits. It is *not* label\n// room — see `funnelChartLabelReserve` for why — but a label whose text can't\n// wrap (one long word) does overflow past the plot edge, and this is what keeps\n// that overflow inside the SVG.\nconst LABEL_SIDE_INSET = 96;\n\n// The plot-area margin a funnel has always been drawn with: the right side\n// reserved, the left not.\nconst BASE_MARGIN = {\n top: 8,\n right: LABEL_SIDE_INSET,\n bottom: 8,\n left: 24,\n};\n\n/**\n * Share of the plot area handed to the funnel when a composite label sits to its\n * right. The rest becomes real label room — see `funnelChartLabelReserve`.\n *\n * A percentage, not a pixel count: the plot area is responsive, and recharts\n * resolves `Funnel`'s `width` against it, so a ratio keeps the reserve\n * proportional at every chart size.\n */\nconst COMPOSITE_FUNNEL_WIDTH = '75%';\n\n/** A label format that pairs two fields, so its text is roughly twice as wide. */\nfunction isCompositeFormat(format: FunnelChartLabelFormat): boolean {\n return format.includes('-');\n}\n\n/**\n * Reserve room for a composite label beside the funnel, by narrowing the\n * **funnel** rather than the plot area.\n *\n * recharts word-wraps a label against the space between its trapezoid's\n * mid-height edge and the *plot area* edge (`getCartesianPosition` clamps\n * `width` to `parentViewBox`, which `Funnel` sets to the plot box). A margin\n * shrinks the plot area and the funnel inside it in lockstep, so widening\n * `margin.right` moves the clamp edge *inward* and leaves a composite label\n * with less room, not more — the reserved strip sits outside `parentViewBox`\n * where the text can never reach it.\n *\n * `Funnel`'s `width` is the lever that works: it scales the funnel within the\n * plot area while `parentViewBox` stays put, and because recharts anchors the\n * funnel at the plot area's left edge (`offsetX = offset.left`), everything it\n * frees lands on the right. Narrowing the funnel to\n * `COMPOSITE_FUNNEL_WIDTH` turns ~35px of wrap width into ~140px on a 460px\n * chart.\n *\n * Returns `undefined` — i.e. leave the funnel at its full width — for every\n * other case, so a funnel with plain labels, no labels, or `inside` labels keeps\n * exactly the geometry it has always had.\n *\n * There is no equivalent lever for `labelPosition=\"left\"`: the widest trapezoid\n * always starts flush at the plot area's left edge, so a left-hand label is\n * bounded by how sharply the funnel narrows and narrowing it further only makes\n * that worse. Composite left-hand labels wrap; that's a recharts limitation, not\n * a tuning choice.\n *\n * A caller-supplied `funnelWidth` always wins — this is only the default.\n */\nexport function funnelChartLabelReserve(options: {\n showLabels: boolean;\n labelPosition: FunnelChartLabelPosition;\n labelFormat: FunnelChartLabelFormat;\n}): string | undefined {\n const { showLabels, labelPosition, labelFormat } = options;\n return showLabels &&\n labelPosition === 'right' &&\n isCompositeFormat(labelFormat)\n ? COMPOSITE_FUNNEL_WIDTH\n : undefined;\n}\n\n/**\n * The plot-area margin. Reserves the left inset when a label list sits there, so\n * a left-hand label isn't clipped at the SVG edge; the right inset is always\n * reserved, since that is the geometry every existing funnel was drawn with.\n *\n * This is overflow protection, not label room — the wrap width comes from\n * `funnelChartLabelReserve`. A caller-supplied `margin` is merged over these\n * defaults per side, so `margin={{ right: 160 }}` keeps the default top, bottom\n * and left rather than collapsing them to zero.\n */\nexport function funnelChartLabelMargin(options: {\n showLabels: boolean;\n labelPosition: FunnelChartLabelPosition;\n showValueLabels: boolean;\n valuePosition: FunnelChartLabelPosition;\n}) {\n const { showLabels, labelPosition, showValueLabels, valuePosition } = options;\n const stageLabelAt = showLabels ? labelPosition : undefined;\n const valueLabelAt = showValueLabels ? valuePosition : undefined;\n\n return {\n ...BASE_MARGIN,\n left:\n stageLabelAt === 'left' || valueLabelAt === 'left'\n ? LABEL_SIDE_INSET\n : BASE_MARGIN.left,\n };\n}\n\n/**\n * Format a conversion share the way the default `percent` label reads it:\n * `0.52 → \"52.0%\"`. Exported so a `percentFormatter` can wrap or replace it.\n */\nexport const funnelChartPercent: TickFormatter = (value) => {\n const n = typeof value === 'number' ? value : Number(value);\n return Number.isFinite(n) ? `${(n * 100).toFixed(1)}%` : String(value);\n};\n\n/**\n * Compose one stage's label text.\n *\n * `base` is the value the percentage is measured against — the funnel's widest\n * stage, not the sum of every stage. A funnel's stages are nested subsets of\n * each other rather than parts of a whole, so their values don't add up to\n * anything meaningful; the share that reads as information is the conversion\n * from the top of the funnel. (A pie's `percent` is the other way round — there\n * a slice really is a share of the total.)\n *\n * Only the numeric part goes through `formatter`, so a label and its tooltip can\n * share one formatter; the share goes through `percentFormatter`, so a locale\n * that doesn't write a bare `%` can replace it. The percent formats degrade to\n * the name (or to the value) rather than printing `NaN%` when there is nothing\n * to divide by.\n */\nexport function funnelChartLabelText(options: {\n name: string;\n value: number | string;\n base: number;\n format: FunnelChartLabelFormat;\n formatter?: TickFormatter;\n percentFormatter?: TickFormatter;\n}): string {\n const {\n name,\n value,\n base,\n format,\n formatter,\n percentFormatter = funnelChartPercent,\n } = options;\n const text = formatter ? formatter(value) : String(value);\n const numeric = typeof value === 'number' ? value : Number(value);\n const share =\n base > 0 && Number.isFinite(numeric)\n ? percentFormatter(numeric / base)\n : '';\n\n switch (format) {\n case 'name':\n return name;\n case 'value':\n return text;\n case 'percent':\n return share;\n case 'name-value':\n return `${name}: ${text}`;\n case 'name-percent':\n return share ? `${name}: ${share}` : name;\n case 'value-percent':\n return share ? `${text} (${share})` : text;\n }\n}\n\n/**\n * Mix one stage of the gradient ramp. Mixing toward the surface (rather than\n * lowering alpha) keeps every stage opaque, so an on-segment label keeps its\n * contrast and the ramp reads the same over any card background.\n */\nfunction gradientStageFill(base: string, index: number, count: number): string {\n if (count <= 1) return base;\n const strength = 100 - ((100 - GRADIENT_MIN_MIX) * index) / (count - 1);\n return `color-mix(in oklab, ${base} ${strength.toFixed(1)}%, var(--ui-background-surface-primary))`;\n}\n\nexport interface FunnelChartProps\n extends\n Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof funnelChartVariants>,\n ChartAnimationProps {\n /**\n * The dataviz palette this chart's series are painted from. Series that\n * state no `color` of their own take a stop of it. See `ChartPalette`.\n */\n palette?: ChartPalette;\n /** Row-per-stage data. Each object holds the stage's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<Record<string, string | number>>;\n /**\n * Per-stage map of `label` / `icon` / `tone`, keyed by the stage's `nameKey`\n * value (imported from the shared `Chart` primitives). Turned into\n * `--color-<name>` custom properties. Series take their colour from the\n * container's `palette`; each entry maps a key to a `label` and an optional\n * `tone`.\n */\n config: ChartConfig;\n /** Numeric field that sizes each stage (the funnel narrows as it drops). */\n dataKey: string;\n /**\n * Label field that names each stage (drives the legend, tooltip, on-chart\n * labels, and the `--color-<name>` lookup). Values should be unique per chart —\n * stages sharing a name share one `config`/color entry.\n */\n nameKey: string;\n /** Flip the funnel so it widens toward the bottom instead of narrowing. */\n reversed?: boolean;\n /** Render a label for each stage — see `labelFormat` and `labelPosition`. */\n showLabels?: boolean;\n /**\n * What each stage's label says. `percent` is the stage's share of the widest\n * stage — its conversion from the top of the funnel.\n */\n labelFormat?: FunnelChartLabelFormat;\n /**\n * Where each stage's label sits. Defaults to `right`, beside the segment.\n * `inside` centres it on the segment, which only works while every stage is\n * wide enough to hold the text — a funnel narrows, so its last stages often\n * are not. Keep the text short there (`percent`) or leave the labels beside\n * the funnel.\n */\n labelPosition?: FunnelChartLabelPosition;\n /**\n * Render a second label carrying the stage's value, so the name and the number\n * can sit on opposite sides of the funnel.\n */\n showValueLabels?: boolean;\n /**\n * Where the value label sits. Defaults to the side opposite `labelPosition`,\n * where every stage has room — so it follows the names rather than pinning\n * itself to one edge. See `labelPosition` before choosing `inside`.\n */\n valuePosition?: FunnelChartLabelPosition;\n /**\n * Colour for both label lists, overriding the contrast-matched default (the\n * on-surface token beside the funnel, the on-fill token on a segment). Pass a\n * `--ui-*` token; only use it when the default doesn't work on your surface.\n */\n labelFill?: string;\n /** Format the numeric part of a label — pass the same formatter used elsewhere. */\n labelFormatter?: TickFormatter;\n /**\n * Format the conversion share behind the `percent` formats. Receives the share\n * as a fraction (`0.52`); defaults to `funnelChartPercent` (`\"52.0%\"`). Pass\n * `createTickFormatter({ style: 'percent', minimumFractionDigits: 1 }, locale)`\n * for a locale that doesn't write a bare `%`.\n */\n percentFormatter?: TickFormatter;\n /** Render the legend. Off by default: a funnel labels its stages on the chart. */\n showLegend?: boolean;\n /** Which edge the legend sits on. */\n legendPos?: 'top' | 'bottom';\n /**\n * How the stages are coloured. `palette` (the default) gives each stage its own\n * `config` colour; `gradient` ramps one hue — `gradientColor` — from the widest\n * stage down to the narrowest.\n */\n colorMode?: FunnelChartColorMode;\n /**\n * The hue `colorMode=\"gradient\"` ramps. Defaults to the first visible stage's\n * own colour — including a `stageSettings` colour set on it — so a palette\n * config becomes a ramp without a second colour decision. Ignored when\n * `colorMode` is `palette`.\n */\n gradientColor?: string;\n /** Per-stage `color` / `hidden` overrides, keyed by the stage's `nameKey` value. */\n stageSettings?: Record<string, FunnelChartStageSettings>;\n /** Outline the hovered segment. */\n showActiveShape?: boolean;\n /**\n * Segment border colour. Reference an existing semantic `--ui-*` token.\n * Defaults to the border token when `strokeWidth` is set on its own.\n */\n stroke?: string;\n /** Segment border width, in px. Implies a default `stroke` when given alone. */\n strokeWidth?: number;\n /**\n * Funnel width — a number in px or a percentage of the plot area (`'70%'`).\n * Defaults to the full plot area, except that a composite `labelFormat` beside\n * the funnel narrows it to leave the label real room to wrap into.\n */\n funnelWidth?: number | string;\n /**\n * Plot-area margin, in px. Merged over the defaults per side, so passing one\n * side keeps the others.\n */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n showTooltip?: boolean;\n /**\n * Replace the default tooltip. Pass a configured `ChartTooltipContent`\n * (imported from this library) — e.g. with a `formatter` / `labelFormatter` —\n * to customize formatting, per-series rows, or extra fields without composing\n * recharts yourself. Ignored when `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n}\n\nconst FunnelChart = React.forwardRef<HTMLDivElement, FunnelChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKey,\n nameKey,\n lastShape = 'triangle',\n reversed = false,\n showLabels = true,\n labelFormat = 'name',\n labelPosition = 'right',\n showValueLabels = false,\n valuePosition,\n labelFill,\n labelFormatter,\n percentFormatter,\n showLegend = false,\n legendPos = 'bottom',\n colorMode = 'palette',\n gradientColor,\n stageSettings,\n showActiveShape = false,\n stroke,\n strokeWidth,\n funnelWidth,\n margin,\n showTooltip = true,\n tooltipContent,\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n\n // Stamp each row with its `fill` and its label texts (the shadcn data-driven\n // pattern). A Funnel's default fill is grey (#808080) and recharts doesn't\n // carry a per-segment color on the tooltip/legend payload item, so putting the\n // color on the data row is what lets a real hover resolve each segment's\n // color. (The forced-open `defaultIndex` VR snapshot can still show a neutral\n // indicator — it synthesizes the open state without a pointer hover.)\n const seriesData = React.useMemo(() => {\n const visible = data.filter(\n (row) => !stageSettings?.[String(row[nameKey])]?.hidden\n );\n // The ramp's default hue is the first visible stage's own colour — its\n // `stageSettings` override if it has one, otherwise the `--color-*`\n // custom property that ChartStyle emits for this series. Reading\n // the override matters because that stage paints with it, so ramping from\n // the config colour would start the ramp on a hue nothing on screen shows.\n const firstName = visible.length ? String(visible[0][nameKey]) : undefined;\n const base =\n gradientColor ??\n (firstName === undefined\n ? undefined\n : (stageSettings?.[firstName]?.color ?? `var(--color-${firstName})`));\n // The percentages are conversions from the funnel's widest stage, and\n // recharts sizes the trapezoids off `Math.max` of the values — so the base\n // is the largest visible value, not the first row. Taking the first row\n // would read as a conversion above 100% whenever the data isn't sorted\n // descending.\n const conversionBase = visible.reduce((widest, row) => {\n const value = Number(row[dataKey]);\n return Number.isFinite(value) && value > widest ? value : widest;\n }, 0);\n\n return visible.map((row, index) => {\n const name = String(row[nameKey]);\n const override = stageSettings?.[name]?.color;\n const fill =\n override ??\n (colorMode === 'gradient' && base\n ? gradientStageFill(base, index, visible.length)\n : `var(--color-${name})`);\n const labelArgs = {\n name,\n value: row[dataKey],\n base: conversionBase,\n formatter: labelFormatter,\n percentFormatter,\n };\n\n return {\n ...row,\n fill,\n [STAGE_LABEL_KEY]: funnelChartLabelText({\n ...labelArgs,\n format: labelFormat,\n }),\n [STAGE_VALUE_LABEL_KEY]: funnelChartLabelText({\n ...labelArgs,\n format: 'value',\n }),\n } as Record<string, string | number>;\n });\n }, [\n colorMode,\n data,\n dataKey,\n gradientColor,\n labelFormat,\n labelFormatter,\n nameKey,\n percentFormatter,\n stageSettings,\n ]);\n\n // recharts 3 builds the legend payload from the graphical item, and `Funnel`\n // — unlike Bar/Line/Area/Pie/Radar/RadialBar/Scatter — never registers one,\n // so a `<Legend>` inside a `FunnelChart` renders empty. The payload is\n // synthesized from the visible stages instead and handed to the shared\n // `ChartLegendContent`, which keeps the funnel on the same legend markers,\n // labels and `config` lookup as every other chart. `payload` carries the row\n // so each entry resolves its own `config` entry via `nameKey`.\n //\n // One entry per *distinct* stage name: same-named stages deliberately share a\n // `--color-<name>`/`config` entry, so a second entry would repeat the first\n // verbatim — and `ChartLegendContent` keys its entries on `value`, so it would\n // also be a duplicate React key.\n const legendPayload = React.useMemo<LegendPayload[]>(() => {\n const seen = new Set<string>();\n return seriesData.flatMap((row) => {\n const value = String(row[nameKey]);\n if (seen.has(value)) return [];\n seen.add(value);\n return [\n {\n value,\n dataKey: nameKey,\n type: 'rect' as const,\n color: String(row.fill),\n payload: row,\n },\n ];\n });\n }, [nameKey, seriesData]);\n\n const resolvedValuePosition =\n valuePosition ?? funnelChartOppositeSide(labelPosition);\n const stagePosition = LABEL_POSITION[labelPosition];\n const valueLabelPosition = LABEL_POSITION[resolvedValuePosition];\n const plotMargin = {\n ...funnelChartLabelMargin({\n showLabels,\n labelPosition,\n showValueLabels,\n valuePosition: resolvedValuePosition,\n }),\n ...margin,\n };\n // A caller-supplied `funnelWidth` wins; otherwise a composite label beside the\n // funnel narrows it to leave itself room to wrap into.\n const resolvedFunnelWidth =\n funnelWidth ??\n funnelChartLabelReserve({ showLabels, labelPosition, labelFormat });\n // recharts defaults a Funnel's stroke to a hardcoded `#fff` that\n // `ChartContainer` neutralizes, so a bare `strokeWidth` would widen an\n // invisible border. Pair it with the border token instead.\n const resolvedStroke =\n stroke ??\n (strokeWidth != null ? 'var(--ui-border-on-surface-border)' : undefined);\n\n return (\n <div\n ref={ref}\n data-last-shape={lastShape}\n className={cn(funnelChartVariants({ lastShape }), className)}\n {...props}\n >\n <ChartContainer config={config} palette={palette} className=\"size-full\">\n <RechartsFunnelChart margin={plotMargin}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ?? (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n )\n }\n />\n )}\n {showLegend && (\n <ChartLegend\n verticalAlign={legendPos}\n content={() => (\n <ChartLegendContent\n payload={legendPayload}\n verticalAlign={legendPos}\n nameKey={nameKey}\n />\n )}\n />\n )}\n <Funnel\n dataKey={dataKey}\n nameKey={nameKey}\n data={seriesData}\n lastShapeType={lastShape ?? 'triangle'}\n reversed={reversed}\n width={resolvedFunnelWidth}\n stroke={resolvedStroke}\n strokeWidth={strokeWidth}\n activeShape={showActiveShape ? ACTIVE_SHAPE : undefined}\n {...animation}\n >\n {seriesData.map((entry, index) => (\n // Keyed by index, not the name: two stages could share a nameKey\n // value, which would collide as a React key. Same-named stages\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell key={index} fill={String(entry.fill)} />\n ))}\n {showLabels && (\n <LabelList\n position={stagePosition}\n dataKey={STAGE_LABEL_KEY}\n className={\n labelFill ? undefined : resolveLabelFillClass(stagePosition)\n }\n fill={labelFill}\n stroke=\"none\"\n />\n )}\n {showValueLabels && (\n <LabelList\n position={valueLabelPosition}\n dataKey={STAGE_VALUE_LABEL_KEY}\n className={\n labelFill\n ? undefined\n : resolveLabelFillClass(valueLabelPosition)\n }\n fill={labelFill}\n stroke=\"none\"\n />\n )}\n </Funnel>\n </RechartsFunnelChart>\n </ChartContainer>\n </div>\n );\n }\n);\nFunnelChart.displayName = 'FunnelChart';\n\nexport { FunnelChart, funnelChartVariants };\n"],"names":["funnelChartVariants","cva","LABEL_POSITION","funnelChartOppositeSide","labelPosition","STAGE_LABEL_KEY","STAGE_VALUE_LABEL_KEY","GRADIENT_MIN_MIX","ACTIVE_SHAPE","LABEL_SIDE_INSET","BASE_MARGIN","COMPOSITE_FUNNEL_WIDTH","isCompositeFormat","format","funnelChartLabelReserve","options","showLabels","labelFormat","funnelChartLabelMargin","showValueLabels","valuePosition","funnelChartPercent","value","n","funnelChartLabelText","name","base","formatter","percentFormatter","text","numeric","share","gradientStageFill","index","count","strength","FunnelChart","React","className","config","palette","data","dataKey","nameKey","lastShape","reversed","labelFill","labelFormatter","showLegend","legendPos","colorMode","gradientColor","stageSettings","showActiveShape","stroke","strokeWidth","funnelWidth","margin","showTooltip","tooltipContent","animate","animationDuration","animationBegin","animationEasing","props","ref","animation","resolveAnimation","seriesData","visible","row","_a","firstName","conversionBase","widest","fill","labelArgs","legendPayload","seen","resolvedValuePosition","stagePosition","valueLabelPosition","plotMargin","resolvedFunnelWidth","resolvedStroke","jsx","cn","ChartContainer","jsxs","RechartsFunnelChart","ChartTooltip","ChartTooltipContent","ChartLegend","ChartLegendContent","Funnel","entry","Cell","LabelList","resolveLabelFillClass"],"mappings":"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"}
|