@burojs/ui-shadcn 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) 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-Bb70LPAy.js +135 -0
  11. package/dist/candidates-gate-Bb70LPAy.js.map +1 -0
  12. package/dist/candidates-gate-D33jXLHO.cjs +153 -0
  13. package/dist/candidates-gate-D33jXLHO.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-B116Bmqi.js +31 -0
  15. package/dist/candidates-relation-picker-B116Bmqi.js.map +1 -0
  16. package/dist/candidates-relation-picker-Cy0rTxvI.cjs +31 -0
  17. package/dist/candidates-relation-picker-Cy0rTxvI.cjs.map +1 -0
  18. package/dist/check-confirm-dialog--gPFnEeg.cjs +1547 -0
  19. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-bRT8lt0d.js +1245 -0
  21. package/dist/check-confirm-dialog-bRT8lt0d.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-Bhn-dhZV.js +1621 -0
  27. package/dist/filter-builder-Bhn-dhZV.js.map +1 -0
  28. package/dist/filter-builder-nXqtM_h4.cjs +1789 -0
  29. package/dist/filter-builder-nXqtM_h4.cjs.map +1 -0
  30. package/dist/flow-runtime-D3avVH0I.cjs +255 -0
  31. package/dist/flow-runtime-D3avVH0I.cjs.map +1 -0
  32. package/dist/flow-runtime-s6lkRPKF.js +252 -0
  33. package/dist/flow-runtime-s6lkRPKF.js.map +1 -0
  34. package/dist/flow-screen-CCvgDI9G.js +704 -0
  35. package/dist/flow-screen-CCvgDI9G.js.map +1 -0
  36. package/dist/flow-screen-Ci_Sik4Y.cjs +708 -0
  37. package/dist/flow-screen-Ci_Sik4Y.cjs.map +1 -0
  38. package/dist/index.cjs +3420 -11986
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1843 -2015
  41. package/dist/index.d.ts +1849 -2021
  42. package/dist/index.js +3090 -11675
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B6FI0ctQ.js +112 -0
  45. package/dist/inline-flow-host-B6FI0ctQ.js.map +1 -0
  46. package/dist/inline-flow-host-DV1et3c6.cjs +112 -0
  47. package/dist/inline-flow-host-DV1et3c6.cjs.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/markdown/index.cjs +1 -1
  57. package/dist/markdown/index.d.cts +2 -3
  58. package/dist/markdown/index.d.ts +2 -3
  59. package/dist/markdown/index.js +1 -1
  60. package/dist/page-flow-route-hdnULWqd.cjs +277 -0
  61. package/dist/page-flow-route-hdnULWqd.cjs.map +1 -0
  62. package/dist/page-flow-route-i3ZfJ1sd.js +277 -0
  63. package/dist/page-flow-route-i3ZfJ1sd.js.map +1 -0
  64. package/dist/place-node-C5SmIAYU.cjs +8139 -0
  65. package/dist/place-node-C5SmIAYU.cjs.map +1 -0
  66. package/dist/place-node-NraNYKPx.js +7597 -0
  67. package/dist/place-node-NraNYKPx.js.map +1 -0
  68. package/dist/related-list-cards-BWZ9GfOG.js +42 -0
  69. package/dist/related-list-cards-BWZ9GfOG.js.map +1 -0
  70. package/dist/related-list-cards-CFq3meUn.cjs +42 -0
  71. package/dist/related-list-cards-CFq3meUn.cjs.map +1 -0
  72. package/dist/related-list-timeline-CCzl71aA.cjs +142 -0
  73. package/dist/related-list-timeline-CCzl71aA.cjs.map +1 -0
  74. package/dist/related-list-timeline-yrPsjA3F.js +142 -0
  75. package/dist/related-list-timeline-yrPsjA3F.js.map +1 -0
  76. package/dist/related-rows-ea43qhhy.cjs +208 -0
  77. package/dist/related-rows-ea43qhhy.cjs.map +1 -0
  78. package/dist/related-rows-o0_t-Yce.js +185 -0
  79. package/dist/related-rows-o0_t-Yce.js.map +1 -0
  80. package/dist/relation-create-IA40t1hi.cjs +364 -0
  81. package/dist/relation-create-IA40t1hi.cjs.map +1 -0
  82. package/dist/relation-create-h6F9B4C4.js +339 -0
  83. package/dist/relation-create-h6F9B4C4.js.map +1 -0
  84. package/dist/relation-many-picker-C8zFEjwd.cjs +467 -0
  85. package/dist/relation-many-picker-C8zFEjwd.cjs.map +1 -0
  86. package/dist/relation-many-picker-bvkjkv-k.js +466 -0
  87. package/dist/relation-many-picker-bvkjkv-k.js.map +1 -0
  88. package/dist/relation-picker-Z0ZJJ5uS.js +493 -0
  89. package/dist/relation-picker-Z0ZJJ5uS.js.map +1 -0
  90. package/dist/relation-picker-dialog-0IzkjmiA.cjs +304 -0
  91. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +1 -0
  92. package/dist/relation-picker-dialog-B6nIiJBf.js +303 -0
  93. package/dist/relation-picker-dialog-B6nIiJBf.js.map +1 -0
  94. package/dist/relation-picker-gag0qSR5.cjs +516 -0
  95. package/dist/relation-picker-gag0qSR5.cjs.map +1 -0
  96. package/dist/resource-page-router-3RUf9d1j.js +1588 -0
  97. package/dist/resource-page-router-3RUf9d1j.js.map +1 -0
  98. package/dist/resource-page-router-PXfMW9nh.cjs +1785 -0
  99. package/dist/resource-page-router-PXfMW9nh.cjs.map +1 -0
  100. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  101. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  102. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  103. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  104. package/dist/search-access-CPPmfuvy.js +654 -0
  105. package/dist/search-access-CPPmfuvy.js.map +1 -0
  106. package/dist/search-access-Dugvttlg.cjs +791 -0
  107. package/dist/search-access-Dugvttlg.cjs.map +1 -0
  108. package/dist/shell-BPlk8hEa.js +205 -0
  109. package/dist/shell-BPlk8hEa.js.map +1 -0
  110. package/dist/shell-DxhlsPwm.cjs +271 -0
  111. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  112. package/dist/skeleton-U2noefj5.js +13 -0
  113. package/dist/skeleton-U2noefj5.js.map +1 -0
  114. package/dist/skeleton-_1gP7wan.cjs +18 -0
  115. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  116. package/dist/styles.css +241 -9
  117. package/dist/toaster-C02XbB9r.js +135 -0
  118. package/dist/toaster-C02XbB9r.js.map +1 -0
  119. package/dist/toaster-Cr51X7bz.cjs +195 -0
  120. package/dist/toaster-Cr51X7bz.cjs.map +1 -0
  121. package/dist/use-dialog-filter-state-C--oMAZG.js +87 -0
  122. package/dist/use-dialog-filter-state-C--oMAZG.js.map +1 -0
  123. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs +92 -0
  124. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +1 -0
  125. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  126. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  127. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  128. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  129. package/package.json +16 -14
  130. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  131. package/dist/use-theme-IHmtrhEt.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-builder-nXqtM_h4.cjs","names":["DEFAULT_LANGUAGE_STORAGE_KEY","cva","cn","React","cn","SelectPrimitive","React","cn","IconChevronDown","IconChevronUp","IconCheck","isFilterGroup","opArity","useEnumValues","useI18nCaps","useApp","resolveEnumLabel","enumValueText","useRelatedRecord","useRowTitle","useState","useCallback","isFilterGroup","useOptionalT","useMemo","IconX","isFilterGroup","isCompletePredicate","opArity","convertValueForOp","EMPTY_FILTER_GROUP","useState","useOptionalT","Popover","PopoverTrigger","Button","cn","PopoverContent","Command","CommandList","CommandGroup","CommandItem","IconX","IconCheck","useOptionalT","Popover","PopoverTrigger","Button","cn","PopoverContent","Command","CommandInput","CommandList","IconLoader2","CommandEmpty","CommandGroup","CommandItem","IconX","IconCheck","useI18nCaps","useApp","useEnumValues","resolveEnumLabel","enumValueText","useApp","useRowTitle","useList","useMemo","useApp","useOptionalT","useState","useRowTitle","useListAccess","useMemo","narrowSearchDecl","useList","Popover","PopoverTrigger","Button","cn","PopoverContent","Command","CommandInput","CommandList","IconLoader2","CommandEmpty","CommandItem","IconX","IconCheck","useOptionalT","useState","useRef","Input","cn","IconX","opArity","useOptionalT","Button","cn","IconX","useOptionalT","Fragment","cn","Button","IconPlus","IconFolderPlus","IconX","useOptionalT","useState","useRef","Popover","PopoverTrigger","Button","IconFilter","PopoverContent","useState"],"sources":["../src/layout/language-storage.ts","../src/primitives/badge.tsx","../src/primitives/table.tsx","../src/primitives/select.tsx","../src/fields/format.ts","../src/list-kit/filter-builder/describe-row.ts","../src/list-kit/filter-builder/describe-value-resolvers.tsx","../src/list-kit/filter-builder/pinned-ownership.ts","../src/list-kit/filter-builder/filter-badges.tsx","../src/list-kit/filter-builder/draft.ts","../src/list-kit/filter-builder/values/use-selectable-value-control.ts","../src/list-kit/filter-builder/values/boolean-value-control.tsx","../src/list-kit/filter-builder/values/select-list-value-control.tsx","../src/list-kit/filter-builder/values/enum-value-control.tsx","../src/list-kit/use-related-labels.ts","../src/list-kit/filter-builder/values/relation-value-control.tsx","../src/list-kit/filter-builder/values/scalar-value-input.tsx","../src/list-kit/filter-builder/values/filter-value-control.tsx","../src/list-kit/filter-builder/filter-row.tsx","../src/list-kit/filter-builder/filter-group-editor.tsx","../src/list-kit/filter-builder/filter-panel.tsx","../src/list-kit/filter-builder/pinned-filter-controls.tsx","../src/list-kit/filter-builder/filter-builder.tsx"],"sourcesContent":["import { DEFAULT_LANGUAGE_STORAGE_KEY } from '@burojs/react';\n\n/**\n * Default localStorage key (`'buro:language'`, owned by `@burojs/react`).\n * Framework-neutral so multiple adopters on the same domain don't collide.\n * Override per-app with `<BuroProvider languageStorageKey>` and pass the same\n * key to `loadLocale(key)` when restoring the choice at boot.\n */\nexport { DEFAULT_LANGUAGE_STORAGE_KEY };\n\n/**\n * Read the locale code a previous session persisted under a localStorage key.\n * Returns `undefined` when nothing is stored or storage is unavailable (SSR,\n * disabled). There is no default here: the caller knows its own registry, so\n * it decides what \"nothing chosen yet\" means — normally `registry.primary()`.\n * The stored code is untrusted; validate it against `registry.locales()`.\n */\nexport function loadLocale(storageKey: string = DEFAULT_LANGUAGE_STORAGE_KEY): string | undefined {\n if (typeof window === 'undefined') return undefined;\n try {\n return window.localStorage.getItem(storageKey) ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Persist a locale code. Counterpart to {@link loadLocale}. */\nexport function saveLocale(code: string, storageKey: string = DEFAULT_LANGUAGE_STORAGE_KEY): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(storageKey, code);\n } catch {\n // localStorage may be disabled — silently no-op\n }\n}\n","'use client';\n\nimport type * as React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/cn.js';\n\nconst badgeVariants = cva(\n // `max-w-full` + `truncate` so a long label ELLIPSES instead of being sliced.\n // A badge is `inline-flex`, and `text-overflow: ellipsis` does not apply to\n // flex children — so inside a table cell (which clips with overflow:hidden)\n // a wide badge was cut mid-glyph with a razor-flat edge, while plain text in\n // the next column truncated correctly. Acceptance testing ranked this the\n // most visible flaw on the busiest screen.\n 'inline-flex max-w-full items-center truncate rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2',\n {\n variants: {\n variant: {\n default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',\n secondary:\n 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',\n destructive:\n 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',\n outline: 'text-foreground',\n // ─── buro-only extensions (not in upstream shadcn registry) ───\n // Semantic status variants — subtle tinted pill (background-tint +\n // dark text) instead of solid color. Mirrors the destructive-soft\n // pattern (AlertDialog body, record form error banner) and keeps\n // status badges readable at admin-grade density without screaming\n // for attention. Light/dark theme parity is preserved by keeping\n // the same background opacity in both modes and swapping text color.\n // Pulling the upstream Badge component will overwrite these — the\n // EnumColor type in @burojs/core depends on them, so re-add after\n // an upstream sync.\n success: 'border-success/20 bg-success/15 text-success-emphasis hover:bg-success/20',\n warning: 'border-warning/20 bg-warning/15 text-warning-emphasis hover:bg-warning/20',\n info: 'border-info/20 bg-info/15 text-info-emphasis hover:bg-info/20',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, children, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props}>\n {/*\n ⚠️ The truncation has to happen on a CHILD, not on the badge itself.\n A badge is `inline-flex`, and `text-overflow: ellipsis` does not apply\n to a flex container's anonymous text child — setting the property on\n the badge looks right in computed styles and still slices the glyph.\n The standard flex-truncation pattern is a `min-w-0 truncate` child,\n which is what this span is. Icons passed as children stay inline and\n truncate with the text.\n */}\n <span className=\"min-w-0 truncate\">{children}</span>\n </div>\n );\n}\n\nexport { Badge, badgeVariants };\n","'use client';\n\nimport * as React from 'react';\nimport { cn } from '../lib/cn.js';\n\nexport type TableProps = React.HTMLAttributes<HTMLTableElement>;\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(({ className, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table ref={ref} className={cn('w-full caption-bottom text-sm', className)} {...props} />\n </div>\n));\nTable.displayName = 'Table';\n\nexport type TableHeaderProps = React.HTMLAttributes<HTMLTableSectionElement>;\n\nconst TableHeader = React.forwardRef<HTMLTableSectionElement, TableHeaderProps>(\n ({ className, ...props }, ref) => (\n <thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />\n ),\n);\nTableHeader.displayName = 'TableHeader';\n\nexport type TableBodyProps = React.HTMLAttributes<HTMLTableSectionElement>;\n\nconst TableBody = React.forwardRef<HTMLTableSectionElement, TableBodyProps>(\n ({ className, ...props }, ref) => (\n <tbody ref={ref} className={cn('[&_tr:last-child]:border-0', className)} {...props} />\n ),\n);\nTableBody.displayName = 'TableBody';\n\nexport type TableRowProps = React.HTMLAttributes<HTMLTableRowElement>;\n\nconst TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(\n ({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n // Hover: canonical shadcn `bg-muted/50` — subtle gray tint.\n // Selected: `bg-primary/10` — subtle BLUE tint, a distinct\n // HUE from hover so selected rows out-emphasize hovered ones.\n // (The default shadcn `bg-muted` for selected reads identical\n // to `bg-muted/50` hover in this app's theme because the\n // playground globals.css aliases `--accent` and `--muted` to\n // the same HSL — opacity-only differentiation lands inside\n // the JND threshold at admin density. Switching the selected\n // state to a primary-tinted layer breaks that tie cleanly.)\n // Selected+hover bumps to `bg-primary/15` so users still get\n // feedback on rows they've already selected.\n // `group/row`: a row's action strip reveals itself on the row's hover or focus.\n 'group/row border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-primary/10 data-[state=selected]:hover:bg-primary/15',\n className,\n )}\n {...props}\n />\n ),\n);\nTableRow.displayName = 'TableRow';\n\nexport type TableHeadProps = React.ThHTMLAttributes<HTMLTableCellElement>;\n\nconst TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(\n ({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n 'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',\n className,\n )}\n {...props}\n />\n ),\n);\nTableHead.displayName = 'TableHead';\n\nexport type TableCellProps = React.TdHTMLAttributes<HTMLTableCellElement>;\n\nconst TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(\n ({ className, ...props }, ref) => (\n <td\n ref={ref}\n className={cn('p-2 align-middle [&:has([role=checkbox])]:pr-0', className)}\n {...props}\n />\n ),\n);\nTableCell.displayName = 'TableCell';\n\nexport type TableCaptionProps = React.HTMLAttributes<HTMLTableCaptionElement>;\n\nconst TableCaption = React.forwardRef<HTMLTableCaptionElement, TableCaptionProps>(\n ({ className, ...props }, ref) => (\n <caption ref={ref} className={cn('mt-4 text-sm text-muted-foreground', className)} {...props} />\n ),\n);\nTableCaption.displayName = 'TableCaption';\n\nexport { Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableCaption };\n","'use client';\n\nimport * as React from 'react';\nimport * as SelectPrimitive from '@radix-ui/react-select';\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react';\nimport { cn } from '../lib/cn.js';\n\nconst Select = SelectPrimitive.Root;\nconst SelectGroup = SelectPrimitive.Group;\nconst SelectValue = SelectPrimitive.Value;\n\nexport type SelectTriggerProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>;\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n SelectTriggerProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n 'flex h-9 w-full items-center justify-between rounded-md border border-input bg-background text-foreground px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled [&>span]:line-clamp-1',\n className,\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <IconChevronDown className=\"h-4 w-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nexport type SelectScrollUpButtonProps = React.ComponentPropsWithoutRef<\n typeof SelectPrimitive.ScrollUpButton\n>;\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n SelectScrollUpButtonProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1', className)}\n {...props}\n >\n <IconChevronUp className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollUpButton>\n));\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;\n\nexport type SelectScrollDownButtonProps = React.ComponentPropsWithoutRef<\n typeof SelectPrimitive.ScrollDownButton\n>;\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n SelectScrollDownButtonProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn('flex cursor-default items-center justify-center py-1', className)}\n {...props}\n >\n <IconChevronDown className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollDownButton>\n));\nSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;\n\nexport type SelectContentProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>;\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(({ className, children, position = 'popper', ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md duration-[var(--duration-overlay)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n position === 'popper' &&\n 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',\n className,\n )}\n position={position}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n 'p-1',\n position === 'popper' &&\n 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nexport type SelectLabelProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>;\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}\n {...props}\n />\n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nexport type SelectItemProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>;\n\nconst SelectItem = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Item>, SelectItemProps>(\n ({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <IconCheck className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n ),\n);\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nexport type SelectSeparatorProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>;\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 my-1 h-px bg-muted', className)}\n {...props}\n />\n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n","import { loadLocale } from '../layout/language-storage.js';\n\n/**\n * Locale-aware Intl formatters for the built-in field renderers. They format\n * with the ACTIVE app locale, so the whole app follows the chosen language.\n *\n * ⚠️ **Pass the locale in.** Every caller inside a `BuroProvider` hands over\n * `useOptionalLocale()`, the registered locale code, which is used as the BCP-47\n * tag as it stands. The argument-less form is a FALLBACK for a renderer mounted\n * with no i18n facade at all (Storybook, an isolated test): it formats with the\n * locale `<LanguageSwitch>` persisted, and with `'en-US'` when none is stored.\n *\n * Formatters are cached per resolved BCP-47 tag: `Intl.*Format` construction is\n * comparatively expensive and these run in table cells, once per row per column.\n */\n/**\n * The BCP-47 tag to format with. A live locale code wins outright — a code\n * registered as `'es'`, `'fr'` or `'pt-BR'` is already a BCP-47 tag. Only when\n * there is no live locale (no provider) does the persisted choice decide, and\n * `'en-US'` is the last resort.\n */\nfunction localeTag(locale?: string): string {\n return locale || loadLocale() || 'en-US';\n}\n\nconst dateCache = new Map<string, Intl.DateTimeFormat>();\nconst dateTimeCache = new Map<string, Intl.DateTimeFormat>();\nconst numberCache = new Map<string, Intl.NumberFormat>();\n\nfunction cached<T>(cache: Map<string, T>, key: string, make: (tag: string) => T): T {\n const hit = cache.get(key);\n if (hit) return hit;\n // A locale code comes from the host's registry (or from storage), so a\n // malformed one is reachable — and `Intl.*Format`\n // answers a malformed tag with a RangeError, which in a table cell is a\n // blank screen. Fall back to the language-neutral `'en-US'` and cache THAT\n // under the bad key, so the throw happens once rather than once per cell.\n let made: T;\n try {\n made = make(key);\n } catch {\n made = make('en-US');\n }\n cache.set(key, made);\n return made;\n}\n\n/**\n * Date only, e.g. `Mar 21, 2026` (en) / `21. März 2026` (de).\n *\n * @param locale live locale code (`useOptionalLocale()`); omit only where no\n * i18n provider is mounted — see this module's docblock.\n */\nexport function formatDate(d: Date, locale?: string): string {\n const tag = localeTag(locale);\n return cached(\n dateCache,\n tag,\n (t) => new Intl.DateTimeFormat(t, { year: 'numeric', month: 'short', day: 'numeric' }),\n ).format(d);\n}\n\n/**\n * Date + time, used where a raw ISO timestamp would otherwise leak to the user.\n *\n * @param locale live locale code (`useOptionalLocale()`); omit only where no\n * i18n provider is mounted — see this module's docblock.\n */\nexport function formatDateTime(d: Date, locale?: string): string {\n const tag = localeTag(locale);\n return cached(\n dateTimeCache,\n tag,\n (t) =>\n new Intl.DateTimeFormat(t, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n }),\n ).format(d);\n}\n\n/**\n * Grouped number, e.g. `2,691` (en) / `2.691` (de).\n *\n * @param locale live locale code (`useOptionalLocale()`); omit only where no\n * i18n provider is mounted — see this module's docblock.\n */\nexport function formatNumber(n: number, locale?: string): string {\n const tag = localeTag(locale);\n return cached(numberCache, tag, (t) => new Intl.NumberFormat(t)).format(n);\n}\n","import { isFilterGroup, opArity } from '@burojs/core';\nimport type { FilterGroupRow, PortableValue } from '@burojs/core';\nimport type { FilterFieldSpec, FilterKind } from '@burojs/react';\n\n/**\n * Positional translator `describeRow` needs. Deliberately NOT\n * `useOptionalT`'s own `(key, options)` shape — this module has no React\n * dependency (pure function, reused by SP3 outside this component tree), so\n * its caller adapts whatever hook it has into this narrow shape instead of\n * `describeRow` importing a hook signature it doesn't use.\n */\nexport type DescribeRowTranslate = (\n key: string,\n params?: Record<string, unknown>,\n fallback?: string,\n) => string;\n\n/**\n * How many group levels `describeRow` will expand below the row it was\n * called on. A badge is a one-line summary, not a transcript of the whole\n * builder — third-level-and-deeper nesting collapses to `…` instead of\n * growing the badge without bound.\n */\nconst MAX_DESCRIBE_DEPTH = 2;\n\n/**\n * Resolve a SCALAR predicate value (never an array — see `describeValue`) to\n * display text, given the field's `FilterKind`. Supplied by the caller\n * (`FilterBadges`) so this module stays React-free while the badge — which\n * DOES have React, and can resolve an enum's localized label or a related\n * record's display name the way `EnumValueControl`/`RelationValueControl`\n * already do — gets to decide how a value renders. Returning `undefined`\n * (not yet resolved, or the caller declines to format this kind) falls back\n * to `String(value)`, same as when no `formatValue` is supplied at all.\n *\n * A field's declared `FilterFieldSpec.options` is resolved directly by\n * `describeValue` BEFORE `formatValue` is even consulted (see there) — it\n * needs no hook, no async fetch, and no `kind` branch of its own: the\n * label is already sitting in `options`, exactly like an enum/relation label\n * is once `formatValue` has resolved it, just without the wait.\n */\nexport type FormatValue = (kind: FilterKind, value: PortableValue) => string | undefined;\n\nfunction describeValue(\n value: PortableValue | undefined,\n t: DescribeRowTranslate,\n spec: FilterFieldSpec | undefined,\n formatValue: FormatValue | undefined,\n): string | null {\n if (value === undefined) return null;\n if (Array.isArray(value)) {\n return t('filters.selectedCount', { count: value.length }, `${value.length} selected`);\n }\n // `options` wins outright, independent of `kind` — this is the whole\n // point of the feature (a plain `string` field with a declared pick-list\n // still gets a friendly badge, not the machine value the picker exists to\n // keep the user from ever having to type). Rendered verbatim, same as the\n // picker itself — no i18n indirection, matching `FilterFieldRef.label`'s\n // own precedent.\n const optionLabel = spec?.options?.find((o) => o.value === String(value))?.label;\n if (optionLabel !== undefined) return optionLabel;\n if (spec && formatValue) {\n const formatted = formatValue(spec.kind, value);\n if (formatted !== undefined) return formatted;\n }\n return String(value);\n}\n\nfunction describeAt(\n row: FilterGroupRow,\n available: readonly FilterFieldSpec[],\n t: DescribeRowTranslate,\n depth: number,\n formatValue: FormatValue | undefined,\n): string {\n if (isFilterGroup(row)) {\n if (depth > MAX_DESCRIBE_DEPTH) return '…';\n const joinLabel = t(`filters.${row.join}`, undefined, row.join);\n const inner = row.rows\n .map((child) => describeAt(child, available, t, depth + 1, formatValue))\n .join(` ${joinLabel} `);\n return `(${inner})`;\n }\n\n // A saved view or fixed filter can reference a field the adopter later\n // dropped from the resource config (same \"ghost field\" case `FilterRow`\n // defends against) — fall back to the raw field name rather than\n // crashing or rendering a blank label.\n const spec = available.find((s) => s.field === row.field);\n const label = spec?.label ?? row.field;\n const opLabel = t(`filters.operators.${row.op}`, undefined, row.op);\n const valueText =\n opArity(row.op) === 'none' ? null : describeValue(row.value, t, spec, formatValue);\n return valueText === null ? `${label} ${opLabel}` : `${label} ${opLabel} ${valueText}`;\n}\n\n/**\n * Human-readable summary of one filter-group row: `<field label> <operator>\n * <value>` for a predicate, or its rows joined by the localized `and`/`or`\n * and wrapped in parentheses for a nested group. Pure and React-free so it\n * is unit-testable on its own and portable to SP3, which reuses it verbatim.\n *\n * Value formatting: a valueless operator (`isNull`/`isNotNull`) omits the\n * value segment entirely; an array value (an `in`/`notIn` predicate) renders\n * as `filters.selectedCount` (\"`n` selected\") instead of listing every\n * element; a scalar value is resolved through the optional `formatValue`\n * (enum label, related-record name — the caller's job, see {@link\n * FormatValue}) and falls back to `String(value)` when `formatValue` is\n * absent or declines to resolve that particular value.\n */\nexport function describeRow(\n row: FilterGroupRow,\n available: readonly FilterFieldSpec[],\n t: DescribeRowTranslate,\n formatValue?: FormatValue,\n): string {\n return describeAt(row, available, t, 1, formatValue);\n}\n\n/** One (enum name) / (relation target, id) pair `describeRow` will ask `formatValue` to resolve. */\nexport interface FormatRefs {\n readonly enumNames: readonly string[];\n readonly relationRefs: readonly {\n readonly to: string;\n readonly id: string;\n /** The target field the value is (`meta.by`); the id when absent. */\n readonly by?: string;\n }[];\n}\n\n/**\n * Walk every row of a filter group (all depths) and collect the distinct\n * enum names / relation `(to, id)` pairs a `formatValue` implementation\n * would need to resolve to render every badge without a raw value —\n * `describe-value-resolvers.tsx` mounts one hook-backed resolver per entry\n * this returns. Pure and React-free, like the rest of this module: the\n * traversal only needs `available` to map a field back to its `FilterKind`,\n * same as `describeAt` itself.\n *\n * Deliberately skips `undefined`/array values — a valueless operator has\n * nothing to resolve, and an `in`/`notIn` predicate already renders as\n * \"`n` selected\" rather than a list of resolved labels (see `describeRow`'s\n * doc), so there is nothing here worth fetching for it either.\n */\n/** The key of a relation value's label: by target, `by` field and value. */\nexport function relationLabelKey(to: string, by: string | undefined, value: string): string {\n return `${by === undefined ? to : `${to}#${by}`}::${value}`;\n}\n\nexport function collectFormatRefs(\n rows: readonly FilterGroupRow[],\n available: readonly FilterFieldSpec[],\n): FormatRefs {\n const enumNames = new Set<string>();\n const relationRefs = new Map<string, { to: string; id: string; by?: string }>();\n\n const walk = (nodes: readonly FilterGroupRow[]): void => {\n for (const row of nodes) {\n if (isFilterGroup(row)) {\n walk(row.rows);\n continue;\n }\n if (row.value === undefined || Array.isArray(row.value)) continue;\n const spec = available.find((s) => s.field === row.field);\n if (!spec) continue;\n if (spec.kind.kind === 'enum') {\n enumNames.add(spec.kind.enumName);\n } else if (spec.kind.kind === 'relation') {\n const id = String(row.value);\n const { to, by } = spec.kind;\n relationRefs.set(\n relationLabelKey(to, by, id),\n by === undefined ? { to, id } : { to, id, by },\n );\n }\n }\n };\n walk(rows);\n\n return { enumNames: Array.from(enumNames), relationRefs: Array.from(relationRefs.values()) };\n}\n","'use client';\nimport { enumValueText, resolveEnumLabel } from '@burojs/core';\nimport { useApp, useEnumValues, useI18nCaps, useRelatedRecord } from '@burojs/react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { useRowTitle } from '../../resource/use-row-title.js';\nimport { type FormatRefs, relationLabelKey } from './describe-row.js';\n\n/**\n * Resolves the badge-visible label for one enum name / relation id at a\n * time, via ordinary React hooks — `useEnumValues` and `useRelatedRecord`\n * are the SAME hooks `EnumValueControl`/`RelationValueControl` already use\n * to show a translated label / a related record's name instead of a raw\n * value, so an applied-filter badge (finding 8 of the SP2 final review,\n * \"badges print raw enum values and raw foreign keys\") stops being the one\n * place in the filter UI that still shows `status eq error` /\n * `location_id eq 501` instead of the resolved label.\n *\n * These render `null` — they exist purely to call a hook and report its\n * result upward, one per DISTINCT `(kind, ref)` pair `FilterBadges` found\n * in the applied group (`collectFormatRefs`, `describe-row.ts`). A dynamic\n * NUMBER of them mount/unmount as the filter set changes, which is\n * business-as-usual React (a keyed list of sibling elements) — the\n * alternative, calling a variable number of hooks inside ONE component,\n * would violate the rules of hooks outright.\n */\n\nexport interface EnumLabelResolverProps {\n readonly enumName: string;\n readonly onResolve: (enumName: string, labels: Record<string, string>) => void;\n}\n\nexport function EnumLabelResolver(props: EnumLabelResolverProps): null {\n const { enumName, onResolve } = props;\n const { values } = useEnumValues(enumName);\n const caps = useI18nCaps();\n const def = useApp().registry.enums.get(enumName);\n\n useEffect(() => {\n if (!values) return;\n const labels: Record<string, string> = {};\n for (const entry of values) {\n labels[entry.value] = resolveEnumLabel(\n caps,\n enumName,\n entry.value,\n enumValueText(def, entry.value) ?? entry.label,\n );\n }\n onResolve(enumName, labels);\n // `onResolve` is the parent's stable (useCallback, no-deps) setState\n // wrapper — including it would be correct either way, but omitting a\n // rapidly-changing prop from deps here would risk a stale closure the\n // NEXT time `values` itself does not change; it is intentionally not\n // memoized away.\n }, [values, enumName, caps, def, onResolve]);\n\n return null;\n}\n\nexport interface RelationLabelResolverProps {\n readonly to: string;\n readonly id: string;\n /** The target field `id` is a value of (`meta.by`); the id field when absent. */\n readonly by?: string;\n /** Called with the label's key (`relationLabelKey`) and the label. */\n readonly onResolve: (key: string, label: string) => void;\n}\n\nexport function RelationLabelResolver(props: RelationLabelResolverProps): null {\n const { to, id, by, onResolve } = props;\n const app = useApp();\n const targetDef = app.registry.get(to);\n const { record } = useRelatedRecord(to, id, by === undefined ? undefined : { by });\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n\n useEffect(() => {\n if (!record) return;\n onResolve(relationLabelKey(to, by, id), rowTitle(record));\n }, [record, to, by, id, rowTitle, onResolve]);\n\n return null;\n}\n\nexport interface FormatResolversProps {\n readonly refs: FormatRefs;\n readonly onEnumResolved: (enumName: string, labels: Record<string, string>) => void;\n readonly onRelationResolved: (key: string, label: string) => void;\n}\n\n/** Mounts one resolver per distinct ref `collectFormatRefs` found. */\nexport function FormatResolvers(props: FormatResolversProps): React.ReactElement {\n const { refs, onEnumResolved, onRelationResolved } = props;\n return (\n <>\n {refs.enumNames.map((name) => (\n <EnumLabelResolver key={`enum:${name}`} enumName={name} onResolve={onEnumResolved} />\n ))}\n {refs.relationRefs.map(({ to, id, by }) => (\n <RelationLabelResolver\n key={`relation:${relationLabelKey(to, by, id)}`}\n to={to}\n id={id}\n {...(by === undefined ? {} : { by })}\n onResolve={onRelationResolved}\n />\n ))}\n </>\n );\n}\n\nexport interface FormatValueMaps {\n readonly enumLabels: Record<string, Record<string, string>>;\n readonly relationLabels: Record<string, string>;\n readonly onEnumResolved: (enumName: string, labels: Record<string, string>) => void;\n readonly onRelationResolved: (key: string, label: string) => void;\n}\n\n/**\n * The two lookup maps `FilterBadges`' `formatValue` reads, plus the stable\n * (identity-per-mount, via `useCallback` with no deps) callbacks the\n * resolver components above report into. Factored out of `FilterBadges`\n * itself so that component stays about RENDERING badges, not about owning\n * this state.\n */\nexport function useFormatValueMaps(): FormatValueMaps {\n const [enumLabels, setEnumLabels] = useState<Record<string, Record<string, string>>>({});\n const [relationLabels, setRelationLabels] = useState<Record<string, string>>({});\n\n const onEnumResolved = useCallback((enumName: string, labels: Record<string, string>) => {\n setEnumLabels((prev) => (prev[enumName] === labels ? prev : { ...prev, [enumName]: labels }));\n }, []);\n\n const onRelationResolved = useCallback((key: string, label: string) => {\n setRelationLabels((prev) => (prev[key] === label ? prev : { ...prev, [key]: label }));\n }, []);\n\n return { enumLabels, relationLabels, onEnumResolved, onRelationResolved };\n}\n","import type { ErasedResourceHandle, FilterExpression, FilterPredicate } from '@burojs/core';\nimport type { FilterFieldSpec } from '@burojs/react';\n\ntype Expression = FilterExpression<ErasedResourceHandle>;\n\nexport function isFilterPredicateExpr(expr: Expression): expr is FilterPredicate {\n return 'field' in expr;\n}\n\n/**\n * OWNERSHIP RULE for the pinned slot (`filters.value`), shared by\n * `PinnedFilterControls` (which reads/writes the owned expression) and\n * `FilterBadges` (which has to know what is NOT owned, to badge it).\n *\n * A pinned control for field `f` owns the FIRST expression in `value` whose\n * `field === f` **and** whose `op` matches the operator the control itself\n * writes (`spec.operators[0]`). The operator check is load-bearing: a\n * pre-upgrade link or saved view can carry `{field: 'name', op:\n * 'startswith'}` from the deleted chip toolbar's own vocabulary, which is\n * NOT what a `contains`-writing pinned control for `name` would ever\n * produce. Adopting it anyway would mean the control's first edit silently\n * REWRITES the operator (the predicate visibly broadens/narrows) with\n * nothing on screen saying so — see SP2 final-review finding 4. Every other\n * expression on the same field — same rule as before — is left completely\n * alone: not read for display by the control, not overwritten, not removed.\n */\nexport function findOwnedIndex(\n value: readonly Expression[],\n spec: Pick<FilterFieldSpec, 'field' | 'operators'>,\n): number {\n const op = spec.operators[0] ?? 'eq';\n return value.findIndex(\n (expr) => isFilterPredicateExpr(expr) && expr.field === spec.field && expr.op === op,\n );\n}\n\n/**\n * Indices of every pinned-slot expression a `pinnedFields` control claims\n * (one claim per spec, via {@link findOwnedIndex}, applied in `pinnedFields`\n * order so two specs never compete for the SAME already-claimed index).\n * Whatever is left over — no control's operator matches it, or the field\n * isn't pinned at all — is an expression nobody owns: written by a\n * pre-upgrade link, a saved view, or `fixedFilters`, and otherwise invisible\n * now that the chip toolbar that used to render it is gone (finding 3).\n */\nexport function claimedPinnedIndices(\n value: readonly Expression[],\n pinnedFields: readonly FilterFieldSpec[],\n): ReadonlySet<number> {\n const claimed = new Set<number>();\n for (const spec of pinnedFields) {\n const op = spec.operators[0] ?? 'eq';\n const index = value.findIndex(\n (expr, i) =>\n !claimed.has(i) &&\n isFilterPredicateExpr(expr) &&\n expr.field === spec.field &&\n expr.op === op,\n );\n if (index >= 0) claimed.add(index);\n }\n return claimed;\n}\n","'use client';\nimport { isFilterGroup } from '@burojs/core';\nimport type {\n ErasedResourceHandle,\n FilterExpression,\n FilterGroup,\n FilterGroupRow,\n ResolvedFixedFilter,\n} from '@burojs/core';\nimport { useOptionalT } from '@burojs/react';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { IconX } from '@tabler/icons-react';\nimport { useMemo } from 'react';\nimport { formatDate, formatDateTime } from '../../fields/format.js';\nimport { Badge } from '../../primitives/badge.js';\nimport type { DescribeRowTranslate, FormatValue } from './describe-row.js';\nimport { collectFormatRefs, describeRow, relationLabelKey } from './describe-row.js';\nimport { FormatResolvers, useFormatValueMaps } from './describe-value-resolvers.js';\nimport type { DraftPath } from './draft.js';\nimport { claimedPinnedIndices, isFilterPredicateExpr } from './pinned-ownership.js';\n\ntype Expression = FilterExpression<ErasedResourceHandle>;\n\nexport interface FilterBadgesProps {\n /** The APPLIED group — badges never read the draft. */\n readonly group: FilterGroup;\n readonly setGroup: (next: FilterGroup) => void;\n readonly available: readonly FilterFieldSpec[];\n /** Call-site narrowing (`filters.fixed`); entries with a `label` render as undeletable badges. */\n readonly fixed: readonly ResolvedFixedFilter[];\n readonly testIdBase: string;\n /** Opens the panel focused on this top-level row — the badge body click. */\n readonly onOpenAt: (path: DraftPath) => void;\n /**\n * Fired AFTER `setGroup`, with the removed row's top-level index (in the\n * group as it stood BEFORE this removal), so a composing region can also\n * drop the same row from `FilterPanel`'s draft — see that component's\n * `externalRemoval` doc for why this is needed (without it, a badge\n * removed while the panel is open comes back the next time the user\n * presses \"Apply\") and why it resolves by `rowId` rather than reusing\n * this index directly.\n */\n readonly onRemoved?: (index: number) => void;\n /**\n * Fields declared as always-visible controls (`views.list.filters.pinned`)\n * — needed here ONLY to compute which pinned-slot expressions (below) a\n * control already owns, per `pinned-ownership.js`'s rule. Not otherwise\n * rendered by this component.\n */\n readonly pinnedFields?: readonly FilterFieldSpec[];\n /**\n * The PINNED slot (`filters.value`) — every pre-upgrade link and saved\n * view lands here, since it is where the deleted chip toolbar used to\n * write. Most of it is claimed by a `PinnedFilterControls` entry and\n * rendered there instead; whatever is left over (finding 3 of the SP2\n * final review) gets a badge here, since otherwise it is applied to the\n * query — filtering the list — with NOTHING on screen showing it, and no\n * way to remove it short of \"Clear all\" (which drops every other filter\n * too).\n */\n readonly pinnedValue?: readonly Expression[];\n readonly setPinnedValue?: (next: readonly Expression[]) => void;\n}\n\n/**\n * A key that moves WITH a row's content and position rather than pure array\n * index — there is no real identity at this level (unlike the draft's\n * `rowId`; the applied `FilterGroup` carries none). Harmless either way\n * since `Badge` here is stateless, but this reads better than a bare index\n * in a list that reorders as rows are removed.\n */\nfunction badgeKey(row: FilterGroupRow, index: number): string {\n if (isFilterGroup(row)) return `${index}:group:${row.join}`;\n return `${index}:${row.field}:${row.op}`;\n}\n\n/**\n * One badge per top-level row of the APPLIED filter group, plus one per\n * `filters.fixed` entry that carries a `label`, plus one per PINNED-slot\n * expression no `pinnedFields` control claims (finding 3, see `pinnedValue`\n * above).\n *\n * Group-row badges have two very different click targets:\n * - the × removes that row and calls `setGroup` IMMEDIATELY, bypassing the\n * panel's draft entirely — this is a deletion, not an edit, and making\n * the user re-open the panel and press \"Apply\" to confirm a removal they\n * already committed to would be a broken expectation;\n * - the body opens the panel with `focusPath` pointing at this row, for a\n * user who wants to ADJUST rather than remove it. It is a real\n * `<button>` (finding 6 of the SP2 final review — it used to be a plain\n * `<div onClick>`, reachable and activatable only by mouse), so it picks\n * up keyboard focus, Enter/Space activation, and a browser-native\n * accessible name (its own text — the `describeRow` summary — same as\n * any other button whose label IS its content) for free. It sits beside\n * the × as a SIBLING, not a nested/overlapping hit target, so activating\n * one never also triggers the other.\n *\n * Fixed-filter badges (labelled call-site narrowing) and orphan pinned-slot\n * badges both render with NO body affordance: fixed badges because the\n * user did not add them and cannot remove them; pinned-slot badges because\n * there is no builder row behind them to focus — only a working × to\n * remove that one expression from the pinned slot.\n */\nexport function FilterBadges(props: FilterBadgesProps): React.ReactElement {\n const {\n group,\n setGroup,\n available,\n fixed,\n testIdBase,\n onOpenAt,\n onRemoved,\n pinnedFields = [],\n pinnedValue = [],\n setPinnedValue,\n } = props;\n const t = useOptionalT();\n // `describeRow` stays React-free and passes `{ count }` as a param; the one\n // counted key it uses (`filters.selectedCount`) is a count family, so the\n // count goes to `t` as the count, where it also interpolates.\n const tt: DescribeRowTranslate = (key, params, fallback) =>\n t(key, {\n params,\n ...(typeof params?.count === 'number' ? { count: params.count } : {}),\n defaultValue: fallback,\n });\n const removeLabel = t('filters.removeRow', { defaultValue: 'Remove filter' });\n\n // Enum/relation label resolution (finding 8): `describeRow` stays\n // React-free, so the lookup maps and the hooks that fill them live here,\n // in the one caller that DOES have React. `collectFormatRefs` walks both\n // the applied group and the unclaimed pinned expressions (both render\n // through `describeRow` below) to find what needs resolving;\n // `FormatResolvers` mounts one hook-backed resolver per distinct ref.\n const { enumLabels, relationLabels, onEnumResolved, onRelationResolved } = useFormatValueMaps();\n const formatValue: FormatValue = (kind, value) => {\n if (kind.kind === 'enum') return enumLabels[kind.enumName]?.[String(value)];\n if (kind.kind === 'relation')\n return relationLabels[relationLabelKey(kind.to, kind.by, String(value))];\n // A temporal predicate stores machine text — a UTC ISO instant for a\n // date-time (`ScalarValueInput` resolves the picked local wall time to\n // one), a plain `YYYY-MM-DD` for a calendar date. Printed verbatim, the\n // badge hands the reader an ISO string in a zone that is not theirs,\n // directly beside the control they picked a local time in. Same\n // formatters the list cells and show panels use, so a filtered-on value\n // reads exactly like the column it filters.\n if (kind.kind === 'date') {\n const d = new Date(String(value));\n // Unparseable (a hand-edited URL, a half-typed value): the raw text is\n // more honest than \"Invalid Date\". `undefined` falls through to\n // `String(value)` in `describeValue`.\n if (Number.isNaN(d.getTime())) return undefined;\n return kind.time ? formatDateTime(d) : formatDate(d);\n }\n return undefined;\n };\n\n const orphanPinned = useMemo(() => {\n const claimed = claimedPinnedIndices(pinnedValue, pinnedFields);\n return pinnedValue\n .map((expr, index) => ({ expr, index }))\n .filter(({ expr, index }) => !claimed.has(index) && isFilterPredicateExpr(expr));\n }, [pinnedValue, pinnedFields]);\n\n const refs = useMemo(\n () =>\n collectFormatRefs(\n [...group.rows, ...orphanPinned.map(({ expr }) => expr as FilterGroupRow)],\n available,\n ),\n [group, orphanPinned, available],\n );\n\n const handleRemove = (index: number) => {\n setGroup({ ...group, rows: group.rows.filter((_, i) => i !== index) });\n onRemoved?.(index);\n };\n\n const handleRemovePinned = (index: number) => {\n setPinnedValue?.(pinnedValue.filter((_, i) => i !== index));\n };\n\n const labelledFixed = fixed.filter((entry) => entry.label !== undefined);\n\n return (\n <div className=\"flex flex-wrap items-center gap-1\">\n <FormatResolvers\n refs={refs}\n onEnumResolved={onEnumResolved}\n onRelationResolved={onRelationResolved}\n />\n {group.rows.map((row, index) => (\n <Badge key={badgeKey(row, index)} variant=\"secondary\" className=\"gap-1 py-1 pr-1\">\n <button\n type=\"button\"\n data-testid={`${testIdBase}:badge:${index}`}\n onClick={() => onOpenAt([index])}\n className=\"max-w-full cursor-pointer truncate bg-transparent p-0 text-left\"\n >\n {describeRow(row, available, tt, formatValue)}\n </button>\n <button\n type=\"button\"\n data-testid={`${testIdBase}:badge:${index}:remove`}\n aria-label={removeLabel}\n onClick={() => handleRemove(index)}\n className=\"rounded-sm hover:bg-accent\"\n >\n <IconX className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n {labelledFixed.map((entry, index) => (\n <Badge\n key={`fixed-${index}`}\n variant=\"outline\"\n data-testid={`${testIdBase}:badge:fixed:${index}`}\n >\n {entry.label}\n </Badge>\n ))}\n {orphanPinned.map(({ expr, index }) => (\n <Badge key={`pinned-${index}`} variant=\"secondary\" className=\"gap-1 py-1\">\n <span data-testid={`${testIdBase}:badge:pinned:${index}`}>\n {describeRow(expr as FilterGroupRow, available, tt, formatValue)}\n </span>\n <button\n type=\"button\"\n data-testid={`${testIdBase}:badge:pinned:${index}:remove`}\n aria-label={removeLabel}\n onClick={() => handleRemovePinned(index)}\n className=\"rounded-sm hover:bg-accent\"\n >\n <IconX className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n </div>\n );\n}\n","import {\n convertValueForOp,\n EMPTY_FILTER_GROUP,\n isCompletePredicate,\n isFilterGroup,\n opArity,\n} from '@burojs/core';\nimport type { FilterGroup, FilterGroupRow, FilterPredicate, PortableValue } from '@burojs/core';\nimport type { FilterFieldSpec } from '@burojs/react';\n\n/**\n * The panel's working copy of a filter group.\n *\n * It differs from `FilterGroup` in exactly one way: every node carries a\n * `rowId`. Builder rows are indistinguishable by content — two rows reading\n * `name contains \"a\"` are a legitimate configuration — so removal, editing\n * and React's reconciliation all need an identity the content cannot give.\n *\n * An index-based key will not do: removing the middle row hands its key to\n * the next one, which moves the caret and the input's contents onto a\n * neighbour mid-typing.\n *\n * `rowId` never leaves this module's output: `fromDraft` strips it, and the\n * applied state, the URL, saved views and the provider request never see it.\n */\nexport interface DraftRow {\n readonly rowId: string;\n readonly field: string;\n readonly op: string;\n readonly value?: PortableValue;\n}\n\nexport interface DraftGroup {\n readonly rowId: string;\n readonly join: 'and' | 'or';\n readonly rows: readonly DraftNode[];\n}\n\nexport type DraftNode = DraftRow | DraftGroup;\n\n/** Index path to a node. `[]` is the root group; `[2, 0]` is row 0 of group 2. */\nexport type DraftPath = readonly number[];\n\n/**\n * How deep the UI lets a user nest. The codec allows far more\n * (`maxFilterDepth` is 16) because the URL is written by more than this\n * panel; the cap here is a readability decision, not a storage limit.\n */\nexport const MAX_UI_DEPTH = 3;\n\nlet counter = 0;\n\n/** Monotonic, module-local, never persisted — see `DraftRow.rowId`. */\nfunction nextId(): string {\n counter += 1;\n return `r${counter}`;\n}\n\nexport function isDraftGroup(node: DraftNode): node is DraftGroup {\n return Object.hasOwn(node, 'rows');\n}\n\nexport function toDraft(group: FilterGroup): DraftGroup {\n return {\n rowId: nextId(),\n join: group.join,\n rows: group.rows.map(toDraftNode),\n };\n}\n\nfunction toDraftNode(row: FilterGroupRow): DraftNode {\n if (isFilterGroup(row)) return toDraft(row);\n const predicate = row as FilterPredicate;\n return {\n rowId: nextId(),\n field: predicate.field,\n op: predicate.op,\n ...(Object.hasOwn(predicate, 'value') ? { value: predicate.value } : {}),\n };\n}\n\nexport function fromDraft(draft: DraftGroup): FilterGroup {\n return {\n join: draft.join,\n rows: draft.rows.map((node) =>\n isDraftGroup(node)\n ? fromDraft(node)\n : ({\n field: node.field,\n op: node.op,\n ...(Object.hasOwn(node, 'value') ? { value: node.value } : {}),\n } as FilterGroupRow),\n ),\n };\n}\n\n/**\n * Drop incomplete rows (a field/operator picked but no value typed yet —\n * see `isCompletePredicate`, `@burojs/core`) and any group that collapses\n * to nothing once its own incomplete rows are gone, from a `FilterGroup`.\n *\n * This is the `FilterGroup`-shaped counterpart of `groupToExpression`'s own\n * pruning: same completeness rule, imported from core rather than\n * re-implemented, so the panel's Apply, the applied-row count on the\n * \"Filters\" button, and the AST the data provider actually runs cannot\n * drift out of sync (SP2 final-review finding 5 — before this existed,\n * `fromDraft`'s output went straight to `setGroup` unpruned, so an\n * abandoned \"Add filter\" click survived Apply: it consumed the node\n * budget, rendered as `Title contains ` — a trailing space, no value — on\n * its own badge, and `groupToExpression` silently dropped it at fetch time\n * regardless, so the badge was actively lying about what the query ran).\n *\n * Deliberately NOT folded into `fromDraft` itself: `fromDraft` is a pure\n * structural draft→group lowering with no opinion on completeness (its own\n * tests round-trip an incomplete row verbatim), reused wherever the draft's\n * shape needs converting regardless of whether the caller wants pruning.\n * Apply (`FilterPanel`) is the one call site that wants BOTH steps, in\n * that order.\n */\nexport function pruneIncompleteRows(group: FilterGroup): FilterGroup {\n const rows: FilterGroupRow[] = [];\n for (const row of group.rows) {\n if (isFilterGroup(row)) {\n const pruned = pruneIncompleteRows(row);\n if (pruned.rows.length > 0) rows.push(pruned);\n } else if (isCompletePredicate(row)) {\n rows.push(row);\n }\n }\n return { join: group.join, rows };\n}\n\n/**\n * Rebuild the spine down to `path`, replacing the addressed group with\n * `replace`'s result. Returns the ORIGINAL draft when the path does not\n * resolve to a group — a stale path from a concurrent edit must be a no-op,\n * not a crash and not a silent write to the wrong node.\n */\nfunction mapGroupAt(\n draft: DraftGroup,\n path: DraftPath,\n replace: (group: DraftGroup) => DraftGroup,\n): DraftGroup {\n if (path.length === 0) return replace(draft);\n const [head, ...rest] = path;\n const child = draft.rows[head!];\n if (child === undefined || !isDraftGroup(child)) return draft;\n const next = mapGroupAt(child, rest, replace);\n if (next === child) return draft;\n const rows = draft.rows.slice();\n rows[head!] = next;\n return { ...draft, rows };\n}\n\n/**\n * How deep the group ADDRESSED BY `path` sits (root `[]` is depth 1).\n * Exported so callers that need to gate on the real depth (e.g. hiding the\n * builder's \"+ Group\" affordance) read it from one place instead of\n * re-deriving `path.length + 1` by hand — a copy that's trivially correct\n * today but has no mechanism keeping it in sync with this one.\n */\nexport function depthOf(path: DraftPath): number {\n return path.length + 1;\n}\n\n/** Default value for an operator based on its arity. */\nfunction defaultValueForOp(op: string): PortableValue | undefined {\n const arity = opArity(op);\n if (arity === 'none') return undefined;\n if (arity === 'multi') return [];\n return '';\n}\n\nexport function addRow(\n draft: DraftGroup,\n path: DraftPath,\n available: readonly FilterFieldSpec[],\n): DraftGroup {\n const spec = available[0];\n if (!spec) return draft;\n const op = spec.operators[0] ?? 'eq';\n const value = defaultValueForOp(op);\n const row: DraftRow = {\n rowId: nextId(),\n field: spec.field,\n op,\n ...(value === undefined ? {} : { value }),\n };\n return mapGroupAt(draft, path, (group) => ({ ...group, rows: [...group.rows, row] }));\n}\n\nexport function addGroup(draft: DraftGroup, path: DraftPath): DraftGroup {\n if (depthOf(path) >= MAX_UI_DEPTH) return draft;\n const group: DraftGroup = { rowId: nextId(), join: 'and', rows: [] };\n return mapGroupAt(draft, path, (parent) => ({ ...parent, rows: [...parent.rows, group] }));\n}\n\nexport function removeAt(draft: DraftGroup, path: DraftPath): DraftGroup {\n if (path.length === 0) return draft;\n const parentPath = path.slice(0, -1);\n const index = path[path.length - 1]!;\n return mapGroupAt(draft, parentPath, (group) => {\n if (group.rows[index] === undefined) return group;\n const rows = group.rows.slice();\n rows.splice(index, 1);\n return { ...group, rows };\n });\n}\n\nexport function setJoin(draft: DraftGroup, path: DraftPath, join: 'and' | 'or'): DraftGroup {\n return mapGroupAt(draft, path, (group) => (group.join === join ? group : { ...group, join }));\n}\n\nexport interface RowPatch {\n readonly field?: string;\n readonly op?: string;\n readonly value?: PortableValue;\n}\n\n/**\n * Apply a patch to one row.\n *\n * Two rules the callers rely on:\n * - changing the OPERATOR carries the value across arities\n * (`convertValueForOp`), so switching `in` → `eq` keeps the first pick\n * instead of clearing three of them;\n * - changing the FIELD resets the value, because a value chosen for one\n * column rarely means anything in another (an enum member, a related\n * record's id). The caller passes the new field's default operator in the\n * same patch.\n */\nexport function updateRow(draft: DraftGroup, path: DraftPath, patch: RowPatch): DraftGroup {\n if (path.length === 0) return draft;\n const parentPath = path.slice(0, -1);\n const index = path[path.length - 1]!;\n return mapGroupAt(draft, parentPath, (group) => {\n const current = group.rows[index];\n if (current === undefined || isDraftGroup(current)) return group;\n\n const fieldChanged = patch.field !== undefined && patch.field !== current.field;\n const op = patch.op ?? current.op;\n let value: PortableValue | undefined;\n if (fieldChanged) {\n value = defaultValueForOp(op);\n } else if (patch.op !== undefined && patch.op !== current.op) {\n value = convertValueForOp(current.value, current.op, patch.op);\n } else {\n value = Object.hasOwn(patch, 'value') ? patch.value : current.value;\n }\n\n const next: DraftRow = {\n rowId: current.rowId,\n field: patch.field ?? current.field,\n op,\n ...(value === undefined ? {} : { value }),\n };\n const rows = group.rows.slice();\n rows[index] = next;\n return { ...group, rows };\n });\n}\n\nexport const EMPTY_DRAFT: () => DraftGroup = () => toDraft(EMPTY_FILTER_GROUP);\n","import type { PortableValue } from '@burojs/core';\nimport { useState } from 'react';\n\nexport interface UseSelectableValueOptions {\n readonly value: PortableValue | undefined;\n readonly arity: 'single' | 'multi';\n readonly onChange: (value: PortableValue | undefined) => void;\n /**\n * What a `single` selection emits, given the picked token. Defaults to\n * the token itself (enum values and relation ids are already the right\n * shape) — `BooleanValueControl` overrides this to turn the token\n * `'true'`/`'false'` into a real `boolean`.\n */\n readonly emitSingle?: (token: string) => PortableValue;\n}\n\nexport interface SelectableValueControl {\n readonly open: boolean;\n readonly setOpen: (open: boolean) => void;\n readonly selected: ReadonlySet<string>;\n /** Toggle (multi) or pick-and-close (single) one option. */\n readonly select: (token: string) => void;\n /** Labels of what's picked, joined by `, `, or `placeholder` when nothing is. */\n readonly triggerLabel: (labelOf: (token: string) => string, placeholder: string) => string;\n /**\n * Empty the selection to `undefined` and close the popover. The ONLY way\n * to empty a `single`-arity control back out: unlike a text input (delete\n * to blank) or a `multi` control (remove every chip), a select-and-close\n * control has no in-band gesture for \"nothing\" once something is picked\n * (SP2 final-review finding 9's `:clear` item — before this, a pinned\n * SINGLE-arity control, e.g. a boolean pinned filter, could be toggled\n * forever but never actually removed). Works for `multi` too (equivalent\n * to removing every chip at once), so callers don't need to branch on\n * arity to decide whether to offer it.\n */\n readonly clear: () => void;\n}\n\n/**\n * Shared \"value + arity → selected set → toggle → trigger label\" plumbing\n * for the three popover-driven value controls (`boolean`, `enum`,\n * `relation`). All three used to duplicate this near-identically, and that\n * duplication is exactly how they drifted on the empty-selection contract:\n * a `multi` selection must empty to `undefined`, never `[]`, so a filter\n * row with nothing picked reads as \"incomplete\" rather than \"active but\n * matching nothing\" — this hook is now the one place that rule lives, so\n * a fourth control (or SP3's relation picker dialog, reusing these) cannot\n * re-drift by copy-pasting a slightly different version.\n *\n * `ScalarValueInput`'s `multi` mode does NOT use this hook — it grows its\n * array via free-text chips rather than toggling a fixed option list — but\n * it follows the same empty-selection rule by hand for the same reason.\n */\nexport function useSelectableValueControl(\n options: UseSelectableValueOptions,\n): SelectableValueControl {\n const { value, arity, onChange, emitSingle = (token) => token } = options;\n const [open, setOpen] = useState(false);\n\n const selected: ReadonlySet<string> =\n arity === 'multi'\n ? new Set(Array.isArray(value) ? value.map(String) : [])\n : new Set(value === undefined || value === null ? [] : [String(value)]);\n\n const select = (token: string): void => {\n if (arity === 'single') {\n onChange(emitSingle(token));\n setOpen(false);\n return;\n }\n const next = new Set(selected);\n if (next.has(token)) next.delete(token);\n else next.add(token);\n onChange(next.size === 0 ? undefined : Array.from(next));\n };\n\n const triggerLabel = (labelOf: (token: string) => string, placeholder: string): string => {\n const picked = Array.from(selected);\n return picked.length === 0 ? placeholder : picked.map(labelOf).join(', ');\n };\n\n const clear = (): void => {\n onChange(undefined);\n setOpen(false);\n };\n\n return { open, setOpen, selected, select, triggerLabel, clear };\n}\n","'use client';\nimport { useOptionalT } from '@burojs/react';\nimport { IconCheck, IconX } from '@tabler/icons-react';\nimport { cn } from '../../../lib/cn.js';\nimport { Button } from '../../../primitives/button.js';\nimport { Command, CommandGroup, CommandItem, CommandList } from '../../../primitives/command.js';\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../primitives/popover.js';\nimport type { FilterValueControlProps } from './filter-value-control.js';\nimport { useSelectableValueControl } from './use-selectable-value-control.js';\n\nexport interface BooleanValueControlProps extends FilterValueControlProps {\n readonly arity: 'single' | 'multi';\n}\n\nconst OPTIONS = ['true', 'false'] as const;\n\n/**\n * Yes/No popover for boolean fields.\n *\n * Arity changes the SHAPE of what leaves via `onChange`, not just how many\n * are picked: `single` (`eq`/`ne`) emits a real boolean (`true`/`false`) —\n * via `emitSingle` — so the provider gets `{op: 'eq', value: true}`;\n * `multi` (`in`/`notIn`) emits an array of the string tokens\n * `'true'`/`'false'`, matching the string-token convention the URL codec\n * already decodes back to real booleans. The selected-set /\n * toggle / trigger-label plumbing lives in `useSelectableValueControl`,\n * shared with `EnumValueControl` and `RelationValueControl`.\n */\nexport function BooleanValueControl(props: BooleanValueControlProps): React.ReactElement {\n const { value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;\n const t = useOptionalT();\n\n // SP2 final-review finding 7: include the field's own label when given.\n const ariaLabel = fieldLabel\n ? t('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : t('filters.value', { defaultValue: 'Value' });\n const yesLabel = t('boolean.yes', { defaultValue: 'Yes' });\n const noLabel = t('boolean.no', { defaultValue: 'No' });\n const labelOf = (token: string): string => (token === 'true' ? yesLabel : noLabel);\n const clearLabel = t('filters.clear', { defaultValue: 'Clear' });\n\n const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({\n value,\n arity,\n onChange,\n emitSingle: (token) => token === 'true',\n });\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n autoFocus={autoFocus}\n data-testid={testId}\n aria-label={ariaLabel}\n className={cn('h-8 justify-between', fill && 'w-full')}\n >\n {triggerLabel(labelOf, ariaLabel)}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[140px] p-0\" align=\"start\">\n <Command>\n <CommandList>\n <CommandGroup>\n {arity === 'single' && selected.size > 0 && (\n <CommandItem\n data-testid={`${testId}:clear`}\n value=\"__clear__\"\n onSelect={() => clear()}\n >\n <IconX className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n {clearLabel}\n </CommandItem>\n )}\n {OPTIONS.map((token) => {\n const isSelected = selected.has(token);\n return (\n <CommandItem\n key={token}\n value={labelOf(token)}\n data-testid={`${testId}:option:${token}`}\n onSelect={() => select(token)}\n >\n <IconCheck\n className={cn('mr-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')}\n aria-hidden=\"true\"\n />\n {labelOf(token)}\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n","'use client';\nimport type { PortableValue } from '@burojs/core';\nimport { useOptionalT } from '@burojs/react';\nimport { IconCheck, IconLoader2, IconX } from '@tabler/icons-react';\nimport { cn } from '../../../lib/cn.js';\nimport { Button } from '../../../primitives/button.js';\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../../../primitives/command.js';\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../primitives/popover.js';\nimport { useSelectableValueControl } from './use-selectable-value-control.js';\n\nexport interface SelectListOption {\n readonly value: string;\n readonly label: string;\n}\n\nexport interface SelectListValueControlProps {\n readonly value: PortableValue | undefined;\n readonly onChange: (value: PortableValue | undefined) => void;\n readonly testId: string;\n readonly arity: 'single' | 'multi';\n readonly options: readonly SelectListOption[];\n /** Same meaning as `EnumValueControl`'s: keeps the trigger mounted while true. */\n readonly isLoading?: boolean;\n readonly autoFocus?: boolean;\n readonly fieldLabel?: string;\n /**\n * Resolves ANY raw token to its display label — including one absent from\n * `options` entirely (e.g. a preselected value from a saved view or a URL\n * param naming an enum member that is no longer in the currently loaded\n * set). Defaults to a plain lookup within `options`, which is the right\n * (and only sensible) answer for a CLOSED, adopter-declared list\n * (`FilterFieldRef.options`) — there is nothing beyond the raw value to\n * fall back to for a token outside that list.\n *\n * `EnumValueControl` overrides this: its ORIGINAL (pre-extraction)\n * behaviour called `resolveEnumLabel` unconditionally, for every raw\n * value, not only ones present in the currently loaded `values` — an\n * i18n-bundle lookup (`enums.<ns>.<raw>.label`) can resolve a translated\n * label for a value the enum no longer advertises, and silently losing\n * that (falling back straight to the raw machine value) for exactly the\n * \"value is stale\" case would be a real behaviour regression, not a\n * refactor. Passing its own `labelFor` here is what keeps that unconditional\n * resolution intact after the popover UI moved into this shared component.\n */\n readonly labelFor?: (raw: string) => string;\n /** Same meaning as `FilterValueControlProps.fill` — see its doc. */\n readonly fill?: boolean;\n}\n\n/**\n * The cmdk-popover picker shared by every value control backed by a closed,\n * known list of `{value, label}` pairs — a registered enum (`EnumValueControl`)\n * and a plain field's adopter-declared `FilterFieldRef.options`\n * (dispatched directly from `FilterValueControl`). Extracted out of what used\n * to be `EnumValueControl`'s own body so `options` could reuse the exact\n * popover/list/clear/loading plumbing instead of forking a second one —\n * `EnumValueControl` is now a thin wrapper that resolves `enumName` into\n * `options` (via `useEnumValues` + the i18n content model) and renders this.\n *\n * `options[].label` is rendered AS GIVEN — this component does no i18n\n * resolution of its own; a caller backed by adopter-authored text (e.g.\n * `FilterFieldRef.options`) or by a registered enum's resolved labels\n * decides that upstream. `onChange` always carries the raw `value`, never\n * the label.\n *\n * The trigger (`data-testid={testId}`) stays MOUNTED while `isLoading` — see\n * `EnumValueControl`'s doc for why (SP2 final-review finding 9).\n */\nexport function SelectListValueControl(props: SelectListValueControlProps): React.ReactElement {\n const {\n options,\n value,\n onChange,\n testId,\n arity,\n autoFocus,\n fieldLabel,\n isLoading = false,\n } = props;\n const t = useOptionalT();\n\n const ariaLabel = fieldLabel\n ? t('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : t('filters.value', { defaultValue: 'Value' });\n const loadingLabel = t('table.loading', { defaultValue: 'Loading…' });\n const clearLabel = t('filters.clear', { defaultValue: 'Clear' });\n const labelFor =\n props.labelFor ?? ((raw: string): string => options.find((o) => o.value === raw)?.label ?? raw);\n\n const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({\n value,\n arity,\n onChange,\n });\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n autoFocus={autoFocus}\n data-testid={testId}\n aria-label={ariaLabel}\n className={cn('h-8 justify-between truncate', props.fill ? 'w-full' : 'max-w-[200px]')}\n >\n <span className=\"truncate\">\n {isLoading ? loadingLabel : triggerLabel(labelFor, ariaLabel)}\n </span>\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[220px] p-0\" align=\"start\">\n <Command>\n <CommandInput placeholder={ariaLabel} data-testid={`${testId}:search`} />\n <CommandList>\n {isLoading && (\n <div\n className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\"\n data-testid={`${testId}:loading`}\n >\n <IconLoader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {loadingLabel}\n </div>\n )}\n {!isLoading && (\n <CommandEmpty>\n {t('relation.noResults', { defaultValue: 'No results.' })}\n </CommandEmpty>\n )}\n {!isLoading && (\n <CommandGroup>\n {arity === 'single' && selected.size > 0 && (\n <CommandItem\n data-testid={`${testId}:clear`}\n value=\"__clear__\"\n onSelect={() => clear()}\n >\n <IconX className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n {clearLabel}\n </CommandItem>\n )}\n {options.map((entry) => {\n const isSelected = selected.has(entry.value);\n const label = labelFor(entry.value);\n return (\n <CommandItem\n key={entry.value}\n value={label}\n data-testid={`${testId}:option:${entry.value}`}\n onSelect={() => select(entry.value)}\n >\n <IconCheck\n className={cn('mr-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')}\n aria-hidden=\"true\"\n />\n {label}\n </CommandItem>\n );\n })}\n </CommandGroup>\n )}\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n","'use client';\nimport { enumValueText, resolveEnumLabel } from '@burojs/core';\nimport { useApp, useEnumValues, useI18nCaps } from '@burojs/react';\nimport type { FilterValueControlProps } from './filter-value-control.js';\nimport { SelectListValueControl } from './select-list-value-control.js';\n\nexport interface EnumValueControlProps extends FilterValueControlProps {\n readonly enumName: string;\n readonly arity: 'single' | 'multi';\n}\n\n/**\n * Enum value control: resolves `enumName`'s registered members\n * (`useEnumValues`, same resolver as `<EnumBadge>`/`<EnumPicker>`) and their\n * labels through the i18n content model (`resolveEnumLabel` + the inline\n * label), then renders `SelectListValueControl` — the\n * cmdk-popover picker this control shares with a plain field's\n * `FilterFieldRef.options` (dispatched directly by `FilterValueControl`).\n * `onChange` always carries the raw enum VALUE, never the label.\n *\n * This file used to own the popover/list/clear/loading JSX itself; it moved\n * to `SelectListValueControl` so `options` could reuse it verbatim instead\n * of forking a second picker. The JSX moved, but this control's own label\n * resolution did NOT change: `labelFor` below is passed through explicitly\n * (rather than relying on `SelectListValueControl`'s default lookup-in-\n * `options`) so it keeps resolving UNCONDITIONALLY for any raw value —\n * including one that is NOT in the currently loaded `values` (a saved view\n * or URL param naming an enum member that was since removed, or any load\n * that simply doesn't include the current value). The bundle\n * (`enums.<ns>.<raw>.label`) can still carry a translated label for that\n * value; falling straight back to the raw machine value in that case would\n * be a real regression, not a refactor.\n */\nexport function EnumValueControl(props: EnumValueControlProps): React.ReactElement {\n const { enumName, value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;\n const caps = useI18nCaps();\n const def = useApp().registry.enums.get(enumName);\n const { values, isLoading } = useEnumValues(enumName);\n\n const labelFor = (raw: string): string =>\n resolveEnumLabel(\n caps,\n enumName,\n raw,\n enumValueText(def, raw) ?? values?.find((v) => v.value === raw)?.label,\n );\n\n // The list to enumerate in the popover — `label` here is never actually\n // read (the control always calls `labelFor` below, both for the list and\n // for the trigger/selected value), but `SelectListOption.label` is\n // required, so it carries the raw value as a harmless placeholder.\n const options = (values ?? []).map((entry) => ({ value: entry.value, label: entry.value }));\n\n return (\n <SelectListValueControl\n value={value}\n onChange={onChange}\n testId={testId}\n arity={arity}\n autoFocus={autoFocus}\n fieldLabel={fieldLabel}\n options={options}\n labelFor={labelFor}\n isLoading={isLoading}\n fill={fill}\n />\n );\n}\n","'use client';\nimport { useMemo } from 'react';\nimport { useApp, useList } from '@burojs/react';\nimport { useRowTitle } from '../resource/use-row-title.js';\n\n/**\n * Resolve the display labels for the currently-selected relation ids so\n * the relation value control's trigger can show \"Alias, Beta\" instead of\n * bare FK ids. Backs `RelationValueControl`'s fallback labels\n * (`list-kit/filter-builder/values/relation-value-control.tsx`).\n *\n * Uses the `useList` HOOK (legal here — this is a hook, called\n * unconditionally at the top of `RelationValueControl`) to fetch the\n * target rows whose id is `in` the selected set. The query is disabled\n * when nothing is selected so an empty control costs zero requests.\n */\nexport function useRelatedLabels(to: string, ids: string[], by?: string): Record<string, string> {\n const app = useApp();\n const targetDef = app.registry.get(to);\n // Without `by`, a value is the target's id field (`data.idField ?? 'id'`), as the picker's.\n const idField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const field = by ?? (typeof idField === 'string' && idField.length > 0 ? idField : 'id');\n const query = useList(to, {\n pagination: { kind: 'offset', page: 1, pageSize: Math.min(Math.max(1, ids.length), 100) },\n ...(ids.length ? { filter: { field, op: 'in', value: ids } } : {}),\n // Titles of chosen keys: never narrowed by a scope.\n scope: 'none',\n enabled: ids.length > 0,\n });\n return useMemo(() => {\n const out: Record<string, string> = {};\n for (const row of (query.data?.data ?? []) as Array<\n { id: unknown } & Record<string, unknown>\n >) {\n const key = row[field];\n if (key !== null && key !== undefined) out[String(key)] = rowTitle(row);\n }\n return out;\n }, [query.data, rowTitle, field]);\n}\n","'use client';\nimport { useApp, useList, useListAccess, useOptionalT } from '@burojs/react';\nimport { IconCheck, IconLoader2, IconX } from '@tabler/icons-react';\nimport { useMemo, useState } from 'react';\nimport { cn } from '../../../lib/cn.js';\nimport { Button } from '../../../primitives/button.js';\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../../../primitives/command.js';\nimport { Popover, PopoverContent, PopoverTrigger } from '../../../primitives/popover.js';\nimport { narrowSearchDecl } from '../../../fields/search-access.js';\nimport { useRowTitle } from '../../../resource/use-row-title.js';\nimport { useRelatedLabels } from '../../use-related-labels.js';\nimport type { FilterValueControlProps } from './filter-value-control.js';\nimport { useSelectableValueControl } from './use-selectable-value-control.js';\n\nexport interface RelationValueControlProps extends FilterValueControlProps {\n readonly to: string;\n /** The target field the column holds (`meta.by`): options write its values, not ids. */\n readonly by?: string;\n readonly arity: 'single' | 'multi';\n}\n\ntype Row = { id: unknown } & Record<string, unknown>;\n\n/**\n * Relation value control: a cmdk popover searching `to` via `useList`\n * (`contains` on the target's display field), same query shape the old\n * chip toolbar's relation chip used. Options and the trigger show the\n * related record's display NAME (`useRowTitle`: the id while the field is hidden); `onChange` always\n * carries the record's id (string for `single`, string array for `multi`)\n * — never the name — which is what makes this control reusable as the\n * value half of SP3's relation picker dialog. The selected-set / toggle /\n * trigger-label plumbing lives in `useSelectableValueControl`, shared with\n * `BooleanValueControl` and `EnumValueControl`.\n *\n * The query is gated on the popover being open so a row full of relation\n * filters doesn't fire N requests on mount; `useRelatedLabels` resolves the\n * label for an already-selected id even while the popover (and its own\n * query) is closed.\n */\nexport function RelationValueControl(props: RelationValueControlProps): React.ReactElement {\n const { to, by, value, onChange, testId, arity, autoFocus, fieldLabel } = props;\n const app = useApp();\n const t = useOptionalT();\n const targetDef = app.registry.get(to);\n const displayField = targetDef?.displayField ?? 'name';\n const [search, setSearch] = useState('');\n\n const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({\n value,\n arity,\n onChange,\n });\n\n // Titles and the search follow the target's field access: a hidden display field titles\n // nothing (the id is shown) and is never searched; none left means no search box.\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const access = useListAccess(to);\n const searchable = useMemo(\n () =>\n access.status === 'ready' &&\n narrowSearchDecl({ fields: [displayField] }, (name) => access.fieldVisible(name)) !==\n undefined,\n [access, displayField],\n );\n const listQuery = useList(to, {\n pagination: { kind: 'offset', page: 1, pageSize: 10 },\n ...(searchable && search.trim()\n ? { filter: { field: displayField, op: 'contains', value: search.trim() } }\n : {}),\n // A list filter offers what the session's scope selection shows.\n scope: 'session',\n enabled: open && access.status === 'ready',\n });\n const rows = (listQuery.data?.data ?? []) as Row[];\n\n // Fallback label source for ids not present in the currently-loaded page\n // (e.g. selected before the popover was ever opened, or filtered out of\n // the current search).\n const fallbackLabels = useRelatedLabels(to, Array.from(selected), by);\n // What an option writes: `row[by]` for a `by` relation, else the target's id field.\n const idField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const valueField = by ?? (typeof idField === 'string' && idField.length > 0 ? idField : 'id');\n const keyOf = (row: Row): string | undefined => {\n const key = row[valueField];\n return key === null || key === undefined || key === '' ? undefined : String(key);\n };\n\n const labelFor = (id: string): string => {\n const fromRows = rows.find((r) => keyOf(r) === id);\n if (fromRows) return rowTitle(fromRows);\n return fallbackLabels[id] ?? id;\n };\n\n // SP2 final-review finding 7: include the field's own label when given.\n const ariaLabel = fieldLabel\n ? t('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : t('filters.value', { defaultValue: 'Value' });\n const clearLabel = t('filters.clear', { defaultValue: 'Clear' });\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n autoFocus={autoFocus}\n data-testid={testId}\n aria-label={ariaLabel}\n className={cn('h-8 justify-between truncate', props.fill ? 'w-full' : 'max-w-[220px]')}\n >\n <span className=\"truncate\">{triggerLabel(labelFor, ariaLabel)}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[240px] p-0\" align=\"start\">\n {/* shouldFilter={false}: filtering happens server-side via useList's\n `contains` operator; cmdk still drives keyboard nav + selection. */}\n <Command shouldFilter={false}>\n {searchable && (\n <CommandInput\n value={search}\n onValueChange={setSearch}\n placeholder={ariaLabel}\n data-testid={`${testId}:search`}\n />\n )}\n <CommandList>\n {listQuery.isLoading && (\n <div\n className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\"\n data-testid={`${testId}:loading`}\n >\n <IconLoader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {t('table.loading', { defaultValue: 'Loading…' })}\n </div>\n )}\n {!listQuery.isLoading && rows.length === 0 && (\n <CommandEmpty>\n {t('relation.noResults', { defaultValue: 'No results.' })}\n </CommandEmpty>\n )}\n {!listQuery.isLoading && arity === 'single' && selected.size > 0 && (\n <CommandItem\n data-testid={`${testId}:clear`}\n value=\"__clear__\"\n onSelect={() => clear()}\n >\n <IconX className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n {clearLabel}\n </CommandItem>\n )}\n {!listQuery.isLoading &&\n rows.map((row) => {\n const id = keyOf(row);\n if (id === undefined) return null;\n const label = rowTitle(row);\n const isSelected = selected.has(id);\n return (\n <CommandItem\n key={id}\n value={`${label} ${id}`}\n data-testid={`${testId}:option:${id}`}\n onSelect={() => select(id)}\n >\n <IconCheck\n className={cn('mr-2 h-4 w-4', isSelected ? 'opacity-100' : 'opacity-0')}\n aria-hidden=\"true\"\n />\n <span className=\"truncate\">{label}</span>\n </CommandItem>\n );\n })}\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n","'use client';\nimport type { PortableValue } from '@burojs/core';\nimport { useOptionalT } from '@burojs/react';\nimport { IconX } from '@tabler/icons-react';\nimport { useEffect, useRef, useState } from 'react';\nimport { cn } from '../../../lib/cn.js';\nimport { Badge } from '../../../primitives/badge.js';\nimport { Input } from '../../../primitives/input.js';\nimport type { FilterValueControlProps } from './filter-value-control.js';\n\nexport interface ScalarValueInputProps extends FilterValueControlProps {\n readonly arity: 'single' | 'multi';\n}\n\ntype Kind = FilterValueControlProps['kind'];\n\n/** A date-TIME field (`f.dateTime()`), as opposed to a plain calendar date. */\nfunction isDateTime(kind: Kind): boolean {\n return kind.kind === 'date' && kind.time === true;\n}\n\n/**\n * `string` → text box, `number` → number spinner, `date` → date picker,\n * date-TIME → `datetime-local`. The last one is why this takes the whole\n * `kind` and not just its discriminant: a timestamp asked about with a\n * date-only box can only ever express midnight, which makes every `eq`\n * unsatisfiable and every range off by up to a day.\n */\nfunction inputTypeFor(kind: Kind): string {\n switch (kind.kind) {\n case 'number':\n return 'number';\n case 'date':\n return isDateTime(kind) ? 'datetime-local' : 'date';\n default:\n return 'text';\n }\n}\n\n/**\n * The stored value as this control's input element wants to see it.\n *\n * Everything except a date-time is verbatim. A date-time is stored as a UTC\n * ISO instant (see `parseOne`) but a `datetime-local` box speaks LOCAL wall\n * time with no zone and a strict `YYYY-MM-DDTHH:mm` shape — handing it the\n * ISO string leaves the box blank, so the value has to be projected into\n * the viewer's own zone on the way in and back out again on the way out.\n * An unparseable stored value renders as empty rather than as garbage the\n * box would silently reject anyway.\n */\nfunction toText(kind: Kind, value: PortableValue | undefined): string {\n if (value === undefined || value === null || value === '') return '';\n if (!isDateTime(kind)) return String(value);\n const d = new Date(String(value));\n if (Number.isNaN(d.getTime())) return '';\n const pad = (n: number): string => String(n).padStart(2, '0');\n return (\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` +\n `T${pad(d.getHours())}:${pad(d.getMinutes())}`\n );\n}\n\n/**\n * Parses one raw input string into the shape the field kind expects. Empty\n * text is \"no value\".\n *\n * A date-time commits as a UTC ISO instant: the providers write a\n * predicate's value into the query untouched (`@burojs/pocketbase`'s\n * `translateFilter`, `@burojs/hasura`'s `filtersToWhere`), so whatever this\n * returns IS what the backend compares against a `timestamptz`. The raw\n * `datetime-local` text carries no zone at all, and a backend reading it as\n * UTC would shift every result by the viewer's offset — so the local wall\n * time the user picked is resolved to a real instant here, once, where the\n * zone is actually known.\n *\n * A plain calendar date is deliberately NOT converted: `2026-08-17` through\n * `toISOString()` lands on the 16th for anyone east of UTC.\n */\nfunction parseOne(kind: Kind, raw: string): PortableValue | undefined {\n if (raw === '') return undefined;\n if (kind.kind === 'number') {\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n }\n if (isDateTime(kind)) {\n // No offset in the text → parsed as local, which is what the box means.\n const d = new Date(raw);\n return Number.isNaN(d.getTime()) ? undefined : d.toISOString();\n }\n return raw;\n}\n\n/**\n * Text/number/date value input for scalar field kinds (`string`, `number`,\n * `date`). Arity comes from the operator, not the field:\n * - `single` is a debounced plain input (`debounceMs`, default 300ms —\n * `0` commits synchronously), ported from `DataTableOperatorFilter`'s\n * value half.\n * - `multi` (in/notIn) turns the same input into a chip composer — Enter\n * commits the typed text as a chip, the chip's × removes it, and the\n * array of chip strings is what `onChange` carries.\n */\nexport function ScalarValueInput(props: ScalarValueInputProps): React.ReactElement {\n const t = useOptionalT();\n const inputType = inputTypeFor(props.kind);\n // SP2 final-review finding 7: include the field's own label in the\n // accessible name when the caller has one to give — see\n // `FilterValueControlProps.fieldLabel`'s doc.\n const ariaLabel = props.fieldLabel\n ? t('filters.valueFor', {\n defaultValue: `Value for ${props.fieldLabel}`,\n params: { field: props.fieldLabel },\n })\n : t('filters.value', { defaultValue: 'Value' });\n\n if (props.arity === 'multi') {\n return <ScalarMultiInput {...props} inputType={inputType} ariaLabel={ariaLabel} />;\n }\n return <ScalarSingleInput {...props} inputType={inputType} ariaLabel={ariaLabel} />;\n}\n\nfunction ScalarSingleInput(\n props: ScalarValueInputProps & { inputType: string; ariaLabel: string },\n): React.ReactElement {\n const {\n value,\n onChange,\n kind,\n testId,\n autoFocus,\n inputType,\n ariaLabel,\n debounceMs = 300,\n } = props;\n const storedText = toText(kind, value);\n const [text, setText] = useState(storedText);\n // Tracks our own last commit so an external value change (URL nav, clear-all,\n // saved view) is adopted, but the echo of our own debounced commit is not —\n // that would clobber characters typed during the debounce window.\n const lastPushedRef = useRef(storedText);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Raw text of a keystroke that has NOT been committed via `onChange` yet —\n // set on every keystroke, cleared the instant its debounced commit fires.\n // Read on unmount to flush a still-pending edit instead of silently\n // dropping it (the row gets removed, or the field switches, inside the\n // 300ms debounce window).\n const pendingRawRef = useRef<string | null>(null);\n // A ref, refreshed every render, so both the debounce timeout AND the\n // unmount flush always call the LATEST `onChange`/`kind` — the timeout\n // closure and the unmount cleanup (empty deps) would otherwise be stuck\n // with whatever they captured when they were created.\n const commitRef = useRef<(raw: string) => void>(() => {});\n useEffect(() => {\n commitRef.current = (raw: string) => {\n const parsed = parseOne(kind, raw);\n // The echo guard has to live in the SAME domain as `storedText`, which\n // is the stored value projected back through `toText`. For a date-time\n // those two are not the same string as `raw` (local text in, UTC ISO\n // stored), so remembering `raw` here would make the very next render\n // read its own commit as an external change and reset the box\n // mid-typing. Round-tripping the parsed value is exact for every kind.\n lastPushedRef.current = toText(kind, parsed);\n pendingRawRef.current = null;\n onChange(parsed);\n };\n });\n\n useEffect(() => {\n if (storedText !== lastPushedRef.current) {\n setText(storedText);\n lastPushedRef.current = storedText;\n // A genuine EXTERNAL change (URL nav, \"clear all\", applying a saved\n // view) pre-empts whatever the user was mid-typing — cancel the\n // pending debounce so it neither fires later NOR gets resurrected by\n // the unmount flush below, which would otherwise re-send stale text\n // the outside world already overrode.\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n pendingRawRef.current = null;\n }\n }, [storedText]);\n\n useEffect(\n () => () => {\n if (timerRef.current) clearTimeout(timerRef.current);\n // Flush what was typed but not yet committed, instead of dropping it.\n // clearTimeout above guarantees the debounced callback itself never\n // also fires, so this can only run once. The sync effect above already\n // cleared `pendingRawRef` if an external change pre-empted the typing,\n // so this only ever flushes a genuinely still-pending user edit.\n if (pendingRawRef.current !== null) commitRef.current(pendingRawRef.current);\n },\n [],\n );\n\n const handleChange = (raw: string) => {\n setText(raw);\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n // `debounceMs <= 0` (the filter-builder panel's draft, `FilterPanel`\n // passes `0`) commits synchronously instead of scheduling — there is no\n // debounce window to flush-on-unmount for, and no race with anything\n // reading this row's value right after the keystroke (e.g. \"Apply\").\n if (debounceMs <= 0) {\n pendingRawRef.current = null;\n commitRef.current(raw);\n return;\n }\n pendingRawRef.current = raw;\n timerRef.current = setTimeout(() => commitRef.current(raw), debounceMs);\n };\n\n return (\n <Input\n type={inputType}\n value={text}\n autoFocus={autoFocus}\n aria-label={ariaLabel}\n placeholder={props.placeholder}\n data-testid={testId}\n onChange={(event) => handleChange(event.target.value)}\n className={cn('h-8', props.fill ? 'w-full' : 'w-[150px]')}\n />\n );\n}\n\nfunction ScalarMultiInput(\n props: ScalarValueInputProps & { inputType: string; ariaLabel: string },\n): React.ReactElement {\n const { value, onChange, kind, testId, autoFocus, inputType, ariaLabel } = props;\n const chips = Array.isArray(value) ? value.map((v) => String(v)) : [];\n const [draft, setDraft] = useState('');\n\n const addChip = () => {\n const parsed = parseOne(kind, draft.trim());\n if (parsed === undefined) return;\n const text = String(parsed);\n setDraft('');\n if (chips.includes(text)) return;\n onChange([...chips, text]);\n };\n\n // Emptying to `undefined` (not `[]`) matches the rule every other control\n // on this contract follows (`useSelectableValueControl`) — a `multi`\n // selection with nothing left is \"no filter\", not \"matches nothing\".\n const removeChip = (chip: string) => {\n const next = chips.filter((c) => c !== chip);\n onChange(next.length === 0 ? undefined : next);\n };\n\n return (\n <div className={cn('flex flex-wrap items-center gap-1', props.fill && 'w-full')}>\n {chips.map((chip) => (\n <Badge\n key={chip}\n variant=\"secondary\"\n data-testid={`${testId}:chip:${chip}`}\n className=\"gap-1 rounded-sm px-1 font-normal\"\n >\n {chip}\n <button\n type=\"button\"\n data-testid={`${testId}:chip:${chip}:remove`}\n aria-label={`Remove ${chip}`}\n onClick={() => removeChip(chip)}\n className=\"rounded-sm hover:bg-accent\"\n >\n <IconX className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n </Badge>\n ))}\n <Input\n type={inputType}\n value={draft}\n autoFocus={autoFocus}\n aria-label={ariaLabel}\n placeholder={props.placeholder}\n data-testid={testId}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== 'Enter') return;\n event.preventDefault();\n addChip();\n }}\n // Chips share the row with the composer, so `fill` gives it a floor\n // to grow from rather than the whole track — otherwise a single chip\n // pushes the input onto its own line.\n className={cn('h-8', props.fill ? 'min-w-[8rem] flex-1' : 'w-[120px]')}\n />\n </div>\n );\n}\n","'use client';\nimport { opArity } from '@burojs/core';\nimport type { PortableValue } from '@burojs/core';\nimport type { FilterKind } from '@burojs/react';\nimport { BooleanValueControl } from './boolean-value-control.js';\nimport { EnumValueControl } from './enum-value-control.js';\nimport { RelationValueControl } from './relation-value-control.js';\nimport { ScalarValueInput } from './scalar-value-input.js';\nimport { SelectListValueControl } from './select-list-value-control.js';\n\nexport interface FilterValueControlProps {\n readonly kind: FilterKind;\n readonly op: string;\n readonly value: PortableValue | undefined;\n readonly onChange: (value: PortableValue | undefined) => void;\n /** `data-testid` prefix; inner parts append `:option:<v>`, `:search`, `:clear`. */\n readonly testId: string;\n /**\n * The field's own label (`FilterFieldSpec.label`), threaded through so\n * this control's accessible name can say WHICH field it is a value for —\n * \"Value for Status\", not a bare \"Value\" indistinguishable from every\n * other row's value control and, worse, from every OTHER pinned control\n * in the toolbar (SP2 final-review finding 7). Optional and additive: a\n * caller with no field label in hand (or a standalone/test mount) still\n * gets the plain generic label every existing test asserts on.\n */\n readonly fieldLabel?: string;\n /**\n * Visible hint inside a text/number box (`ScalarValueInput`). Only a caller\n * that shows the control with nothing naming its field beside it passes one:\n * the pinned toolbar (`PinnedFilterControls`) gives the field's label, so a\n * pinned text filter is not an unlabelled empty box (REF-44). A builder row\n * names its field in its own field picker and passes none.\n */\n readonly placeholder?: string;\n readonly autoFocus?: boolean;\n /**\n * Delay (ms) before a scalar single-value edit (`ScalarValueInput`'s\n * text/number/date box) commits upward. Only that control debounces —\n * enum/relation/boolean commit on selection already, so they ignore this.\n * Defaults to 300 (the legacy chip UX: don't spam the URL/provider on\n * every keystroke). Pass `0` for a purely local draft — the filter-builder\n * panel (`FilterPanel`) does, since its draft is in-memory React state,\n * not a query param, so committing every keystroke is cheap and correct;\n * a pinned control (`PinnedFilterControls`), which writes straight to the\n * URL/provider, keeps the default.\n */\n readonly debounceMs?: number;\n /**\n * `FilterFieldSpec.options` (`@burojs/react`), threaded straight through\n * from `FilterFieldRef.options` (`@burojs/core`). When present AND\n * non-empty, THIS wins over the `kind`-derived dispatch below — a picker\n * over exactly these value/label pairs, reusing `EnumValueControl`'s own\n * popover (`SelectListValueControl`) rather than a free-text box,\n * regardless of `kind`. `kind` itself is untouched by `options`: a plain\n * `string` field with `options` still categorises (and renders its list\n * column) as a `string`, never as `enum` — see `FilterFieldRef.options`'s\n * own doc for why that distinction is the entire point.\n *\n * An EMPTY array falls through to the `kind`-derived dispatch exactly as\n * `undefined` does — a picker with nothing to pick from is strictly worse\n * than the field's ordinary control, not a valid (if degenerate) state.\n */\n readonly options?: readonly { readonly value: string; readonly label: string }[];\n /**\n * Stretch to the width of whatever contains this control, instead of the\n * intrinsic width each kind picks for itself.\n *\n * The builder's rows (`FilterRow`) need it: their value column is one\n * shared flex track, and a control that sizes itself leaves every row's\n * remove button at a different x — which is exactly what made a panel with\n * more than two rows look shattered. The pinned toolbar\n * (`PinnedFilterControls`) must NOT have it: those controls sit inline\n * among other toolbar items with no column to fill, and stretching them\n * there would be the same bug with the roles reversed. So the width comes\n * from the CALLER — the only place that knows whether there is a column —\n * rather than from a blanket `w-full` on the controls themselves.\n */\n readonly fill?: boolean;\n}\n\n/**\n * The value half of a builder row.\n *\n * Arity comes from the OPERATOR, the input type from the FIELD — the two\n * are independent, which is why `in` on a string field is a chip list while\n * `contains` on the same field is a text box, and why `isNull` renders\n * nothing at all rather than a disabled input.\n *\n * Deliberately free of TanStack: these controls replace chips that read and\n * wrote through `column.getFilterValue()`, and that coupling is exactly what\n * kept them out of the relation picker (SP3).\n */\nexport function FilterValueControl(props: FilterValueControlProps): React.ReactElement | null {\n const arity = opArity(props.op);\n if (arity === 'none') return null;\n // `length` (not a bare truthiness check on the array itself): an EMPTY\n // `options` array is truthy but has nothing to pick from — falling\n // through to the kind-derived dispatch below is strictly more useful\n // than rendering a picker whose popover can never show a single row.\n if (props.options && props.options.length > 0) {\n return (\n <SelectListValueControl\n value={props.value}\n onChange={props.onChange}\n testId={props.testId}\n arity={arity}\n autoFocus={props.autoFocus}\n fieldLabel={props.fieldLabel}\n options={props.options}\n fill={props.fill}\n />\n );\n }\n switch (props.kind.kind) {\n case 'enum':\n return <EnumValueControl {...props} enumName={props.kind.enumName} arity={arity} />;\n /**\n * `f.enum(['a','b'])` — inline members, no registry entry, therefore no\n * labels and no loader. `SelectListValueControl` is the same popover\n * `EnumValueControl` renders once it has resolved its list, so the\n * control an adopter sees is identical; only the label text differs, and\n * it differs the same way the COLUMN already does for such a field\n * (raw member values). Passing this through `EnumValueControl` was not\n * an option: that control's entire job is the registry lookup.\n *\n * An adopter who wants labels here has both existing routes, unchanged:\n * register the enum and declare `f.string({ enum: '<name>' })`, or\n * supply `views.list.filters.fields: [{ field, options }]`, which wins\n * over this dispatch above.\n */\n case 'enum-values':\n return (\n <SelectListValueControl\n value={props.value}\n onChange={props.onChange}\n testId={props.testId}\n arity={arity}\n autoFocus={props.autoFocus}\n fieldLabel={props.fieldLabel}\n options={props.kind.values.map((entry) => ({ value: entry, label: entry }))}\n />\n );\n case 'relation':\n return (\n <RelationValueControl\n {...props}\n to={props.kind.to}\n {...(props.kind.by === undefined ? {} : { by: props.kind.by })}\n arity={arity}\n />\n );\n case 'boolean':\n return <BooleanValueControl {...props} arity={arity} />;\n default:\n return <ScalarValueInput {...props} arity={arity} />;\n }\n}\n","'use client';\nimport type { PortableValue } from '@burojs/core';\nimport { useOptionalT } from '@burojs/react';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from '../../lib/cn.js';\nimport { Button } from '../../primitives/button.js';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '../../primitives/select.js';\nimport type { DraftPath, DraftRow, RowPatch } from './draft.js';\nimport { FilterValueControl } from './values/filter-value-control.js';\n\n/**\n * The row's own track sizing, and the reason it is spelled out here rather\n * than left to each control.\n *\n * Every row of a group renders these three tracks with the same\n * constraints, so they resolve to the same widths and the field / operator /\n * value / × columns line up down the whole panel. Before this, each part\n * sized itself — a 160px field select, a 150px operator select, and a value\n * control whose width depended on its KIND (150px input, up-to-220px\n * relation trigger, intrinsic boolean trigger) — so no two rows agreed on\n * where anything started, and the × landed wherever the value happened to\n * end.\n *\n * `basis-*` with `shrink` rather than a fixed `w-*`: a nested group indents\n * its children, so at depth 3 there is materially less room than at depth 1.\n * Fixed widths would overflow the panel there; these compress instead, and —\n * because every row in a group compresses by the same amount — stay aligned\n * while doing it. `min-w-0` is what permits the compression at all.\n *\n * The row itself takes no width of its own: it is a grid item in its group's\n * content track (`FilterGroupEditor`), which stretches it to that track.\n */\nconst ROW_CLASS = 'flex min-w-0 items-center gap-2';\nconst FIELD_CLASS = 'h-8 min-w-0 shrink basis-40';\nconst OPERATOR_CLASS = 'h-8 min-w-0 shrink basis-[9.5rem]';\n\nexport interface FilterRowProps {\n readonly row: DraftRow;\n readonly path: DraftPath;\n readonly available: readonly FilterFieldSpec[];\n readonly onPatch: (path: DraftPath, patch: RowPatch) => void;\n readonly onRemove: (path: DraftPath) => void;\n readonly testIdBase: string;\n /** Forwarded to `FilterValueControl` — see its doc. */\n readonly debounceMs?: number;\n}\n\n/**\n * One predicate row: field / operator / value, plus a remove button.\n *\n * Purely controlled — every interaction is translated into a `RowPatch` (or\n * a plain removal) and handed to the caller via `onPatch`/`onRemove`. No\n * mutation of `row` happens here; `updateRow`/`removeAt` (Task 3) own that.\n */\nexport function FilterRow(props: FilterRowProps): React.ReactElement {\n const { row, path, available, onPatch, onRemove, testIdBase, debounceMs } = props;\n const t = useOptionalT();\n const pathStr = path.join('-');\n const base = `${testIdBase}:row:${pathStr}`;\n const spec = available.find((s) => s.field === row.field);\n\n const removeLabel = t('filters.removeRow', { defaultValue: 'Remove filter' });\n\n // The saved view's field was dropped from the adopter's config after the\n // fact. Silently discarding the row would lose the user's intent; render\n // it disabled (nothing sensible to show for kind/operators) with a\n // working remove button instead of crashing or vanishing.\n if (!spec) {\n return (\n <div className={ROW_CLASS}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled\n data-testid={`${base}:field`}\n className={cn(FIELD_CLASS, 'justify-start truncate opacity-60')}\n >\n {row.field}\n </Button>\n {/* Keeps the × in the same column as every other row's, even though\n this row has no operator or value to put between them. */}\n <div className=\"flex-1\" />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={removeLabel}\n data-testid={`${base}:remove`}\n onClick={() => onRemove(path)}\n className=\"shrink-0\"\n >\n <IconX className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n );\n }\n\n const fieldTestId = `${base}:field`;\n const opTestId = `${base}:op`;\n\n const handleFieldChange = (field: string) => {\n // One patch, not two: the row must never render, even for a frame, with\n // an operator that doesn't belong to the newly selected field.\n const nextSpec = available.find((s) => s.field === field);\n const op = nextSpec?.operators[0] ?? row.op;\n onPatch(path, { field, op });\n };\n const handleOpChange = (op: string) => onPatch(path, { op });\n const handleValueChange = (value: PortableValue | undefined) => onPatch(path, { value });\n\n // SP2 final-review finding 7: the field/operator selects had NO\n // accessible name of their own — a screen reader announced two identical\n // unlabelled comboboxes per row (times however many rows). `SelectValue`\n // already shows the picked label visually; `aria-label` gives the\n // TRIGGER itself a name independent of what happens to be selected.\n const fieldAriaLabel = t('filters.field', { defaultValue: 'Field' });\n const operatorAriaLabel = t('filters.operator', { defaultValue: 'Operator' });\n\n return (\n <div className={ROW_CLASS}>\n <Select value={row.field} onValueChange={handleFieldChange}>\n <SelectTrigger\n data-testid={fieldTestId}\n aria-label={fieldAriaLabel}\n className={FIELD_CLASS}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {available.map((s) => (\n <SelectItem\n key={s.field}\n value={s.field}\n data-testid={`${fieldTestId}:option:${s.field}`}\n >\n {s.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={row.op} onValueChange={handleOpChange}>\n <SelectTrigger\n data-testid={opTestId}\n aria-label={operatorAriaLabel}\n className={OPERATOR_CLASS}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {spec.operators.map((op) => (\n <SelectItem key={op} value={op} data-testid={`${opTestId}:option:${op}`}>\n {t(`filters.operators.${op}`, { defaultValue: op })}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {/* The value control's own width is deliberately given up here (`fill`)\n — see `FilterValueControlProps.fill`. `min-w-0` lets this track\n actually shrink: a flex item's default `min-width: auto` refuses to\n go below its content, which is what let a long relation label push\n the × out of line. */}\n <div className=\"min-w-0 flex-1\">\n <FilterValueControl\n kind={spec.kind}\n op={row.op}\n value={row.value}\n onChange={handleValueChange}\n testId={`${base}:value`}\n debounceMs={debounceMs}\n fieldLabel={spec.label}\n options={spec.options}\n fill\n />\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n aria-label={removeLabel}\n data-testid={`${base}:remove`}\n onClick={() => onRemove(path)}\n className=\"shrink-0\"\n >\n <IconX className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n );\n}\n","'use client';\nimport { useOptionalT } from '@burojs/react';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { IconFolderPlus, IconPlus, IconX } from '@tabler/icons-react';\nimport { Fragment } from 'react';\nimport { cn } from '../../lib/cn.js';\nimport { Button } from '../../primitives/button.js';\nimport {\n MAX_UI_DEPTH,\n addGroup,\n addRow,\n depthOf,\n isDraftGroup,\n removeAt,\n setJoin,\n updateRow,\n} from './draft.js';\nimport type { DraftGroup, DraftNode, DraftPath, RowPatch } from './draft.js';\nimport { FilterRow } from './filter-row.js';\n\nexport interface FilterGroupEditorProps {\n readonly group: DraftGroup;\n readonly path: DraftPath;\n readonly available: readonly FilterFieldSpec[];\n readonly onChange: (next: DraftGroup) => void;\n readonly testIdBase: string;\n /** Root group has no remove button; nested ones do. */\n readonly onRemove?: (path: DraftPath) => void;\n /** Forwarded to every row's `FilterValueControl` — see its doc. */\n readonly debounceMs?: number;\n}\n\n/**\n * The two join words, in a stable order — the toggle renders BOTH so its\n * width is the wider of them regardless of which is in effect (see the\n * comment at the render site).\n */\nconst JOIN_VALUES = ['and', 'or'] as const;\n\n/**\n * The group's own layout: a two-track grid, connector then content.\n *\n * The connector — \"Where\", the join toggle, the repeated join word — has to\n * occupy a COLUMN, not just whatever each row's own word happens to\n * measure. They do not measure alike (\"Where\" against a bare \"and\", in\n * some languages a single letter), and letting each size itself started\n * every row at a different x, which is what made a filter set of more than\n * two rows read as scattered fragments rather than a list.\n *\n * `auto` for that track, rather than a width: the column comes out exactly\n * as wide as the widest connector actually rendered in this group, and\n * identically so for every row in it. A hard-coded width would have to be a\n * guess about the longest translation of \"Where\" any adopter might ship —\n * generous enough for a language nobody in the room reads, and therefore\n * dead space in every language they do. It was 5.5rem while the join was a\n * `<Select>` (a 90px trigger for a choice between two three-letter words),\n * then 4rem once that became a text toggle; `auto` retires the number.\n *\n * `minmax(0, 1fr)` for the content track, NOT a bare `1fr`: a grid track's\n * default minimum is `auto`, which refuses to shrink below its content, so\n * a long relation label in a row's value control would push the whole row\n * past the panel instead of letting the row compress (see `FilterRow`).\n */\nconst GROUP_CLASS = 'grid grid-cols-[auto_minmax(0,1fr)] items-start gap-2';\n/** Connector cell: right-aligned against the field column it introduces. */\nconst CONNECTOR_CLASS = 'pt-1.5 text-right text-sm text-muted-foreground';\n\n/**\n * Public entry point. `group`/`onChange` here are, by construction, the\n * TRUE root of the draft tree and its setter — this is the only call site\n * that can honestly say that, so it is the only place `addGroupAt` gets\n * built. Every recursive descent from here on stays inside\n * `GroupEditorInner`, which threads that closure down unchanged so a\n * \"+ Group\" click at any depth still reaches `addGroup` with a genuine\n * absolute path against the real root (see `GroupEditorInner`'s doc).\n */\nexport function FilterGroupEditor(props: FilterGroupEditorProps): React.ReactElement {\n const { group, onChange } = props;\n const addGroupAt = (targetPath: DraftPath) => onChange(addGroup(group, targetPath));\n return <GroupEditorInner {...props} addGroupAt={addGroupAt} />;\n}\n\ninterface GroupEditorInnerProps extends FilterGroupEditorProps {\n /**\n * Calls `addGroup` against the TRUE root with `targetPath` as a genuine\n * absolute path, so `addGroup`'s own `depthOf` guard is the thing\n * enforcing the depth cap — not a second, hand-maintained copy of it.\n * A nested `GroupEditorInner` cannot build this itself: its own `group`\n * prop is only its local subtree, which carries no memory of how deep it\n * actually sits, so `addGroup(thatSubtree, [])` would always see\n * `depthOf([]) === 1` regardless of real nesting. Threaded down from the\n * root unchanged instead.\n */\n readonly addGroupAt: (path: DraftPath) => void;\n}\n\n/**\n * One group of the filter builder, rendered recursively for nested groups.\n *\n * `group` is this node's OWN subtree, not the whole draft — every mutation\n * BELOW is expressed as a call into Task 3's pure functions with a path\n * RELATIVE to `group` itself (`[]` for the group, `[i]` for its child `i`);\n * `addGroup` is the one exception, routed through `addGroupAt` instead (see\n * its doc). A nested group's own `onChange` therefore reports only its own\n * next value; folding that into this level's `rows` array and forwarding\n * the result to *this* level's `onChange` is what threads an edit up to the\n * `useState` that ultimately owns the draft — plain recursive composition,\n * not a re-implementation of any drafted mutation. It preserves reference\n * identity when the child reports no real change, matching the guarantee\n * `mapGroupAt` (`draft.ts`) already gives every OTHER call in this file.\n *\n * `path` is the ABSOLUTE path from the true root and is used for\n * `data-testid`s, the depth cap, and as the address handed to `addGroupAt` —\n * it plays no part in addressing the LOCAL `group`.\n */\nfunction GroupEditorInner(props: GroupEditorInnerProps): React.ReactElement {\n const { group, path, available, onChange, testIdBase, onRemove, addGroupAt, debounceMs } = props;\n const t = useOptionalT();\n const pathStr = path.join('-');\n const base = `${testIdBase}:group:${pathStr}`;\n const canNestGroup = depthOf(path) < MAX_UI_DEPTH;\n\n const joinLabel = (join: 'and' | 'or'): string => t(`filters.${join}`, { defaultValue: join });\n\n // Shared across every child row/group at this level: the child always\n // echoes back its OWN absolute `path`, so the local index is simply that\n // path's last segment — one handler suffices instead of one closure per\n // row.\n const handlePatch = (rowPath: DraftPath, patch: RowPatch) => {\n const index = rowPath[rowPath.length - 1];\n if (index === undefined) return;\n onChange(updateRow(group, [index], patch));\n };\n const handleRemove = (nodePath: DraftPath) => {\n const index = nodePath[nodePath.length - 1];\n if (index === undefined) return;\n onChange(removeAt(group, [index]));\n };\n // Nested groups report only their own next value (no path), so folding\n // it into `group.rows` needs the index captured per-child. Bail out\n // without touching `group`/calling `onChange` at all when the child is\n // referentially unchanged (e.g. it re-selected its own current join) —\n // otherwise a no-op edit deep in the tree would still manufacture a new\n // object at every level up to the root, defeating the identity\n // `mapGroupAt` works to preserve.\n const handleChildGroupChange = (index: number, next: DraftGroup) => {\n if (next === group.rows[index]) return;\n const rows = group.rows.slice();\n rows[index] = next;\n onChange({ ...group, rows });\n };\n const handleAddRow = () => onChange(addRow(group, [], available));\n const handleAddGroup = () => addGroupAt(path);\n const handleSetJoin = (join: 'and' | 'or') => onChange(setJoin(group, [], join));\n /** The value the toggle switches TO — there are only two. */\n const otherJoin: 'and' | 'or' = group.join === 'and' ? 'or' : 'and';\n\n return (\n <div className={GROUP_CLASS} data-testid={base}>\n {group.rows.map((node: DraftNode, index) => {\n const childPath = [...path, index];\n // A Fragment, not a wrapper element: the connector and the content\n // have to be DIRECT children of the grid to sit in its tracks. A\n // per-row `<div>` would make each row its own formatting context\n // again — which is exactly the layout this replaced.\n return (\n <Fragment key={node.rowId}>\n <div className={CONNECTOR_CLASS}>\n {index === 0 && t('filters.where', { defaultValue: 'Where' })}\n {/* The group's join, on the one row that owns it. A two-value\n choice does not need a popover with a list in it: one click\n is the whole interaction, and the underline is what says so.\n Rendered in the FOREGROUND colour, unlike the muted words\n around it, because this is the only interactive thing in\n the column. */}\n {index === 1 && (\n <button\n type=\"button\"\n data-testid={`${base}:join`}\n // Its own text is a single Russian letter at worst — no\n // accessible name at all. Name it by the action instead,\n // and announce the state it is in for a reader that\n // surfaces `aria-pressed` (`or` is the non-default).\n aria-label={t('filters.toggleJoin', {\n defaultValue: `Join: ${joinLabel(group.join)} — activate to switch to ${joinLabel(otherJoin)}`,\n params: { current: joinLabel(group.join), next: joinLabel(otherJoin) },\n })}\n aria-pressed={group.join === 'or'}\n onClick={() => handleSetJoin(otherJoin)}\n className=\"inline-grid justify-items-end rounded-xs font-medium text-foreground underline underline-offset-2 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1\"\n >\n {/* Both words, stacked in one grid cell, with the one not\n in effect merely INVISIBLE rather than removed.\n Otherwise the button measures whichever word it is\n currently showing, and since the connector track sizes\n itself to its widest content, clicking a short word into a longer one\n (and wider than \"Where\" too) resized the column\n and shunted every row of the panel sideways. Reserving\n both widths costs nothing and makes the toggle a\n no-reflow interaction.\n `invisible` (visibility: hidden), never `hidden`\n (display: none): the point is to keep the space. It\n also drops the spare word from the accessibility tree,\n so the button is not read as \"andor\". */}\n {JOIN_VALUES.map((j) => (\n <span\n key={j}\n className={cn('col-start-1 row-start-1', j !== group.join && 'invisible')}\n >\n {joinLabel(j)}\n </span>\n ))}\n </button>\n )}\n {index >= 2 && joinLabel(group.join)}\n </div>\n {isDraftGroup(node) ? (\n // `bg-muted/30`: a nested group is one logical operand of its\n // parent, and a hairline border alone made that easy to miss\n // in a panel that is already mostly rectangles.\n <div className=\"min-w-0 rounded-md border bg-muted/30 p-2\">\n <GroupEditorInner\n group={node}\n path={childPath}\n available={available}\n onChange={(next) => handleChildGroupChange(index, next)}\n testIdBase={testIdBase}\n onRemove={handleRemove}\n addGroupAt={addGroupAt}\n debounceMs={debounceMs}\n />\n </div>\n ) : (\n <FilterRow\n row={node}\n path={childPath}\n available={available}\n onPatch={handlePatch}\n onRemove={handleRemove}\n testIdBase={testIdBase}\n debounceMs={debounceMs}\n />\n )}\n </Fragment>\n );\n })}\n {/* Empty connector cell: puts the buttons below in the CONTENT track,\n in line with the rows they add to, rather than under the \"Where\".\n It contributes no width of its own, so it cannot widen the\n self-sizing connector track. */}\n <div aria-hidden=\"true\" />\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid={`${base}:add-row`}\n onClick={handleAddRow}\n >\n <IconPlus className=\"h-4 w-4\" aria-hidden=\"true\" />\n {t('filters.addRow', { defaultValue: 'Add filter' })}\n </Button>\n {canNestGroup && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid={`${base}:add-group`}\n onClick={handleAddGroup}\n >\n <IconFolderPlus className=\"h-4 w-4\" aria-hidden=\"true\" />\n {t('filters.addGroup', { defaultValue: 'Add group' })}\n </Button>\n )}\n {/* Named by its own text, like the two beside it. A bare × here was\n the odd one out in a row of labelled buttons, and its meaning —\n remove the GROUP, not the row above it — was carried only by an\n `aria-label` nobody using a mouse ever sees. */}\n {onRemove && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid={`${base}:remove`}\n onClick={() => onRemove(path)}\n >\n <IconX className=\"h-4 w-4\" aria-hidden=\"true\" />\n {t('filters.removeGroup', { defaultValue: 'Remove group' })}\n </Button>\n )}\n </div>\n </div>\n );\n}\n","'use client';\nimport type { FilterGroup } from '@burojs/core';\nimport { useOptionalT } from '@burojs/react';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { IconFilter } from '@tabler/icons-react';\nimport { useEffect, useRef, useState } from 'react';\nimport { Button } from '../../primitives/button.js';\nimport { Popover, PopoverContent, PopoverTrigger } from '../../primitives/popover.js';\nimport { EMPTY_DRAFT, fromDraft, pruneIncompleteRows, removeAt, toDraft } from './draft.js';\nimport type { DraftGroup, DraftPath } from './draft.js';\nimport { FilterGroupEditor } from './filter-group-editor.js';\n\n/**\n * A signal, not a value: every removal `FilterBadges` reports gets a FRESH\n * object here (even for the same `index` twice in a row), so the effect\n * that consumes it (below) can key on identity rather than trying to diff\n * indices for \"did this actually change\". `index` is the removed badge's\n * position in the APPLIED group's top-level rows AT THE MOMENT OF THE\n * CLICK — see `FilterPanel`'s doc for how that gets turned into the right\n * draft row.\n */\nexport interface DraftRemovalSignal {\n readonly index: number;\n}\n\nexport interface FilterPanelProps {\n /** The APPLIED group. The panel's own draft is seeded from this at open time — see the module doc. */\n readonly group: FilterGroup;\n readonly setGroup: (next: FilterGroup) => void;\n readonly available: readonly FilterFieldSpec[];\n /** `pagination.setPage` — Apply resets to page 1, same as every other filter mutation. */\n readonly setPage: (page: number) => void;\n readonly testIdBase: string;\n /** Controlled: a badge-body click (`FilterBadges`) opens this same panel from outside. */\n readonly open: boolean;\n readonly onOpenChange: (open: boolean) => void;\n /** Row to focus when opened via a badge click. */\n readonly focusPath?: DraftPath;\n /**\n * Set by the composing region (`FilterBadges`' × handler) whenever a\n * top-level row is removed from the APPLIED group while this panel may be\n * open. The panel drops the SAME top-level row from its own draft — see\n * the module doc's \"badge × while open\" paragraph for why this exists and\n * why it resolves by `rowId`, not by re-using the index as-is.\n */\n readonly externalRemoval?: DraftRemovalSignal;\n}\n\n/** Leaf rows at every depth, of the APPLIED group — what the button badges. Deliberately not `draft.ts`'s `countRows`, which is typed for the draft's `rowId`-carrying tree, not this plain `FilterGroup`. */\nfunction countGroupRows(group: FilterGroup): number {\n let total = 0;\n for (const row of group.rows) {\n total += 'rows' in row ? countGroupRows(row) : 1;\n }\n return total;\n}\n\n/**\n * The filter-builder popover: a `Popover`, not a `Dialog` (spec §6.2), so it\n * sits anchored to the trigger button and dismisses on outside-click/Escape\n * like any other toolbar popover in this kit.\n *\n * Draft lifecycle (the one rule every other decision here serves):\n * - the draft is seeded from `group` (the APPLIED value) at the moment the\n * panel transitions from closed to open — never on every render while\n * open, or an unrelated `group` prop update (another tab writing the URL)\n * would clobber an edit in progress;\n * - closing WITHOUT pressing \"Apply\" simply abandons that draft — nothing\n * writes back, and the next open re-seeds from `group` again, so any\n * edit made in a session that didn't end in \"Apply\" leaves no trace;\n * - \"Apply\" writes `setGroup(pruneIncompleteRows(fromDraft(draft)))`, then\n * resets pagination to page 1 (same as every other filter mutation),\n * then closes. Pruning matters: without it, a row the user started but\n * never finished (a field/operator picked, no value typed) survived\n * Apply verbatim — it read `Title contains ` (trailing space, no value)\n * on its own badge, inflated the \"Filters\" button's count, and consumed\n * the node budget of anything that gets written to (URL, a saved view)\n * while `groupToExpression` silently dropped it from the actual query\n * regardless, so the badge was outright lying about what ran (SP2\n * final-review finding 5). `pruneIncompleteRows` reuses `@burojs/core`'s\n * own `isCompletePredicate` (via `groupToExpression`'s completeness\n * rule) rather than a second hand-rolled copy, so this panel, its own\n * count below, and the AST the provider runs cannot drift apart;\n * - \"Reset\" clears the draft to empty but does NOT apply — the user still\n * has to press \"Apply\" to make that empty state take effect, same as any\n * other edit.\n *\n * One consequence of \"the draft does not react to `group` while open\" is\n * worth spelling out: a badge's × (`FilterBadges`) removes a row from the\n * APPLIED group IMMEDIATELY, bypassing the draft — but if the panel is open\n * and that same row is still sitting untouched in the draft (which it will\n * be, unless the user already edited it), a later \"Apply\" would otherwise\n * resurrect it from the draft. `externalRemoval` is how the composing\n * region tells this panel to drop that row from the draft too, at the same\n * moment it disappears from the applied group, so \"×\" stays a real deletion\n * even while the panel is open.\n *\n * That removal resolves by IDENTITY, not by re-using the badge's index\n * against the draft: `topLevelIdsRef` snapshots the draft's top-level\n * `rowId`s at seed time, positionally aligned with `group.rows` at that same\n * moment. A badge's index looks up the `rowId` it originally stood for in\n * that snapshot, and the row actually removed from the (possibly since\n * edited/reordered-by-length) draft is whichever CURRENT top-level node\n * still carries that `rowId` — `updateRow` (Task 3) guarantees a row keeps\n * its `rowId` across edits, so this stays correct even if the user has\n * since changed that row's field/op/value inside the panel. Positional\n * index would silently delete the wrong row the moment the two lists\n * diverge — e.g. the user removes a DIFFERENT row with the row's own ×\n * inside the panel first, shortening the draft but not the (not-yet-\n * reflected) applied group, before also clicking a badge.\n *\n * ⚠️ `topLevelIdsRef` is captured once, at seed time, and — like the draft\n * itself — is not kept in sync with a `group` change that comes from\n * OUTSIDE this panel while it is open (a saved view applied elsewhere,\n * \"clear all\"). This is not a new gap: \"Apply\" already discards such an\n * external change by writing the stale pre-mutation draft over it\n * regardless (the draft-lifecycle rule above), so a badge click during that\n * same window inherits an already-broken interaction rather than creating a\n * new failure mode — at worst it resolves a stale `rowId` that still\n * happens to exist in the untouched draft and removes that (still part of\n * the very draft \"Apply\" is about to discard wholesale), never something\n * outside the draft entirely.\n *\n * Every row's value control gets `debounceMs={0}`: the draft is local React\n * state, not a query param, so committing on every keystroke is cheap and\n * correct — and, unlike the default 300ms debounce, leaves no window where\n * \"Apply\" could read a stale, not-yet-committed edit.\n */\nexport function FilterPanel(props: FilterPanelProps): React.ReactElement {\n const {\n group,\n setGroup,\n available,\n setPage,\n testIdBase,\n open,\n onOpenChange,\n focusPath,\n externalRemoval,\n } = props;\n const t = useOptionalT();\n const [draft, setDraft] = useState<DraftGroup>(() => toDraft(group));\n const wasOpenRef = useRef(open);\n // Snapshot of the draft's top-level `rowId`s, positionally aligned with\n // `group.rows` as of the last seed — see the module doc's \"resolves by\n // IDENTITY\" paragraph for why a badge's index is translated through this\n // instead of being used directly against the draft.\n const topLevelIdsRef = useRef<readonly string[]>([]);\n // The popover's own content node. The focus lookup below queries WITHIN\n // this instead of `document` so it cannot cross into a DIFFERENT\n // `FilterPanel` instance that happens to share the same `testIdBase` — a\n // real case for a self-referential relation (`reference_locations.parent_id`,\n // SP3): the list page's own filter panel and the picker dialog's filter\n // panel for the SAME resource can both be mounted, and open, at once. An\n // unscoped `document.querySelector` would resolve to whichever one's row\n // happens to come first in DOM order, not necessarily this panel's own.\n const panelRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (open && !wasOpenRef.current) {\n const seeded = toDraft(group);\n setDraft(seeded);\n topLevelIdsRef.current = seeded.rows.map((node) => node.rowId);\n // `focusPath` is how a badge-body click (`FilterBadges`) tells this\n // panel which row it came from. `requestAnimationFrame` gives the\n // popover's portal a frame to actually mount `PopoverContent` before\n // we look for the row inside it.\n if (focusPath && focusPath.length > 0) {\n const targetTestId = `${testIdBase}:row:${focusPath.join('-')}:field`;\n requestAnimationFrame(() => {\n panelRef.current?.querySelector<HTMLElement>(`[data-testid=\"${targetTestId}\"]`)?.focus();\n });\n }\n }\n wasOpenRef.current = open;\n // Intentionally NOT reactive to `group`/`draft` — this effect only cares\n // about the closed→open TRANSITION (`wasOpenRef`), not about every\n // render where `open` happens to be true.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n useEffect(() => {\n if (!externalRemoval) return;\n const { index } = externalRemoval;\n const rowId = topLevelIdsRef.current[index];\n // Keep the snapshot aligned with the shrinking applied group regardless\n // of whether the draft still has this row — the NEXT badge's index is\n // relative to the group with THIS row already gone.\n topLevelIdsRef.current = topLevelIdsRef.current.filter((_, i) => i !== index);\n if (rowId === undefined) return;\n setDraft((current) => {\n const foundIndex = current.rows.findIndex((node) => node.rowId === rowId);\n // The user already removed/edited this row themselves (its own × or\n // a field/op change that somehow dropped it) before the badge click\n // landed — nothing to remove, and CRITICALLY not a license to remove\n // some other row at a merely-similar position.\n return foundIndex < 0 ? current : removeAt(current, [foundIndex]);\n });\n // `externalRemoval` is a fresh object per removal (see its own doc) —\n // identity is the whole signal, so it is the only dependency.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [externalRemoval]);\n\n const handleApply = () => {\n setGroup(pruneIncompleteRows(fromDraft(draft)));\n setPage(1);\n onOpenChange(false);\n };\n\n const handleReset = () => setDraft(EMPTY_DRAFT());\n\n const appliedCount = countGroupRows(group);\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" data-testid={`${testIdBase}:button`}>\n <IconFilter className=\"h-4 w-4\" aria-hidden=\"true\" />\n {t('filters.title', { defaultValue: 'Filters' })}\n {appliedCount > 0 && (\n <span\n data-testid={`${testIdBase}:count`}\n className=\"ml-1 rounded-full bg-primary px-1.5 text-xs text-primary-foreground\"\n >\n {appliedCount}\n </span>\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent\n ref={panelRef}\n data-testid={`${testIdBase}:panel`}\n align=\"start\"\n // A DEFINITE width, not `w-auto`: the rows inside size their value\n // column off the panel (`FilterRow`), so an auto-width panel and a\n // fill-width value column would each be waiting on the other, and\n // the panel would resize as rows were added or a field changed.\n // Capped against the viewport so it stays usable on a narrow screen.\n className=\"w-[min(46rem,calc(100vw-2rem))]\"\n >\n <FilterGroupEditor\n group={draft}\n path={[]}\n available={available}\n onChange={setDraft}\n testIdBase={testIdBase}\n debounceMs={0}\n />\n <div className=\"mt-3 flex justify-end gap-2 border-t pt-3\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n data-testid={`${testIdBase}:reset`}\n onClick={handleReset}\n >\n {t('filters.reset', { defaultValue: 'Reset' })}\n </Button>\n <Button type=\"button\" size=\"sm\" data-testid={`${testIdBase}:apply`} onClick={handleApply}>\n {t('filters.apply', { defaultValue: 'Apply' })}\n </Button>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n","'use client';\nimport type {\n ErasedResourceHandle,\n FilterExpression,\n FilterPredicate,\n PortableValue,\n} from '@burojs/core';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { findOwnedIndex } from './pinned-ownership.js';\nimport { FilterValueControl } from './values/filter-value-control.js';\n\ntype Expression = FilterExpression<ErasedResourceHandle>;\n\nexport interface PinnedFilterControlsProps {\n readonly pinnedFields: readonly FilterFieldSpec[];\n /** The pinned slot: a flat array of AST expressions (`filters.value`). */\n readonly value: readonly Expression[];\n readonly set: (next: readonly Expression[]) => void;\n readonly testIdBase: string;\n}\n\nfunction isEmptyValue(value: PortableValue | undefined): boolean {\n return value === undefined || value === '' || (Array.isArray(value) && value.length === 0);\n}\n\n/**\n * One always-visible control per `pinnedFields` entry — the \"pinned\" slot\n * (`filters.value`/`filters.set`) rendered outside the builder panel.\n *\n * OWNERSHIP RULE (SP1 final review decision, restated here because it lives\n * nowhere else — and shared verbatim with `FilterBadges` via\n * `pinned-ownership.js`, see finding 3 of the SP2 final review):\n * `pinnedFields` is a list of field SPECS; `filters.value` is a flat array\n * of AST expressions with no structural tag linking an expression back to\n * the control that wrote it. The rule this component uses to bridge that\n * gap: the control for field `f` owns the FIRST expression in `value`\n * whose `field === f` **and** whose `op` matches this control's own\n * operator (`spec.operators[0]`, below). Setting a value replaces that\n * expression IN PLACE (same index); clearing it removes it. Every OTHER\n * expression on the same field — put there by `fixedFilters`, a saved\n * view, or a pre-upgrade link written by the deleted chip toolbar in ITS\n * OWN operator vocabulary (e.g. `startswith` where this control writes\n * `contains`) — is left completely alone: not read for display, not\n * overwritten, not removed. This is what stops a pinned control from\n * silently eating a call-site filter it does not own, or REWRITING one\n * it merely resembles — an earlier version of this rule matched on\n * `field` alone, so the first edit through a pinned control would adopt\n * and silently rewrite the operator of an expression it merely happened\n * to share a field with (SP2 final-review finding 4); the operator check\n * is what makes an unowned same-field expression fall through to\n * `FilterBadges`'s \"nobody claimed this\" badge instead.\n *\n * The operator used to read/write the owned expression is always\n * `spec.operators[0]` — the control renders no operator picker, so it has\n * no other operator to use even if some OTHER expression on the same field\n * happens to carry a different one.\n */\nexport function PinnedFilterControls(props: PinnedFilterControlsProps): React.ReactElement {\n const { pinnedFields, value, set, testIdBase } = props;\n return (\n <>\n {pinnedFields.map((spec) => (\n <PinnedControl\n key={spec.field}\n spec={spec}\n value={value}\n set={set}\n testIdBase={testIdBase}\n />\n ))}\n </>\n );\n}\n\ninterface PinnedControlProps {\n readonly spec: FilterFieldSpec;\n readonly value: readonly Expression[];\n readonly set: (next: readonly Expression[]) => void;\n readonly testIdBase: string;\n}\n\nfunction PinnedControl(props: PinnedControlProps): React.ReactElement {\n const { spec, value, set, testIdBase } = props;\n const op = spec.operators[0] ?? 'eq';\n const index = findOwnedIndex(value, spec);\n const owned = index >= 0 ? (value[index] as FilterPredicate) : undefined;\n\n const handleChange = (next: PortableValue | undefined) => {\n if (isEmptyValue(next)) {\n if (index < 0) return;\n set(value.filter((_, i) => i !== index));\n return;\n }\n const predicate: FilterPredicate = { field: spec.field, op, value: next } as FilterPredicate;\n if (index < 0) {\n set([...value, predicate]);\n } else {\n const nextValue = value.slice();\n nextValue[index] = predicate;\n set(nextValue);\n }\n };\n\n return (\n <FilterValueControl\n kind={spec.kind}\n op={op}\n value={owned?.value}\n onChange={handleChange}\n testId={`${testIdBase}:pinned:${spec.field}`}\n fieldLabel={spec.label}\n placeholder={spec.label}\n options={spec.options}\n />\n );\n}\n","'use client';\nimport type {\n ErasedResourceHandle,\n FilterExpression,\n FilterGroup,\n ResolvedFixedFilter,\n} from '@burojs/core';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { useState } from 'react';\nimport type { DraftPath } from './draft.js';\nimport { FilterBadges } from './filter-badges.js';\nimport type { DraftRemovalSignal } from './filter-panel.js';\nimport { FilterPanel } from './filter-panel.js';\nimport { PinnedFilterControls } from './pinned-filter-controls.js';\n\ntype Expression = FilterExpression<ErasedResourceHandle>;\n\n// Module-scope, frozen, reused as the default when a caller (the relation\n// picker's dialog, which has no pinned slot at all — see\n// `use-dialog-filter-state.ts`'s doc) omits `pinnedFields`/`pinnedValue`.\n// A `= []` default PARAMETER re-evaluates to a NEW array reference on every\n// render in which the prop is omitted, which defeats `FilterBadges`' own\n// `useMemo([pinnedValue, pinnedFields])` (`orphanPinned`) every single\n// render — this fixes that at the source instead of every caller having to\n// remember to pass a stable empty array of its own.\nconst EMPTY_PINNED_FIELDS: readonly FilterFieldSpec[] = Object.freeze([]);\nconst EMPTY_PINNED_VALUE: readonly Expression[] = Object.freeze([]);\n\nexport interface FilterBuilderProps {\n readonly group: FilterGroup;\n readonly setGroup: (next: FilterGroup) => void;\n readonly available: readonly FilterFieldSpec[];\n readonly fixed: readonly ResolvedFixedFilter[];\n readonly pinnedFields?: readonly FilterFieldSpec[];\n readonly pinnedValue?: readonly Expression[];\n readonly setPinnedValue?: (next: readonly Expression[]) => void;\n /** Apply / a pinned write / a badge × all reset to page 1, same as every other filter mutation. */\n readonly setPage: (page: number) => void;\n readonly testIdBase: string;\n}\n\n/**\n * Props-taking composite of the filter builder: `PinnedFilterControls` +\n * `FilterPanel` + `FilterBadges`, wired together with the `open` +\n * `focusPath` + `externalRemoval` state that link them.\n *\n * This is the piece `FilterRegion` used to assemble by hand, reading its\n * inputs from `useResourceListContext()`. That wiring is now here instead —\n * moved, not rewritten — so a caller with no list controller at all (the\n * relation picker's advanced-search dialog, SP3) can mount the same trio by\n * passing plain props. `FilterRegion` itself is now a thin wrapper: it still\n * owns the controller lookup, the search box and \"Clear all\" (call-site\n * concerns, not the builder's), and hands everything else to this\n * component.\n */\nexport function FilterBuilder(props: FilterBuilderProps): React.ReactElement {\n const {\n group,\n setGroup,\n available,\n fixed,\n pinnedFields = EMPTY_PINNED_FIELDS,\n pinnedValue = EMPTY_PINNED_VALUE,\n setPinnedValue,\n setPage,\n testIdBase,\n } = props;\n\n // Panel open/focus state, plus the badge-removal → draft signal — the same\n // three pieces `filter-panel.test.tsx`'s `Harness` used to wire by hand,\n // and `FilterRegion` wired a second time, now owned here instead.\n const [open, setOpen] = useState(false);\n const [focusPath, setFocusPath] = useState<DraftPath | undefined>(undefined);\n const [externalRemoval, setExternalRemoval] = useState<DraftRemovalSignal | undefined>(undefined);\n\n // `focusPath` is a ONE-SHOT instruction from a badge-body click, consumed\n // by `FilterPanel`'s closed→open transition effect. Clearing it here on\n // every CLOSE (not just leaving it sitting from the last badge click)\n // stops it from leaking into the next open: without this, opening via a\n // badge and then closing left `focusPath` set, so the very next open via\n // the plain \"Filters\" button re-focused whatever row the badge last\n // pointed at instead of opening with no focus request at all.\n const handleOpenChange = (next: boolean) => {\n setOpen(next);\n if (!next) setFocusPath(undefined);\n };\n\n // ⚠️ Both `setGroup` (the builder's own applied group) and `setPinnedValue`\n // (the pinned slot) are DELIBERATE pass-throughs from the caller —\n // composing them with `setPage(1)` happens HERE so every caller (a page's\n // `FilterRegion`, or a dialog with its own no-op `setPage`) gets the same\n // treatment without having to remember it: picking a pinned value or\n // removing a badge on a now-shorter result set must not strand the caller\n // on a now out-of-range page.\n const setGroupResettingPage = (next: FilterGroup) => {\n setPage(1);\n setGroup(next);\n };\n const setPinnedResettingPage = (next: readonly Expression[]) => {\n setPage(1);\n setPinnedValue?.(next);\n };\n\n return (\n <>\n <PinnedFilterControls\n pinnedFields={pinnedFields}\n value={pinnedValue}\n set={setPinnedResettingPage}\n testIdBase={testIdBase}\n />\n {available.length > 0 && (\n <FilterPanel\n group={group}\n setGroup={setGroup}\n available={available}\n setPage={setPage}\n testIdBase={testIdBase}\n open={open}\n onOpenChange={handleOpenChange}\n focusPath={focusPath}\n externalRemoval={externalRemoval}\n />\n )}\n <FilterBadges\n group={group}\n setGroup={setGroupResettingPage}\n available={available}\n fixed={fixed}\n pinnedFields={pinnedFields}\n pinnedValue={pinnedValue}\n setPinnedValue={setPinnedResettingPage}\n testIdBase={testIdBase}\n onOpenAt={(path) => {\n setFocusPath(path);\n setOpen(true);\n }}\n onRemoved={(index) => setExternalRemoval({ index })}\n />\n </>\n );\n}\n"],"mappings":"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"}