@acronis-platform/ui-react 5.5.8 → 6.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.
Files changed (34) hide show
  1. package/dist/components/ui/chart/chart.js +14 -11
  2. package/dist/components/ui/chart/chart.js.map +1 -1
  3. package/dist/components/ui/chart-state/chart-state.js +47 -37
  4. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  5. package/dist/components/ui/chart-widget/chart-widget.js +21 -23
  6. package/dist/components/ui/chart-widget/chart-widget.js.map +1 -1
  7. package/dist/components/ui/data-table/data-table-column-header.js +22 -14
  8. package/dist/components/ui/data-table/data-table-column-header.js.map +1 -1
  9. package/dist/components/ui/data-table/data-table.js +137 -137
  10. package/dist/components/ui/data-table/data-table.js.map +1 -1
  11. package/dist/components/ui/dialog/dialog.js +45 -32
  12. package/dist/components/ui/dialog/dialog.js.map +1 -1
  13. package/dist/components/ui/pie-chart/pie-chart.js +88 -84
  14. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  15. package/dist/index.js +86 -87
  16. package/dist/llms/chart-state.md +81 -164
  17. package/dist/llms/chart-widget.md +27 -25
  18. package/dist/llms/dialog-root.md +40 -13
  19. package/dist/llms/dialog.md +26 -1
  20. package/dist/llms.txt +3 -3
  21. package/dist/react.js +86 -87
  22. package/dist/src/components/ui/chart/chart.d.ts +3 -3
  23. package/dist/src/components/ui/chart-state/chart-state.d.ts +5 -14
  24. package/dist/src/components/ui/chart-state/index.d.ts +0 -1
  25. package/dist/src/components/ui/chart-widget/chart-widget.d.ts +10 -15
  26. package/dist/src/components/ui/data-table/data-table.d.ts +2 -2
  27. package/dist/src/components/ui/dialog/dialog.d.ts +9 -0
  28. package/dist/src/components/ui/dialog/dialog.docs.d.ts +5 -1
  29. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +1 -1
  30. package/dist/ui-react.css +1 -1
  31. package/package.json +13 -13
  32. package/dist/components/ui/chart-state/chart-state-illustrations.js +0 -412
  33. package/dist/components/ui/chart-state/chart-state-illustrations.js.map +0 -1
  34. package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +0 -6
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.js","names":[],"sources":["../../../../src/components/ui/dialog/dialog.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Dialog as DialogPrimitive } from '@base-ui/react/dialog';\nimport { TimesIcon } from '@acronis-platform/icons-react/stroke-mono';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\nimport { Button } from '../button';\nimport { ButtonIcon } from '../button-icon';\nimport { DialogFooterDefault } from '../dialog-footer-default';\nimport { InputText } from '../input-text';\nimport { Loading } from '../loading';\n\n// Initial version ported from `@acronis-platform/shadcn-uikit`'s `dialog`\n// (packages/ui-legacy/src/components/ui/dialog.tsx). A modal overlay built on\n// the Base UI Dialog primitive (keyboard, focus trap, scroll lock, ARIA come\n// from Base UI). A `--ui-dialog-*` / `--ui-button-icon-*` token tier covers the\n// container + close button (reconciled against the Figma \"DialogDefault\" node,\n// 6343:58898); the composable DialogRoot / DialogHeaderRoot / DialogFooterRoot\n// / DialogBodyRoot parts below are internal-only (no public export, no Figma\n// node of their own) and keep the prior semantic-token approach — not to be\n// confused with the recipe-local `DialogHeader`/`DialogBody` further down,\n// which back the public `Dialog` and are reconciled against Figma:\n// • overlay -> var(--ui-background-backdrop-screen) (legacy `bg-black/80`)\n// • popup -> var(--ui-dialog-container-color) / var(--ui-dialog-container-border-radius)\n// • header / footer -> bg-background = --ui-background-surface-primary (white\n// bars over the muted body), divided by border-border\n// • title -> text-foreground / description -> text-muted-foreground\n// • close -> var(--ui-button-icon-global-icon-color-*) (idle/hover/active\n// share one blue in the default brand), bg via\n// var(--ui-button-icon-global-container-color-*), focus ring var(--ui-focus-primary)\n// Enter/exit animations use `tw-animate-css` (imported in styles/index.css),\n// keyed to Base UI's data-[open] / data-[closed] state attributes — overlay\n// fades, popup fades + zooms. Those keyframes only animate `transform`/\n// `opacity`, so the nested-dialog recede below can safely live on the CSS\n// `scale` property + an `::after` scrim without fighting them.\n//\n// Nested dialogs: Base UI flags a dialog as nested purely by React-tree\n// position (`Boolean(parentDialogRootContext)` in DialogRoot) and then\n// deliberately skips rendering that dialog's own backdrop, so stacked\n// dialogs don't darken progressively. To compensate, Base UI sets\n// `data-nested-dialog-open` (plus a `--nested-dialogs` count) on the PARENT\n// popup while a nested dialog is open, expecting the consuming library to\n// give the parent a \"recede\" treatment for a visible depth cue. That's what\n// the data-[nested-dialog-open] rules below do: the parent popup scales\n// down slightly and gets a token-tinted scrim (a reduced-opacity mix of the\n// same --ui-background-backdrop-screen the overlay uses, not a hardcoded\n// color) while the child — which keeps its own backdrop suppressed — sits\n// on top. Stacked-modal depth reads through relative layer contrast, so the\n// scrim is theme-split in approach, not just strength: in light the parent\n// dims (36% of the backdrop token reads clearly over the white container).\n// In dark the container and the backdrop token are the same near-black, so\n// no scrim percentage can separate the layers — and repainting the nested\n// container with a lighter tier token would flatten its header/footer\n// chrome, whose surfaces share those very values. There the separation\n// comes from the Dialog tier's hairline border on the nested popup (an\n// edge stroke that follows each brand's dialog chrome), while the parent\n// keeps the stronger 80% scrim mainly to fade its content.\n// `size` has two values: `sm` (512px, the Figma\n// default, backed by `--ui-dialog-container-size-sm`) and `large` (832px) — the\n// latter predates the Figma node and has no token; it's kept as a plain\n// arbitrary value solely for backward compatibility with existing call sites\n// that need a wider popup (see the `Large` story).\n//\n// Dialog's composable primitive parts (this file) are internal-only — not\n// re-exported from the package root — except `DialogClose`, which stays\n// public: it's required by the `wide` variant's documented custom-`footer`\n// escape hatch (see the `footer` prop and dialog.mdx). The public `Dialog`\n// component below is the sole sanctioned entry point; it's built on these\n// parts internally. (In Figma the matching component set is named\n// \"DialogDefault\"; the code-facing name stays `Dialog` — see dialog.figma.tsx.)\n\n// Figma's \"DialogWelcome\" frame (variant=carousel, node 6353:6164) wraps its\n// popup preview in a `p-[48px]` ancestor — the minimum margin the popup must\n// keep from the viewport edge on any screen size. `max-w`/`max-h` (not\n// padding, since the popup itself is centered via `fixed` + transform)\n// enforce it here so every consumer keeps that margin, not just the frame\n// that surfaced it.\nconst dialogContentVariants = cva(\n 'fixed left-1/2 top-1/2 z-50 flex max-h-[calc(100dvh-96px)] w-full min-w-[var(--ui-dialog-container-width-min)] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-[var(--ui-dialog-container-border-radius)] bg-[var(--ui-dialog-container-color)] text-foreground shadow-lg duration-200 transition-[scale] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-[color-mix(in_oklab,var(--ui-background-backdrop-screen)_36%,transparent)] dark:after:bg-[color-mix(in_oklab,var(--ui-background-backdrop-screen)_80%,transparent)] after:opacity-0 after:transition-opacity after:duration-200 after:content-[\"\"] data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95 data-[nested-dialog-open]:scale-[0.96] data-[nested-dialog-open]:after:opacity-100 dark:data-[nested]:border dark:data-[nested]:border-[var(--ui-dialog-header-border-color)]',\n {\n variants: {\n size: {\n sm: 'max-w-[min(var(--ui-dialog-container-size-sm),calc(100dvw-96px))]',\n large: 'max-w-[min(832px,calc(100dvw-96px))]',\n },\n },\n defaultVariants: {\n size: 'sm',\n },\n }\n);\n\nconst DialogRoot = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Backdrop>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Backdrop>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Backdrop\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-[var(--ui-background-backdrop-screen)] duration-200 data-[open]:animate-in data-[open]:fade-in-0 data-[closed]:animate-out data-[closed]:fade-out-0',\n className\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = 'DialogOverlay';\n\nexport interface DialogContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Popup>,\n VariantProps<typeof dialogContentVariants> {\n /**\n * Popup max-width. `sm` (512px, default) is the Figma-defined size; `large`\n * (832px) is a backward-compatibility size with no design token (see the\n * `Large` story).\n */\n size?: VariantProps<typeof dialogContentVariants>['size'];\n /**\n * Render the content inside a portal (default `true`). Base UI requires the\n * Popup to sit in a Portal for correct stacking; set `false` for inline usage\n * (e.g. when the caller supplies its own `DialogPortal`, or in tests).\n */\n portal?: boolean;\n /**\n * Portal container. Pass a shadow-root mount for isolated-style previews\n * (the docs demos do this via `useShadowMount`).\n */\n portalContainer?: DialogPrimitive.Portal.Props['container'];\n /** Keep the content mounted while closed (Base UI `Portal` prop). */\n keepMounted?: DialogPrimitive.Portal.Props['keepMounted'];\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Popup>,\n DialogContentProps\n>(\n (\n {\n className,\n children,\n size,\n portal = true,\n portalContainer,\n keepMounted,\n ...props\n },\n ref\n ) => {\n const ctxContainer = usePortalContainer();\n const resolvedContainer = portalContainer ?? ctxContainer;\n\n const popup = (\n <>\n <DialogOverlay />\n <DialogPrimitive.Popup\n ref={ref}\n className={cn(dialogContentVariants({ size }), className)}\n {...props}\n >\n {children}\n </DialogPrimitive.Popup>\n </>\n );\n\n return portal ? (\n <DialogPrimitive.Portal container={resolvedContainer} keepMounted={keepMounted}>\n {popup}\n </DialogPrimitive.Portal>\n ) : (\n popup\n );\n }\n);\nDialogContent.displayName = 'DialogContent';\n\nconst DialogHeaderRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex h-16 items-center gap-4 border-b border-border bg-background px-5 py-4',\n className\n )}\n {...props}\n />\n));\nDialogHeaderRoot.displayName = 'DialogHeaderRoot';\n\nconst DialogFooterRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex h-16 items-center justify-end gap-4 border-t border-border bg-background px-6 py-4',\n className\n )}\n {...props}\n />\n));\nDialogFooterRoot.displayName = 'DialogFooterRoot';\n\nconst DialogBodyRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn('flex-1 overflow-auto p-6', className)} {...props} />\n));\nDialogBodyRoot.displayName = 'DialogBodyRoot';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n 'flex-1 text-2xl font-normal leading-8 text-foreground',\n className\n )}\n {...props}\n />\n));\nDialogTitle.displayName = 'DialogTitle';\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = 'DialogDescription';\n\nexport interface DialogCloseButtonProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close> {\n /** Accessible name for screen readers. Defaults to `'Close'`. */\n closeLabel?: string;\n}\n\nconst DialogCloseButton = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Close>,\n DialogCloseButtonProps\n>(({ className, closeLabel = 'Close', ...props }, ref) => (\n <DialogPrimitive.Close\n ref={ref}\n render={<ButtonIcon variant=\"ghost\" />}\n className={className}\n {...props}\n >\n <TimesIcon />\n <span className=\"sr-only\">{closeLabel}</span>\n </DialogPrimitive.Close>\n));\nDialogCloseButton.displayName = 'DialogCloseButton';\n\n// The recipe-specific header: title + close button, reconciled against the\n// Figma \"DialogHeader\" node (4220:3516) via the `--ui-dialog-header-*` tier.\n// Local to this file (not exported) — distinct from the generic, unstyled\n// `DialogHeaderRoot` composable primitive above.\ninterface DialogHeaderProps {\n title: string;\n closeLabel?: string;\n}\n\nfunction DialogHeader({ title, closeLabel }: DialogHeaderProps) {\n return (\n <div className=\"flex h-[var(--ui-dialog-header-height)] shrink-0 items-center gap-[var(--ui-dialog-header-gap)] border-b-[length:var(--ui-dialog-header-border-width)] border-[var(--ui-dialog-header-border-color)] bg-[var(--ui-dialog-header-color)] px-[var(--ui-dialog-header-padding-x)]\">\n <DialogTitle>{title}</DialogTitle>\n <DialogCloseButton closeLabel={closeLabel} />\n </div>\n );\n}\n\n// The recipe-specific body: the variant's canned copy (or the `children`\n// override), reconciled against the Figma DialogDefault node via the\n// `--ui-dialog-body-*` tier. Local to this file — distinct from the generic\n// `DialogBodyRoot` composable primitive above.\ninterface DialogBodyProps {\n children: React.ReactNode;\n /** Makes the body unfocusable/unclickable while the loading overlay is shown. */\n inert?: boolean;\n}\n\nfunction DialogBody({ children, inert }: DialogBodyProps) {\n return (\n <div\n inert={inert}\n className=\"flex min-h-[var(--ui-dialog-body-height-min)] flex-col justify-center-safe gap-[var(--ui-dialog-body-gap)] overflow-y-auto px-4 py-[var(--ui-dialog-body-padding-y)]\"\n >\n {typeof children === 'string' ? (\n <p className=\"text-sm leading-6 text-foreground\">{children}</p>\n ) : (\n children\n )}\n </div>\n );\n}\n\n// The recipe-specific footer actions: a secondary (dismiss) button plus a\n// primary action, the latter either closing the dialog itself (`read-only`'s\n// \"Done\") or firing `onPrimaryAction` and leaving the close to the caller.\n// Local to this file — distinct from the generic `DialogFooterRoot`.\ninterface DialogFooterActionsProps {\n secondaryLabel?: string;\n primaryLabel: string;\n primaryVariant: 'default' | 'destructive';\n primaryCloses?: boolean;\n onPrimaryAction?: () => void;\n}\n\nfunction DialogFooterActions({\n secondaryLabel,\n primaryLabel,\n primaryVariant,\n primaryCloses,\n onPrimaryAction,\n}: DialogFooterActionsProps) {\n return (\n <>\n {secondaryLabel && (\n <DialogClose render={<Button variant=\"secondary\">{secondaryLabel}</Button>} />\n )}\n {primaryCloses ? (\n <DialogClose\n onClick={onPrimaryAction}\n render={<Button variant={primaryVariant}>{primaryLabel}</Button>}\n />\n ) : (\n <Button variant={primaryVariant} onClick={onPrimaryAction}>\n {primaryLabel}\n </Button>\n )}\n </>\n );\n}\n\n// A higher-level \"recipe\" built on top of the Dialog primitive parts above\n// (DialogRoot + DialogContent give the portal, backdrop, focus trap, scroll\n// lock and open state; DialogTitle wires the accessible name; DialogCloseButton\n// / DialogClose give the dismiss controls). It bakes in the seven Figma\n// \"DialogDefault\" use-cases (node 6343:58898) plus one legacy `wide` escape\n// hatch kept for backward compatibility: each `variant` picks a canned title,\n// body copy and footer buttons; `children` overrides only the body slot;\n// `title` overrides only the title; `footer` overrides only the footer's\n// action content (the free-form escape hatch `wide` relies on); `hasLoading`\n// drops a spinner overlay over the body + footer. `hasHeader`/`hasFooter`\n// (both default `true`) hide the header/footer chrome entirely — a\n// ui-react-only extension beyond the Figma DialogDefault contract, which\n// always shows both; the title stays rendered off-screen when the header is\n// hidden so the dialog keeps an accessible name.\n//\n// Colors resolve to shipped semantic tokens via the bridged Tailwind names the\n// Dialog family already uses (text-foreground, the backdrop + focus tokens\n// inside DialogContent / DialogCloseButton) and the Button / InputText token\n// tiers — nothing is hand-authored. The loading scrim renders `Loading`'s\n// `onSurfaceSecondary` variant (`--ui-loading-element-container-color-secondary`),\n// matching the design's translucent surface fill.\n//\n// Header + footer geometry/color resolve to the `--ui-dialog-header-*` /\n// `--ui-footer-*` tiers, reconciled against the DialogDefault Figma node.\n\nexport type DialogVariant =\n | 'default'\n | 'rename'\n | 'save changes'\n | 'reset password'\n | 'discard changes'\n | 'accept'\n | 'read-only'\n | 'wide';\n\ninterface DialogVariantContent {\n /**\n * The variant's canned title. A function receives `objectName` (falls back\n * to its own placeholder default when omitted) for variants whose title\n * embeds the acted-on object's name.\n */\n title: string | ((objectName?: string) => string);\n /**\n * Same `objectName` interpolation as `title`, for body copy/fields that\n * embed it. The `rename` variant's field also receives `objectNameLabel`\n * for its accessible name.\n */\n body:\n | React.ReactNode\n | ((objectName?: string, objectNameLabel?: string) => React.ReactNode);\n /** Secondary (dismiss) button label. Omitted when the variant has none. */\n secondaryLabel?: string;\n primaryLabel: string;\n primaryVariant: 'default' | 'destructive';\n /** Whether the primary button dismisses the dialog (e.g. read-only \"Done\"). */\n primaryCloses?: boolean;\n}\n\nconst DIALOG_VARIANT_CONTENT: Record<DialogVariant, DialogVariantContent> = {\n default: {\n title: 'Dialog title',\n body: 'Drop any content into this slot.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Label',\n primaryVariant: 'default',\n },\n rename: {\n title: (objectName = 'object name') => `Rename ${objectName}`,\n // `objectName` prefills the field with the real current name; the field's\n // accessible name (`objectNameLabel`) is separate — it's the label, not the value.\n body: (objectName = 'Current name', objectNameLabel = 'Object name') => (\n <InputText aria-label={objectNameLabel} defaultValue={objectName} />\n ),\n secondaryLabel: 'Cancel',\n primaryLabel: 'Rename',\n primaryVariant: 'default',\n },\n 'save changes': {\n title: 'Save changes',\n body: 'You have unsaved changes. Do you want to save them before leaving?',\n secondaryLabel: 'Go back',\n primaryLabel: 'Save',\n primaryVariant: 'default',\n },\n 'reset password': {\n title: 'Reset password',\n body: 'Further instructions will be sent to your email address.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Reset',\n primaryVariant: 'default',\n },\n 'discard changes': {\n title: 'Discard changes',\n body: (objectName = 'Object name') => (\n <p className=\"text-sm leading-6 text-foreground\">\n Are you sure you want to discard the unsaved changes to{' '}\n <strong className=\"font-semibold\">{objectName}</strong>?\n </p>\n ),\n secondaryLabel: 'Go back',\n primaryLabel: 'Confirm',\n primaryVariant: 'destructive',\n },\n accept: {\n title: (objectName = 'object name') => `Accept ${objectName}`,\n body: 'Click Accept to confirm that you have read, understood, and agree to the terms and conditions below.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Accept',\n primaryVariant: 'default',\n },\n 'read-only': {\n title: 'License agreement',\n body: 'This is a read-only dialog used to view legal documents such as the License Agreement, EULA updates, Terms of Service, and other legal documents.',\n primaryLabel: 'Done',\n primaryVariant: 'default',\n primaryCloses: true,\n },\n // No Figma preset — a legacy, free-form escape hatch (see the `footer` prop\n // and the `Large` story). This fallback title/body/footer only renders if\n // the caller supplies neither `footer` nor `title`/`children` — every real\n // `wide` call site overrides them.\n wide: {\n title: 'Dialog title',\n body: 'Drop any content into this slot.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Confirm',\n primaryVariant: 'default',\n },\n};\n\ntype DialogRootProps = React.ComponentPropsWithoutRef<typeof DialogRoot>;\n\nexport interface DialogProps extends Omit<DialogRootProps, 'children'> {\n /**\n * Selects the canned title / body / footer preset. Defaults to `'default'`.\n * `'wide'` is a legacy escape hatch (no canned preset) kept for backward\n * compatibility — pair it with `size=\"large\"` and the `footer` prop.\n */\n variant?: DialogVariant;\n /**\n * Show a spinner overlay across the body + footer, and make both `inert`\n * (unfocusable, unclickable, hidden from assistive tech) so a keyboard user\n * can't activate the visually obscured footer buttons while busy.\n */\n hasLoading?: boolean;\n /**\n * Overrides the loading overlay's accessible label. Defaults to `Loading`'s\n * own default (`'Data is loading…'`). Ignored when `hasLoading` is `false`.\n */\n loadingLabel?: string;\n /**\n * Show the header (title + close button). Defaults to `true`. When `false`,\n * the title is still rendered off-screen so the dialog keeps an accessible\n * name — only the visible bar and close button are omitted. Beyond the\n * strict Figma contract (all seven `DialogDefault` variants always show the\n * header); a ui-react-only extension, like `wide`/`size=\"large\"`.\n */\n hasHeader?: boolean;\n /**\n * Show the footer (action buttons). Defaults to `true`. Beyond the strict\n * Figma contract (all seven `DialogDefault` variants always show the\n * footer); a ui-react-only extension, like `wide`/`size=\"large\"`. When\n * `false`, `footer` (and the canned footer it would otherwise replace) is\n * not rendered either.\n */\n hasFooter?: boolean;\n /** Overrides the variant's default body content. */\n children?: React.ReactNode;\n /** Overrides the variant's default title. */\n title?: string;\n /**\n * The real name of the object being acted on (e.g. a file or resource\n * name). Interpolated into the `rename`/`discard changes`/`accept`\n * variants' canned title/body in place of the generic placeholder text;\n * ignored by variants that don't reference an object name.\n */\n objectName?: string;\n /**\n * Overrides the accessible name of the `rename` variant's text field\n * (default `'Object name'`). Ignored by every other variant.\n */\n objectNameLabel?: string;\n /**\n * Overrides the variant's default secondary (dismiss) button label. Passing\n * this for a variant with no secondary button by default (e.g. `read-only`)\n * also makes the button appear. Ignored when `footer` is provided.\n */\n secondaryLabel?: string;\n /** Overrides the variant's default primary button label. Ignored when `footer` is provided. */\n primaryLabel?: string;\n /**\n * Fires when the primary footer button is clicked. The dialog does not\n * close automatically afterwards — pair this with `open`/`onOpenChange` to\n * close it once the action completes. Ignored when `footer` is provided\n * (wire behavior onto your own buttons there instead).\n */\n onPrimaryAction?: () => void;\n /**\n * Replaces the footer's action content entirely with free-form buttons —\n * the escape hatch the `wide` variant is meant to be paired with, for\n * legacy call sites that don't fit one of the seven canned presets. Ignored\n * (along with the canned footer) when `hasFooter` is `false`.\n */\n footer?: React.ReactNode;\n /** Overrides the close button's accessible name. Defaults to `'Close'`. */\n closeLabel?: string;\n /**\n * Popup max-width (forwarded to `DialogContent`). Defaults to `'sm'`, or to\n * `'large'` when `variant` is `'wide'`.\n */\n size?: DialogContentProps['size'];\n /** Render inside a portal (forwarded to `DialogContent`). Defaults to `true`. */\n portal?: boolean;\n /** Portal container (forwarded to `DialogContent`). */\n portalContainer?: DialogContentProps['portalContainer'];\n /** Keep the content mounted while closed (forwarded to `DialogContent`). */\n keepMounted?: DialogContentProps['keepMounted'];\n /** Extra classes merged onto the popup container. */\n className?: string;\n}\n\nconst Dialog = React.forwardRef<HTMLDivElement, DialogProps>(\n (\n {\n variant = 'default',\n hasLoading = false,\n loadingLabel,\n hasHeader = true,\n hasFooter = true,\n children,\n title,\n objectName,\n objectNameLabel,\n secondaryLabel,\n primaryLabel,\n onPrimaryAction,\n footer,\n closeLabel,\n size,\n portal,\n portalContainer,\n keepMounted,\n className,\n ...rootProps\n },\n ref\n ) => {\n const content = DIALOG_VARIANT_CONTENT[variant];\n const resolvedTitle =\n typeof content.title === 'function' ? content.title(objectName) : content.title;\n const resolvedBody =\n typeof content.body === 'function'\n ? content.body(objectName, objectNameLabel)\n : content.body;\n const bodyContent = children ?? resolvedBody;\n const titleText = title ?? resolvedTitle;\n const secondaryLabelText = secondaryLabel ?? content.secondaryLabel;\n const primaryLabelText = primaryLabel ?? content.primaryLabel;\n const resolvedSize = size ?? (variant === 'wide' ? 'large' : 'sm');\n\n return (\n <DialogRoot {...rootProps}>\n <DialogContent\n ref={ref}\n size={resolvedSize}\n portal={portal}\n portalContainer={portalContainer}\n keepMounted={keepMounted}\n className={className}\n aria-busy={hasLoading || undefined}\n >\n {hasHeader ? (\n <DialogHeader title={titleText} closeLabel={closeLabel} />\n ) : (\n // Header hidden, but the dialog still needs an accessible name.\n <DialogTitle className=\"sr-only\">{titleText}</DialogTitle>\n )}\n\n <DialogBody inert={hasLoading}>{bodyContent}</DialogBody>\n\n {hasFooter && (\n <DialogFooterDefault inert={hasLoading}>\n {footer ?? (\n <DialogFooterActions\n secondaryLabel={secondaryLabelText}\n primaryLabel={primaryLabelText}\n primaryVariant={content.primaryVariant}\n primaryCloses={content.primaryCloses}\n onPrimaryAction={onPrimaryAction}\n />\n )}\n </DialogFooterDefault>\n )}\n\n {hasLoading && (\n <Loading\n variant=\"onSurfaceSecondary\"\n hasLabel={false}\n label={loadingLabel}\n className={cn(\n 'absolute inset-x-0 bottom-0 rounded-none',\n hasHeader ? 'top-[var(--ui-dialog-header-height)]' : 'top-0'\n )}\n />\n )}\n </DialogContent>\n </DialogRoot>\n );\n }\n);\nDialog.displayName = 'Dialog';\n\nexport {\n DialogRoot,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogCloseButton,\n DialogTrigger,\n DialogContent,\n DialogHeaderRoot,\n DialogFooterRoot,\n DialogTitle,\n DialogBodyRoot,\n DialogDescription,\n dialogContentVariants,\n Dialog,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"dialog.js","names":[],"sources":["../../../../src/components/ui/dialog/dialog.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Dialog as DialogPrimitive } from '@base-ui/react/dialog';\nimport { TimesIcon } from '@acronis-platform/icons-react/stroke-mono';\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\nimport { Button } from '../button';\nimport { ButtonIcon } from '../button-icon';\nimport { DialogFooterDefault } from '../dialog-footer-default';\nimport { InputText } from '../input-text';\nimport { Loading } from '../loading';\n\n// Initial version ported from `@acronis-platform/shadcn-uikit`'s `dialog`\n// (packages/ui-legacy/src/components/ui/dialog.tsx). A modal overlay built on\n// the Base UI Dialog primitive (keyboard, focus trap, scroll lock, ARIA come\n// from Base UI). A `--ui-dialog-*` / `--ui-button-icon-*` token tier covers the\n// container + close button (reconciled against the Figma \"DialogDefault\" node,\n// 6343:58898); the composable DialogRoot / DialogHeaderRoot / DialogFooterRoot\n// / DialogBodyRoot parts below are internal-only (no public export, no Figma\n// node of their own) and keep the prior semantic-token approach — not to be\n// confused with the recipe-local `DialogHeader`/`DialogBody` further down,\n// which back the public `Dialog` and are reconciled against Figma:\n// • overlay -> var(--ui-background-backdrop-screen) (legacy `bg-black/80`)\n// • popup -> var(--ui-dialog-container-color) / var(--ui-dialog-container-border-radius)\n// • header / footer -> bg-background = --ui-background-surface-primary (white\n// bars over the muted body), divided by border-border\n// • title -> text-foreground / description -> text-muted-foreground\n// • close -> var(--ui-button-icon-global-icon-color-*) (idle/hover/active\n// share one blue in the default brand), bg via\n// var(--ui-button-icon-global-container-color-*), focus ring var(--ui-focus-primary)\n// Enter/exit animations use `tw-animate-css` (imported in styles/index.css),\n// keyed to Base UI's data-[open] / data-[closed] state attributes — overlay\n// fades, popup fades + zooms. Those keyframes only animate `transform`/\n// `opacity`, so the nested-dialog recede below can safely live on the CSS\n// `scale` property + an `::after` scrim without fighting them.\n//\n// Nested dialogs: Base UI flags a dialog as nested purely by React-tree\n// position (`Boolean(parentDialogRootContext)` in DialogRoot) and then\n// deliberately skips rendering that dialog's own backdrop, so stacked\n// dialogs don't darken progressively. To compensate, Base UI sets\n// `data-nested-dialog-open` (plus a `--nested-dialogs` count) on the PARENT\n// popup while a nested dialog is open, expecting the consuming library to\n// give the parent a \"recede\" treatment for a visible depth cue. That's what\n// the data-[nested-dialog-open] rules below do: the parent popup scales\n// down slightly and gets a token-tinted scrim (a reduced-opacity mix of the\n// same --ui-background-backdrop-screen the overlay uses, not a hardcoded\n// color) while the child — which keeps its own backdrop suppressed — sits\n// on top. Stacked-modal depth reads through relative layer contrast, so the\n// scrim is theme-split in approach, not just strength: in light the parent\n// dims (36% of the backdrop token reads clearly over the white container).\n// In dark the container and the backdrop token are the same near-black, so\n// no scrim percentage can separate the layers — and repainting the nested\n// container with a lighter tier token would flatten its header/footer\n// chrome, whose surfaces share those very values. There the separation\n// comes from the Dialog tier's hairline border on the nested popup (an\n// edge stroke that follows each brand's dialog chrome), while the parent\n// keeps the stronger 80% scrim mainly to fade its content.\n// `size` has two values: `sm` (512px, the Figma\n// default, backed by `--ui-dialog-container-size-sm`) and `large` (832px) — the\n// latter predates the Figma node and has no token; it's kept as a plain\n// arbitrary value solely for backward compatibility with existing call sites\n// that need a wider popup (see the `Large` story).\n//\n// Dialog's composable primitive parts (this file) are internal-only — not\n// re-exported from the package root — except `DialogClose`, which stays\n// public: it's required by the `wide` variant's documented custom-`footer`\n// escape hatch (see the `footer` prop and dialog.mdx). The public `Dialog`\n// component below is the sole sanctioned entry point; it's built on these\n// parts internally. (In Figma the matching component set is named\n// \"DialogDefault\"; the code-facing name stays `Dialog` — see dialog.figma.tsx.)\n\n// Figma's \"DialogWelcome\" frame (variant=carousel, node 6353:6164) wraps its\n// popup preview in a `p-[48px]` ancestor — the minimum margin the popup must\n// keep from the viewport edge on any screen size. `max-w`/`max-h` (not\n// padding, since the popup itself is centered via `fixed` + transform)\n// enforce it here so every consumer keeps that margin, not just the frame\n// that surfaced it.\nconst dialogContentVariants = cva(\n 'fixed left-1/2 z-50 flex max-h-[calc(100dvh-96px)] w-full min-w-[var(--ui-dialog-container-width-min)] -translate-x-1/2 flex-col overflow-hidden rounded-[var(--ui-dialog-container-border-radius)] bg-[var(--ui-dialog-container-color)] text-foreground shadow-lg duration-200 transition-[scale] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-[color-mix(in_oklab,var(--ui-background-backdrop-screen)_36%,transparent)] dark:after:bg-[color-mix(in_oklab,var(--ui-background-backdrop-screen)_80%,transparent)] after:opacity-0 after:transition-opacity after:duration-200 after:content-[\"\"] data-[open]:animate-in data-[open]:fade-in-0 data-[open]:zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95 data-[nested-dialog-open]:scale-[0.96] data-[nested-dialog-open]:after:opacity-100 dark:data-[nested]:border dark:data-[nested]:border-[var(--ui-dialog-header-border-color)]',\n {\n variants: {\n size: {\n sm: 'max-w-[min(var(--ui-dialog-container-size-sm),calc(100dvw-96px))]',\n large: 'max-w-[min(832px,calc(100dvw-96px))]',\n },\n verticalAlign: {\n center: 'top-1/2 -translate-y-1/2',\n top: 'top-[48px]',\n },\n },\n defaultVariants: {\n size: 'sm',\n verticalAlign: 'center',\n },\n }\n);\n\nconst DialogRoot = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Backdrop>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Backdrop>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Backdrop\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-[var(--ui-background-backdrop-screen)] duration-200 data-[open]:animate-in data-[open]:fade-in-0 data-[closed]:animate-out data-[closed]:fade-out-0',\n className\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = 'DialogOverlay';\n\nexport interface DialogContentProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Popup>,\n VariantProps<typeof dialogContentVariants> {\n /**\n * Popup max-width. `sm` (512px, default) is the Figma-defined size; `large`\n * (832px) is a backward-compatibility size with no design token (see the\n * `Large` story).\n */\n size?: VariantProps<typeof dialogContentVariants>['size'];\n /**\n * Vertical placement of the popup. `center` (default) centers it in the\n * viewport; `top` pins it 48px from the top edge, leaving the rest of the\n * viewport below it.\n */\n verticalAlign?: 'center' | 'top';\n /**\n * Render the content inside a portal (default `true`). Base UI requires the\n * Popup to sit in a Portal for correct stacking; set `false` for inline usage\n * (e.g. when the caller supplies its own `DialogPortal`, or in tests).\n */\n portal?: boolean;\n /**\n * Portal container. Pass a shadow-root mount for isolated-style previews\n * (the docs demos do this via `useShadowMount`).\n */\n portalContainer?: DialogPrimitive.Portal.Props['container'];\n /** Keep the content mounted while closed (Base UI `Portal` prop). */\n keepMounted?: DialogPrimitive.Portal.Props['keepMounted'];\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Popup>,\n DialogContentProps\n>(\n (\n {\n className,\n children,\n size,\n verticalAlign,\n portal = true,\n portalContainer,\n keepMounted,\n ...props\n },\n ref\n ) => {\n const ctxContainer = usePortalContainer();\n const resolvedContainer = portalContainer ?? ctxContainer;\n\n const popup = (\n <>\n <DialogOverlay />\n <DialogPrimitive.Popup\n ref={ref}\n className={cn(\n dialogContentVariants({ size, verticalAlign: verticalAlign ?? 'center' }),\n className\n )}\n {...props}\n >\n {children}\n </DialogPrimitive.Popup>\n </>\n );\n\n return portal ? (\n <DialogPrimitive.Portal container={resolvedContainer} keepMounted={keepMounted}>\n {popup}\n </DialogPrimitive.Portal>\n ) : (\n popup\n );\n }\n);\nDialogContent.displayName = 'DialogContent';\n\nconst DialogHeaderRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex h-16 items-center gap-4 border-b border-border bg-background px-5 py-4',\n className\n )}\n {...props}\n />\n));\nDialogHeaderRoot.displayName = 'DialogHeaderRoot';\n\nconst DialogFooterRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'flex h-16 items-center justify-end gap-4 border-t border-border bg-background px-6 py-4',\n className\n )}\n {...props}\n />\n));\nDialogFooterRoot.displayName = 'DialogFooterRoot';\n\nconst DialogBodyRoot = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn('flex-1 overflow-auto p-6', className)} {...props} />\n));\nDialogBodyRoot.displayName = 'DialogBodyRoot';\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n 'flex-1 text-2xl font-normal leading-8 text-foreground',\n className\n )}\n {...props}\n />\n));\nDialogTitle.displayName = 'DialogTitle';\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nDialogDescription.displayName = 'DialogDescription';\n\nexport interface DialogCloseButtonProps\n extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close> {\n /** Accessible name for screen readers. Defaults to `'Close'`. */\n closeLabel?: string;\n}\n\nconst DialogCloseButton = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Close>,\n DialogCloseButtonProps\n>(({ className, closeLabel = 'Close', ...props }, ref) => (\n <DialogPrimitive.Close\n ref={ref}\n render={<ButtonIcon variant=\"ghost\" />}\n className={className}\n {...props}\n >\n <TimesIcon />\n <span className=\"sr-only\">{closeLabel}</span>\n </DialogPrimitive.Close>\n));\nDialogCloseButton.displayName = 'DialogCloseButton';\n\n// The recipe-specific header: title + close button, reconciled against the\n// Figma \"DialogHeader\" node (4220:3516) via the `--ui-dialog-header-*` tier.\n// Local to this file (not exported) — distinct from the generic, unstyled\n// `DialogHeaderRoot` composable primitive above.\ninterface DialogHeaderProps {\n title: string;\n closeLabel?: string;\n}\n\nfunction DialogHeader({ title, closeLabel }: DialogHeaderProps) {\n return (\n <div className=\"flex h-[var(--ui-dialog-header-height)] shrink-0 items-center gap-[var(--ui-dialog-header-gap)] border-b-[length:var(--ui-dialog-header-border-width)] border-[var(--ui-dialog-header-border-color)] bg-[var(--ui-dialog-header-color)] px-[var(--ui-dialog-header-padding-x)]\">\n <DialogTitle>{title}</DialogTitle>\n <DialogCloseButton closeLabel={closeLabel} />\n </div>\n );\n}\n\n// The recipe-specific body: the variant's canned copy (or the `children`\n// override), reconciled against the Figma DialogDefault node via the\n// `--ui-dialog-body-*` tier. Local to this file — distinct from the generic\n// `DialogBodyRoot` composable primitive above.\ninterface DialogBodyProps {\n children: React.ReactNode;\n /** Makes the body unfocusable/unclickable while the loading overlay is shown. */\n inert?: boolean;\n}\n\nfunction DialogBody({ children, inert }: DialogBodyProps) {\n return (\n <div\n inert={inert}\n className=\"flex min-h-[var(--ui-dialog-body-height-min)] flex-col justify-center-safe gap-[var(--ui-dialog-body-gap)] overflow-y-auto px-4 py-[var(--ui-dialog-body-padding-y)]\"\n >\n {typeof children === 'string' ? (\n <p className=\"text-sm leading-6 text-foreground\">{children}</p>\n ) : (\n children\n )}\n </div>\n );\n}\n\n// The recipe-specific footer actions: a secondary (dismiss) button plus a\n// primary action, the latter either closing the dialog itself (`read-only`'s\n// \"Done\") or firing `onPrimaryAction` and leaving the close to the caller.\n// Local to this file — distinct from the generic `DialogFooterRoot`.\ninterface DialogFooterActionsProps {\n secondaryLabel?: string;\n primaryLabel: string;\n primaryVariant: 'default' | 'destructive';\n primaryCloses?: boolean;\n onPrimaryAction?: () => void;\n}\n\nfunction DialogFooterActions({\n secondaryLabel,\n primaryLabel,\n primaryVariant,\n primaryCloses,\n onPrimaryAction,\n}: DialogFooterActionsProps) {\n return (\n <>\n {secondaryLabel && (\n <DialogClose render={<Button variant=\"secondary\">{secondaryLabel}</Button>} />\n )}\n {primaryCloses ? (\n <DialogClose\n onClick={onPrimaryAction}\n render={<Button variant={primaryVariant}>{primaryLabel}</Button>}\n />\n ) : (\n <Button variant={primaryVariant} onClick={onPrimaryAction}>\n {primaryLabel}\n </Button>\n )}\n </>\n );\n}\n\n// A higher-level \"recipe\" built on top of the Dialog primitive parts above\n// (DialogRoot + DialogContent give the portal, backdrop, focus trap, scroll\n// lock and open state; DialogTitle wires the accessible name; DialogCloseButton\n// / DialogClose give the dismiss controls). It bakes in the seven Figma\n// \"DialogDefault\" use-cases (node 6343:58898) plus one legacy `wide` escape\n// hatch kept for backward compatibility: each `variant` picks a canned title,\n// body copy and footer buttons; `children` overrides only the body slot;\n// `title` overrides only the title; `footer` overrides only the footer's\n// action content (the free-form escape hatch `wide` relies on); `hasLoading`\n// drops a spinner overlay over the body + footer. `hasHeader`/`hasFooter`\n// (both default `true`) hide the header/footer chrome entirely — a\n// ui-react-only extension beyond the Figma DialogDefault contract, which\n// always shows both; the title stays rendered off-screen when the header is\n// hidden so the dialog keeps an accessible name.\n//\n// Colors resolve to shipped semantic tokens via the bridged Tailwind names the\n// Dialog family already uses (text-foreground, the backdrop + focus tokens\n// inside DialogContent / DialogCloseButton) and the Button / InputText token\n// tiers — nothing is hand-authored. The loading scrim renders `Loading`'s\n// `onSurfaceSecondary` variant (`--ui-loading-element-container-color-secondary`),\n// matching the design's translucent surface fill.\n//\n// Header + footer geometry/color resolve to the `--ui-dialog-header-*` /\n// `--ui-footer-*` tiers, reconciled against the DialogDefault Figma node.\n\nexport type DialogVariant =\n | 'default'\n | 'rename'\n | 'save changes'\n | 'reset password'\n | 'discard changes'\n | 'accept'\n | 'read-only'\n | 'wide';\n\ninterface DialogVariantContent {\n /**\n * The variant's canned title. A function receives `objectName` (falls back\n * to its own placeholder default when omitted) for variants whose title\n * embeds the acted-on object's name.\n */\n title: string | ((objectName?: string) => string);\n /**\n * Same `objectName` interpolation as `title`, for body copy/fields that\n * embed it. The `rename` variant's field also receives `objectNameLabel`\n * for its accessible name.\n */\n body:\n | React.ReactNode\n | ((objectName?: string, objectNameLabel?: string) => React.ReactNode);\n /** Secondary (dismiss) button label. Omitted when the variant has none. */\n secondaryLabel?: string;\n primaryLabel: string;\n primaryVariant: 'default' | 'destructive';\n /** Whether the primary button dismisses the dialog (e.g. read-only \"Done\"). */\n primaryCloses?: boolean;\n}\n\nconst DIALOG_VARIANT_CONTENT: Record<DialogVariant, DialogVariantContent> = {\n default: {\n title: 'Dialog title',\n body: 'Drop any content into this slot.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Label',\n primaryVariant: 'default',\n },\n rename: {\n title: (objectName = 'object name') => `Rename ${objectName}`,\n // `objectName` prefills the field with the real current name; the field's\n // accessible name (`objectNameLabel`) is separate — it's the label, not the value.\n body: (objectName = 'Current name', objectNameLabel = 'Object name') => (\n <InputText aria-label={objectNameLabel} defaultValue={objectName} />\n ),\n secondaryLabel: 'Cancel',\n primaryLabel: 'Rename',\n primaryVariant: 'default',\n },\n 'save changes': {\n title: 'Save changes',\n body: 'You have unsaved changes. Do you want to save them before leaving?',\n secondaryLabel: 'Go back',\n primaryLabel: 'Save',\n primaryVariant: 'default',\n },\n 'reset password': {\n title: 'Reset password',\n body: 'Further instructions will be sent to your email address.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Reset',\n primaryVariant: 'default',\n },\n 'discard changes': {\n title: 'Discard changes',\n body: (objectName = 'Object name') => (\n <p className=\"text-sm leading-6 text-foreground\">\n Are you sure you want to discard the unsaved changes to{' '}\n <strong className=\"font-semibold\">{objectName}</strong>?\n </p>\n ),\n secondaryLabel: 'Go back',\n primaryLabel: 'Confirm',\n primaryVariant: 'destructive',\n },\n accept: {\n title: (objectName = 'object name') => `Accept ${objectName}`,\n body: 'Click Accept to confirm that you have read, understood, and agree to the terms and conditions below.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Accept',\n primaryVariant: 'default',\n },\n 'read-only': {\n title: 'License agreement',\n body: 'This is a read-only dialog used to view legal documents such as the License Agreement, EULA updates, Terms of Service, and other legal documents.',\n primaryLabel: 'Done',\n primaryVariant: 'default',\n primaryCloses: true,\n },\n // No Figma preset — a legacy, free-form escape hatch (see the `footer` prop\n // and the `Large` story). This fallback title/body/footer only renders if\n // the caller supplies neither `footer` nor `title`/`children` — every real\n // `wide` call site overrides them.\n wide: {\n title: 'Dialog title',\n body: 'Drop any content into this slot.',\n secondaryLabel: 'Cancel',\n primaryLabel: 'Confirm',\n primaryVariant: 'default',\n },\n};\n\ntype DialogRootProps = React.ComponentPropsWithoutRef<typeof DialogRoot>;\n\nexport interface DialogProps extends Omit<DialogRootProps, 'children'> {\n /**\n * Selects the canned title / body / footer preset. Defaults to `'default'`.\n * `'wide'` is a legacy escape hatch (no canned preset) kept for backward\n * compatibility — pair it with `size=\"large\"` and the `footer` prop.\n */\n variant?: DialogVariant;\n /**\n * Show a spinner overlay across the body + footer, and make both `inert`\n * (unfocusable, unclickable, hidden from assistive tech) so a keyboard user\n * can't activate the visually obscured footer buttons while busy.\n */\n hasLoading?: boolean;\n /**\n * Overrides the loading overlay's accessible label. Defaults to `Loading`'s\n * own default (`'Data is loading…'`). Ignored when `hasLoading` is `false`.\n */\n loadingLabel?: string;\n /**\n * Show the header (title + close button). Defaults to `true`. When `false`,\n * the title is still rendered off-screen so the dialog keeps an accessible\n * name — only the visible bar and close button are omitted. Beyond the\n * strict Figma contract (all seven `DialogDefault` variants always show the\n * header); a ui-react-only extension, like `wide`/`size=\"large\"`.\n */\n hasHeader?: boolean;\n /**\n * Show the footer (action buttons). Defaults to `true`. Beyond the strict\n * Figma contract (all seven `DialogDefault` variants always show the\n * footer); a ui-react-only extension, like `wide`/`size=\"large\"`. When\n * `false`, `footer` (and the canned footer it would otherwise replace) is\n * not rendered either.\n */\n hasFooter?: boolean;\n /** Overrides the variant's default body content. */\n children?: React.ReactNode;\n /** Overrides the variant's default title. */\n title?: string;\n /**\n * The real name of the object being acted on (e.g. a file or resource\n * name). Interpolated into the `rename`/`discard changes`/`accept`\n * variants' canned title/body in place of the generic placeholder text;\n * ignored by variants that don't reference an object name.\n */\n objectName?: string;\n /**\n * Overrides the accessible name of the `rename` variant's text field\n * (default `'Object name'`). Ignored by every other variant.\n */\n objectNameLabel?: string;\n /**\n * Overrides the variant's default secondary (dismiss) button label. Passing\n * this for a variant with no secondary button by default (e.g. `read-only`)\n * also makes the button appear. Ignored when `footer` is provided.\n */\n secondaryLabel?: string;\n /** Overrides the variant's default primary button label. Ignored when `footer` is provided. */\n primaryLabel?: string;\n /**\n * Fires when the primary footer button is clicked. The dialog does not\n * close automatically afterwards — pair this with `open`/`onOpenChange` to\n * close it once the action completes. Ignored when `footer` is provided\n * (wire behavior onto your own buttons there instead).\n */\n onPrimaryAction?: () => void;\n /**\n * Replaces the footer's action content entirely with free-form buttons —\n * the escape hatch the `wide` variant is meant to be paired with, for\n * legacy call sites that don't fit one of the seven canned presets. Ignored\n * (along with the canned footer) when `hasFooter` is `false`.\n */\n footer?: React.ReactNode;\n /** Overrides the close button's accessible name. Defaults to `'Close'`. */\n closeLabel?: string;\n /**\n * Popup max-width (forwarded to `DialogContent`). Defaults to `'sm'`, or to\n * `'large'` when `variant` is `'wide'`.\n */\n size?: DialogContentProps['size'];\n /** Vertical placement: `'center'` (default) or `'top'` (forwarded to `DialogContent`). */\n verticalAlign?: DialogContentProps['verticalAlign'];\n /** Render inside a portal (forwarded to `DialogContent`). Defaults to `true`. */\n portal?: boolean;\n /** Portal container (forwarded to `DialogContent`). */\n portalContainer?: DialogContentProps['portalContainer'];\n /** Keep the content mounted while closed (forwarded to `DialogContent`). */\n keepMounted?: DialogContentProps['keepMounted'];\n /** Extra classes merged onto the popup container. */\n className?: string;\n}\n\nconst Dialog = React.forwardRef<HTMLDivElement, DialogProps>(\n (\n {\n variant = 'default',\n hasLoading = false,\n loadingLabel,\n hasHeader = true,\n hasFooter = true,\n children,\n title,\n objectName,\n objectNameLabel,\n secondaryLabel,\n primaryLabel,\n onPrimaryAction,\n footer,\n closeLabel,\n size,\n verticalAlign,\n portal,\n portalContainer,\n keepMounted,\n className,\n ...rootProps\n },\n ref\n ) => {\n const content = DIALOG_VARIANT_CONTENT[variant];\n const resolvedTitle =\n typeof content.title === 'function' ? content.title(objectName) : content.title;\n const resolvedBody =\n typeof content.body === 'function'\n ? content.body(objectName, objectNameLabel)\n : content.body;\n const bodyContent = children ?? resolvedBody;\n const titleText = title ?? resolvedTitle;\n const secondaryLabelText = secondaryLabel ?? content.secondaryLabel;\n const primaryLabelText = primaryLabel ?? content.primaryLabel;\n const resolvedSize = size ?? (variant === 'wide' ? 'large' : 'sm');\n\n return (\n <DialogRoot {...rootProps}>\n <DialogContent\n ref={ref}\n size={resolvedSize}\n verticalAlign={verticalAlign}\n portal={portal}\n portalContainer={portalContainer}\n keepMounted={keepMounted}\n className={className}\n aria-busy={hasLoading || undefined}\n >\n {hasHeader ? (\n <DialogHeader title={titleText} closeLabel={closeLabel} />\n ) : (\n // Header hidden, but the dialog still needs an accessible name.\n <DialogTitle className=\"sr-only\">{titleText}</DialogTitle>\n )}\n\n <DialogBody inert={hasLoading}>{bodyContent}</DialogBody>\n\n {hasFooter && (\n <DialogFooterDefault inert={hasLoading}>\n {footer ?? (\n <DialogFooterActions\n secondaryLabel={secondaryLabelText}\n primaryLabel={primaryLabelText}\n primaryVariant={content.primaryVariant}\n primaryCloses={content.primaryCloses}\n onPrimaryAction={onPrimaryAction}\n />\n )}\n </DialogFooterDefault>\n )}\n\n {hasLoading && (\n <Loading\n variant=\"onSurfaceSecondary\"\n hasLabel={false}\n label={loadingLabel}\n className={cn(\n 'absolute inset-x-0 bottom-0 rounded-none',\n hasHeader ? 'top-[var(--ui-dialog-header-height)]' : 'top-0'\n )}\n />\n )}\n </DialogContent>\n </DialogRoot>\n );\n }\n);\nDialog.displayName = 'Dialog';\n\nexport {\n DialogRoot,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogCloseButton,\n DialogTrigger,\n DialogContent,\n DialogHeaderRoot,\n DialogFooterRoot,\n DialogTitle,\n DialogBodyRoot,\n DialogDescription,\n dialogContentVariants,\n Dialog,\n};\n"],"mappings":";;;;;;;;;;;;;AA+EA,IAAM,IAAwB,EAC5B,m7BACA;CACE,UAAU;EACR,MAAM;GACJ,IAAI;GACJ,OAAO;EACT;EACA,eAAe;GACb,QAAQ;GACR,KAAK;EACP;CACF;CACA,iBAAiB;EACf,MAAM;EACN,eAAe;CACjB;AACF,CACF,GAEM,IAAa,EAAgB;AAEb,EAAgB,SAEjB,EAAgB;AAErC,IAAM,IAAc,EAAgB,OAE9B,IAAgB,EAAM,YAGzB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,EAAgB,UAAjB;CACO;CACL,WAAW,EACT,6KACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAc,cAAc;AAgC5B,IAAM,IAAgB,EAAM,YAKxB,EACE,cACA,aACA,SACA,kBACA,YAAS,IACT,oBACA,gBACA,GAAG,KAEL,MACG;CACH,IAAM,IAAe,EAAmB,GAClC,IAAoB,KAAmB,GAEvC,IACJ,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,CAAgB,CAAA,GAChB,kBAAC,EAAgB,OAAjB;EACO;EACL,WAAW,EACT,EAAsB;GAAE;GAAM,eAAe,KAAiB;EAAS,CAAC,GACxE,CACF;EACA,GAAI;EAEH;CACoB,CAAA,CACvB,EAAA,CAAA;CAGJ,OAAO,IACL,kBAAC,EAAgB,QAAjB;EAAwB,WAAW;EAAgC;EAChE,UAAA;CACqB,CAAA,IAExB;AAEJ,CACF;AACA,EAAc,cAAc;AAE5B,IAAM,IAAmB,EAAM,YAG5B,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,OAAD;CACO;CACL,WAAW,EACT,+EACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAiB,cAAc;AAE/B,IAAM,IAAmB,EAAM,YAG5B,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,OAAD;CACO;CACL,WAAW,EACT,2FACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAiB,cAAc;AAE/B,IAAM,IAAiB,EAAM,YAG1B,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,OAAD;CAAU;CAAK,WAAW,EAAG,4BAA4B,CAAS;CAAG,GAAI;AAAQ,CAAA,CAClF;AACD,EAAe,cAAc;AAE7B,IAAM,IAAc,EAAM,YAGvB,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,EAAgB,OAAjB;CACO;CACL,WAAW,EACT,yDACA,CACF;CACA,GAAI;AACL,CAAA,CACF;AACD,EAAY,cAAc;AAE1B,IAAM,IAAoB,EAAM,YAG7B,EAAE,cAAW,GAAG,KAAS,MAC1B,kBAAC,EAAgB,aAAjB;CACO;CACL,WAAW,EAAG,iCAAiC,CAAS;CACxD,GAAI;AACL,CAAA,CACF;AACD,EAAkB,cAAc;AAQhC,IAAM,IAAoB,EAAM,YAG7B,EAAE,cAAW,gBAAa,SAAS,GAAG,KAAS,MAChD,kBAAC,EAAgB,OAAjB;CACO;CACL,QAAQ,kBAAC,GAAD,EAAY,SAAQ,QAAS,CAAA;CAC1B;CACX,GAAI;CAJN,UAAA,CAME,kBAAC,GAAD,CAAY,CAAA,GACZ,kBAAC,QAAD;EAAM,WAAU;EAAW,UAAA;CAAiB,CAAA,CACvB;AACxB,CAAA,CAAA;AACD,EAAkB,cAAc;AAWhC,SAAS,EAAa,EAAE,UAAO,iBAAiC;CAC9D,OACE,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,GAAD,EAAA,UAAc,EAAmB,CAAA,GACjC,kBAAC,GAAD,EAA+B,cAAa,CAAA,CACzC;;AAET;AAYA,SAAS,EAAW,EAAE,aAAU,YAA0B;CACxD,OACE,kBAAC,OAAD;EACS;EACP,WAAU;EAET,UAAA,OAAO,KAAa,WACnB,kBAAC,KAAD;GAAG,WAAU;GAAqC;EAAY,CAAA,IAE9D;CAEC,CAAA;AAET;AAcA,SAAS,EAAoB,EAC3B,mBACA,iBACA,mBACA,kBACA,sBAC2B;CAC3B,OACE,kBAAA,GAAA,EAAA,UAAA,CACG,KACC,kBAAC,GAAD,EAAa,QAAQ,kBAAC,GAAD;EAAQ,SAAQ;EAAa,UAAA;CAAuB,CAAA,EAAI,CAAA,GAE9E,IACC,kBAAC,GAAD;EACE,SAAS;EACT,QAAQ,kBAAC,GAAD;GAAQ,SAAS;GAAiB,UAAA;EAAqB,CAAA;CAChE,CAAA,IAED,kBAAC,GAAD;EAAQ,SAAS;EAAgB,SAAS;EACvC,UAAA;CACK,CAAA,CAEV,EAAA,CAAA;AAEN;AA4DA,IAAM,IAAsE;CAC1E,SAAS;EACP,OAAO;EACP,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,QAAQ;EACN,QAAQ,IAAa,kBAAkB,UAAU;EAGjD,OAAO,IAAa,gBAAgB,IAAkB,kBACpD,kBAAC,GAAD;GAAW,cAAY;GAAiB,cAAc;EAAa,CAAA;EAErE,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,gBAAgB;EACd,OAAO;EACP,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,kBAAkB;EAChB,OAAO;EACP,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,mBAAmB;EACjB,OAAO;EACP,OAAO,IAAa,kBAClB,kBAAC,KAAD;GAAG,WAAU;GAAb,UAAA;IAAiD;IACS;IACxD,kBAAC,UAAD;KAAQ,WAAU;KAAiB,UAAA;IAAmB,CAAA;IAAC;GACtD;;EAEL,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,QAAQ;EACN,QAAQ,IAAa,kBAAkB,UAAU;EACjD,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;CACA,aAAa;EACX,OAAO;EACP,MAAM;EACN,cAAc;EACd,gBAAgB;EAChB,eAAe;CACjB;CAKA,MAAM;EACJ,OAAO;EACP,MAAM;EACN,gBAAgB;EAChB,cAAc;EACd,gBAAgB;CAClB;AACF,GA+FM,IAAS,EAAM,YAEjB,EACE,aAAU,WACV,gBAAa,IACb,iBACA,eAAY,IACZ,eAAY,IACZ,aACA,UACA,eACA,oBACA,mBACA,iBACA,oBACA,WACA,eACA,SACA,kBACA,WACA,oBACA,gBACA,cACA,GAAG,KAEL,MACG;CACH,IAAM,IAAU,EAAuB,IACjC,IACJ,OAAO,EAAQ,SAAU,aAAa,EAAQ,MAAM,CAAU,IAAI,EAAQ,OACtE,IACJ,OAAO,EAAQ,QAAS,aACpB,EAAQ,KAAK,GAAY,CAAe,IACxC,EAAQ,MACR,IAAc,KAAY,GAC1B,IAAY,KAAS,GACrB,IAAqB,KAAkB,EAAQ,gBAC/C,IAAmB,KAAgB,EAAQ,cAC3C,IAAe,MAAS,MAAY,SAAS,UAAU;CAE7D,OACE,kBAAC,GAAD;EAAY,GAAI;EACd,UAAA,kBAAC,GAAD;GACO;GACL,MAAM;GACS;GACP;GACS;GACJ;GACF;GACX,aAAW,KAAc,KAAA;GAR3B,UAAA;IAUG,IACC,kBAAC,GAAD;KAAc,OAAO;KAAuB;IAAa,CAAA,IAGzD,kBAAC,GAAD;KAAa,WAAU;KAAW,UAAA;IAAuB,CAAA;IAG3D,kBAAC,GAAD;KAAY,OAAO;KAAa,UAAA;IAAwB,CAAA;IAEvD,KACC,kBAAC,GAAD;KAAqB,OAAO;KACzB,UAAA,KACC,kBAAC,GAAD;MACE,gBAAgB;MAChB,cAAc;MACd,gBAAgB,EAAQ;MACxB,eAAe,EAAQ;MACN;KAClB,CAAA;IAEgB,CAAA;IAGtB,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,UAAU;KACV,OAAO;KACP,WAAW,EACT,4CACA,IAAY,yCAAyC,OACvD;IACD,CAAA;GAEU;;CACL,CAAA;AAEhB,CACF;AACA,EAAO,cAAc"}
@@ -6,20 +6,20 @@ import { ChartContainer as r, ChartLegendContent as i, ChartTooltip as a, ChartT
6
6
  import { CHART_LABEL_FILL_CLASS as l, resolveAnimation as u, resolveLabelFillClass as d } from "../chart/chart-format.js";
7
7
  import * as f from "react";
8
8
  import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
9
- import { Cell as g, Curve as ee, Label as te, LabelList as ne, Pie as re, PieChart as ie } from "recharts";
9
+ import { Cell as ee, Curve as te, Label as ne, LabelList as re, Pie as g, PieChart as _ } from "recharts";
10
10
  //#region src/components/ui/pie-chart/pie-chart.tsx
11
- var _ = t("", {
11
+ var v = t("", {
12
12
  variants: { shape: {
13
13
  pie: "",
14
14
  donut: ""
15
15
  } },
16
16
  defaultVariants: { shape: "pie" }
17
- }), v = (e) => typeof e == "number" && Number.isFinite(e) ? e : void 0, y = (e, t) => {
18
- let n = v(e);
17
+ }), y = (e) => typeof e == "number" && Number.isFinite(e) ? e : void 0, b = (e, t) => {
18
+ let n = y(e);
19
19
  if (!(n === void 0 || t <= 0)) return `${(n / t * 100).toFixed(1)}%`;
20
20
  };
21
- function b(e) {
22
- let { format: t, name: n, value: r, total: i, formatter: a } = e, o = r == null ? "" : a ? a(r) : String(r), s = y(r, i);
21
+ function x(e) {
22
+ let { format: t, name: n, value: r, total: i, formatter: a } = e, o = r == null ? "" : a ? a(r) : String(r), s = b(r, i);
23
23
  switch (t) {
24
24
  case "name-value": return o ? `${n}: ${o}` : `${n}`;
25
25
  case "name-percent": return s ? `${n}: ${s}` : `${n}`;
@@ -27,10 +27,10 @@ function b(e) {
27
27
  default: return o;
28
28
  }
29
29
  }
30
- function x(e) {
30
+ function S(e) {
31
31
  let { config: t, total: n } = e;
32
32
  return function(e, r, i) {
33
- let a = t[String(r)], o = y(e, n), s = e?.toLocaleString() ?? "";
33
+ let a = t[String(r)], o = b(e, n), s = e?.toLocaleString() ?? "";
34
34
  return /* @__PURE__ */ h(p, { children: [a?.icon ? /* @__PURE__ */ m(a.icon, {}) : /* @__PURE__ */ m("div", {
35
35
  className: "h-2.5 w-2.5 shrink-0 rounded-full",
36
36
  style: { backgroundColor: i.payload?.fill ?? i.color }
@@ -46,18 +46,18 @@ function x(e) {
46
46
  })] });
47
47
  };
48
48
  }
49
- function S(e) {
49
+ function C(e) {
50
50
  let { nameKey: t, config: n, total: r } = e;
51
51
  return /* @__PURE__ */ m(o, {
52
52
  nameKey: t,
53
53
  hideLabel: !0,
54
- formatter: x({
54
+ formatter: S({
55
55
  config: n,
56
56
  total: r
57
57
  })
58
58
  });
59
59
  }
60
- function ae({ centerLabel: e }) {
60
+ function ie({ centerLabel: e }) {
61
61
  return function({ viewBox: t }) {
62
62
  if (!t || !("cx" in t)) return null;
63
63
  let { cx: n = 0, cy: r = 0 } = t, i = e.value != null, a = e.label != null, o = i && a;
@@ -78,63 +78,63 @@ function ae({ centerLabel: e }) {
78
78
  })] });
79
79
  };
80
80
  }
81
- var C = f.forwardRef(({ className: t, config: p, palette: y, data: x, dataKey: C, nameKey: w, shape: T = "donut", centerLabel: E, innerRadius: D = 48, outerRadius: O, paddingAngle: k = 2, cornerRadius: A, startAngle: j = 90, endAngle: M = -270, minAngle: N, sliceSettings: P, showTooltip: F = !0, showLegend: I = !0, legendFontSize: L, legendAriaLabel: R, legendValueFormatter: z, margin: B, tooltipContent: V, tooltipFormat: oe = "value", animate: H, animationDuration: U, animationBegin: W, animationEasing: G, showLabels: K = !1, labelPosition: q, labelFormat: se = "value", labelFormatter: ce, labelLine: le = !1, ...ue }, de) => {
82
- let J = u({
83
- animate: H,
84
- animationDuration: U,
85
- animationBegin: W,
86
- animationEasing: G
87
- }), Y = q ?? "outside", fe = T === "donut" ? D : 0, X = f.useMemo(() => I ? s(p, y ?? n) : null, [
88
- I,
81
+ var w = f.forwardRef(({ className: t, config: p, palette: b, data: S, dataKey: w, nameKey: T, shape: E = "donut", centerLabel: D, innerRadius: ae = 48, outerRadius: oe, paddingAngle: se = 2, cornerRadius: O, startAngle: k = 90, endAngle: A = -270, minAngle: j, sliceSettings: M, showTooltip: N = !0, showLegend: P = !0, legendFontSize: F, legendAriaLabel: I, legendValueFormatter: L, margin: R, tooltipContent: z, tooltipFormat: B = "value", animate: V, animationDuration: H, animationBegin: ce, animationEasing: U, showLabels: W = !1, labelPosition: G, labelFormat: le = "value", labelFormatter: ue, labelLine: de = !1, ...fe }, pe) => {
82
+ let me = u({
83
+ animate: V,
84
+ animationDuration: H,
85
+ animationBegin: ce,
86
+ animationEasing: U
87
+ }), K = G ?? "outside", he = E === "donut" ? ae : 0, q = f.useMemo(() => P ? s(p, b ?? n) : null, [
88
+ P,
89
89
  p,
90
- y
91
- ]), pe = f.useMemo(() => X ? x.map((e) => ({
92
- value: String(e[w]),
93
- dataKey: w,
94
- color: X[String(e[w])]?.color ?? `var(--color-${c(String(e[w]))})`,
90
+ b
91
+ ]), J = f.useMemo(() => q ? S.map((e) => ({
92
+ value: String(e[T]),
93
+ dataKey: T,
94
+ color: q[String(e[T])]?.color ?? `var(--color-${c(String(e[T]))})`,
95
95
  type: "rect",
96
96
  payload: e
97
97
  })) : [], [
98
- X,
99
- x,
100
- w
101
- ]), Z = x.reduce((e, t) => e + (v(t[C]) ?? 0), 0), Q = (e) => {
98
+ q,
99
+ S,
100
+ T
101
+ ]), Y = !!(P && J.length), X = S.reduce((e, t) => e + (y(t[w]) ?? 0), 0), Z = (e) => {
102
102
  if (!e) return null;
103
- let t = P?.[String(e[w])];
104
- return t?.hideLabel ? null : b({
105
- format: t?.labelFormat ?? se,
106
- name: e[w],
107
- value: e[C],
108
- total: Z,
109
- formatter: ce
103
+ let t = M?.[String(e[T])];
104
+ return t?.hideLabel ? null : x({
105
+ format: t?.labelFormat ?? le,
106
+ name: e[T],
107
+ value: e[w],
108
+ total: X,
109
+ formatter: ue
110
110
  }) || null;
111
- }, $ = K && le, me = /* @__PURE__ */ h(ie, {
112
- margin: B ?? {
111
+ }, Q = W && de, ge = /* @__PURE__ */ h(_, {
112
+ margin: R ?? {
113
113
  top: 0,
114
114
  right: 0,
115
115
  bottom: 0,
116
116
  left: 0
117
117
  },
118
- children: [F && /* @__PURE__ */ m(a, { content: V ?? (oe === "value-percent" ? S({
119
- nameKey: w,
118
+ children: [N && /* @__PURE__ */ m(a, { content: z ?? (B === "value-percent" ? C({
119
+ nameKey: T,
120
120
  config: p,
121
- total: Z
121
+ total: X
122
122
  }) : /* @__PURE__ */ m(o, {
123
- nameKey: w,
123
+ nameKey: T,
124
124
  hideLabel: !0
125
- })) }), /* @__PURE__ */ h(re, {
126
- data: x,
127
- dataKey: C,
128
- nameKey: w,
129
- innerRadius: fe,
130
- outerRadius: O ?? 60,
131
- paddingAngle: k,
132
- cornerRadius: A,
133
- startAngle: j,
134
- endAngle: M,
135
- minAngle: N,
136
- label: $ ? (e) => {
137
- let t = Q(e.payload);
125
+ })) }), /* @__PURE__ */ h(g, {
126
+ data: S,
127
+ dataKey: w,
128
+ nameKey: T,
129
+ innerRadius: he,
130
+ outerRadius: oe ?? 60,
131
+ paddingAngle: se,
132
+ cornerRadius: O,
133
+ startAngle: k,
134
+ endAngle: A,
135
+ minAngle: j,
136
+ label: Q ? (e) => {
137
+ let t = Z(e.payload);
138
138
  return t ? /* @__PURE__ */ m("text", {
139
139
  x: e.x,
140
140
  y: e.y,
@@ -145,52 +145,56 @@ var C = f.forwardRef(({ className: t, config: p, palette: y, data: x, dataKey: C
145
145
  children: t
146
146
  }) : /* @__PURE__ */ m("g", {});
147
147
  } : !1,
148
- labelLine: $ ? ({ key: e, payload: t, ...n }) => Q(t) ? /* @__PURE__ */ m(ee, {
148
+ labelLine: Q ? ({ key: e, payload: t, ...n }) => Z(t) ? /* @__PURE__ */ m(te, {
149
149
  ...n,
150
150
  type: "linear",
151
151
  className: "recharts-pie-label-line"
152
152
  }) : /* @__PURE__ */ m("g", {}) : !1,
153
- ...J,
153
+ ...me,
154
154
  children: [
155
- x.map((e, t) => /* @__PURE__ */ m(g, { fill: P?.[String(e[w])]?.color ?? `var(--color-${c(String(e[w]))})` }, t)),
156
- T === "donut" && E && /* @__PURE__ */ m(te, { content: ae({ centerLabel: E }) }),
157
- K && !$ && /* @__PURE__ */ m(ne, {
158
- valueAccessor: (e) => Q(e.payload),
159
- position: Y,
160
- className: d(Y),
155
+ S.map((e, t) => /* @__PURE__ */ m(ee, { fill: M?.[String(e[T])]?.color ?? `var(--color-${c(String(e[T]))})` }, t)),
156
+ E === "donut" && D && /* @__PURE__ */ m(ne, { content: ie({ centerLabel: D }) }),
157
+ W && !Q && /* @__PURE__ */ m(re, {
158
+ valueAccessor: (e) => Z(e.payload),
159
+ position: K,
160
+ className: d(K),
161
161
  fontSize: 12
162
162
  })
163
163
  ]
164
164
  })]
165
- }), he = K ? $ ? "size-[320px]" : Y === "outside" || Y === "end" ? "size-[200px]" : "size-[120px]" : "size-[120px]";
165
+ }), $ = W && (Q || K === "outside" || K === "end") ? 320 : 120, _e = $ === 320 ? "aspect-square w-full max-w-[320px]" : "size-[120px]", ve = $ === 320 ? "max-w-[320px] flex-[1_1_320px]" : "flex-1 basis-[120px]";
166
166
  return /* @__PURE__ */ h("div", {
167
- ref: de,
168
- "data-shape": T,
169
- className: e("flex flex-row items-center gap-4", !I && "justify-center", _({ shape: T }), t),
170
- ...ue,
167
+ ref: pe,
168
+ "data-shape": E,
169
+ className: e("flex w-full min-w-0 gap-4", Y ? "flex-wrap items-stretch" : "items-center justify-center", v({ shape: E }), t),
170
+ ...fe,
171
171
  children: [/* @__PURE__ */ m("div", {
172
- className: e(he, "shrink-0"),
173
- children: /* @__PURE__ */ m(r, {
174
- config: p,
175
- palette: y,
176
- className: "size-full",
177
- children: me
172
+ className: e("flex min-w-0 items-center justify-center", Y ? ve : "w-full"),
173
+ children: /* @__PURE__ */ m("div", {
174
+ className: _e,
175
+ children: /* @__PURE__ */ m(r, {
176
+ config: p,
177
+ palette: b,
178
+ className: "size-full",
179
+ children: ge
180
+ })
178
181
  })
179
- }), I && X && /* @__PURE__ */ m(i, {
182
+ }), Y && q && /* @__PURE__ */ m(i, {
180
183
  variant: "list",
181
- payload: pe,
182
- config: X,
183
- nameKey: w,
184
- fontSize: L,
185
- ariaLabel: R,
186
- valueKey: C,
187
- valueFormatter: z,
188
- className: "min-w-0 flex-1"
184
+ payload: J,
185
+ config: q,
186
+ nameKey: T,
187
+ fontSize: F,
188
+ ariaLabel: I,
189
+ valueKey: w,
190
+ valueFormatter: L,
191
+ valueClassName: "text-[var(--ui-text-on-surface-primary)]",
192
+ className: "min-w-0 flex-1 basis-[120px] self-center"
189
193
  })]
190
194
  });
191
195
  });
192
- C.displayName = "PieChart";
196
+ w.displayName = "PieChart";
193
197
  //#endregion
194
- export { C as PieChart, b as pieChartLabelText, x as pieChartValuePercentRow, S as pieChartValuePercentTooltip, _ as pieChartVariants };
198
+ export { w as PieChart, x as pieChartLabelText, S as pieChartValuePercentRow, C as pieChartValuePercentTooltip, v as pieChartVariants };
195
199
 
196
200
  //# sourceMappingURL=pie-chart.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../../src/components/ui/pie-chart/pie-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Curve,\n Label,\n LabelList,\n Pie,\n PieChart as RechartsPieChart,\n type CurveProps,\n type PieLabelRenderProps,\n} from 'recharts';\n\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveChartColors,\n resolveLabelFillClass,\n CHART_DEFAULT_PALETTE,\n CHART_LABEL_FILL_CLASS,\n CHART_LABEL_FONT_SIZE,\n toCssKey,\n type ChartConfig,\n type ChartLegendFontSize,\n type ChartLegendAriaLabelProps,\n type ChartPalette,\n type ChartTooltipContentProps,\n type ChartAnimationProps,\n type ChartDataLabelProps,\n type PolarLabelPosition,\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 Pie-chart variant set: `shape` (a filled pie vs a\n// hollow-centre donut). The class stays empty because recharts' SVG — not CSS —\n// draws the arc: `shape` drives the `<Pie>`'s `innerRadius` (0 for a pie, the\n// `innerRadius` prop for a donut). 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-shape`.\nconst pieChartVariants = cva('', {\n variants: {\n shape: {\n pie: '',\n donut: '',\n },\n },\n defaultVariants: {\n shape: 'pie',\n },\n});\n\n/** Row-per-slice datum: the `nameKey` label plus the `dataKey` value. */\ntype PieChartDatum = Record<string, string | number>;\n\n/**\n * What a slice's data label reads. `percent` is the slice's share of the sum of\n * every slice value, to one decimal.\n */\nexport type PieChartLabelFormat =\n | 'value'\n | 'name-value'\n | 'name-percent'\n | 'percent';\n\n/** What the tooltip's value reads — the raw value, or the value and its share. */\nexport type PieChartTooltipFormat = 'value' | 'value-percent';\n\n/** Per-slice overrides, keyed by the slice's `nameKey` value. */\nexport interface PieChartSliceSettings {\n /**\n * Fill for this slice, overriding its `config` color. Reference an existing\n * semantic `--ui-*` token, same as `config`.\n */\n color?: string;\n /** Drop this slice's data label while every other slice keeps its own. */\n hideLabel?: boolean;\n /** Label format for this slice only — overrides the chart-level `labelFormat`. */\n labelFormat?: PieChartLabelFormat;\n}\n\nconst toNumericValue = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\n/**\n * A slice's share of the chart's total, to one decimal. The single place the\n * share is computed, so a slice's label and its tooltip row can't disagree.\n * `undefined` when there is nothing to divide — a non-numeric slice value, or a\n * total of zero — which is what lets the callers degrade instead of printing\n * `NaN%`.\n */\nconst slicePercentText = (\n value: unknown,\n total: number\n): string | undefined => {\n const numeric = toNumericValue(value);\n if (numeric === undefined || total <= 0) return undefined;\n return `${((numeric / total) * 100).toFixed(1)}%`;\n};\n\n/**\n * Compose one slice's data-label text. Exported for unit tests; not part of the\n * package's public API.\n *\n * A percent needs a numeric slice value *and* a non-zero total, and a value\n * label needs a value at all — so every format degrades rather than printing\n * `NaN%` or a dangling `\"Chrome: \"`. The two name-carrying formats fall back to\n * the bare name; the value-only ones to `''`, which `resolveSliceLabel`\n * normalizes to `null` (the one value recharts renders no `<text>` for).\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only. A percent is always composed by `slicePercentText` and\n * never passes through the formatter.\n */\nexport function pieChartLabelText(options: {\n format: PieChartLabelFormat;\n name: string | number;\n value: string | number | undefined;\n total: number;\n formatter?: TickFormatter;\n}): string {\n const { format, name, value, total, formatter } = options;\n const valueText =\n value == null ? '' : formatter ? formatter(value) : String(value);\n const percentText = slicePercentText(value, total);\n\n switch (format) {\n case 'name-value':\n return valueText ? `${name}: ${valueText}` : `${name}`;\n case 'name-percent':\n return percentText ? `${name}: ${percentText}` : `${name}`;\n case 'percent':\n return percentText ?? '';\n default:\n return valueText;\n }\n}\n\n/**\n * The row renderer behind `tooltipFormat=\"value-percent\"` — a slice's value\n * followed by its share. Exported for unit tests; not part of the package's\n * public API.\n *\n * A `formatter` replaces the default row wholesale, so the swatch and the name\n * are rebuilt here to match it: the shared `ChartTooltipContent` prefers a\n * `config` entry's `icon` over the color swatch, and this has to do the same or\n * a chart with icons would silently lose them under this preset — and the\n * name/value `gap-4`, which has to be repeated here because a `formatter`\n * replaces the shared row that carries it.\n */\nexport function pieChartValuePercentRow(options: {\n config: ChartConfig;\n total: number;\n}): NonNullable<ChartTooltipContentProps['formatter']> {\n const { config, total } = options;\n // Named rather than an arrow: it returns JSX, so an anonymous function reads\n // to eslint-plugin-react as a component with no display name.\n return function ValuePercentRow(value, name, item) {\n const itemConfig = config[String(name)];\n const percent = slicePercentText(value, total);\n const valueText = value?.toLocaleString() ?? '';\n return (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n // Dotted, not the legend's square swatch: the shared row's\n // \"a row is always dotted, whatever marker the legend gives that\n // series\" invariant applies here too.\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.payload?.fill ?? item.color }}\n />\n )}\n {/*\n * Mirrors the shared default row's `gap-4` (see `ChartTooltipContent`):\n * a `formatter` replaces that row wholesale, so the floor under the\n * name/value separation has to be restated here. Without it a value\n * *and* its share outgrows the tooltip's `min-w-[8rem]`, leaving\n * `justify-between` no free space to distribute.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label ?? name}\n </span>\n <span className=\"font-medium tabular-nums text-foreground\">\n {percent ? `${valueText} (${percent})` : valueText}\n </span>\n </div>\n </>\n );\n };\n}\n\n/**\n * The configured `ChartTooltipContent` for `tooltipFormat=\"value-percent\"`.\n * Exported so the visual-regression story renders the very same element the\n * component does — recharts can only open a hover tooltip statically via\n * `defaultIndex` on a raw composition, so the preset has to be reachable from\n * outside the component to be snapshotted at all.\n */\nexport function pieChartValuePercentTooltip(options: {\n nameKey: string;\n config: ChartConfig;\n total: number;\n}): React.ReactElement {\n const { nameKey, config, total } = options;\n return (\n <ChartTooltipContent\n nameKey={nameKey}\n hideLabel\n formatter={pieChartValuePercentRow({ config, total })}\n />\n );\n}\n\nexport interface PieChartCenterLabel {\n // Rendered as SVG <text>, which only lays out text — hence string | number,\n // not ReactNode.\n /** Headline metric rendered large in the donut hole (e.g. a total). */\n value?: string | number;\n /** Caption rendered under the value. */\n label?: string | number;\n}\n\nexport interface PieChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof pieChartVariants>,\n ChartAnimationProps,\n ChartDataLabelProps,\n ChartLegendAriaLabelProps {\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-slice data. Each object holds the slice's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<PieChartDatum>;\n /**\n * Per-slice map of `label` / `icon` / `tone`, keyed by the slice'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 slice. */\n dataKey: string;\n /**\n * Label field that names each slice (drives the legend, tooltip, and\n * `--color-<name>` lookup). Values should be unique per chart — rows sharing a\n * name share one `config`/color entry.\n */\n nameKey: string;\n /** Inner radius of the arc when `shape=\"donut\"` (ignored for `shape=\"pie\"`). */\n innerRadius?: number;\n /**\n * Custom content for the donut hole — a headline `value` and/or a `label`\n * caption, stacked and centered. Rendered only for `shape=\"donut\"` (a filled\n * pie has no hole).\n */\n centerLabel?: PieChartCenterLabel;\n /** Outer radius of the arc. Omit to use recharts' responsive default. */\n outerRadius?: number;\n /** Gap between slices, in degrees. */\n paddingAngle?: number;\n /** Corner radius of each slice. */\n cornerRadius?: number;\n /**\n * Angle the sweep starts at, in degrees (0 is 3 o'clock, counter-clockwise).\n * Pair with `endAngle` for a semicircle (`180` → `0`) or an arc.\n *\n * A partial sweep still centres on the full plot area, so a half turn fills\n * only half its box — give the chart a short, wide one rather than expecting\n * the arc to grow into a square.\n */\n startAngle?: number;\n /** Angle the sweep ends at, in degrees. Defaults to a full `360` circle. */\n endAngle?: number;\n /**\n * Smallest angle a non-zero slice may occupy, in degrees — keeps a tiny slice\n * visible (and hoverable) at the cost of being drawn out of proportion.\n */\n minAngle?: number;\n /**\n * Per-slice overrides keyed by the slice's `nameKey` value: its `color`, and\n * whether/how its data label reads. A name with no entry keeps the chart-level\n * behavior.\n */\n sliceSettings?: Record<string, PieChartSliceSettings>;\n showTooltip?: boolean;\n showLegend?: boolean;\n /**\n * Font size of the legend text (labels and values). Defaults to `'xs'` —\n * the size the legend has always rendered at. See `ChartLegendFontSize`.\n */\n legendFontSize?: ChartLegendFontSize;\n /**\n * Format each slice's value in the list legend. When omitted the raw value is\n * stringified with `String()`.\n */\n legendValueFormatter?: (value: string | number) => string;\n /** Plot-area margin, in px. Omit to use recharts' default (5 on every side). */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\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-slice rows, or extra fields without composing\n * recharts yourself. Takes precedence over `tooltipFormat`. Ignored when\n * `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n /**\n * How the default tooltip reads a slice: its `value` alone (the default), or\n * the value followed by its share of the total (`value-percent`). The preset\n * covers the common case without hand-rolling a `tooltipContent`.\n *\n * The share is rendered as `NN.N%`, same as a data label's — not locale-aware.\n * Pass your own `tooltipContent` if you need to format it.\n */\n tooltipFormat?: PieChartTooltipFormat;\n /**\n * Position of the value labels when `showLabels` is on. Defaults to `outside`,\n * which keeps them on the chart surface — the only placement that reliably\n * has contrast, since slice fills are caller-supplied saturated colors. The\n * on-arc placements are available but switch to the on-fill label token.\n * Ignored when `labelLine` is on — a leader line only ever ends outside the arc.\n */\n labelPosition?: PolarLabelPosition;\n /**\n * What each data label reads when `showLabels` is on — the value alone (the\n * default), or the slice name and/or its share of the total. Overridable per\n * slice via `sliceSettings`.\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only: a share is always rendered as `NN.N%` (a `.` decimal and\n * a bare `%`, not locale-aware) and never passes through the formatter.\n */\n labelFormat?: PieChartLabelFormat;\n /**\n * Draw a leader line from each slice to its data label, in the slice's own\n * color. Moves the labels outside the arc (see `labelPosition`); slices whose\n * label is hidden by `sliceSettings` get no line either.\n *\n * A label reaching out past the arc needs horizontal room — give the chart a\n * box wider than it is tall, or shrink the arc (`outerRadius`/`margin`), or\n * the outermost labels are clipped at the surface edge.\n */\n labelLine?: boolean;\n}\n\nfunction renderCenterLabel({ centerLabel }: { centerLabel: PieChartCenterLabel }) {\n return function CenterLabelContent({ viewBox }: { viewBox?: object }) {\n if (!viewBox || !('cx' in viewBox)) return null;\n const { cx = 0, cy = 0 } = viewBox as { cx?: number; cy?: number };\n const hasValue = centerLabel.value != null;\n const hasLabel = centerLabel.label != null;\n const both = hasValue && hasLabel;\n return (\n <g>\n {hasValue && (\n <text\n x={cx}\n y={both ? cy - 10 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-foreground text-2xl font-semibold\"\n >\n {centerLabel.value}\n </text>\n )}\n {hasLabel && (\n <text\n x={cx}\n y={both ? cy + 13 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-muted-foreground text-sm\"\n >\n {centerLabel.label}\n </text>\n )}\n </g>\n );\n };\n}\n\nconst PieChart = React.forwardRef<HTMLDivElement, PieChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKey,\n nameKey,\n shape = 'donut',\n centerLabel,\n innerRadius = 48,\n outerRadius,\n paddingAngle = 2,\n cornerRadius,\n startAngle = 90,\n endAngle = -270,\n minAngle,\n sliceSettings,\n showTooltip = true,\n showLegend = true,\n legendFontSize,\n legendAriaLabel,\n legendValueFormatter,\n margin,\n tooltipContent,\n tooltipFormat = 'value',\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormat = 'value',\n labelFormatter,\n labelLine = false,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const pieLabelPosition = labelPosition ?? 'outside';\n const resolvedInnerRadius = shape === 'donut' ? innerRadius : 0;\n\n // Resolve colors so the external list legend has the same values as the chart\n // plot without needing to be inside ChartContainer's context.\n const resolvedConfigForLegend = React.useMemo(\n () => (showLegend ? resolveChartColors(config, palette ?? CHART_DEFAULT_PALETTE) : null),\n [showLegend, config, palette]\n );\n const rightLegendPayload: LegendPayload[] = React.useMemo(() => {\n if (!resolvedConfigForLegend) return [];\n return data.map((row) => ({\n value: String(row[nameKey]),\n dataKey: nameKey,\n color:\n resolvedConfigForLegend[String(row[nameKey])]?.color ??\n `var(--color-${toCssKey(String(row[nameKey]))})`,\n type: 'rect' as const,\n payload: row as Record<string, unknown>,\n }));\n }, [resolvedConfigForLegend, data, nameKey]);\n\n // The denominator behind every percent — the labels' and the tooltip's — so\n // the two always agree. Non-numeric cells count as nothing rather than NaN.\n const total = data.reduce(\n (sum, row) => sum + (toNumericValue(row[dataKey]) ?? 0),\n 0\n );\n\n // Always null, never '', for a slice with nothing to show — whether its\n // label was switched off or its format degraded to empty. recharts renders\n // no <text> at all for a nullish label value (`Label` bails on `isNullish`),\n // where an empty string still emits one wrapping an empty <tspan>.\n const resolveSliceLabel = (row: PieChartDatum | undefined): string | null => {\n if (!row) return null;\n const settings = sliceSettings?.[String(row[nameKey])];\n if (settings?.hideLabel) return null;\n return (\n pieChartLabelText({\n format: settings?.labelFormat ?? labelFormat,\n name: row[nameKey],\n value: row[dataKey],\n total,\n formatter: labelFormatter,\n }) || null\n );\n };\n\n // Leader lines are the one thing a `LabelList` can't draw: recharts only\n // renders them alongside the `label` prop's own outside placement. So the\n // labels move to that path when `labelLine` is on, and stay on the\n // position-aware `LabelList` otherwise.\n const showLeaderLines = showLabels && labelLine;\n\n const renderLeaderLabel = (labelProps: PieLabelRenderProps) => {\n const text = resolveSliceLabel(labelProps.payload as PieChartDatum);\n // An empty <g>, not null: recharts only takes the render function's output\n // when it's an element — a nullish return falls through to its own <Text>\n // painting the raw value.\n if (!text) return <g />;\n return (\n <text\n x={labelProps.x}\n y={labelProps.y}\n textAnchor={labelProps.textAnchor}\n dominantBaseline=\"central\"\n // Always the on-surface fill: this path only ever places labels\n // outside the arc, never over a slice.\n className={CHART_LABEL_FILL_CLASS}\n fontSize={CHART_LABEL_FONT_SIZE}\n >\n {text}\n </text>\n );\n };\n\n // recharts draws a line per sector independently of what the label renders,\n // so a slice hidden via `sliceSettings` would keep a line pointing at\n // nothing. Rebuilding the default `Curve` here is what lets it be dropped.\n const renderLeaderLine = ({\n key,\n payload,\n ...lineProps\n }: CurveProps & { key?: React.Key; payload?: PieChartDatum }) => {\n if (!resolveSliceLabel(payload)) return <g />;\n return (\n <Curve {...lineProps} type=\"linear\" className=\"recharts-pie-label-line\" />\n );\n };\n\n // Shared recharts chart composition (used in both layout modes).\n const pieChartContent = (\n <RechartsPieChart margin={margin ?? { top: 0, right: 0, bottom: 0, left: 0 }}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ??\n (tooltipFormat === 'value-percent' ? (\n pieChartValuePercentTooltip({ nameKey, config, total })\n ) : (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n ))\n }\n />\n )}\n <Pie\n data={data as unknown[]}\n dataKey={dataKey}\n nameKey={nameKey}\n innerRadius={resolvedInnerRadius}\n outerRadius={outerRadius ?? 60}\n paddingAngle={paddingAngle}\n cornerRadius={cornerRadius}\n startAngle={startAngle}\n endAngle={endAngle}\n minAngle={minAngle}\n label={showLeaderLines ? renderLeaderLabel : false}\n labelLine={showLeaderLines ? renderLeaderLine : false}\n {...animation}\n >\n {data.map((entry, index) => (\n // Keyed by index, not the name: two rows may share a nameKey\n // value, which would collide as a React key. Same-named rows\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell\n key={index}\n fill={\n sliceSettings?.[String(entry[nameKey])]?.color ??\n `var(--color-${toCssKey(String(entry[nameKey]))})`\n }\n />\n ))}\n {shape === 'donut' && centerLabel && (\n <Label content={renderCenterLabel({ centerLabel })} />\n )}\n {showLabels && !showLeaderLines && (\n <LabelList\n // `valueAccessor` rather than `dataKey`: the label text can\n // carry the slice's name and share, which only the whole row\n // (and the chart's total) can compose. recharts ignores the\n // accessor when a dataKey is set, so the two can't be combined.\n valueAccessor={(entry) =>\n resolveSliceLabel(entry.payload as PieChartDatum)\n }\n // Always explicit: recharts' polar fallback for an unset\n // position is the sector centroid — inside the fill — which\n // is exactly the placement the on-surface token can't survive.\n position={pieLabelPosition}\n className={resolveLabelFillClass(pieLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Pie>\n </RechartsPieChart>\n );\n\n // Grow the SVG container when labels need space outside the arc so they\n // stay within the chart boundary instead of escaping into the layout.\n const chartDivSize = showLabels\n ? showLeaderLines\n ? 'size-[320px]'\n : pieLabelPosition === 'outside' || pieLabelPosition === 'end'\n ? 'size-[200px]'\n : 'size-[120px]'\n : 'size-[120px]';\n\n return (\n <div\n ref={ref}\n data-shape={shape}\n className={cn(\n 'flex flex-row items-center gap-4',\n !showLegend && 'justify-center',\n pieChartVariants({ shape }),\n className\n )}\n {...props}\n >\n <div className={cn(chartDivSize, 'shrink-0')}>\n <ChartContainer config={config} palette={palette} className=\"size-full\">\n {pieChartContent}\n </ChartContainer>\n </div>\n {showLegend && resolvedConfigForLegend && (\n <ChartLegendContent\n variant=\"list\"\n payload={rightLegendPayload}\n config={resolvedConfigForLegend}\n nameKey={nameKey}\n fontSize={legendFontSize}\n ariaLabel={legendAriaLabel}\n valueKey={dataKey}\n valueFormatter={legendValueFormatter}\n className=\"min-w-0 flex-1\"\n />\n )}\n </div>\n );\n }\n);\nPieChart.displayName = 'PieChart';\n\nexport { PieChart, pieChartVariants };\n"],"mappings":"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"}
1
+ {"version":3,"file":"pie-chart.js","names":[],"sources":["../../../../src/components/ui/pie-chart/pie-chart.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Cell,\n Curve,\n Label,\n LabelList,\n Pie,\n PieChart as RechartsPieChart,\n type CurveProps,\n type PieLabelRenderProps,\n} from 'recharts';\n\nimport type { LegendPayload } from 'recharts/types/component/DefaultLegendContent';\n\nimport { cn } from '@/lib/utils';\nimport {\n ChartContainer,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n resolveAnimation,\n resolveChartColors,\n resolveLabelFillClass,\n CHART_DEFAULT_PALETTE,\n CHART_LABEL_FILL_CLASS,\n CHART_LABEL_FONT_SIZE,\n toCssKey,\n type ChartConfig,\n type ChartLegendFontSize,\n type ChartLegendAriaLabelProps,\n type ChartPalette,\n type ChartTooltipContentProps,\n type ChartAnimationProps,\n type ChartDataLabelProps,\n type PolarLabelPosition,\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 Pie-chart variant set: `shape` (a filled pie vs a\n// hollow-centre donut). The class stays empty because recharts' SVG — not CSS —\n// draws the arc: `shape` drives the `<Pie>`'s `innerRadius` (0 for a pie, the\n// `innerRadius` prop for a donut). 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-shape`.\nconst pieChartVariants = cva('', {\n variants: {\n shape: {\n pie: '',\n donut: '',\n },\n },\n defaultVariants: {\n shape: 'pie',\n },\n});\n\n/** Row-per-slice datum: the `nameKey` label plus the `dataKey` value. */\ntype PieChartDatum = Record<string, string | number>;\n\n/**\n * What a slice's data label reads. `percent` is the slice's share of the sum of\n * every slice value, to one decimal.\n */\nexport type PieChartLabelFormat =\n | 'value'\n | 'name-value'\n | 'name-percent'\n | 'percent';\n\n/** What the tooltip's value reads — the raw value, or the value and its share. */\nexport type PieChartTooltipFormat = 'value' | 'value-percent';\n\n/** Per-slice overrides, keyed by the slice's `nameKey` value. */\nexport interface PieChartSliceSettings {\n /**\n * Fill for this slice, overriding its `config` color. Reference an existing\n * semantic `--ui-*` token, same as `config`.\n */\n color?: string;\n /** Drop this slice's data label while every other slice keeps its own. */\n hideLabel?: boolean;\n /** Label format for this slice only — overrides the chart-level `labelFormat`. */\n labelFormat?: PieChartLabelFormat;\n}\n\nconst toNumericValue = (value: unknown): number | undefined =>\n typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n\n/**\n * A slice's share of the chart's total, to one decimal. The single place the\n * share is computed, so a slice's label and its tooltip row can't disagree.\n * `undefined` when there is nothing to divide — a non-numeric slice value, or a\n * total of zero — which is what lets the callers degrade instead of printing\n * `NaN%`.\n */\nconst slicePercentText = (\n value: unknown,\n total: number\n): string | undefined => {\n const numeric = toNumericValue(value);\n if (numeric === undefined || total <= 0) return undefined;\n return `${((numeric / total) * 100).toFixed(1)}%`;\n};\n\n/**\n * Compose one slice's data-label text. Exported for unit tests; not part of the\n * package's public API.\n *\n * A percent needs a numeric slice value *and* a non-zero total, and a value\n * label needs a value at all — so every format degrades rather than printing\n * `NaN%` or a dangling `\"Chrome: \"`. The two name-carrying formats fall back to\n * the bare name; the value-only ones to `''`, which `resolveSliceLabel`\n * normalizes to `null` (the one value recharts renders no `<text>` for).\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only. A percent is always composed by `slicePercentText` and\n * never passes through the formatter.\n */\nexport function pieChartLabelText(options: {\n format: PieChartLabelFormat;\n name: string | number;\n value: string | number | undefined;\n total: number;\n formatter?: TickFormatter;\n}): string {\n const { format, name, value, total, formatter } = options;\n const valueText =\n value == null ? '' : formatter ? formatter(value) : String(value);\n const percentText = slicePercentText(value, total);\n\n switch (format) {\n case 'name-value':\n return valueText ? `${name}: ${valueText}` : `${name}`;\n case 'name-percent':\n return percentText ? `${name}: ${percentText}` : `${name}`;\n case 'percent':\n return percentText ?? '';\n default:\n return valueText;\n }\n}\n\n/**\n * The row renderer behind `tooltipFormat=\"value-percent\"` — a slice's value\n * followed by its share. Exported for unit tests; not part of the package's\n * public API.\n *\n * A `formatter` replaces the default row wholesale, so the swatch and the name\n * are rebuilt here to match it: the shared `ChartTooltipContent` prefers a\n * `config` entry's `icon` over the color swatch, and this has to do the same or\n * a chart with icons would silently lose them under this preset — and the\n * name/value `gap-4`, which has to be repeated here because a `formatter`\n * replaces the shared row that carries it.\n */\nexport function pieChartValuePercentRow(options: {\n config: ChartConfig;\n total: number;\n}): NonNullable<ChartTooltipContentProps['formatter']> {\n const { config, total } = options;\n // Named rather than an arrow: it returns JSX, so an anonymous function reads\n // to eslint-plugin-react as a component with no display name.\n return function ValuePercentRow(value, name, item) {\n const itemConfig = config[String(name)];\n const percent = slicePercentText(value, total);\n const valueText = value?.toLocaleString() ?? '';\n return (\n <>\n {itemConfig?.icon ? (\n <itemConfig.icon />\n ) : (\n <div\n // Dotted, not the legend's square swatch: the shared row's\n // \"a row is always dotted, whatever marker the legend gives that\n // series\" invariant applies here too.\n className=\"h-2.5 w-2.5 shrink-0 rounded-full\"\n style={{ backgroundColor: item.payload?.fill ?? item.color }}\n />\n )}\n {/*\n * Mirrors the shared default row's `gap-4` (see `ChartTooltipContent`):\n * a `formatter` replaces that row wholesale, so the floor under the\n * name/value separation has to be restated here. Without it a value\n * *and* its share outgrows the tooltip's `min-w-[8rem]`, leaving\n * `justify-between` no free space to distribute.\n */}\n <div className=\"flex flex-1 items-center justify-between gap-4 leading-none\">\n <span className=\"text-muted-foreground\">\n {itemConfig?.label ?? name}\n </span>\n <span className=\"font-medium tabular-nums text-foreground\">\n {percent ? `${valueText} (${percent})` : valueText}\n </span>\n </div>\n </>\n );\n };\n}\n\n/**\n * The configured `ChartTooltipContent` for `tooltipFormat=\"value-percent\"`.\n * Exported so the visual-regression story renders the very same element the\n * component does — recharts can only open a hover tooltip statically via\n * `defaultIndex` on a raw composition, so the preset has to be reachable from\n * outside the component to be snapshotted at all.\n */\nexport function pieChartValuePercentTooltip(options: {\n nameKey: string;\n config: ChartConfig;\n total: number;\n}): React.ReactElement {\n const { nameKey, config, total } = options;\n return (\n <ChartTooltipContent\n nameKey={nameKey}\n hideLabel\n formatter={pieChartValuePercentRow({ config, total })}\n />\n );\n}\n\nexport interface PieChartCenterLabel {\n // Rendered as SVG <text>, which only lays out text — hence string | number,\n // not ReactNode.\n /** Headline metric rendered large in the donut hole (e.g. a total). */\n value?: string | number;\n /** Caption rendered under the value. */\n label?: string | number;\n}\n\nexport interface PieChartProps\n extends Omit<React.ComponentProps<'div'>, 'children'>,\n VariantProps<typeof pieChartVariants>,\n ChartAnimationProps,\n ChartDataLabelProps,\n ChartLegendAriaLabelProps {\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-slice data. Each object holds the slice's `nameKey` label + its `dataKey` numeric value. */\n data: ReadonlyArray<PieChartDatum>;\n /**\n * Per-slice map of `label` / `icon` / `tone`, keyed by the slice'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 slice. */\n dataKey: string;\n /**\n * Label field that names each slice (drives the legend, tooltip, and\n * `--color-<name>` lookup). Values should be unique per chart — rows sharing a\n * name share one `config`/color entry.\n */\n nameKey: string;\n /** Inner radius of the arc when `shape=\"donut\"` (ignored for `shape=\"pie\"`). */\n innerRadius?: number;\n /**\n * Custom content for the donut hole — a headline `value` and/or a `label`\n * caption, stacked and centered. Rendered only for `shape=\"donut\"` (a filled\n * pie has no hole).\n */\n centerLabel?: PieChartCenterLabel;\n /** Outer radius of the arc. Omit to use recharts' responsive default. */\n outerRadius?: number;\n /** Gap between slices, in degrees. */\n paddingAngle?: number;\n /** Corner radius of each slice. */\n cornerRadius?: number;\n /**\n * Angle the sweep starts at, in degrees (0 is 3 o'clock, counter-clockwise).\n * Pair with `endAngle` for a semicircle (`180` → `0`) or an arc.\n *\n * A partial sweep still centres on the full plot area, so a half turn fills\n * only half its box — give the chart a short, wide one rather than expecting\n * the arc to grow into a square.\n */\n startAngle?: number;\n /** Angle the sweep ends at, in degrees. Defaults to a full `360` circle. */\n endAngle?: number;\n /**\n * Smallest angle a non-zero slice may occupy, in degrees — keeps a tiny slice\n * visible (and hoverable) at the cost of being drawn out of proportion.\n */\n minAngle?: number;\n /**\n * Per-slice overrides keyed by the slice's `nameKey` value: its `color`, and\n * whether/how its data label reads. A name with no entry keeps the chart-level\n * behavior.\n */\n sliceSettings?: Record<string, PieChartSliceSettings>;\n showTooltip?: boolean;\n showLegend?: boolean;\n /**\n * Font size of the legend text (labels and values). Defaults to `'xs'` —\n * the size the legend has always rendered at. See `ChartLegendFontSize`.\n */\n legendFontSize?: ChartLegendFontSize;\n /**\n * Format each slice's value in the list legend. When omitted the raw value is\n * stringified with `String()`.\n */\n legendValueFormatter?: (value: string | number) => string;\n /** Plot-area margin, in px. Omit to use recharts' default (5 on every side). */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\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-slice rows, or extra fields without composing\n * recharts yourself. Takes precedence over `tooltipFormat`. Ignored when\n * `showTooltip` is false.\n */\n tooltipContent?: React.ComponentProps<typeof ChartTooltip>['content'];\n /**\n * How the default tooltip reads a slice: its `value` alone (the default), or\n * the value followed by its share of the total (`value-percent`). The preset\n * covers the common case without hand-rolling a `tooltipContent`.\n *\n * The share is rendered as `NN.N%`, same as a data label's — not locale-aware.\n * Pass your own `tooltipContent` if you need to format it.\n */\n tooltipFormat?: PieChartTooltipFormat;\n /**\n * Position of the value labels when `showLabels` is on. Defaults to `outside`,\n * which keeps them on the chart surface — the only placement that reliably\n * has contrast, since slice fills are caller-supplied saturated colors. The\n * on-arc placements are available but switch to the on-fill label token.\n * Ignored when `labelLine` is on — a leader line only ever ends outside the arc.\n */\n labelPosition?: PolarLabelPosition;\n /**\n * What each data label reads when `showLabels` is on — the value alone (the\n * default), or the slice name and/or its share of the total. Overridable per\n * slice via `sliceSettings`.\n *\n * `labelFormatter` formats the slice's *value*, so it reaches `value` and\n * `name-value` only: a share is always rendered as `NN.N%` (a `.` decimal and\n * a bare `%`, not locale-aware) and never passes through the formatter.\n */\n labelFormat?: PieChartLabelFormat;\n /**\n * Draw a leader line from each slice to its data label, in the slice's own\n * color. Moves the labels outside the arc (see `labelPosition`); slices whose\n * label is hidden by `sliceSettings` get no line either.\n *\n * A label reaching out past the arc needs horizontal room — give the chart a\n * box wider than it is tall, or shrink the arc (`outerRadius`/`margin`), or\n * the outermost labels are clipped at the surface edge.\n */\n labelLine?: boolean;\n}\n\nfunction renderCenterLabel({ centerLabel }: { centerLabel: PieChartCenterLabel }) {\n return function CenterLabelContent({ viewBox }: { viewBox?: object }) {\n if (!viewBox || !('cx' in viewBox)) return null;\n const { cx = 0, cy = 0 } = viewBox as { cx?: number; cy?: number };\n const hasValue = centerLabel.value != null;\n const hasLabel = centerLabel.label != null;\n const both = hasValue && hasLabel;\n return (\n <g>\n {hasValue && (\n <text\n x={cx}\n y={both ? cy - 10 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-foreground text-2xl font-semibold\"\n >\n {centerLabel.value}\n </text>\n )}\n {hasLabel && (\n <text\n x={cx}\n y={both ? cy + 13 : cy}\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n className=\"fill-muted-foreground text-sm\"\n >\n {centerLabel.label}\n </text>\n )}\n </g>\n );\n };\n}\n\nconst PieChart = React.forwardRef<HTMLDivElement, PieChartProps>(\n (\n {\n className,\n config,\n palette,\n data,\n dataKey,\n nameKey,\n shape = 'donut',\n centerLabel,\n innerRadius = 48,\n outerRadius,\n paddingAngle = 2,\n cornerRadius,\n startAngle = 90,\n endAngle = -270,\n minAngle,\n sliceSettings,\n showTooltip = true,\n showLegend = true,\n legendFontSize,\n legendAriaLabel,\n legendValueFormatter,\n margin,\n tooltipContent,\n tooltipFormat = 'value',\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n showLabels = false,\n labelPosition,\n labelFormat = 'value',\n labelFormatter,\n labelLine = false,\n ...props\n },\n ref\n ) => {\n const animation = resolveAnimation({\n animate,\n animationDuration,\n animationBegin,\n animationEasing,\n });\n const pieLabelPosition = labelPosition ?? 'outside';\n const resolvedInnerRadius = shape === 'donut' ? innerRadius : 0;\n\n // Resolve colors so the external list legend has the same values as the chart\n // plot without needing to be inside ChartContainer's context.\n const resolvedConfigForLegend = React.useMemo(\n () => (showLegend ? resolveChartColors(config, palette ?? CHART_DEFAULT_PALETTE) : null),\n [showLegend, config, palette]\n );\n const rightLegendPayload: LegendPayload[] = React.useMemo(() => {\n if (!resolvedConfigForLegend) return [];\n return data.map((row) => ({\n value: String(row[nameKey]),\n dataKey: nameKey,\n color:\n resolvedConfigForLegend[String(row[nameKey])]?.color ??\n `var(--color-${toCssKey(String(row[nameKey]))})`,\n type: 'rect' as const,\n payload: row as Record<string, unknown>,\n }));\n }, [resolvedConfigForLegend, data, nameKey]);\n const hasLegend = Boolean(showLegend && rightLegendPayload.length);\n\n // The denominator behind every percent — the labels' and the tooltip's — so\n // the two always agree. Non-numeric cells count as nothing rather than NaN.\n const total = data.reduce(\n (sum, row) => sum + (toNumericValue(row[dataKey]) ?? 0),\n 0\n );\n\n // Always null, never '', for a slice with nothing to show — whether its\n // label was switched off or its format degraded to empty. recharts renders\n // no <text> at all for a nullish label value (`Label` bails on `isNullish`),\n // where an empty string still emits one wrapping an empty <tspan>.\n const resolveSliceLabel = (row: PieChartDatum | undefined): string | null => {\n if (!row) return null;\n const settings = sliceSettings?.[String(row[nameKey])];\n if (settings?.hideLabel) return null;\n return (\n pieChartLabelText({\n format: settings?.labelFormat ?? labelFormat,\n name: row[nameKey],\n value: row[dataKey],\n total,\n formatter: labelFormatter,\n }) || null\n );\n };\n\n // Leader lines are the one thing a `LabelList` can't draw: recharts only\n // renders them alongside the `label` prop's own outside placement. So the\n // labels move to that path when `labelLine` is on, and stay on the\n // position-aware `LabelList` otherwise.\n const showLeaderLines = showLabels && labelLine;\n\n const renderLeaderLabel = (labelProps: PieLabelRenderProps) => {\n const text = resolveSliceLabel(labelProps.payload as PieChartDatum);\n // An empty <g>, not null: recharts only takes the render function's output\n // when it's an element — a nullish return falls through to its own <Text>\n // painting the raw value.\n if (!text) return <g />;\n return (\n <text\n x={labelProps.x}\n y={labelProps.y}\n textAnchor={labelProps.textAnchor}\n dominantBaseline=\"central\"\n // Always the on-surface fill: this path only ever places labels\n // outside the arc, never over a slice.\n className={CHART_LABEL_FILL_CLASS}\n fontSize={CHART_LABEL_FONT_SIZE}\n >\n {text}\n </text>\n );\n };\n\n // recharts draws a line per sector independently of what the label renders,\n // so a slice hidden via `sliceSettings` would keep a line pointing at\n // nothing. Rebuilding the default `Curve` here is what lets it be dropped.\n const renderLeaderLine = ({\n key,\n payload,\n ...lineProps\n }: CurveProps & { key?: React.Key; payload?: PieChartDatum }) => {\n if (!resolveSliceLabel(payload)) return <g />;\n return (\n <Curve {...lineProps} type=\"linear\" className=\"recharts-pie-label-line\" />\n );\n };\n\n // Shared recharts chart composition (used in both layout modes).\n const pieChartContent = (\n <RechartsPieChart margin={margin ?? { top: 0, right: 0, bottom: 0, left: 0 }}>\n {showTooltip && (\n <ChartTooltip\n content={\n tooltipContent ??\n (tooltipFormat === 'value-percent' ? (\n pieChartValuePercentTooltip({ nameKey, config, total })\n ) : (\n <ChartTooltipContent nameKey={nameKey} hideLabel />\n ))\n }\n />\n )}\n <Pie\n data={data as unknown[]}\n dataKey={dataKey}\n nameKey={nameKey}\n innerRadius={resolvedInnerRadius}\n outerRadius={outerRadius ?? 60}\n paddingAngle={paddingAngle}\n cornerRadius={cornerRadius}\n startAngle={startAngle}\n endAngle={endAngle}\n minAngle={minAngle}\n label={showLeaderLines ? renderLeaderLabel : false}\n labelLine={showLeaderLines ? renderLeaderLine : false}\n {...animation}\n >\n {data.map((entry, index) => (\n // Keyed by index, not the name: two rows may share a nameKey\n // value, which would collide as a React key. Same-named rows\n // intentionally share a color/config entry via `--color-<name>`.\n <Cell\n key={index}\n fill={\n sliceSettings?.[String(entry[nameKey])]?.color ??\n `var(--color-${toCssKey(String(entry[nameKey]))})`\n }\n />\n ))}\n {shape === 'donut' && centerLabel && (\n <Label content={renderCenterLabel({ centerLabel })} />\n )}\n {showLabels && !showLeaderLines && (\n <LabelList\n // `valueAccessor` rather than `dataKey`: the label text can\n // carry the slice's name and share, which only the whole row\n // (and the chart's total) can compose. recharts ignores the\n // accessor when a dataKey is set, so the two can't be combined.\n valueAccessor={(entry) =>\n resolveSliceLabel(entry.payload as PieChartDatum)\n }\n // Always explicit: recharts' polar fallback for an unset\n // position is the sector centroid — inside the fill — which\n // is exactly the placement the on-surface token can't survive.\n position={pieLabelPosition}\n className={resolveLabelFillClass(pieLabelPosition)}\n fontSize={CHART_LABEL_FONT_SIZE}\n />\n )}\n </Pie>\n </RechartsPieChart>\n );\n\n // Outside labels need the full 320px SVG canvas; constraining it to half of\n // the widget clips labels at the SVG boundary. Inside labels fit the 120px\n // plot canvas.\n const chartAreaSize =\n showLabels &&\n (showLeaderLines ||\n pieLabelPosition === 'outside' ||\n pieLabelPosition === 'end')\n ? 320\n : 120;\n const chartDivSize =\n chartAreaSize === 320\n ? 'aspect-square w-full max-w-[320px]'\n : 'size-[120px]';\n const chartColumnSize =\n chartAreaSize === 320\n ? 'max-w-[320px] flex-[1_1_320px]'\n : 'flex-1 basis-[120px]';\n\n return (\n <div\n ref={ref}\n data-shape={shape}\n className={cn(\n 'flex w-full min-w-0 gap-4',\n hasLegend ? 'flex-wrap items-stretch' : 'items-center justify-center',\n pieChartVariants({ shape }),\n className\n )}\n {...props}\n >\n <div\n className={cn(\n 'flex min-w-0 items-center justify-center',\n hasLegend ? chartColumnSize : 'w-full'\n )}\n >\n <div className={chartDivSize}>\n <ChartContainer\n config={config}\n palette={palette}\n className=\"size-full\"\n >\n {pieChartContent}\n </ChartContainer>\n </div>\n </div>\n {hasLegend && resolvedConfigForLegend && (\n <ChartLegendContent\n variant=\"list\"\n payload={rightLegendPayload}\n config={resolvedConfigForLegend}\n nameKey={nameKey}\n fontSize={legendFontSize}\n ariaLabel={legendAriaLabel}\n valueKey={dataKey}\n valueFormatter={legendValueFormatter}\n valueClassName=\"text-[var(--ui-text-on-surface-primary)]\"\n className=\"min-w-0 flex-1 basis-[120px] self-center\"\n />\n )}\n </div>\n );\n }\n);\nPieChart.displayName = 'PieChart';\n\nexport { PieChart, pieChartVariants };\n"],"mappings":"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"}