@burojs/ui-shadcn 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +22 -5
  2. package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
  3. package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
  4. package/dist/bulk-summary-GxMDP4bw.js +108 -0
  5. package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
  6. package/dist/button-BxnnE28H.cjs +55 -0
  7. package/dist/button-BxnnE28H.cjs.map +1 -0
  8. package/dist/button-MWVeZaqj.js +44 -0
  9. package/dist/button-MWVeZaqj.js.map +1 -0
  10. package/dist/candidates-gate-CX2PBu6l.js +63 -0
  11. package/dist/candidates-gate-CX2PBu6l.js.map +1 -0
  12. package/dist/candidates-gate-DiFI52r7.cjs +70 -0
  13. package/dist/candidates-gate-DiFI52r7.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-CBsVcnPR.js +82 -0
  15. package/dist/candidates-relation-picker-CBsVcnPR.js.map +1 -0
  16. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +84 -0
  17. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +1 -0
  18. package/dist/check-confirm-dialog-BRByTFpg.cjs +1534 -0
  19. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-CkaVTPT9.js +1232 -0
  21. package/dist/check-confirm-dialog-CkaVTPT9.js.map +1 -0
  22. package/dist/dialog-B_rt9S1c.js +63 -0
  23. package/dist/dialog-B_rt9S1c.js.map +1 -0
  24. package/dist/dialog-DRNUq9EA.cjs +112 -0
  25. package/dist/dialog-DRNUq9EA.cjs.map +1 -0
  26. package/dist/filter-builder-B3mZFL-K.cjs +1761 -0
  27. package/dist/filter-builder-B3mZFL-K.cjs.map +1 -0
  28. package/dist/filter-builder-DOnaXNfP.js +1605 -0
  29. package/dist/filter-builder-DOnaXNfP.js.map +1 -0
  30. package/dist/flow-runtime-B6zOHYqI.cjs +255 -0
  31. package/dist/flow-runtime-B6zOHYqI.cjs.map +1 -0
  32. package/dist/flow-runtime-D8h99FSg.js +252 -0
  33. package/dist/flow-runtime-D8h99FSg.js.map +1 -0
  34. package/dist/flow-screen-CX05b3G9.cjs +705 -0
  35. package/dist/flow-screen-CX05b3G9.cjs.map +1 -0
  36. package/dist/flow-screen-CmbARSqg.js +701 -0
  37. package/dist/flow-screen-CmbARSqg.js.map +1 -0
  38. package/dist/index.cjs +2825 -11912
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1612 -1862
  41. package/dist/index.d.ts +1700 -1950
  42. package/dist/index.js +2498 -11605
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B7U1OJUe.cjs +112 -0
  45. package/dist/inline-flow-host-B7U1OJUe.cjs.map +1 -0
  46. package/dist/inline-flow-host-C3H2UpSA.js +112 -0
  47. package/dist/inline-flow-host-C3H2UpSA.js.map +1 -0
  48. package/dist/input-BI0czGyI.cjs +23 -0
  49. package/dist/input-BI0czGyI.cjs.map +1 -0
  50. package/dist/input-CUPStzZO.js +17 -0
  51. package/dist/input-CUPStzZO.js.map +1 -0
  52. package/dist/label-66W1qbke.js +29 -0
  53. package/dist/label-66W1qbke.js.map +1 -0
  54. package/dist/label-w6JYf2cF.cjs +36 -0
  55. package/dist/label-w6JYf2cF.cjs.map +1 -0
  56. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +14 -0
  57. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +1 -0
  58. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +19 -0
  59. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +1 -0
  60. package/dist/markdown/index.cjs +1 -1
  61. package/dist/markdown/index.js +1 -1
  62. package/dist/page-flow-route-CQ7HsbBm.cjs +277 -0
  63. package/dist/page-flow-route-CQ7HsbBm.cjs.map +1 -0
  64. package/dist/page-flow-route-CyXSvXSE.js +277 -0
  65. package/dist/page-flow-route-CyXSvXSE.js.map +1 -0
  66. package/dist/peek-filter-CnqHwTMU.cjs +51 -0
  67. package/dist/peek-filter-CnqHwTMU.cjs.map +1 -0
  68. package/dist/peek-filter-ErLfO153.js +28 -0
  69. package/dist/peek-filter-ErLfO153.js.map +1 -0
  70. package/dist/place-node--GdsP688.js +7315 -0
  71. package/dist/place-node--GdsP688.js.map +1 -0
  72. package/dist/place-node-SIp4XYQH.cjs +7833 -0
  73. package/dist/place-node-SIp4XYQH.cjs.map +1 -0
  74. package/dist/related-list-cards-B6x9HQQp.js +42 -0
  75. package/dist/related-list-cards-B6x9HQQp.js.map +1 -0
  76. package/dist/related-list-cards-nxuODlIA.cjs +42 -0
  77. package/dist/related-list-cards-nxuODlIA.cjs.map +1 -0
  78. package/dist/related-list-timeline-BYJKEbmA.cjs +142 -0
  79. package/dist/related-list-timeline-BYJKEbmA.cjs.map +1 -0
  80. package/dist/related-list-timeline-DEdfd-rr.js +142 -0
  81. package/dist/related-list-timeline-DEdfd-rr.js.map +1 -0
  82. package/dist/related-rows-B4levHcR.js +186 -0
  83. package/dist/related-rows-B4levHcR.js.map +1 -0
  84. package/dist/related-rows-zh5f_4QR.cjs +209 -0
  85. package/dist/related-rows-zh5f_4QR.cjs.map +1 -0
  86. package/dist/relation-create-BOaUe12G.cjs +362 -0
  87. package/dist/relation-create-BOaUe12G.cjs.map +1 -0
  88. package/dist/relation-create-Bg-eEJLs.js +337 -0
  89. package/dist/relation-create-Bg-eEJLs.js.map +1 -0
  90. package/dist/relation-many-picker-B-NCEUgX.cjs +465 -0
  91. package/dist/relation-many-picker-B-NCEUgX.cjs.map +1 -0
  92. package/dist/relation-many-picker-gdAOD-qK.js +464 -0
  93. package/dist/relation-many-picker-gdAOD-qK.js.map +1 -0
  94. package/dist/relation-picker-BAY77ivO.js +462 -0
  95. package/dist/relation-picker-BAY77ivO.js.map +1 -0
  96. package/dist/relation-picker-Da_uhFvA.cjs +479 -0
  97. package/dist/relation-picker-Da_uhFvA.cjs.map +1 -0
  98. package/dist/relation-picker-dialog-M0pQaWlp.cjs +300 -0
  99. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +1 -0
  100. package/dist/relation-picker-dialog-hA9rYrZB.js +299 -0
  101. package/dist/relation-picker-dialog-hA9rYrZB.js.map +1 -0
  102. package/dist/resource-page-router-OSQyRA5b.cjs +1779 -0
  103. package/dist/resource-page-router-OSQyRA5b.cjs.map +1 -0
  104. package/dist/resource-page-router-T4vS5gbJ.js +1582 -0
  105. package/dist/resource-page-router-T4vS5gbJ.js.map +1 -0
  106. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  107. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  108. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  109. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  110. package/dist/search-access-B7G5oR8n.cjs +812 -0
  111. package/dist/search-access-B7G5oR8n.cjs.map +1 -0
  112. package/dist/search-access-CURsWn-U.js +669 -0
  113. package/dist/search-access-CURsWn-U.js.map +1 -0
  114. package/dist/shell-BPlk8hEa.js +205 -0
  115. package/dist/shell-BPlk8hEa.js.map +1 -0
  116. package/dist/shell-DxhlsPwm.cjs +271 -0
  117. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  118. package/dist/skeleton-U2noefj5.js +13 -0
  119. package/dist/skeleton-U2noefj5.js.map +1 -0
  120. package/dist/skeleton-_1gP7wan.cjs +18 -0
  121. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  122. package/dist/styles.css +241 -9
  123. package/dist/toaster-C7CiYA4Z.cjs +248 -0
  124. package/dist/toaster-C7CiYA4Z.cjs.map +1 -0
  125. package/dist/toaster-CfCRLgR-.js +164 -0
  126. package/dist/toaster-CfCRLgR-.js.map +1 -0
  127. package/dist/use-dialog-filter-state-BBCC0OEK.cjs +92 -0
  128. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +1 -0
  129. package/dist/use-dialog-filter-state-BDcgS5Oz.js +87 -0
  130. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +1 -0
  131. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  132. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  133. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  134. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  135. package/package.json +16 -14
  136. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  137. package/dist/use-theme-IHmtrhEt.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-access-CURsWn-U.js","names":["Command","CommandPrimitive"],"sources":["../src/primitives/command.tsx","../src/fields/claimed-quick-link.tsx","../src/resource/record-display-name.ts","../src/resource/use-row-title.ts","../src/quick/peek-context.ts","../src/quick/use-peek-intent.tsx","../src/fields/relation-display.tsx","../src/fields/search-access.ts"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { Command as CommandPrimitive } from 'cmdk';\nimport { IconSearch } from '@tabler/icons-react';\nimport { cn } from '../lib/cn.js';\nimport {\n Dialog,\n DialogContent,\n DialogTitle,\n DialogDescription,\n type DialogContentProps,\n} from './dialog.js';\n\n/**\n * Shadcn-canonical Command primitive built on `cmdk`. Provides\n * keyboard-navigable, ARIA-correct combobox/listbox semantics: arrow\n * keys to navigate items, Enter to select, type-ahead search via the\n * embedded `<CommandInput>`. Use this for any searchable list inside a\n * Popover (typeaheads, command palettes, relation pickers).\n *\n * Filter behavior: by default cmdk filters items client-side based on\n * the input text. For server-side filtering (typical for relation\n * pickers that hit `useList` with a `contains` operator), pass\n * `shouldFilter={false}` to the root `<Command>` and drive the option\n * list yourself from the server-side results.\n */\nconst Command = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn(\n 'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',\n className,\n )}\n {...props}\n />\n));\nCommand.displayName = CommandPrimitive.displayName;\n\nconst CommandInput = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <IconSearch className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" aria-hidden=\"true\" />\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled',\n className,\n )}\n {...props}\n />\n </div>\n));\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n));\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n <CommandPrimitive.Empty\n ref={ref}\n className=\"py-6 text-center text-sm text-muted-foreground\"\n {...props}\n />\n));\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n 'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',\n className,\n )}\n {...props}\n />\n));\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 h-px bg-border', className)}\n {...props}\n />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n \"group relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden data-[disabled=true]:pointer-events-none data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nexport type CommandLoadingProps = React.ComponentPropsWithoutRef<typeof CommandPrimitive.Loading>;\n\nconst CommandLoading = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Loading>,\n CommandLoadingProps\n>(({ ...props }, ref) => <CommandPrimitive.Loading ref={ref} {...props} />);\nCommandLoading.displayName = CommandPrimitive.Loading.displayName;\n\n/**\n * Props for `<CommandDialog>`. Mirrors Radix Dialog's `Root` props\n * (`open`, `onOpenChange`, `defaultOpen`, `modal`) plus optional\n * accessibility labels and dialog-content className overrides.\n *\n * The shadcn-canonical pattern wraps a Radix `<Dialog>` around the\n * `<Command>` root so cmdk's keyboard navigation (arrow keys, Enter,\n * type-ahead) operates on the items rendered as children. We render\n * a visually-hidden `<DialogTitle>` + `<DialogDescription>` for the\n * a11y tree (Radix requires a labelled dialog or it warns at runtime).\n */\nexport interface CommandDialogProps {\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n defaultOpen?: boolean;\n modal?: boolean;\n /** Accessible title (visually hidden). Defaults to \"Command Palette\". */\n title?: string;\n /** Accessible description (visually hidden). Defaults to \"Search and run actions.\" */\n description?: string;\n /** Forwarded to the `<DialogContent>` className. */\n className?: string;\n /** Forwarded to the inner `<Command>` className. */\n commandClassName?: string;\n /** The Command groups + items + input. */\n children?: React.ReactNode;\n /** Optional dialog-content overrides (e.g. style for animations). */\n contentProps?: Omit<DialogContentProps, 'children' | 'className'>;\n}\n\n/**\n * Modal command palette. Combines Radix Dialog with cmdk's `<Command>`\n * root so adopters can compose `<CommandInput>`, `<CommandList>`,\n * `<CommandGroup>`, `<CommandItem>` as children and get focus-trapped\n * keyboard navigation out of the box.\n *\n * Usage:\n * ```tsx\n * <CommandDialog open={open} onOpenChange={setOpen}>\n * <CommandInput placeholder=\"Search…\" />\n * <CommandList>\n * <CommandEmpty>No matches.</CommandEmpty>\n * <CommandGroup heading=\"Resources\">\n * <CommandItem onSelect={...}>Documents</CommandItem>\n * </CommandGroup>\n * </CommandList>\n * </CommandDialog>\n * ```\n */\nfunction CommandDialog({\n open,\n onOpenChange,\n defaultOpen,\n modal,\n title = 'Command Palette',\n description = 'Search and run actions.',\n className,\n commandClassName,\n children,\n contentProps,\n}: CommandDialogProps) {\n return (\n <Dialog open={open} onOpenChange={onOpenChange} defaultOpen={defaultOpen} modal={modal}>\n <DialogContent\n {...contentProps}\n // Width: cap to 480px regardless of breakpoint (the shadcn default\n // `DialogContent` uses `max-w-lg` ~512px). `sm:max-w-[480px]`\n // re-asserts at the `sm` breakpoint so any built-in `sm:max-w-lg`\n // override loses to ours.\n className={cn('overflow-hidden p-0 shadow-lg max-w-[480px] sm:max-w-[480px]', className)}\n // Backdrop blur for the command palette specifically: softens\n // the underlying chrome so the palette feels like a focused\n // overlay rather than a stacked panel. Other dialogs keep the\n // shadcn-default solid backdrop.\n overlayClassName=\"backdrop-blur-sm\"\n >\n {/* Visually-hidden labels: Radix Dialog needs them for a11y, but\n the palette UI itself supplies its own search affordance. */}\n <DialogTitle className=\"sr-only\">{title}</DialogTitle>\n <DialogDescription className=\"sr-only\">{description}</DialogDescription>\n <Command\n className={cn(\n '[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5',\n commandClassName,\n )}\n >\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\nCommandDialog.displayName = 'CommandDialog';\n\nexport {\n Command,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandSeparator,\n CommandLoading,\n CommandDialog,\n};\n","'use client';\nimport { QUICK_ANCHOR_ATTRIBUTE, quickAnchorRef } from '@burojs/react';\nimport type * as React from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\n\n/** A plain left click: the main button, no modifier. Anything else follows the `href`. */\nexport function isPlainLeftClick(event: React.MouseEvent): boolean {\n return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;\n}\n\n/** Claims the anchor an app's link component rendered (it may not forward a ref). */\nfunction claimAnchor(wrapper: HTMLSpanElement | null): void {\n const anchor = wrapper?.querySelector('a') ?? null;\n quickAnchorRef(anchor);\n}\n\n/**\n * A quick opener's anchor (spec 4.5): the app's link component to the record's page, marked with\n * `data-buro-quick` and claimed for its opener, so the navigation guard leaves its click to it. A\n * plain left click calls `preventDefault()` and `onOpen()`; a modified click follows the `href`\n * (a new tab, guarded as any link).\n */\nexport function ClaimedQuickLink({\n href,\n testid,\n className,\n onOpen,\n children,\n}: {\n readonly href: string;\n readonly testid?: string;\n readonly className?: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const RouterLink = useLinkComponent();\n return (\n <span ref={claimAnchor} className=\"contents\">\n <RouterLink\n href={href}\n {...(testid === undefined ? {} : { 'data-testid': testid })}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n {...(className === undefined ? {} : { className })}\n onClick={(event: React.MouseEvent<HTMLAnchorElement>) => {\n event.stopPropagation();\n if (!isPlainLeftClick(event) || event.currentTarget.target === '_blank') return;\n event.preventDefault();\n onOpen();\n }}\n >\n {children}\n </RouterLink>\n </span>\n );\n}\n","/**\n * A record's display name: its `displayField` value when present and non-empty, else its `id`.\n * Exported for consumers building delete dialogs, breadcrumbs and relation labels.\n */\nexport function recordDisplayName(record: Record<string, unknown>, displayField?: string): string {\n if (displayField && record[displayField] != null && record[displayField] !== '') {\n return String(record[displayField]);\n }\n return String(record.id ?? '');\n}\n","import { useListAccess } from '@burojs/react';\nimport { useCallback } from 'react';\nimport { recordDisplayName } from './record-display-name.js';\n\n/**\n * The display name of a row of `target` as a list surface may show it (P5 spec 3.6, decision\n * P5-27): the row's `displayField` value only while that field is visible in any-record reach,\n * else the id. While the decision is pending the id is shown as well, never the value. Picker\n * options, relation displays, related labels and palette results read their titles through this.\n */\nexport function useRowTitle(\n target: string,\n displayField: string | undefined,\n): (row: Record<string, unknown>) => string {\n const access = useListAccess(target);\n const visible =\n displayField !== undefined && access.status === 'ready' && access.fieldVisible(displayField);\n return useCallback(\n (row) => recordDisplayName(row, visible ? displayField : undefined),\n [visible, displayField],\n );\n}\n","'use client';\nimport { createContext } from 'react';\n\n/**\n * @internal What the popover host gives the screen inside it: the anchor element it is positioned\n * against (the relation display the peek was opened from), and a setter for the popover's\n * accessible name once the screen knows the record's title.\n */\nexport interface PeekHostValue {\n readonly anchor: HTMLElement | null;\n setLabel(label: string): void;\n}\n\n/** @internal Provided by `PopoverPresentationHost`; `null` outside a popover. */\nexport const PeekHostContext = createContext<PeekHostValue | null>(null);\n\n/**\n * @internal True inside a peek (provided by `PeekScreen`): a relation display there attaches no\n * hover intent, so a peek never opens a second peek (spec 4.6).\n */\nexport const PeekContext = createContext(false);\n","'use client';\nimport {\n PEEK_COMPONENT,\n type PeekScreenProps,\n useCurrentUIActivation,\n useEntryGate,\n useQuickOpener,\n useUIPresentationRuntime,\n} from '@burojs/react';\nimport {\n type FocusEvent,\n type PointerEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport { PeekContext } from './peek-context.js';\n\n/** The hover (and keyboard-focus) delay before a peek opens (spec 4.6). */\nexport const PEEK_DELAY_MS = 500;\n/** Focus this soon after a Tab or arrow keydown is keyboard modality. */\nconst KEYBOARD_FOCUS_WINDOW_MS = 100;\n\nconst NAVIGATION_KEYS = new Set(['Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']);\n\n/** When the last Tab or arrow keydown happened; a pointer down forgets it. */\nlet lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\nconst listening = new WeakSet<Document>();\n\nfunction listenForModality(doc: Document): void {\n if (listening.has(doc)) return;\n listening.add(doc);\n doc.addEventListener(\n 'keydown',\n (event) => {\n if (NAVIGATION_KEYS.has(event.key)) lastNavigationKeyAt = Date.now();\n },\n true,\n );\n const forget = () => {\n lastNavigationKeyAt = Number.NEGATIVE_INFINITY;\n };\n doc.addEventListener('pointerdown', forget, true);\n doc.addEventListener('mousedown', forget, true);\n}\n\n/** Whether a focus now came from the keyboard (a Tab or arrow keydown just before it). */\nfunction keyboardModality(): boolean {\n return Date.now() - lastNavigationKeyAt <= KEYBOARD_FOCUS_WINDOW_MS;\n}\n\n/** Elements about to be focused by code: their next focus opens no peek. */\nconst focusedByCode = new WeakSet<Element>();\n\n/**\n * @internal Focus `element` by code (the peek's anchor, when its title opens `quick.show`): the\n * focus opens no peek, whatever key was pressed last.\n */\nexport function focusWithoutPeek(element: HTMLElement): void {\n focusedByCode.add(element);\n element.focus();\n focusedByCode.delete(element);\n}\n\n/** What opens a record from where an anchor sits: its display's own quick opener. */\nexport type PeekAnchorOpener = (target: string, id: string) => void;\n\n/**\n * Each anchor's opener, taken where the display renders: inside a quick sheet it pushes into that\n * sheet, inside a dialog it closes the dialog first. The peek renders beside the overlays, outside\n * the sheet's context, so its title opens through this instead of its own opener.\n */\nconst anchorOpeners = new WeakMap<Element, PeekAnchorOpener>();\n\n/** @internal The opener registered for `anchor` by its peek intent, if any. */\nexport function peekAnchorOpener(anchor: Element | null): PeekAnchorOpener | undefined {\n return anchor === null ? undefined : anchorOpeners.get(anchor);\n}\n\n/** What a peek previews: the display's target and the key it holds (an id, or a `by` key). */\nexport interface PeekTarget {\n readonly to: string;\n readonly id?: string;\n readonly by?: { readonly field: string; readonly key: string };\n}\n\n/** The anchor inside the wrapper: the opener (`data-buro-quick`), else the first element. */\nfunction anchorIn(wrapper: HTMLElement | null): HTMLElement | null {\n if (wrapper === null) return null;\n const marked = wrapper.querySelector<HTMLElement>('[data-buro-quick]');\n if (marked !== null) return marked;\n const first = wrapper.firstElementChild;\n return first instanceof HTMLElement ? first : null;\n}\n\nconst noSubscription = () => () => undefined;\n\n/**\n * A relation display's hover intent (spec 4.6): a `mouse` or `pen` pointer resting on the anchor,\n * or keyboard-modality focus on it (a Tab or arrow keydown just before, never focus set by code),\n * for {@link PEEK_DELAY_MS} asks the entry gate for the target's peek; only `open` presents it,\n * after dismissing any other open peek. Leaving or blurring before then cancels. Nothing is read\n * before the peek opens. Inside a peek it renders its children only.\n */\nexport function PeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const inside = useContext(PeekContext);\n if (inside) return <>{children}</>;\n return <ActivePeekIntent target={target}>{children}</ActivePeekIntent>;\n}\n\nfunction ActivePeekIntent({\n target,\n children,\n}: {\n readonly target: PeekTarget;\n readonly children: ReactNode;\n}): ReactElement {\n const gate = useEntryGate();\n const runtime = useUIPresentationRuntime();\n const activation = useCurrentUIActivation();\n const [anchor, setAnchor] = useState<HTMLElement | null>(null);\n const wrapper = useRef<HTMLSpanElement | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n // The latest of each, read when the timer fires (the access answer may have changed since).\n const latest = useRef({ gate, target, activation });\n latest.current = { gate, target, activation };\n\n const attach = useCallback((element: HTMLSpanElement | null) => {\n wrapper.current = element;\n if (element !== null && element.ownerDocument !== undefined)\n listenForModality(element.ownerDocument);\n setAnchor(anchorIn(element));\n }, []);\n\n const expanded = useSyncExternalStore(\n anchor === null ? noSubscription : runtime.subscribe,\n () =>\n anchor !== null &&\n runtime\n .getSnapshot()\n .some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === anchor),\n () => false,\n );\n const peekable = gate(target.to, 'peek').state === 'open';\n\n // The anchor is the app's link (it may not forward props): its popup state is set on it.\n useEffect(() => {\n if (anchor === null) return;\n if (!peekable) {\n anchor.removeAttribute('aria-haspopup');\n anchor.removeAttribute('aria-expanded');\n return;\n }\n anchor.setAttribute('aria-haspopup', 'dialog');\n anchor.setAttribute('aria-expanded', expanded ? 'true' : 'false');\n }, [anchor, peekable, expanded]);\n\n // The opener of where this display sits (a sheet pushes, a dialog closes first), for every\n // display with a peek: an eager one, a lazy `by` one and one under undecided access alike.\n const quickOpen = useQuickOpener();\n const onOpen = useCallback<PeekAnchorOpener>((to, id) => void quickOpen(to, id), [quickOpen]);\n useEffect(() => {\n if (anchor === null) return;\n anchorOpeners.set(anchor, onOpen);\n return () => {\n if (anchorOpeners.get(anchor) === onOpen) anchorOpeners.delete(anchor);\n };\n }, [anchor, onOpen]);\n\n const cancel = useCallback(() => {\n if (timer.current === null) return;\n clearTimeout(timer.current);\n timer.current = null;\n }, []);\n useEffect(() => cancel, [cancel]);\n\n const fire = useCallback(() => {\n timer.current = null;\n const { gate: ask, target: what, activation: from } = latest.current;\n const element = anchorIn(wrapper.current);\n if (element === null || !element.isConnected) return;\n // Only `open` presents: undecided access reads nothing (spec 4.14), a refusal opens nothing.\n const answer = ask(what.to, 'peek');\n if (answer.state !== 'open') return;\n const snapshot = runtime.getSnapshot();\n if (snapshot.some((d) => d.component === PEEK_COMPONENT && d.anchor?.element === element))\n return;\n // One peek at a time.\n for (const descriptor of snapshot)\n if (descriptor.component === PEEK_COMPONENT) runtime.dismiss(descriptor.id);\n const props: PeekScreenProps = {\n target: answer.target,\n ...(what.id === undefined ? {} : { id: what.id }),\n ...(what.by === undefined ? {} : { by: what.by }),\n };\n runtime.present({\n component: PEEK_COMPONENT,\n host: 'popover',\n anchor: { element },\n props: props as unknown as Readonly<Record<string, unknown>>,\n activation: from,\n });\n }, [runtime]);\n\n const schedule = useCallback(() => {\n if (timer.current !== null) return;\n timer.current = setTimeout(fire, PEEK_DELAY_MS);\n }, [fire]);\n\n return (\n // biome-ignore lint/a11y/noStaticElementInteractions: a `display: contents` wrapper that only observes the anchor inside it; the anchor is the interactive element\n <span\n ref={attach}\n className=\"contents\"\n onPointerEnter={(event: PointerEvent) => {\n if (event.pointerType === 'mouse' || event.pointerType === 'pen') schedule();\n }}\n onPointerLeave={cancel}\n onFocus={(event: FocusEvent) => {\n if (focusedByCode.has(event.target)) return;\n if (keyboardModality()) schedule();\n }}\n onBlur={cancel}\n // A click is always quick.show (spec 4.6): it cancels a pending peek.\n onPointerDown={cancel}\n onClickCapture={cancel}\n >\n {children}\n </span>\n );\n}\n","'use client';\nimport type * as React from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n hasRecordPage,\n relationAmbiguousError,\n resourceKey,\n relationOpenOf,\n type RelationOpen,\n resourceShowHref,\n type ResourceDef,\n type ResourceRefInput,\n} from '@burojs/core';\nimport {\n QUICK_ANCHOR_ATTRIBUTE,\n useApp,\n useEntryGate,\n useList,\n useOptionalT,\n useQuickOpener,\n useRelatedRecord,\n} from '@burojs/react';\nimport { useRowTitle } from '../resource/use-row-title.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { PeekIntent, type PeekTarget } from '../quick/use-peek-intent.js';\nimport { ClaimedQuickLink } from './claimed-quick-link.js';\nimport type { FieldRendererProps } from './registry.js';\n\ntype App = ReturnType<typeof useApp>;\n\n/**\n * Shared link styling for in-table relation/row links. Persistent muted\n * dotted underline at idle that snaps to a solid foreground underline on\n * hover. Color stays at `text-foreground` so the link blends with body\n * text — affordance is carried by the underline alone, avoiding the\n * \"every link in brand color\" noise common in admin tables.\n *\n * Used by `<RelationDisplay>` and the first-cell row-anchor link in\n * `<ResourceList>`.\n *\n * `underline-offset-4` is intentional (vs the more common `-2`) to\n * clear descenders on Cyrillic glyphs (e.g. \"ц\" / \"р\") seen in our\n * playground's Locations column.\n */\nexport const relationLinkClassName =\n 'rounded text-foreground underline decoration-muted-foreground/40 decoration-dotted underline-offset-4 transition-all hover:decoration-foreground hover:decoration-solid focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring';\n\n/**\n * Render a foreign-key value as a clickable link to the related\n * entity's show page.\n *\n * Resolution order:\n * 1. Read `field.meta.to` to get the target resource name (set by\n * `f.relation({ to: '<name>' })`).\n * 2. Look up the target resource's `displayField` from the app\n * registry — used to resolve the link text.\n * 3. Fetch the target record via `useRelatedRecord(to, id)`.\n * 4. Render `<a href=\"/<to>/<id>\">{displayName}</a>`.\n *\n * Fallbacks (matches the fail-soft policy used by `<EnumBadge>`):\n * - Value null/undefined/empty → em-dash placeholder, no fetch.\n * - Target resource not in registry → raw value as muted plain text.\n * Do NOT fire the fetch — the data provider would 404.\n * - Loading → small skeleton placeholder.\n * - Fetch errored → raw value muted, with the error message as title.\n * - Target row not found (no error, no record) → raw value muted with\n * `title=\"<resource>#<id> not found\"`.\n */\nexport function RelationDisplay({ value, field }: FieldRendererProps): React.ReactElement {\n const app = useApp();\n const meta = field?.meta as\n | { to?: ResourceRefInput; by?: unknown; resolve?: unknown }\n | undefined;\n const by = typeof meta?.by === 'string' ? meta.by : undefined;\n const lazy = meta?.resolve === 'lazy';\n // A target this surface cannot name — a builder field's thunk `to` that no app registration\n // lowered (a declarative form or display), or a malformed ref — renders like no target, as the\n // picker does, instead of throwing during render.\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const id = value as string | number | null | undefined;\n const idStr = String(value ?? '');\n const baseId = `relation-display:${to ?? 'unknown'}:${idStr}`;\n // What a click on the title opens: the overlay (default), the page, or nothing (`ui.open`).\n const open = relationOpenOf({ ui: (field as { readonly ui?: unknown } | undefined)?.ui });\n\n // Empty / null FK — render em-dash, do not fetch.\n // NB: do NOT short-circuit on `value === 0`; the relation schema is\n // `string | number`, and a target row with PK 0 (rare but valid) would\n // otherwise be silently treated as missing.\n if (value == null || value === '') {\n return (\n <span data-testid={`${baseId}:empty`} className=\"text-muted-foreground\">\n —\n </span>\n );\n }\n\n // Target resource not declared on the field. Without a target we\n // cannot build a link — degrade to plain text.\n if (!to) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] no meta.to declared on field — rendering raw value '${idStr}'`,\n );\n }\n return <span data-testid={`relation-display:unknown:${idStr}:no-target`}>{idStr}</span>;\n }\n\n // `lazy` (spec 4.3): nothing registers, nothing is read — the key itself is shown.\n if (lazy) return <LazyRelationDisplay to={to} by={by} idStr={idStr} app={app} open={open} />;\n\n return (\n <GatedRelationDisplay\n to={to}\n by={by}\n id={id as string | number}\n idStr={idStr}\n app={app}\n open={open}\n />\n );\n}\n\n/**\n * Public, adopter-facing entry point for rendering a foreign-key value.\n * Thin convenience wrapper over `<RelationDisplay>`: adopters write\n * `<RelationField to=\"reference_locations\" value={id} />` instead of\n * constructing the field-registry `{ name, meta }` shape by hand.\n * `RelationDisplay` itself stays the field-kind renderer registered for\n * `f.relation(...)` — its `{ value, field }` signature is dictated by the\n * `FieldRendererProps` contract and is unchanged.\n *\n * `RelationDisplay` reads the target resource from `field.meta.to` (see\n * `RelationDisplay` above), so this wrapper just shapes that `meta` object;\n * `field.name` is set to `''` since standalone usage has no owning field.\n */\nexport function RelationField({\n to,\n value,\n}: {\n /** Target resource name (matches `f.relation({ to: '...' })`). */\n to: string;\n /** FK value to resolve, or null/undefined/'' when empty. */\n value: unknown;\n}): React.ReactElement {\n return <RelationDisplay value={value} field={{ name: '', meta: { to } }} />;\n}\n\n/** What a key's peek previews: the row by id, or by the `by` field the key holds. */\nfunction peekTarget(to: string, by: string | undefined, key: string): PeekTarget {\n return by === undefined ? { to, id: key } : { to, by: { field: by, key } };\n}\n\n/** The definition whose record pages a link to `def`'s record lands on (`routesTo.show`). */\nfunction showPageDef(app: App, def: ResourceDef): ResourceDef | undefined {\n const delegate = def.routesTo?.show;\n if (delegate === undefined) return def;\n try {\n return app.registry.get(resourceKey(delegate as ResourceRefInput));\n } catch {\n return undefined;\n }\n}\n\n/** Whether `def`'s record has a show page to link to, following `routesTo.show`. */\nexport function linksToPage(app: App, def: ResourceDef): boolean {\n const target = showPageDef(app, def);\n return target !== undefined && hasRecordPage(target, 'show');\n}\n\n/**\n * A relation display's opener (spec 4.5): the title as an anchor to the page (when there is one)\n * or a button (when there is none), carrying `data-buro-quick` and claimed for the opener, whose\n * plain left click opens `quick.show` instead of navigating. A modified click follows the `href`.\n */\nfunction QuickOpener({\n href,\n testid,\n onOpen,\n children,\n}: {\n readonly href: string | null;\n readonly testid: string;\n readonly onOpen: () => void;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n if (href === null)\n return (\n <button\n type=\"button\"\n data-testid={testid}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onOpen();\n }}\n >\n {children}\n </button>\n );\n return (\n <ClaimedQuickLink href={href} testid={testid} className={relationLinkClassName} onOpen={onOpen}>\n {children}\n </ClaimedQuickLink>\n );\n}\n\n/**\n * A lazy relation's key (spec 4.3, 4.5): nothing is read until it is clicked. An id key is the\n * opener of the target's `quick.show`; a `by` key is a button whose click reads the one row the\n * key names (`where <by> eq <key>`, two rows) and opens it — no row renders the not-available\n * text, two rows the muted key with the development error, and neither opens anything.\n */\nfunction LazyRelationDisplay({\n to,\n by,\n idStr,\n app,\n open: openMode,\n}: Omit<BodyProps, 'id'>): React.ReactElement {\n const RouterLink = useLinkComponent();\n const gate = useEntryGate();\n const open = useQuickOpener();\n const targetDef = app.registry.get(to);\n const baseId = `relation-display:${to}:${idStr}`;\n if (!targetDef || openMode === 'none') return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const answer = gate(to, 'show');\n // Undecided: the key as text, nothing read; its peek asks the gate again when it would open.\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <span data-testid={`${baseId}:lazy`}>{idStr}</span>\n </PeekIntent>\n );\n // Denied: the key as text, nothing to open (no read, no anchor).\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n const page = linksToPage(app, targetDef);\n // `open: 'page'`: the key is a plain page link whatever the gate says about the overlay.\n if (openMode === 'page') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (answer.state !== 'open') {\n if (by !== undefined || !page) return <span data-testid={`${baseId}:lazy`}>{idStr}</span>;\n return (\n <RouterLink\n href={resourceShowHref(app.registry, targetDef, to, idStr)}\n data-testid={`${baseId}:lazy`}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {idStr}\n </RouterLink>\n );\n }\n if (by !== undefined)\n return (\n <PeekIntent target={peekTarget(to, by, idStr)}>\n <LazyByOpener to={to} by={by} idStr={idStr} target={answer.target} />\n </PeekIntent>\n );\n const openKey = () => void open(to, idStr);\n return (\n <PeekIntent target={peekTarget(to, undefined, idStr)}>\n <QuickOpener\n href={page ? resourceShowHref(app.registry, targetDef, to, idStr) : null}\n testid={`${baseId}:lazy`}\n onOpen={openKey}\n >\n {idStr}\n </QuickOpener>\n </PeekIntent>\n );\n}\n\n/** A lazy `by` key's button: one `where <by> eq <key>` read on click, then the row it names. */\nfunction LazyByOpener({\n to,\n by,\n idStr,\n target,\n}: {\n readonly to: string;\n readonly by: string;\n readonly idStr: string;\n readonly target: string;\n}): React.ReactElement {\n const app = useApp();\n const t = useOptionalT();\n const open = useQuickOpener();\n const [asked, setAsked] = useState(false);\n const read = useList(to, {\n ...(asked\n ? {\n filter: { field: by, op: 'eq', value: idStr } as never,\n pagination: { kind: 'offset', page: 1, pageSize: 2 } as never,\n }\n : {}),\n enabled: asked,\n retry: false,\n });\n const rows = (read.data as { readonly data?: readonly Record<string, unknown>[] } | undefined)\n ?.data;\n const baseId = `relation-display:${to}:${idStr}`;\n const idField = (app.registry.get(target)?.data as { readonly idField?: unknown } | undefined)\n ?.idField;\n const rowId =\n rows?.length === 1\n ? rows[0]?.[typeof idField === 'string' && idField.length > 0 ? idField : 'id']\n : undefined;\n // Only a click opens: a click before the row is read leaves a request here, the effect below\n // answers it once; a refetch that changes the rows later opens nothing.\n const requested = useRef(false);\n useEffect(() => {\n if (!asked || rows === undefined || !requested.current) return;\n if (rows.length > 1) {\n requested.current = false;\n if (process.env.NODE_ENV !== 'production')\n console.error(`[burojs] ${relationAmbiguousError(to, by, idStr).message}`);\n return;\n }\n requested.current = false;\n if (rows.length === 1 && rowId !== undefined && rowId !== null) open(to, String(rowId));\n }, [asked, rows, rowId, open, to, by, idStr]);\n if (asked && rows !== undefined && rows.length === 0)\n return (\n <span data-testid={`${baseId}:unavailable`} className=\"text-muted-foreground\">\n {t('relation.unavailable', { defaultValue: 'Not available' })}\n </span>\n );\n if (asked && rows !== undefined && rows.length > 1)\n return (\n <span data-testid={`${baseId}:ambiguous`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n return (\n <button\n type=\"button\"\n data-testid={`${baseId}:lazy`}\n {...{ [QUICK_ANCHOR_ATTRIBUTE]: '' }}\n className={`${relationLinkClassName} text-left`}\n onClick={(event) => {\n event.preventDefault();\n event.stopPropagation();\n // A key already read opens its row at once; otherwise the read answers this click.\n if (asked && rowId !== undefined && rowId !== null) open(to, String(rowId));\n else {\n requested.current = true;\n setAsked(true);\n }\n }}\n >\n {idStr}\n </button>\n );\n}\n\ninterface BodyProps {\n to: string;\n /** The target field the key holds (`meta.by`); the target's id field when absent. */\n by?: string;\n id: string | number;\n idStr: string;\n app: App;\n /** What a click on the title opens (`ui.open`). */\n open: RelationOpen;\n}\n\n/**\n * The entry gate decides before anything is read (spec 4.4, 4.14): while it is undecided (the\n * permission map loads) or when `show` is denied, the key renders as plain text and the display\n * registers no read; otherwise the body reads the title and renders the opener — or, when the\n * target has no quick show (`absent`), the page link of before, or text when there is no page.\n */\nfunction GatedRelationDisplay(props: BodyProps): React.ReactElement {\n const gate = useEntryGate();\n const target = props.to;\n const { idStr } = props;\n const answer = gate(target, 'show');\n // Undecided: plain text and no read; its peek (hover) asks the gate again when it would open,\n // and opens nothing until access is decided (spec 4.14).\n if (answer.state === 'undecided')\n return (\n <PeekIntent target={peekTarget(target, props.by, idStr)}>\n <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>\n </PeekIntent>\n );\n if (answer.state === 'hidden' || answer.state === 'disabled')\n return <span data-testid={`relation-display:${target}:${idStr}:text`}>{idStr}</span>;\n return (\n <RelationDisplayBody\n {...props}\n quickTarget={answer.state === 'open' && props.open === 'quick' ? answer.target : null}\n />\n );\n}\n\n/**\n * Split body so the hook is only called when we know `to` is set and\n * the value is non-falsy. React's rules of hooks require unconditional\n * hook calls per render — separating the body keeps the early-return\n * branches above hook-free.\n */\nfunction RelationDisplayBody({\n to,\n by,\n id,\n idStr,\n app,\n open: openMode,\n quickTarget,\n}: BodyProps & { readonly quickTarget: string | null }): React.ReactElement {\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const baseId = `relation-display:${to}:${idStr}`;\n const RouterLink = useLinkComponent();\n const open = useQuickOpener();\n const { record, isLoading, error, ambiguous } = useRelatedRecord(\n to,\n id,\n by === undefined ? undefined : { by },\n );\n\n // Target resource not in registry — degrade to plain text. No link\n // can be built without a basePath. Same fail-soft as `<EnumBadge>`.\n if (!targetDef) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationDisplay] target resource '${to}' not registered — rendering raw value '${idStr}'`,\n );\n }\n return (\n <span data-testid={`${baseId}:unregistered`} className=\"text-muted-foreground\">\n {idStr}\n </span>\n );\n }\n\n if (isLoading) {\n return (\n <span\n data-testid={`relation-display-loading:${to}`}\n className=\"inline-block h-5 w-20 animate-pulse rounded bg-muted\"\n aria-busy=\"true\"\n />\n );\n }\n\n // A key two rows hold: the raw key, muted — never either row (spec 4.2, fail closed).\n if (ambiguous) {\n return (\n <span\n data-testid={`${baseId}:ambiguous`}\n className=\"text-muted-foreground\"\n title={`${to}: more than one row has ${by ?? 'id'} ${idStr}`}\n >\n {idStr}\n </span>\n );\n }\n\n if (error) {\n return (\n <span data-testid={`${baseId}:error`} className=\"text-muted-foreground\" title={error.message}>\n {idStr}\n </span>\n );\n }\n\n if (!record) {\n return (\n <span\n data-testid={`${baseId}:not-found`}\n className=\"text-muted-foreground\"\n title={`${to} #${idStr} not found`}\n >\n {idStr}\n </span>\n );\n }\n\n const displayName = rowTitle(record);\n // The link names the resolved row by its id field (spec 4.2), never by the key: a `by` key is\n // not an id, and the target's id field need not be `id`.\n const idField = (targetDef.data as { readonly idField?: unknown } | undefined)?.idField;\n const rowId = record[typeof idField === 'string' && idField.length > 0 ? idField : 'id'];\n if (rowId === null || rowId === undefined || rowId === '')\n return (\n <span data-testid={`${baseId}:no-id`} className=\"text-muted-foreground\">\n {displayName}\n </span>\n );\n const page = linksToPage(app, targetDef);\n const href = page ? resourceShowHref(app.registry, targetDef, to, String(rowId)) : null;\n // `open: 'none'`: the title is text, no anchor and no button.\n if (openMode === 'none') return <span data-testid={baseId}>{displayName}</span>;\n if (quickTarget !== null)\n return (\n <RowOpener to={to} rowId={String(rowId)} href={href} testid={baseId} open={open}>\n {displayName}\n </RowOpener>\n );\n // No quick show (switched off, or no host wired): the page link, or text without a page.\n if (href === null) return <span data-testid={baseId}>{displayName}</span>;\n return (\n <RouterLink\n href={href}\n data-testid={baseId}\n className={relationLinkClassName}\n onClick={(e) => e.stopPropagation()}\n >\n {displayName}\n </RouterLink>\n );\n}\n\n/**\n * A resolved row's opener: a click opens its `quick.show`, a hover its peek — by the row's id,\n * the record descriptor `quick.show` reads, so the two share one read.\n */\nfunction RowOpener({\n to,\n rowId,\n href,\n testid,\n open,\n children,\n}: {\n readonly to: string;\n readonly rowId: string;\n readonly href: string | null;\n readonly testid: string;\n readonly open: ReturnType<typeof useQuickOpener>;\n readonly children: React.ReactNode;\n}): React.ReactElement {\n const onOpen = useCallback(() => void open(to, rowId), [open, to, rowId]);\n return (\n <PeekIntent target={peekTarget(to, undefined, rowId)}>\n <QuickOpener href={href} testid={testid} onOpen={onOpen}>\n {children}\n </QuickOpener>\n </PeekIntent>\n );\n}\n\n/**\n * A to-many relation (`f.relationMany`): each key rendered as {@link RelationDisplay} renders\n * one — eager keys share the aggregator's batched read, lazy keys read nothing.\n */\nexport function RelationManyDisplay({ value, field }: FieldRendererProps): React.ReactElement {\n const keys = Array.isArray(value) ? value : [];\n if (keys.length === 0)\n return (\n <span data-testid=\"relation-many-display:empty\" className=\"text-muted-foreground\">\n —\n </span>\n );\n return (\n <span className=\"inline-flex flex-wrap gap-x-2\" data-testid=\"relation-many-display\">\n {keys.map((key, index) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: a key may repeat; its position is its identity\n <RelationDisplay key={`${String(key)}:${index}`} value={key} field={field} />\n ))}\n </span>\n );\n}\n","import type { ErasedResourceHandle, FilterExpression, SearchDecl } from '@burojs/core';\nimport { filterFields } from '@burojs/react';\n\n/**\n * A declared search set without the fields its user may not view (P5 spec 3.7, decision P5-17).\n * No field left means no search. A function search is kept: its output is checked by\n * {@link namesHiddenField}.\n */\nexport function narrowSearchDecl(\n declared: SearchDecl | undefined,\n visible: (field: string) => boolean,\n): SearchDecl | undefined {\n if (declared === undefined || typeof declared === 'function') return declared;\n if (!Array.isArray(declared.fields)) return declared;\n const fields = declared.fields.filter((ref) =>\n visible(typeof ref === 'string' ? ref : ref.field),\n );\n if (fields.length === declared.fields.length) return declared;\n return fields.length === 0 ? undefined : { ...declared, fields };\n}\n\n/**\n * Whether a composed filter names a field its user may not view: an author predicate (a fixed\n * filter) or a function search's output. Such a read is forbidden, never narrowed — dropping a\n * narrowing predicate would fail open.\n */\nexport function namesHiddenField(\n filter: FilterExpression<ErasedResourceHandle> | undefined,\n visible: (field: string) => boolean,\n): boolean {\n return filter !== undefined && filterFields(filter).some((field) => !visible(field));\n}\n"],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ import { l as cn } from "./use-theme-dnE3Od3I.js";
