@acronis-platform/ui-react 5.5.9 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ui/chart/chart.js +14 -11
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state.js +47 -37
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +21 -23
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -1
- package/dist/components/ui/data-table/data-table.js +174 -173
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/dialog/dialog.js +52 -38
- package/dist/components/ui/dialog/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu.js +8 -2
- package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
- package/dist/components/ui/page-header/page-header.js +4 -4
- package/dist/components/ui/page-header/page-header.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +88 -84
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/index.js +45 -46
- package/dist/llms/chart-state.md +81 -164
- package/dist/llms/chart-widget.md +27 -25
- package/dist/llms/data-table.md +24 -14
- package/dist/llms/dialog-root.md +40 -13
- package/dist/llms/dialog.md +109 -14
- package/dist/llms/dropdown-menu.md +68 -3
- package/dist/llms/page-header.md +68 -11
- package/dist/llms/stepper-item.md +10 -7
- package/dist/llms.txt +4 -4
- package/dist/react.js +45 -46
- package/dist/src/components/ui/chart/chart.d.ts +3 -3
- package/dist/src/components/ui/chart-state/chart-state.d.ts +5 -14
- package/dist/src/components/ui/chart-state/index.d.ts +0 -1
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +10 -15
- package/dist/src/components/ui/data-table/data-table.d.ts +11 -5
- package/dist/src/components/ui/data-table/data-table.docs.d.ts +10 -4
- package/dist/src/components/ui/dialog/dialog.d.ts +10 -0
- package/dist/src/components/ui/dialog/dialog.docs.d.ts +7 -1
- package/dist/src/components/ui/dropdown-menu/dropdown-menu.d.ts +8 -1
- package/dist/src/components/ui/dropdown-menu/index.d.ts +1 -1
- package/dist/src/components/ui/page-header/page-header.d.ts +3 -1
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +1 -1
- package/dist/ui-react.css +1 -1
- package/package.json +13 -13
- package/dist/components/ui/chart-state/chart-state-illustrations.js +0 -412
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +0 -1
- 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":";;;;;;;;;;;;;AA+EA,IAAM,IAAwB,EAC5B,48BACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,OAAO;CACT,EACF;CACA,iBAAiB,EACf,MAAM,KACR;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;AA0B5B,IAAM,IAAgB,EAAM,YAKxB,EACE,cACA,aACA,SACA,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,EAAG,EAAsB,EAAE,QAAK,CAAC,GAAG,CAAS;EACxD,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,GA6FM,IAAS,EAAM,YAEjB,EACE,aAAU,WACV,gBAAa,IACb,iBACA,eAAY,IACZ,eAAY,IACZ,aACA,UACA,eACA,oBACA,mBACA,iBACA,oBACA,WACA,eACA,SACA,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;GACE;GACS;GACJ;GACF;GACX,aAAW,KAAc,KAAA;GAP3B,UAAA;IASG,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"}
|
|
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// Product mockups place dialogs 48px below the viewport top. The max-width\n// and max-height also preserve 48px margins at the other viewport edges.\nconst dialogContentVariants = cva(\n 'fixed left-1/2 z-50 flex max-h-[calc(100dvh-96px)] w-full min-w-[min(var(--ui-dialog-container-width-min),calc(100dvw-96px))] -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: 'top',\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. `top` (default) pins it 48px from the\n * viewport top; `center` centers it vertically in the viewport.\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 ?? 'top' }),\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 className?: string;\n /** Makes the body unfocusable/unclickable while the loading overlay is shown. */\n inert?: boolean;\n}\n\nfunction DialogBody({ children, className, inert }: DialogBodyProps) {\n return (\n <div\n inert={inert}\n className={cn(\n '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 className\n )}\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: `'top'` (default, 48px from viewport top) or `'center'` (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 body wrapper to adjust its layout or overflow. */\n bodyClassName?: string;\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 bodyClassName,\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 className={bodyClassName} inert={hasLoading}>\n {bodyContent}\n </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":";;;;;;;;;;;;;AA2EA,IAAM,IAAwB,EAC5B,08BACA;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;AA+B5B,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;EAAM,CAAC,GACrE,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;AAaA,SAAS,EAAW,EAAE,aAAU,cAAW,YAA0B;CACnE,OACE,kBAAC,OAAD;EACS;EACP,WAAW,EACT,wKACA,CACF;EAEC,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,GAiGM,IAAS,EAAM,YAEjB,EACE,aAAU,WACV,gBAAa,IACb,iBACA,eAAY,IACZ,eAAY,IACZ,aACA,UACA,eACA,oBACA,mBACA,iBACA,oBACA,WACA,eACA,kBACA,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,WAAW;KAAe,OAAO;KAC1C,UAAA;IACS,CAAA;IAEX,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"}
|
|
@@ -5,13 +5,19 @@ import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
|
5
5
|
import { CheckSmallIcon as a, ChevronRightIcon as o } from "@acronis-platform/icons-react/stroke-mono";
|
|
6
6
|
import { Menu as s } from "@base-ui/react/menu";
|
|
7
7
|
//#region src/components/ui/dropdown-menu/dropdown-menu.tsx
|
|
8
|
-
|
|
8
|
+
function c({ modal: e = !1, ...t }) {
|
|
9
|
+
return /* @__PURE__ */ r(s.Root, {
|
|
10
|
+
modal: e,
|
|
11
|
+
...t
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
var l = s.Trigger, u = n.forwardRef(({ className: t, ...n }, i) => /* @__PURE__ */ r(s.Group, {
|
|
9
15
|
ref: i,
|
|
10
16
|
className: e("flex flex-col items-start overflow-clip gap-[var(--ui-button-menu-dropdown-section-list-gap)] px-[var(--ui-button-menu-dropdown-section-container-padding-x)] py-[var(--ui-button-menu-dropdown-section-container-padding-y)] [&:not(:first-child)]:border-t [&:not(:first-child)]:border-[length:var(--ui-button-menu-dropdown-section-container-border-width)] [&:not(:first-child)]:border-[var(--ui-button-menu-dropdown-section-container-border-color)]", t),
|
|
11
17
|
...n
|
|
12
18
|
}));
|
|
13
19
|
u.displayName = "DropdownMenuGroup";
|
|
14
|
-
var d = s.Portal, f = s.SubmenuRoot, p = "z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden flex flex-col gap-[var(--ui-button-menu-dropdown-container-gap)] bg-[var(--ui-button-menu-dropdown-container-color)] border-solid border-[length:var(--ui-button-menu-dropdown-container-border-width)] border-[var(--ui-button-menu-dropdown-container-border-color)] rounded-[var(--ui-button-menu-dropdown-container-border-radius)] px-[var(--ui-button-menu-dropdown-container-padding-x)] py-[var(--ui-button-menu-dropdown-container-padding-y)] shadow-md outline-none duration-200 data-[open]:animate-in data-[closed]:animate-out data-[open]:fade-in-0 data-[closed]:fade-out-0 data-[open]:zoom-in-95 data-[closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2", m = "relative flex w-full shrink-0 cursor-default select-none items-start gap-[var(--ui-button-menu-dropdown-item-container-gap)] min-h-[var(--ui-button-menu-dropdown-item-container-height)] px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 outline-none transition-colors bg-[var(--ui-button-menu-dropdown-item-container-color-idle)] text-[var(--ui-button-menu-dropdown-item-label-color)] [&_svg]:text-[var(--ui-button-menu-dropdown-item-icon-color)] data-[highlighted]:bg-[var(--ui-button-menu-dropdown-item-container-color-hover)] data-[highlighted]:active:bg-[var(--ui-button-menu-dropdown-item-container-color-active)] [&:focus-visible:not(:hover)]:rounded-[3px] [&:focus-visible:not(:hover)]:ring-[3px] [&:focus-visible:not(:hover)]:ring-inset [&:focus-visible:not(:hover)]:ring-[var(--ui-focus-primary)] data-[disabled]:
|
|
20
|
+
var d = s.Portal, f = s.SubmenuRoot, p = "z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden flex flex-col gap-[var(--ui-button-menu-dropdown-container-gap)] bg-[var(--ui-button-menu-dropdown-container-color)] border-solid border-[length:var(--ui-button-menu-dropdown-container-border-width)] border-[var(--ui-button-menu-dropdown-container-border-color)] rounded-[var(--ui-button-menu-dropdown-container-border-radius)] px-[var(--ui-button-menu-dropdown-container-padding-x)] py-[var(--ui-button-menu-dropdown-container-padding-y)] shadow-md outline-none duration-200 data-[open]:animate-in data-[closed]:animate-out data-[open]:fade-in-0 data-[closed]:fade-out-0 data-[open]:zoom-in-95 data-[closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2", m = "relative flex w-full shrink-0 cursor-default select-none items-start gap-[var(--ui-button-menu-dropdown-item-container-gap)] min-h-[var(--ui-button-menu-dropdown-item-container-height)] px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 outline-none transition-colors bg-[var(--ui-button-menu-dropdown-item-container-color-idle)] text-[var(--ui-button-menu-dropdown-item-label-color)] [&_svg]:text-[var(--ui-button-menu-dropdown-item-icon-color)] data-[highlighted]:bg-[var(--ui-button-menu-dropdown-item-container-color-hover)] data-[highlighted]:active:bg-[var(--ui-button-menu-dropdown-item-container-color-active)] [&:focus-visible:not(:hover)]:rounded-[3px] [&:focus-visible:not(:hover)]:ring-[3px] [&:focus-visible:not(:hover)]:ring-inset [&:focus-visible:not(:hover)]:ring-[var(--ui-focus-primary)] data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", h = "flex h-6 w-4 shrink-0 items-center justify-center text-[var(--ui-button-menu-dropdown-item-icon-color)]", g = "ps-[calc(var(--ui-button-menu-dropdown-item-container-padding-x)+1rem+var(--ui-button-menu-dropdown-item-container-gap))]", _ = n.forwardRef(({ className: n, side: i, align: a, sideOffset: o = 4, portal: c = !0, portalContainer: l, keepMounted: u, ...d }, f) => {
|
|
15
21
|
let m = t(), h = l ?? m, g = /* @__PURE__ */ r(s.Positioner, {
|
|
16
22
|
side: i,
|
|
17
23
|
align: a,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.js","names":[],"sources":["../../../../src/components/ui/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Menu as MenuPrimitive } from '@base-ui/react/menu';\nimport { CheckSmallIcon, ChevronRightIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\n// A menu of actions anchored to a trigger, built on the Base UI Menu primitive\n// (positioning, focus management, typeahead, outside-press / Esc dismissal,\n// ARIA). Themed from the `--ui-button-menu-dropdown-*` token tier generated by\n// @acronis-platform/tokens-pd (ButtonMenu/default.css). Enter/exit animations\n// use `tw-animate-css` keyed to data-[open]/[closed]/[side].\n\nconst DropdownMenu = MenuPrimitive.Root;\nconst DropdownMenuTrigger = MenuPrimitive.Trigger;\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <MenuPrimitive.Group\n ref={ref}\n className={cn(\n 'flex flex-col items-start overflow-clip gap-[var(--ui-button-menu-dropdown-section-list-gap)] px-[var(--ui-button-menu-dropdown-section-container-padding-x)] py-[var(--ui-button-menu-dropdown-section-container-padding-y)] [&:not(:first-child)]:border-t [&:not(:first-child)]:border-[length:var(--ui-button-menu-dropdown-section-container-border-width)] [&:not(:first-child)]:border-[var(--ui-button-menu-dropdown-section-container-border-color)]',\n className\n )}\n {...props}\n />\n));\nDropdownMenuGroup.displayName = 'DropdownMenuGroup';\nconst DropdownMenuPortal = MenuPrimitive.Portal;\nconst DropdownMenuSub = MenuPrimitive.SubmenuRoot;\n\nconst popupClassName =\n 'z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden flex flex-col gap-[var(--ui-button-menu-dropdown-container-gap)] bg-[var(--ui-button-menu-dropdown-container-color)] border-solid border-[length:var(--ui-button-menu-dropdown-container-border-width)] border-[var(--ui-button-menu-dropdown-container-border-color)] rounded-[var(--ui-button-menu-dropdown-container-border-radius)] px-[var(--ui-button-menu-dropdown-container-padding-x)] py-[var(--ui-button-menu-dropdown-container-padding-y)] shadow-md outline-none duration-200 data-[open]:animate-in data-[closed]:animate-out data-[open]:fade-in-0 data-[closed]:fade-out-0 data-[open]:zoom-in-95 data-[closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2';\n\nconst itemClassName =\n 'relative flex w-full shrink-0 cursor-default select-none items-start gap-[var(--ui-button-menu-dropdown-item-container-gap)] min-h-[var(--ui-button-menu-dropdown-item-container-height)] px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 outline-none transition-colors bg-[var(--ui-button-menu-dropdown-item-container-color-idle)] text-[var(--ui-button-menu-dropdown-item-label-color)] [&_svg]:text-[var(--ui-button-menu-dropdown-item-icon-color)] data-[highlighted]:bg-[var(--ui-button-menu-dropdown-item-container-color-hover)] data-[highlighted]:active:bg-[var(--ui-button-menu-dropdown-item-container-color-active)] [&:focus-visible:not(:hover)]:rounded-[3px] [&:focus-visible:not(:hover)]:ring-[3px] [&:focus-visible:not(:hover)]:ring-inset [&:focus-visible:not(:hover)]:ring-[var(--ui-focus-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0';\n\n// A checkbox/radio indicator lives in the same in-flow leading slot a menu-item\n// icon would occupy, so its label lands on the item gap grid (padding-x + 16px\n// glyph + gap) instead of a hardcoded indent. `h-6` matches the item's\n// line-height so the glyph centers on the first line of a wrapping label.\nconst indicatorSlotClassName =\n 'flex h-6 w-4 shrink-0 items-center justify-center text-[var(--ui-button-menu-dropdown-item-icon-color)]';\n\n// Indent for plain items/labels that must line up with the labels of sibling\n// checkbox/radio items.\nconst indicatorInsetClassName =\n 'ps-[calc(var(--ui-button-menu-dropdown-item-container-padding-x)+1rem+var(--ui-button-menu-dropdown-item-container-gap))]';\n\nexport interface DropdownMenuContentProps\n extends React.ComponentPropsWithoutRef<typeof MenuPrimitive.Popup> {\n side?: MenuPrimitive.Positioner.Props['side'];\n align?: MenuPrimitive.Positioner.Props['align'];\n sideOffset?: number;\n /** Render inside a portal (default `true`). */\n portal?: boolean;\n /** Portal container — pass a shadow-root mount for isolated-style previews. */\n portalContainer?: MenuPrimitive.Portal.Props['container'];\n keepMounted?: MenuPrimitive.Portal.Props['keepMounted'];\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Popup>,\n DropdownMenuContentProps\n>(\n (\n {\n className,\n side,\n align,\n sideOffset = 4,\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 positioner = (\n <MenuPrimitive.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n className=\"z-50\"\n >\n <MenuPrimitive.Popup\n ref={ref}\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n );\n return portal ? (\n <MenuPrimitive.Portal container={resolvedContainer} keepMounted={keepMounted}>\n {positioner}\n </MenuPrimitive.Portal>\n ) : (\n positioner\n );\n }\n);\nDropdownMenuContent.displayName = 'DropdownMenuContent';\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Popup>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Popup> & {\n sideOffset?: number;\n }\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <MenuPrimitive.Positioner sideOffset={sideOffset} className=\"z-50\">\n <MenuPrimitive.Popup\n ref={ref}\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n));\nDropdownMenuSubContent.displayName = 'DropdownMenuSubContent';\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Item> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <MenuPrimitive.Item\n ref={ref}\n className={cn(itemClassName, inset && indicatorInsetClassName, className)}\n {...props}\n />\n));\nDropdownMenuItem.displayName = 'DropdownMenuItem';\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.SubmenuTrigger>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.SubmenuTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <MenuPrimitive.SubmenuTrigger\n ref={ref}\n className={cn(\n itemClassName,\n 'data-[popup-open]:bg-[var(--ui-button-menu-dropdown-item-container-color-active)]',\n inset && indicatorInsetClassName,\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ms-auto self-center text-[var(--ui-button-menu-dropdown-extras-cascade-icon-color)] rtl:rotate-180\" />\n </MenuPrimitive.SubmenuTrigger>\n));\nDropdownMenuSubTrigger.displayName = 'DropdownMenuSubTrigger';\n\n\nfunction DropdownMenuShortcut({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) {\n return (\n <span\n className={cn('ms-auto shrink-0 text-sm font-normal leading-6 text-[var(--ui-button-menu-dropdown-extras-shortcut-label-color)]', className)}\n {...props}\n />\n );\n}\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut';\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.CheckboxItem>\n>(({ className, children, ...props }, ref) => (\n <MenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(itemClassName, className)}\n {...props}\n >\n <span className={indicatorSlotClassName}>\n <MenuPrimitive.CheckboxItemIndicator className=\"flex\">\n <CheckSmallIcon size={16} />\n </MenuPrimitive.CheckboxItemIndicator>\n </span>\n {children}\n </MenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName = 'DropdownMenuCheckboxItem';\n\nconst DropdownMenuRadioGroup = MenuPrimitive.RadioGroup;\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <MenuPrimitive.RadioItem\n ref={ref}\n className={cn(itemClassName, className)}\n {...props}\n >\n <span className={indicatorSlotClassName}>\n {/* The dot is the indicator element itself — Base UI renders it as a\n `<span>`, and an inline wrapper would collapse a sized child to 0. */}\n <MenuPrimitive.RadioItemIndicator className=\"block size-2 rounded-full bg-current\" />\n </span>\n {children}\n </MenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = 'DropdownMenuRadioItem';\n\nconst DropdownMenuLabel = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 text-[var(--ui-button-menu-dropdown-item-label-color)]',\n inset && indicatorInsetClassName,\n className\n )}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = 'DropdownMenuLabel';\n\nconst DropdownMenuSeparator = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n role=\"separator\"\n className={cn(\n 'my-[var(--ui-button-menu-dropdown-section-list-gap)] h-[var(--ui-button-menu-dropdown-section-container-border-width)] bg-[var(--ui-button-menu-dropdown-section-container-border-color)]',\n className\n )}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = 'DropdownMenuSeparator';\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuPortal,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-menu.js","names":[],"sources":["../../../../src/components/ui/dropdown-menu/dropdown-menu.tsx"],"sourcesContent":["import * as React from 'react';\nimport { Menu as MenuPrimitive } from '@base-ui/react/menu';\nimport {\n CheckSmallIcon,\n ChevronRightIcon,\n} from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { usePortalContainer } from '@/lib/portal-container';\n\n// A menu of actions anchored to a trigger, built on the Base UI Menu primitive\n// (positioning, focus management, typeahead, outside-press / Esc dismissal,\n// ARIA). Themed from the `--ui-button-menu-dropdown-*` token tier generated by\n// @acronis-platform/tokens-pd (ButtonMenu/default.css). Enter/exit animations\n// use `tw-animate-css` keyed to data-[open]/[closed]/[side].\n\nexport type DropdownMenuProps<Payload = unknown> = Omit<\n MenuPrimitive.Root.Props<Payload>,\n 'modal'\n> & {\n /**\n * Whether opening the menu blocks outside interaction and locks page scroll.\n * Defaults to `false`.\n */\n modal?: boolean;\n};\n\nfunction DropdownMenu<Payload = unknown>({\n modal = false,\n ...props\n}: DropdownMenuProps<Payload>) {\n return <MenuPrimitive.Root<Payload> modal={modal} {...props} />;\n}\n\nconst DropdownMenuTrigger = MenuPrimitive.Trigger;\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <MenuPrimitive.Group\n ref={ref}\n className={cn(\n 'flex flex-col items-start overflow-clip gap-[var(--ui-button-menu-dropdown-section-list-gap)] px-[var(--ui-button-menu-dropdown-section-container-padding-x)] py-[var(--ui-button-menu-dropdown-section-container-padding-y)] [&:not(:first-child)]:border-t [&:not(:first-child)]:border-[length:var(--ui-button-menu-dropdown-section-container-border-width)] [&:not(:first-child)]:border-[var(--ui-button-menu-dropdown-section-container-border-color)]',\n className\n )}\n {...props}\n />\n));\nDropdownMenuGroup.displayName = 'DropdownMenuGroup';\nconst DropdownMenuPortal = MenuPrimitive.Portal;\nconst DropdownMenuSub = MenuPrimitive.SubmenuRoot;\n\nconst popupClassName =\n 'z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden flex flex-col gap-[var(--ui-button-menu-dropdown-container-gap)] bg-[var(--ui-button-menu-dropdown-container-color)] border-solid border-[length:var(--ui-button-menu-dropdown-container-border-width)] border-[var(--ui-button-menu-dropdown-container-border-color)] rounded-[var(--ui-button-menu-dropdown-container-border-radius)] px-[var(--ui-button-menu-dropdown-container-padding-x)] py-[var(--ui-button-menu-dropdown-container-padding-y)] shadow-md outline-none duration-200 data-[open]:animate-in data-[closed]:animate-out data-[open]:fade-in-0 data-[closed]:fade-out-0 data-[open]:zoom-in-95 data-[closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2';\n\n// Keep pointer events enabled so nested Tooltip triggers work on disabled items;\n// MenuPrimitive.Item still owns the disabled semantics and prevents activation.\nconst itemClassName =\n 'relative flex w-full shrink-0 cursor-default select-none items-start gap-[var(--ui-button-menu-dropdown-item-container-gap)] min-h-[var(--ui-button-menu-dropdown-item-container-height)] px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 outline-none transition-colors bg-[var(--ui-button-menu-dropdown-item-container-color-idle)] text-[var(--ui-button-menu-dropdown-item-label-color)] [&_svg]:text-[var(--ui-button-menu-dropdown-item-icon-color)] data-[highlighted]:bg-[var(--ui-button-menu-dropdown-item-container-color-hover)] data-[highlighted]:active:bg-[var(--ui-button-menu-dropdown-item-container-color-active)] [&:focus-visible:not(:hover)]:rounded-[3px] [&:focus-visible:not(:hover)]:ring-[3px] [&:focus-visible:not(:hover)]:ring-inset [&:focus-visible:not(:hover)]:ring-[var(--ui-focus-primary)] data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0';\n\n// A checkbox/radio indicator lives in the same in-flow leading slot a menu-item\n// icon would occupy, so its label lands on the item gap grid (padding-x + 16px\n// glyph + gap) instead of a hardcoded indent. `h-6` matches the item's\n// line-height so the glyph centers on the first line of a wrapping label.\nconst indicatorSlotClassName =\n 'flex h-6 w-4 shrink-0 items-center justify-center text-[var(--ui-button-menu-dropdown-item-icon-color)]';\n\n// Indent for plain items/labels that must line up with the labels of sibling\n// checkbox/radio items.\nconst indicatorInsetClassName =\n 'ps-[calc(var(--ui-button-menu-dropdown-item-container-padding-x)+1rem+var(--ui-button-menu-dropdown-item-container-gap))]';\n\nexport interface DropdownMenuContentProps extends React.ComponentPropsWithoutRef<\n typeof MenuPrimitive.Popup\n> {\n side?: MenuPrimitive.Positioner.Props['side'];\n align?: MenuPrimitive.Positioner.Props['align'];\n sideOffset?: number;\n /** Render inside a portal (default `true`). */\n portal?: boolean;\n /** Portal container — pass a shadow-root mount for isolated-style previews. */\n portalContainer?: MenuPrimitive.Portal.Props['container'];\n keepMounted?: MenuPrimitive.Portal.Props['keepMounted'];\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Popup>,\n DropdownMenuContentProps\n>(\n (\n {\n className,\n side,\n align,\n sideOffset = 4,\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 positioner = (\n <MenuPrimitive.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n className=\"z-50\"\n >\n <MenuPrimitive.Popup\n ref={ref}\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n );\n return portal ? (\n <MenuPrimitive.Portal\n container={resolvedContainer}\n keepMounted={keepMounted}\n >\n {positioner}\n </MenuPrimitive.Portal>\n ) : (\n positioner\n );\n }\n);\nDropdownMenuContent.displayName = 'DropdownMenuContent';\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Popup>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Popup> & {\n sideOffset?: number;\n }\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <MenuPrimitive.Positioner sideOffset={sideOffset} className=\"z-50\">\n <MenuPrimitive.Popup\n ref={ref}\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n));\nDropdownMenuSubContent.displayName = 'DropdownMenuSubContent';\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.Item> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <MenuPrimitive.Item\n ref={ref}\n className={cn(itemClassName, inset && indicatorInsetClassName, className)}\n {...props}\n />\n));\nDropdownMenuItem.displayName = 'DropdownMenuItem';\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.SubmenuTrigger>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.SubmenuTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <MenuPrimitive.SubmenuTrigger\n ref={ref}\n className={cn(\n itemClassName,\n 'data-[popup-open]:bg-[var(--ui-button-menu-dropdown-item-container-color-active)]',\n inset && indicatorInsetClassName,\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ms-auto self-center text-[var(--ui-button-menu-dropdown-extras-cascade-icon-color)] rtl:rotate-180\" />\n </MenuPrimitive.SubmenuTrigger>\n));\nDropdownMenuSubTrigger.displayName = 'DropdownMenuSubTrigger';\n\nfunction DropdownMenuShortcut({\n className,\n ...props\n}: React.HTMLAttributes<HTMLSpanElement>) {\n return (\n <span\n className={cn(\n 'ms-auto shrink-0 text-sm font-normal leading-6 text-[var(--ui-button-menu-dropdown-extras-shortcut-label-color)]',\n className\n )}\n {...props}\n />\n );\n}\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut';\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.CheckboxItem>\n>(({ className, children, ...props }, ref) => (\n <MenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(itemClassName, className)}\n {...props}\n >\n <span className={indicatorSlotClassName}>\n <MenuPrimitive.CheckboxItemIndicator className=\"flex\">\n <CheckSmallIcon size={16} />\n </MenuPrimitive.CheckboxItemIndicator>\n </span>\n {children}\n </MenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName = 'DropdownMenuCheckboxItem';\n\nconst DropdownMenuRadioGroup = MenuPrimitive.RadioGroup;\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof MenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof MenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <MenuPrimitive.RadioItem\n ref={ref}\n className={cn(itemClassName, className)}\n {...props}\n >\n <span className={indicatorSlotClassName}>\n {/* The dot is the indicator element itself — Base UI renders it as a\n `<span>`, and an inline wrapper would collapse a sized child to 0. */}\n <MenuPrimitive.RadioItemIndicator className=\"block size-2 rounded-full bg-current\" />\n </span>\n {children}\n </MenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = 'DropdownMenuRadioItem';\n\nconst DropdownMenuLabel = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & { inset?: boolean }\n>(({ className, inset, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n 'px-[var(--ui-button-menu-dropdown-item-container-padding-x)] py-[var(--ui-button-menu-dropdown-item-container-padding-y)] text-sm font-semibold leading-6 text-[var(--ui-button-menu-dropdown-item-label-color)]',\n inset && indicatorInsetClassName,\n className\n )}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = 'DropdownMenuLabel';\n\nconst DropdownMenuSeparator = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n role=\"separator\"\n className={cn(\n 'my-[var(--ui-button-menu-dropdown-section-list-gap)] h-[var(--ui-button-menu-dropdown-section-container-border-width)] bg-[var(--ui-button-menu-dropdown-section-container-border-color)]',\n className\n )}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = 'DropdownMenuSeparator';\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuPortal,\n};\n"],"mappings":"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"}
|
|
@@ -40,11 +40,11 @@ var x = d.forwardRef(({ className: t, ...n }, r) => /* @__PURE__ */ p("div", {
|
|
|
40
40
|
...n
|
|
41
41
|
}));
|
|
42
42
|
x.displayName = "PageHeaderRow";
|
|
43
|
-
var S = d.forwardRef(({
|
|
44
|
-
ref:
|
|
43
|
+
var S = d.forwardRef(({ as: t = "h1", className: n, ...r }, i) => /* @__PURE__ */ p(t, {
|
|
44
|
+
ref: i,
|
|
45
45
|
"data-slot": "page-header-title",
|
|
46
|
-
className: e("shrink-0 whitespace-nowrap text-2xl font-normal leading-8 text-foreground",
|
|
47
|
-
...
|
|
46
|
+
className: e("shrink-0 whitespace-nowrap text-2xl font-normal leading-8 text-foreground", n),
|
|
47
|
+
...r
|
|
48
48
|
}));
|
|
49
49
|
S.displayName = "PageHeaderTitle";
|
|
50
50
|
var C = d.forwardRef(({ className: t, children: a, ...o }, s) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-header.js","names":[],"sources":["../../../../src/components/ui/page-header/page-header.tsx"],"sourcesContent":["import * as React from 'react';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport { CircleInfoIcon, EllipsisIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { Button, type ButtonProps } from '../button';\nimport { ButtonIcon } from '../button-icon';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '../dropdown-menu';\nimport { Tag } from '../tag';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';\n\n// The page header region from the Figma \"PageHeader\" component (shadcn-uikit /\n// ui-react file, node 2905-7678): a title row (title left, an optional edit\n// icon-button, a tags slot, an actions slot) and an optional description row\n// (text + an optional edit icon-button), capped at 512px. The design's\n// breadcrumb is a separate sibling above PageHeader (see the \"Breakpoints\"\n// page composition), not one of its own parts — render a `Breadcrumb` next to\n// it, don't nest one inside. Title uses the design's `headings/title` style\n// (24px/32px, regular) in `--ui-text-on-surface-primary`; the description uses\n// `link/default` (14px/24px, regular) in `--ui-text-on-surface-secondary` — the\n// only two colors here. The edit affordance is a plain `ButtonIcon` the\n// consumer places as a sibling; PageHeader doesn't own a dedicated part for it\n// since it's the same atom either row.\n\n// Detects whether a row's full-width content (measured off-screen, at natural\n// size) exceeds the space actually available to it — the Figma \"Breakpoints\"\n// page requires an all-or-nothing collapse (fit everything, or collapse), not\n// a partial \"show however many fit\" reflow. Re-measures on resize and after\n// every render (children content, e.g. tag count, isn't in a dependency array).\n// Unlike `useIsOverflowing` in sidebar-primary.tsx (which measures one element\n// against itself and returns a bare boolean), this compares a hidden\n// full-size clone against the live container and returns the refs alongside\n// the collapsed flag.\nfunction useRowOverflow() {\n const containerRef = React.useRef<HTMLDivElement>(null);\n const measureRef = React.useRef<HTMLDivElement>(null);\n const [collapsed, setCollapsed] = React.useState(false);\n\n React.useEffect(() => {\n const container = containerRef.current;\n const measure = measureRef.current;\n if (!container || !measure) return;\n\n const recalc = () =>\n setCollapsed(measure.scrollWidth > container.clientWidth);\n\n recalc();\n const observer = new ResizeObserver(recalc);\n observer.observe(container);\n return () => observer.disconnect();\n });\n\n return { containerRef, measureRef, collapsed };\n}\n\nconst PageHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header\"\n role=\"banner\"\n className={cn('flex w-full flex-col items-start gap-1', className)}\n {...props}\n />\n));\nPageHeader.displayName = 'PageHeader';\n\n// The title row: title, optional edit button, tags slot, actions slot.\nconst PageHeaderRow = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header-row\"\n className={cn('flex w-full items-center gap-2', className)}\n {...props}\n />\n));\nPageHeaderRow.displayName = 'PageHeaderRow';\n\nconst PageHeaderTitle = React.forwardRef<\n HTMLHeadingElement,\n React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n <h1\n ref={ref}\n data-slot=\"page-header-title\"\n className={cn(\n 'shrink-0 whitespace-nowrap text-2xl font-normal leading-8 text-foreground',\n className\n )}\n {...props}\n />\n));\nPageHeaderTitle.displayName = 'PageHeaderTitle';\n\ntype TagElement = React.ReactElement<{ children?: React.ReactNode }>;\n\n// The tags slot: grows to share the row's remaining width with the actions\n// slot (so actions stay flush right even when tags are omitted entirely).\n// Per the Figma \"Breakpoints\" page annotation: if all tags don't fit, show\n// only the first tag and fold the rest under a \"+#\" tag whose tooltip lists\n// their labels on hover — not a partial \"show however many fit\" reflow.\nconst PageHeaderTags = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, children, ...props }, ref) => {\n const items = React.Children.toArray(children) as TagElement[];\n const { containerRef, measureRef, collapsed } = useRowOverflow();\n const showOverflow = collapsed && items.length > 1;\n const hiddenItems = showOverflow ? items.slice(1) : [];\n\n const tagsChildren = (\n <>\n {showOverflow ? (\n <>\n {items[0]}\n <Tooltip>\n <TooltipTrigger render={<Tag icon={<CircleInfoIcon size={16} />} />}>\n {`+${hiddenItems.length}`}\n </TooltipTrigger>\n <TooltipContent>\n <ul>\n {hiddenItems.map((item, index) => (\n <li key={index}>{item.props?.children}</li>\n ))}\n </ul>\n </TooltipContent>\n </Tooltip>\n </>\n ) : (\n items\n )}\n {items.length > 1 && (\n <div\n ref={measureRef}\n aria-hidden\n className=\"invisible absolute flex items-center gap-2\"\n >\n {items}\n </div>\n )}\n </>\n );\n\n return useRender({\n ref: [ref, containerRef],\n defaultTagName: 'div',\n props: {\n 'data-slot': 'page-header-tags',\n ...mergeProps<'div'>(\n {\n className: cn(\n 'flex min-w-0 flex-1 items-center gap-2 overflow-hidden',\n className\n ),\n children: tagsChildren,\n },\n props\n ),\n },\n });\n});\nPageHeaderTags.displayName = 'PageHeaderTags';\n\ntype ActionElement = React.ReactElement<\n Omit<ButtonProps, 'render'> & {\n // Only the element form is meaningful once folded — a function-form\n // render receives Button's own state shape, not the \"More\" menu item's.\n render?: React.ReactElement;\n }\n>;\n\n// Fold only reduces a secondary action to a generic \"Menu Item\" label — the\n// Figma \"Breakpoints\" page annotation and its \"More\" menu mockup only cover a\n// plain button with a single click action. A trigger-style component (e.g.\n// ButtonMenu, which opens its own menu rather than firing one action) has no\n// single action to reduce to, so it's excluded even if given `variant=\"secondary\"`.\nconst isSecondaryAction = (item: ActionElement) =>\n item.type === Button && item.props?.variant === 'secondary';\n\n// The actions slot: grows to share the row's remaining width with the tags\n// slot. Per the Figma \"Breakpoints\" page annotation: if all buttons don't\n// fit, fold all secondary buttons under a single \"More\" ButtonIcon — primary\n// buttons are never hidden.\nconst PageHeaderActions = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, children, ...props }, ref) => {\n const items = React.Children.toArray(children) as ActionElement[];\n const secondaryItems = items.filter(isSecondaryAction);\n const { containerRef, measureRef, collapsed } = useRowOverflow();\n const showOverflow = collapsed && secondaryItems.length > 0;\n\n // Each secondary item becomes zero or one output items: dropped, except\n // the first (in original order), which becomes the \"More\" menu holding\n // all of them — hence `flatMap` over `map`/`filter`.\n const moreButton = (\n <DropdownMenu key=\"page-header-actions-more\">\n <DropdownMenuTrigger\n render={\n <ButtonIcon variant=\"secondary\" aria-label=\"More actions\">\n <EllipsisIcon size={16} />\n </ButtonIcon>\n }\n />\n <DropdownMenuContent align=\"end\">\n {secondaryItems.map((secondaryItem, index) => {\n const { variant, children, ...rest } = secondaryItem.props;\n return (\n <DropdownMenuItem\n key={index}\n {...(rest as React.ComponentPropsWithoutRef<typeof DropdownMenuItem>)}\n >\n {children}\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n\n const collapseSecondaryActions = (item: ActionElement) => {\n if (!isSecondaryAction(item)) return [item];\n const isFirstSecondaryAction = item === secondaryItems[0];\n return isFirstSecondaryAction ? [moreButton] : [];\n };\n\n const visibleItems = showOverflow\n ? items.flatMap(collapseSecondaryActions)\n : items;\n\n const actionsChildren = (\n <>\n {visibleItems}\n {secondaryItems.length > 0 && (\n <div\n ref={measureRef}\n aria-hidden\n className=\"invisible absolute flex items-center gap-4\"\n >\n {items}\n </div>\n )}\n </>\n );\n\n return useRender({\n ref: [ref, containerRef],\n defaultTagName: 'div',\n props: {\n 'data-slot': 'page-header-actions',\n ...mergeProps<'div'>(\n {\n className: cn(\n 'flex min-w-0 flex-1 items-center justify-end gap-4 overflow-hidden',\n className\n ),\n children: actionsChildren,\n },\n props\n ),\n },\n });\n});\nPageHeaderActions.displayName = 'PageHeaderActions';\n\n// The description row: caps the text (+ its optional edit button) at 512px,\n// matching the Figma's explicit \"Description max width 512px\" note.\nconst PageHeaderDescriptionRow = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header-description-row\"\n className={cn('flex max-w-lg items-start gap-2', className)}\n {...props}\n />\n));\nPageHeaderDescriptionRow.displayName = 'PageHeaderDescriptionRow';\n\nconst PageHeaderDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n <p\n ref={ref}\n data-slot=\"page-header-description\"\n className={cn(\n 'min-w-0 flex-1 text-sm font-normal leading-6 text-muted-foreground',\n className\n )}\n {...props}\n />\n));\nPageHeaderDescription.displayName = 'PageHeaderDescription';\n\nexport {\n PageHeader,\n PageHeaderRow,\n PageHeaderTitle,\n PageHeaderTags,\n PageHeaderActions,\n PageHeaderDescriptionRow,\n PageHeaderDescription,\n useRowOverflow,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"page-header.js","names":[],"sources":["../../../../src/components/ui/page-header/page-header.tsx"],"sourcesContent":["import * as React from 'react';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport { CircleInfoIcon, EllipsisIcon } from '@acronis-platform/icons-react/stroke-mono';\n\nimport { cn } from '@/lib/utils';\nimport { Button, type ButtonProps } from '../button';\nimport { ButtonIcon } from '../button-icon';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '../dropdown-menu';\nimport { Tag } from '../tag';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';\n\n// The page header region from the Figma \"PageHeader\" component (shadcn-uikit /\n// ui-react file, node 2905-7678): a title row (title left, an optional edit\n// icon-button, a tags slot, an actions slot) and an optional description row\n// (text + an optional edit icon-button), capped at 512px. The design's\n// breadcrumb is a separate sibling above PageHeader (see the \"Breakpoints\"\n// page composition), not one of its own parts — render a `Breadcrumb` next to\n// it, don't nest one inside. Title uses the design's `headings/title` style\n// (24px/32px, regular) in `--ui-text-on-surface-primary`; the description uses\n// `link/default` (14px/24px, regular) in `--ui-text-on-surface-secondary` — the\n// only two colors here. The edit affordance is a plain `ButtonIcon` the\n// consumer places as a sibling; PageHeader doesn't own a dedicated part for it\n// since it's the same atom either row.\n\n// Detects whether a row's full-width content (measured off-screen, at natural\n// size) exceeds the space actually available to it — the Figma \"Breakpoints\"\n// page requires an all-or-nothing collapse (fit everything, or collapse), not\n// a partial \"show however many fit\" reflow. Re-measures on resize and after\n// every render (children content, e.g. tag count, isn't in a dependency array).\n// Unlike `useIsOverflowing` in sidebar-primary.tsx (which measures one element\n// against itself and returns a bare boolean), this compares a hidden\n// full-size clone against the live container and returns the refs alongside\n// the collapsed flag.\nfunction useRowOverflow() {\n const containerRef = React.useRef<HTMLDivElement>(null);\n const measureRef = React.useRef<HTMLDivElement>(null);\n const [collapsed, setCollapsed] = React.useState(false);\n\n React.useEffect(() => {\n const container = containerRef.current;\n const measure = measureRef.current;\n if (!container || !measure) return;\n\n const recalc = () =>\n setCollapsed(measure.scrollWidth > container.clientWidth);\n\n recalc();\n const observer = new ResizeObserver(recalc);\n observer.observe(container);\n return () => observer.disconnect();\n });\n\n return { containerRef, measureRef, collapsed };\n}\n\nconst PageHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header\"\n role=\"banner\"\n className={cn('flex w-full flex-col items-start gap-1', className)}\n {...props}\n />\n));\nPageHeader.displayName = 'PageHeader';\n\n// The title row: title, optional edit button, tags slot, actions slot.\nconst PageHeaderRow = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header-row\"\n className={cn('flex w-full items-center gap-2', className)}\n {...props}\n />\n));\nPageHeaderRow.displayName = 'PageHeaderRow';\n\ntype PageHeaderTitleProps = React.HTMLAttributes<HTMLHeadingElement> & {\n as?: 'h1' | 'h2';\n};\n\nconst PageHeaderTitle = React.forwardRef<\n HTMLHeadingElement,\n PageHeaderTitleProps\n>(({ as: Heading = 'h1', className, ...props }, ref) => (\n <Heading\n ref={ref}\n data-slot=\"page-header-title\"\n className={cn(\n 'shrink-0 whitespace-nowrap text-2xl font-normal leading-8 text-foreground',\n className\n )}\n {...props}\n />\n));\nPageHeaderTitle.displayName = 'PageHeaderTitle';\n\ntype TagElement = React.ReactElement<{ children?: React.ReactNode }>;\n\n// The tags slot: grows to share the row's remaining width with the actions\n// slot (so actions stay flush right even when tags are omitted entirely).\n// Per the Figma \"Breakpoints\" page annotation: if all tags don't fit, show\n// only the first tag and fold the rest under a \"+#\" tag whose tooltip lists\n// their labels on hover — not a partial \"show however many fit\" reflow.\nconst PageHeaderTags = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, children, ...props }, ref) => {\n const items = React.Children.toArray(children) as TagElement[];\n const { containerRef, measureRef, collapsed } = useRowOverflow();\n const showOverflow = collapsed && items.length > 1;\n const hiddenItems = showOverflow ? items.slice(1) : [];\n\n const tagsChildren = (\n <>\n {showOverflow ? (\n <>\n {items[0]}\n <Tooltip>\n <TooltipTrigger render={<Tag icon={<CircleInfoIcon size={16} />} />}>\n {`+${hiddenItems.length}`}\n </TooltipTrigger>\n <TooltipContent>\n <ul>\n {hiddenItems.map((item, index) => (\n <li key={index}>{item.props?.children}</li>\n ))}\n </ul>\n </TooltipContent>\n </Tooltip>\n </>\n ) : (\n items\n )}\n {items.length > 1 && (\n <div\n ref={measureRef}\n aria-hidden\n className=\"invisible absolute flex items-center gap-2\"\n >\n {items}\n </div>\n )}\n </>\n );\n\n return useRender({\n ref: [ref, containerRef],\n defaultTagName: 'div',\n props: {\n 'data-slot': 'page-header-tags',\n ...mergeProps<'div'>(\n {\n className: cn(\n 'flex min-w-0 flex-1 items-center gap-2 overflow-hidden',\n className\n ),\n children: tagsChildren,\n },\n props\n ),\n },\n });\n});\nPageHeaderTags.displayName = 'PageHeaderTags';\n\ntype ActionElement = React.ReactElement<\n Omit<ButtonProps, 'render'> & {\n // Only the element form is meaningful once folded — a function-form\n // render receives Button's own state shape, not the \"More\" menu item's.\n render?: React.ReactElement;\n }\n>;\n\n// Fold only reduces a secondary action to a generic \"Menu Item\" label — the\n// Figma \"Breakpoints\" page annotation and its \"More\" menu mockup only cover a\n// plain button with a single click action. A trigger-style component (e.g.\n// ButtonMenu, which opens its own menu rather than firing one action) has no\n// single action to reduce to, so it's excluded even if given `variant=\"secondary\"`.\nconst isSecondaryAction = (item: ActionElement) =>\n item.type === Button && item.props?.variant === 'secondary';\n\n// The actions slot: grows to share the row's remaining width with the tags\n// slot. Per the Figma \"Breakpoints\" page annotation: if all buttons don't\n// fit, fold all secondary buttons under a single \"More\" ButtonIcon — primary\n// buttons are never hidden.\nconst PageHeaderActions = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, children, ...props }, ref) => {\n const items = React.Children.toArray(children) as ActionElement[];\n const secondaryItems = items.filter(isSecondaryAction);\n const { containerRef, measureRef, collapsed } = useRowOverflow();\n const showOverflow = collapsed && secondaryItems.length > 0;\n\n // Each secondary item becomes zero or one output items: dropped, except\n // the first (in original order), which becomes the \"More\" menu holding\n // all of them — hence `flatMap` over `map`/`filter`.\n const moreButton = (\n <DropdownMenu key=\"page-header-actions-more\">\n <DropdownMenuTrigger\n render={\n <ButtonIcon variant=\"secondary\" aria-label=\"More actions\">\n <EllipsisIcon size={16} />\n </ButtonIcon>\n }\n />\n <DropdownMenuContent align=\"end\">\n {secondaryItems.map((secondaryItem, index) => {\n const { variant, children, ...rest } = secondaryItem.props;\n return (\n <DropdownMenuItem\n key={index}\n {...(rest as React.ComponentPropsWithoutRef<typeof DropdownMenuItem>)}\n >\n {children}\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n\n const collapseSecondaryActions = (item: ActionElement) => {\n if (!isSecondaryAction(item)) return [item];\n const isFirstSecondaryAction = item === secondaryItems[0];\n return isFirstSecondaryAction ? [moreButton] : [];\n };\n\n const visibleItems = showOverflow\n ? items.flatMap(collapseSecondaryActions)\n : items;\n\n const actionsChildren = (\n <>\n {visibleItems}\n {secondaryItems.length > 0 && (\n <div\n ref={measureRef}\n aria-hidden\n className=\"invisible absolute flex items-center gap-4\"\n >\n {items}\n </div>\n )}\n </>\n );\n\n return useRender({\n ref: [ref, containerRef],\n defaultTagName: 'div',\n props: {\n 'data-slot': 'page-header-actions',\n ...mergeProps<'div'>(\n {\n className: cn(\n 'flex min-w-0 flex-1 items-center justify-end gap-4 overflow-hidden',\n className\n ),\n children: actionsChildren,\n },\n props\n ),\n },\n });\n});\nPageHeaderActions.displayName = 'PageHeaderActions';\n\n// The description row: caps the text (+ its optional edit button) at 512px,\n// matching the Figma's explicit \"Description max width 512px\" note.\nconst PageHeaderDescriptionRow = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-slot=\"page-header-description-row\"\n className={cn('flex max-w-lg items-start gap-2', className)}\n {...props}\n />\n));\nPageHeaderDescriptionRow.displayName = 'PageHeaderDescriptionRow';\n\nconst PageHeaderDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n <p\n ref={ref}\n data-slot=\"page-header-description\"\n className={cn(\n 'min-w-0 flex-1 text-sm font-normal leading-6 text-muted-foreground',\n className\n )}\n {...props}\n />\n));\nPageHeaderDescription.displayName = 'PageHeaderDescription';\n\nexport {\n PageHeader,\n PageHeaderRow,\n PageHeaderTitle,\n PageHeaderTags,\n PageHeaderActions,\n PageHeaderDescriptionRow,\n PageHeaderDescription,\n useRowOverflow,\n};\n"],"mappings":"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"}
|