2
+ import { i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-B_rt9S1c.js";
3
+ import { useOptionalT } from "@burojs/react";
4
+ import { IconX } from "@tabler/icons-react";
5
+ import * as React from "react";
6
+ import { createContext, useEffect, useRef } from "react";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
+ import { cva } from "class-variance-authority";
9
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
10
+ //#region src/primitives/sheet.tsx
11
+ const Sheet = SheetPrimitive.Root;
12
+ const SheetTrigger = SheetPrimitive.Trigger;
13
+ const SheetClose = SheetPrimitive.Close;
14
+ const SheetPortal = SheetPrimitive.Portal;
15
+ const SheetOverlay = React.forwardRef(({ className, ...props }, ref) => jsx(SheetPrimitive.Overlay, {
16
+ className: cn("fixed inset-0 z-50 bg-overlay/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-[var(--duration-panel)] ease-[var(--ease-overlay)]", className),
17
+ ...props,
18
+ ref
19
+ }));
20
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
21
+ const sheetVariants = cva("fixed z-50 gap-4 bg-background text-foreground p-6 shadow-lg duration-[var(--duration-panel)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out", {
22
+ variants: { side: {
23
+ top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
24
+ bottom: "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
25
+ left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
26
+ right: "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm"
27
+ } },
28
+ defaultVariants: { side: "right" }
29
+ });
30
+ const SheetContent = React.forwardRef(({ side = "right", className, children, showClose = true, ...props }, ref) => {
31
+ const t = useOptionalT();
32
+ return jsxs(SheetPortal, { children: [jsx(SheetOverlay, {}), jsxs(SheetPrimitive.Content, {
33
+ ref,
34
+ className: cn(sheetVariants({ side }), className),
35
+ ...props,
36
+ children: [children, showClose && jsxs(SheetPrimitive.Close, {
37
+ className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary",
38
+ children: [jsx(IconX, { className: "h-4 w-4" }), jsx("span", {
39
+ className: "sr-only",
40
+ children: t("actions.close", { defaultValue: "Close" })
41
+ })]
42
+ })]
43
+ })] });
44
+ });
45
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
46
+ function SheetHeader({ className, ...props }) {
47
+ return jsx("div", {
48
+ className: cn("flex flex-col space-y-2 text-center sm:text-left", className),
49
+ ...props
50
+ });
51
+ }
52
+ SheetHeader.displayName = "SheetHeader";
53
+ function SheetFooter({ className, ...props }) {
54
+ return jsx("div", {
55
+ className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className),
56
+ ...props
57
+ });
58
+ }
59
+ SheetFooter.displayName = "SheetFooter";
60
+ const SheetTitle = React.forwardRef(({ className, ...props }, ref) => jsx(SheetPrimitive.Title, {
61
+ ref,
62
+ className: cn("text-lg font-semibold text-foreground", className),
63
+ ...props
64
+ }));
65
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
66
+ const SheetDescription = React.forwardRef(({ className, ...props }, ref) => jsx(SheetPrimitive.Description, {
67
+ ref,
68
+ className: cn("text-sm text-muted-foreground", className),
69
+ ...props
70
+ }));
71
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
72
+ //#endregion
73
+ //#region src/ui-presentations/shell.tsx
74
+ const OwnTitleContext = createContext(false);
75
+ function focusOwnHeading(event) {
76
+ const container = event.currentTarget;
77
+ if (!(container instanceof HTMLElement)) return;
78
+ const heading = container.querySelector("[data-buro-overlay-heading]");
79
+ if (!(heading instanceof HTMLElement)) return;
80
+ event.preventDefault();
81
+ heading.focus();
82
+ }
83
+ function WrappedDialogContent({ wrapper, copy, children }) {
84
+ return wrapper ? jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: copy.title }), jsx(DialogDescription, {
85
+ className: copy.description === void 0 ? "sr-only" : void 0,
86
+ children: copy.description ?? ""
87
+ })] }), children] }) : jsxs(Fragment$1, { children: [
88
+ jsx(DialogTitle, {
89
+ className: "sr-only",
90
+ children: copy.title
91
+ }),
92
+ jsx(DialogDescription, {
93
+ className: "sr-only",
94
+ children: copy.description ?? ""
95
+ }),
96
+ children
97
+ ] });
98
+ }
99
+ function WrappedSheetContent({ wrapper, copy, children }) {
100
+ return wrapper ? jsxs(Fragment$1, { children: [jsxs(SheetHeader, { children: [jsx(SheetTitle, { children: copy.title }), jsx(SheetDescription, {
101
+ className: copy.description === void 0 ? "sr-only" : void 0,
102
+ children: copy.description ?? ""
103
+ })] }), children] }) : jsxs(Fragment$1, { children: [
104
+ jsx(SheetTitle, {
105
+ className: "sr-only",
106
+ children: copy.title
107
+ }),
108
+ jsx(SheetDescription, {
109
+ className: "sr-only",
110
+ children: copy.description ?? ""
111
+ }),
112
+ children
113
+ ] });
114
+ }
115
+ function useFocusRestoration() {
116
+ const returnFocus = useRef(typeof document === "undefined" || !(document.activeElement instanceof HTMLElement) ? null : document.activeElement);
117
+ useEffect(() => () => {
118
+ const element = returnFocus.current;
119
+ queueMicrotask(() => {
120
+ if (element?.isConnected && element !== document.body) {
121
+ element.focus();
122
+ return;
123
+ }
124
+ const main = document.querySelector("main");
125
+ if (main === null) return;
126
+ if (!main.hasAttribute("tabindex")) main.setAttribute("tabindex", "-1");
127
+ main.focus();
128
+ });
129
+ }, []);
130
+ }
131
+ function keepForPopover(event) {
132
+ const target = event.target;
133
+ if (target instanceof Element && target.closest("[data-buro-popover]") !== null) event.preventDefault();
134
+ }
135
+ const REDUCED_MOTION = "motion-reduce:animate-none motion-reduce:transition-none";
136
+ function DialogPresentationShell({ dismiss, wrapper, copy, ownTitle = false, children }) {
137
+ useFocusRestoration();
138
+ return jsx(Dialog, {
139
+ open: true,
140
+ onOpenChange: (open) => {
141
+ if (!open) dismiss();
142
+ },
143
+ children: ownTitle ? jsx(DialogContent, {
144
+ className: `flex max-h-[85vh] flex-col ${REDUCED_MOTION}`,
145
+ overlayClassName: REDUCED_MOTION,
146
+ showClose: wrapper,
147
+ "aria-describedby": void 0,
148
+ "aria-modal": "true",
149
+ onOpenAutoFocus: focusOwnHeading,
150
+ onPointerDownOutside: keepForPopover,
151
+ children: jsx(OwnTitleContext.Provider, {
152
+ value: true,
153
+ children
154
+ })
155
+ }) : jsx(DialogContent, {
156
+ className: REDUCED_MOTION,
157
+ overlayClassName: REDUCED_MOTION,
158
+ showClose: wrapper,
159
+ "aria-modal": "true",
160
+ onPointerDownOutside: keepForPopover,
161
+ children: jsx(WrappedDialogContent, {
162
+ wrapper,
163
+ copy,
164
+ children
165
+ })
166
+ })
167
+ });
168
+ }
169
+ function SheetPresentationShell({ dismiss, wrapper, copy, ownTitle = false, children }) {
170
+ useFocusRestoration();
171
+ return jsx(Sheet, {
172
+ open: true,
173
+ onOpenChange: (open) => {
174
+ if (!open) dismiss();
175
+ },
176
+ children: ownTitle ? jsx(SheetContent, {
177
+ side: "right",
178
+ className: `flex flex-col sm:max-w-xl ${REDUCED_MOTION}`,
179
+ showClose: wrapper,
180
+ "aria-describedby": void 0,
181
+ "aria-modal": "true",
182
+ onOpenAutoFocus: focusOwnHeading,
183
+ onPointerDownOutside: keepForPopover,
184
+ children: jsx(OwnTitleContext.Provider, {
185
+ value: true,
186
+ children
187
+ })
188
+ }) : jsx(SheetContent, {
189
+ side: "right",
190
+ className: `sm:max-w-xl ${REDUCED_MOTION}`,
191
+ showClose: wrapper,
192
+ "aria-modal": "true",
193
+ onPointerDownOutside: keepForPopover,
194
+ children: jsx(WrappedSheetContent, {
195
+ wrapper,
196
+ copy,
197
+ children
198
+ })
199
+ })
200
+ });
201
+ }
202
+ //#endregion
203
+ export { SheetClose as a, SheetFooter as c, SheetTrigger as d, Sheet as i, SheetHeader as l, OwnTitleContext as n, SheetContent as o, SheetPresentationShell as r, SheetDescription as s, DialogPresentationShell as t, SheetTitle as u };
204
+
205
+ //# sourceMappingURL=shell-BPlk8hEa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-BPlk8hEa.js","names":[],"sources":["../src/primitives/sheet.tsx","../src/ui-presentations/shell.tsx"],"sourcesContent":["'use client';\nimport { useOptionalT } from '@burojs/react';\n\nimport * as React from 'react';\nimport * as SheetPrimitive from '@radix-ui/react-dialog';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from '../lib/cn.js';\n\nconst Sheet = SheetPrimitive.Root;\nconst SheetTrigger = SheetPrimitive.Trigger;\nconst SheetClose = SheetPrimitive.Close;\nconst SheetPortal = SheetPrimitive.Portal;\n\nexport type SheetOverlayProps = React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>;\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n SheetOverlayProps\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n className={cn(\n 'fixed inset-0 z-50 bg-overlay/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-[var(--duration-panel)] ease-[var(--ease-overlay)]',\n className,\n )}\n {...props}\n ref={ref}\n />\n));\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName;\n\nconst sheetVariants = cva(\n 'fixed z-50 gap-4 bg-background text-foreground p-6 shadow-lg duration-[var(--duration-panel)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out',\n {\n variants: {\n side: {\n top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',\n bottom:\n 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',\n left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',\n right:\n 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',\n },\n },\n defaultVariants: {\n side: 'right',\n },\n },\n);\n\nexport interface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {\n /** Whether the standard visual close button is rendered. Defaults to true. */\n showClose?: boolean;\n}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = 'right', className, children, showClose = true, ...props }, ref) => {\n const t = useOptionalT();\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n {showClose && (\n <SheetPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary\">\n <IconX className=\"h-4 w-4\" />\n <span className=\"sr-only\">{t('actions.close', { defaultValue: 'Close' })}</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPortal>\n );\n});\nSheetContent.displayName = SheetPrimitive.Content.displayName;\n\nexport type SheetHeaderProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction SheetHeader({ className, ...props }: SheetHeaderProps) {\n return (\n <div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />\n );\n}\nSheetHeader.displayName = 'SheetHeader';\n\nexport type SheetFooterProps = React.HTMLAttributes<HTMLDivElement>;\n\nfunction SheetFooter({ className, ...props }: SheetFooterProps) {\n return (\n <div\n className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}\n {...props}\n />\n );\n}\nSheetFooter.displayName = 'SheetFooter';\n\nexport type SheetTitleProps = React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>;\n\nconst SheetTitle = React.forwardRef<React.ElementRef<typeof SheetPrimitive.Title>, SheetTitleProps>(\n ({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn('text-lg font-semibold text-foreground', className)}\n {...props}\n />\n ),\n);\nSheetTitle.displayName = SheetPrimitive.Title.displayName;\n\nexport type SheetDescriptionProps = React.ComponentPropsWithoutRef<\n typeof SheetPrimitive.Description\n>;\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n SheetDescriptionProps\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n));\nSheetDescription.displayName = SheetPrimitive.Description.displayName;\n\nexport {\n Sheet,\n SheetTrigger,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n SheetClose,\n};\n","import { createContext, useEffect, useRef, type ReactNode } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../primitives/dialog.js';\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from '../primitives/sheet.js';\n\nexport interface PresentationCopy {\n readonly title: string;\n readonly description?: string;\n}\n\ninterface PresentationShellProps {\n readonly dismiss: () => void;\n readonly wrapper: boolean;\n readonly copy: PresentationCopy;\n /**\n * The presented component renders the overlay's title itself, as the Radix `Title` (a quick\n * entry's record title, plan ruling 2): the shell renders no title and no description, and\n * focus moves to the element marked `data-buro-overlay-heading` on open.\n */\n readonly ownTitle?: boolean;\n readonly children: ReactNode;\n}\n\n/**\n * @internal True inside a kit shell asked for `ownTitle`: the presented component may render the\n * Radix `Title` (an app's host, which may not be a Radix dialog at all, leaves it `false`).\n */\nexport const OwnTitleContext = createContext(false);\n\n/** On open, focus the presented component's own heading instead of the first tabbable. */\nfunction focusOwnHeading(event: Event): void {\n const container = event.currentTarget;\n if (!(container instanceof HTMLElement)) return;\n const heading = container.querySelector('[data-buro-overlay-heading]');\n if (!(heading instanceof HTMLElement)) return;\n event.preventDefault();\n heading.focus();\n}\n\nfunction WrappedDialogContent({\n wrapper,\n copy,\n children,\n}: Pick<PresentationShellProps, 'wrapper' | 'copy' | 'children'>) {\n return wrapper ? (\n <>\n <DialogHeader>\n <DialogTitle>{copy.title}</DialogTitle>\n <DialogDescription className={copy.description === undefined ? 'sr-only' : undefined}>\n {copy.description ?? ''}\n </DialogDescription>\n </DialogHeader>\n {children}\n </>\n ) : (\n <>\n <DialogTitle className=\"sr-only\">{copy.title}</DialogTitle>\n <DialogDescription className=\"sr-only\">{copy.description ?? ''}</DialogDescription>\n {children}\n </>\n );\n}\n\nfunction WrappedSheetContent({\n wrapper,\n copy,\n children,\n}: Pick<PresentationShellProps, 'wrapper' | 'copy' | 'children'>) {\n return wrapper ? (\n <>\n <SheetHeader>\n <SheetTitle>{copy.title}</SheetTitle>\n <SheetDescription className={copy.description === undefined ? 'sr-only' : undefined}>\n {copy.description ?? ''}\n </SheetDescription>\n </SheetHeader>\n {children}\n </>\n ) : (\n <>\n <SheetTitle className=\"sr-only\">{copy.title}</SheetTitle>\n <SheetDescription className=\"sr-only\">{copy.description ?? ''}</SheetDescription>\n {children}\n </>\n );\n}\n\nfunction useFocusRestoration(): void {\n // Capture before Radix moves focus into the portal. The host may be removed\n // immediately after `dismiss`, so defer restoration until Radix cleanup has\n // completed its own focus handling.\n const returnFocus = useRef<HTMLElement | null>(\n typeof document === 'undefined' || !(document.activeElement instanceof HTMLElement)\n ? null\n : document.activeElement,\n );\n useEffect(\n () => () => {\n const element = returnFocus.current;\n queueMicrotask(() => {\n // The opener is gone (its row was deleted, say): the nearest surviving landmark instead\n // (spec 4.12), never the document body.\n if (element?.isConnected && element !== document.body) {\n element.focus();\n return;\n }\n const main = document.querySelector<HTMLElement>('main');\n if (main === null) return;\n if (!main.hasAttribute('tabindex')) main.setAttribute('tabindex', '-1');\n main.focus();\n });\n },\n [],\n );\n}\n\n/**\n * A pointer down inside a popover (a peek) is not outside the overlay. The popover is a React\n * sibling of the sheet or dialog (`<UIPresentations>` renders every presentation side by side),\n * and Radix decides \"inside\" by the React tree, so without this a click on a peek's title — which\n * pushes into this sheet — would first dismiss it (or ask about its dirty form).\n */\nfunction keepForPopover(event: { readonly target: EventTarget | null; preventDefault(): void }) {\n const target = event.target;\n if (target instanceof Element && target.closest('[data-buro-popover]') !== null)\n event.preventDefault();\n}\n\n/** No slide and no fade under `prefers-reduced-motion` (spec 4.12). */\nconst REDUCED_MOTION = 'motion-reduce:animate-none motion-reduce:transition-none';\n\n/** Shared Radix lifecycle shell for a centered temporary presentation. */\nexport function DialogPresentationShell({\n dismiss,\n wrapper,\n copy,\n ownTitle = false,\n children,\n}: PresentationShellProps) {\n useFocusRestoration();\n return (\n <Dialog\n open\n onOpenChange={(open) => {\n if (!open) dismiss();\n }}\n >\n {ownTitle ? (\n <DialogContent\n className={`flex max-h-[85vh] flex-col ${REDUCED_MOTION}`}\n overlayClassName={REDUCED_MOTION}\n showClose={wrapper}\n aria-describedby={undefined}\n aria-modal=\"true\"\n onOpenAutoFocus={focusOwnHeading}\n onPointerDownOutside={keepForPopover}\n >\n <OwnTitleContext.Provider value={true}>{children}</OwnTitleContext.Provider>\n </DialogContent>\n ) : (\n <DialogContent\n className={REDUCED_MOTION}\n overlayClassName={REDUCED_MOTION}\n showClose={wrapper}\n aria-modal=\"true\"\n onPointerDownOutside={keepForPopover}\n >\n <WrappedDialogContent wrapper={wrapper} copy={copy}>\n {children}\n </WrappedDialogContent>\n </DialogContent>\n )}\n </Dialog>\n );\n}\n\n/** Shared Radix lifecycle shell for a right-hand temporary presentation. */\nexport function SheetPresentationShell({\n dismiss,\n wrapper,\n copy,\n ownTitle = false,\n children,\n}: PresentationShellProps) {\n useFocusRestoration();\n return (\n <Sheet\n open\n onOpenChange={(open) => {\n if (!open) dismiss();\n }}\n >\n {ownTitle ? (\n <SheetContent\n side=\"right\"\n className={`flex flex-col sm:max-w-xl ${REDUCED_MOTION}`}\n showClose={wrapper}\n aria-describedby={undefined}\n aria-modal=\"true\"\n onOpenAutoFocus={focusOwnHeading}\n onPointerDownOutside={keepForPopover}\n >\n <OwnTitleContext.Provider value={true}>{children}</OwnTitleContext.Provider>\n </SheetContent>\n ) : (\n <SheetContent\n side=\"right\"\n className={`sm:max-w-xl ${REDUCED_MOTION}`}\n showClose={wrapper}\n aria-modal=\"true\"\n onPointerDownOutside={keepForPopover}\n >\n <WrappedSheetContent wrapper={wrapper} copy={copy}>\n {children}\n </WrappedSheetContent>\n </SheetContent>\n )}\n </Sheet>\n );\n}\n"],"mappings":"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"}