@burojs/ui-shadcn 0.8.0 → 0.10.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 (126) hide show
  1. package/dist/{bulk-summary-2HLgz7Il.cjs → bulk-summary-Chc61Pyw.cjs} +9 -7
  2. package/dist/bulk-summary-Chc61Pyw.cjs.map +1 -0
  3. package/dist/{bulk-summary-D6A0ua21.js → bulk-summary-DpSQD2Fq.js} +9 -7
  4. package/dist/bulk-summary-DpSQD2Fq.js.map +1 -0
  5. package/dist/{candidates-relation-picker-CGXItMN0.js → candidates-relation-picker-DRsHKe6N.js} +3 -3
  6. package/dist/{candidates-relation-picker-CGXItMN0.js.map → candidates-relation-picker-DRsHKe6N.js.map} +1 -1
  7. package/dist/{candidates-relation-picker-C47mF260.cjs → candidates-relation-picker-GIxsp7D_.cjs} +3 -3
  8. package/dist/{candidates-relation-picker-C47mF260.cjs.map → candidates-relation-picker-GIxsp7D_.cjs.map} +1 -1
  9. package/dist/{check-confirm-dialog-BT04Mieo.cjs → check-confirm-dialog-BMSb7wFv.cjs} +76 -9
  10. package/dist/check-confirm-dialog-BMSb7wFv.cjs.map +1 -0
  11. package/dist/{check-confirm-dialog-CtiRzOAt.js → check-confirm-dialog-B_u3YVHS.js} +72 -11
  12. package/dist/{check-confirm-dialog-CtiRzOAt.js.map → check-confirm-dialog-B_u3YVHS.js.map} +1 -1
  13. package/dist/{filter-builder-z3Axzf9Y.js → filter-builder-DX-xC3Yd.js} +14 -6
  14. package/dist/filter-builder-DX-xC3Yd.js.map +1 -0
  15. package/dist/{filter-builder-ByGhxLf_.cjs → filter-builder-Dt5E8PSX.cjs} +14 -6
  16. package/dist/filter-builder-Dt5E8PSX.cjs.map +1 -0
  17. package/dist/{flow-runtime-BVFjMU5-.cjs → flow-runtime-CQ4wf8co.cjs} +3 -3
  18. package/dist/{flow-runtime-BVFjMU5-.cjs.map → flow-runtime-CQ4wf8co.cjs.map} +1 -1
  19. package/dist/{flow-runtime-DrUWyTS0.js → flow-runtime-Q2vD7R3c.js} +3 -3
  20. package/dist/{flow-runtime-DrUWyTS0.js.map → flow-runtime-Q2vD7R3c.js.map} +1 -1
  21. package/dist/{flow-screen-3fk6J33y.js → flow-screen-B1y2yizP.js} +68 -29
  22. package/dist/flow-screen-B1y2yizP.js.map +1 -0
  23. package/dist/{flow-screen-6y5E_HT6.cjs → flow-screen-sw2cP70t.cjs} +67 -28
  24. package/dist/flow-screen-sw2cP70t.cjs.map +1 -0
  25. package/dist/index.cjs +2304 -247
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +119 -12
  28. package/dist/index.d.ts +119 -12
  29. package/dist/index.js +2225 -172
  30. package/dist/index.js.map +1 -1
  31. package/dist/{inline-flow-host-C4KmZIYE.cjs → inline-flow-host-CkhoA69R.cjs} +3 -3
  32. package/dist/{inline-flow-host-C4KmZIYE.cjs.map → inline-flow-host-CkhoA69R.cjs.map} +1 -1
  33. package/dist/{inline-flow-host-YnnvWjdU.js → inline-flow-host-DqbXqcPS.js} +3 -3
  34. package/dist/{inline-flow-host-YnnvWjdU.js.map → inline-flow-host-DqbXqcPS.js.map} +1 -1
  35. package/dist/{lazy-relation-picker-dialog-DN4bvQfg.js → lazy-relation-picker-dialog-BaIEWLbF.js} +3 -52
  36. package/dist/lazy-relation-picker-dialog-BaIEWLbF.js.map +1 -0
  37. package/dist/{lazy-relation-picker-dialog-BAKBRn3V.cjs → lazy-relation-picker-dialog-CeRzOAi0.cjs} +3 -52
  38. package/dist/lazy-relation-picker-dialog-CeRzOAi0.cjs.map +1 -0
  39. package/dist/{page-flow-route-CtEfqAvU.js → page-flow-route-CP7cS7FY.js} +7 -7
  40. package/dist/page-flow-route-CP7cS7FY.js.map +1 -0
  41. package/dist/{page-flow-route-DEjAxHe3.cjs → page-flow-route-UDx8qvdM.cjs} +10 -10
  42. package/dist/page-flow-route-UDx8qvdM.cjs.map +1 -0
  43. package/dist/{place-node-Bhl8B3fF.cjs → place-node-C5ZmuHaN.cjs} +208 -55
  44. package/dist/place-node-C5ZmuHaN.cjs.map +1 -0
  45. package/dist/{place-node-BdxT-30h.js → place-node-DwWAigv0.js} +205 -58
  46. package/dist/place-node-DwWAigv0.js.map +1 -0
  47. package/dist/record-trail-store-Cjo9vtod.cjs +126 -0
  48. package/dist/record-trail-store-Cjo9vtod.cjs.map +1 -0
  49. package/dist/record-trail-store-Dn6bVry4.js +91 -0
  50. package/dist/record-trail-store-Dn6bVry4.js.map +1 -0
  51. package/dist/{related-list-cards-Bc_vfCE_.cjs → related-list-cards-BypKfm9t.cjs} +3 -3
  52. package/dist/{related-list-cards-Bc_vfCE_.cjs.map → related-list-cards-BypKfm9t.cjs.map} +1 -1
  53. package/dist/{related-list-cards-BvkD5OcC.js → related-list-cards-Dcmruy09.js} +3 -3
  54. package/dist/{related-list-cards-BvkD5OcC.js.map → related-list-cards-Dcmruy09.js.map} +1 -1
  55. package/dist/{related-list-timeline-k0DtnxTs.cjs → related-list-timeline-D-KClgKb.cjs} +4 -4
  56. package/dist/related-list-timeline-D-KClgKb.cjs.map +1 -0
  57. package/dist/{related-list-timeline-DCrwN9oO.js → related-list-timeline-DZON_qSc.js} +4 -4
  58. package/dist/related-list-timeline-DZON_qSc.js.map +1 -0
  59. package/dist/{related-rows-BW9Jr07T.cjs → related-rows-DgF05ssi.cjs} +4 -4
  60. package/dist/{related-rows-BW9Jr07T.cjs.map → related-rows-DgF05ssi.cjs.map} +1 -1
  61. package/dist/{related-rows-CPE-9FFr.js → related-rows-ETZl5CB9.js} +4 -4
  62. package/dist/{related-rows-CPE-9FFr.js.map → related-rows-ETZl5CB9.js.map} +1 -1
  63. package/dist/{relation-create-DDEYcKfw.cjs → relation-create-B8zngAcA.cjs} +82 -13
  64. package/dist/relation-create-B8zngAcA.cjs.map +1 -0
  65. package/dist/{relation-create-C43cdPQt.js → relation-create-CeCWyxIb.js} +79 -16
  66. package/dist/relation-create-CeCWyxIb.js.map +1 -0
  67. package/dist/{relation-many-picker-BlrGRwMV.js → relation-many-picker-CT7nKfE2.js} +12 -10
  68. package/dist/relation-many-picker-CT7nKfE2.js.map +1 -0
  69. package/dist/{relation-many-picker-Br-zBHxT.cjs → relation-many-picker-Cq0EHY5I.cjs} +12 -10
  70. package/dist/relation-many-picker-Cq0EHY5I.cjs.map +1 -0
  71. package/dist/{relation-picker-x7P5HKie.js → relation-picker-CCyuMeGq.js} +13 -10
  72. package/dist/relation-picker-CCyuMeGq.js.map +1 -0
  73. package/dist/{relation-picker-ADjxI_TI.cjs → relation-picker-DmwTzgsd.cjs} +13 -10
  74. package/dist/relation-picker-DmwTzgsd.cjs.map +1 -0
  75. package/dist/{relation-picker-dialog-CyKliBkN.js → relation-picker-dialog-CoWKjNv8.js} +5 -5
  76. package/dist/relation-picker-dialog-CoWKjNv8.js.map +1 -0
  77. package/dist/{relation-picker-dialog-DimVQF9Y.cjs → relation-picker-dialog-mbwyNK43.cjs} +5 -5
  78. package/dist/relation-picker-dialog-mbwyNK43.cjs.map +1 -0
  79. package/dist/{relation-select-Cr3CfNLy.cjs → relation-select-CguIo2M5.cjs} +2 -2
  80. package/dist/{relation-select-Cr3CfNLy.cjs.map → relation-select-CguIo2M5.cjs.map} +1 -1
  81. package/dist/{relation-select-B8Fq4o-t.js → relation-select-nAYxhopN.js} +2 -2
  82. package/dist/{relation-select-B8Fq4o-t.js.map → relation-select-nAYxhopN.js.map} +1 -1
  83. package/dist/{success-notices-Dl3M1fZP.cjs → success-notices-Cl4b6MLQ.cjs} +3 -2
  84. package/dist/success-notices-Cl4b6MLQ.cjs.map +1 -0
  85. package/dist/{success-notices-LDEgEkc4.js → success-notices-CrcWOGSo.js} +3 -2
  86. package/dist/success-notices-CrcWOGSo.js.map +1 -0
  87. package/dist/{use-dialog-filter-state-tNvqAAjh.cjs → use-dialog-filter-state-CpWblz3C.cjs} +2 -2
  88. package/dist/{use-dialog-filter-state-tNvqAAjh.cjs.map → use-dialog-filter-state-CpWblz3C.cjs.map} +1 -1
  89. package/dist/{use-dialog-filter-state-B_MqzHaT.js → use-dialog-filter-state-ZYxOp5WO.js} +2 -2
  90. package/dist/{use-dialog-filter-state-B_MqzHaT.js.map → use-dialog-filter-state-ZYxOp5WO.js.map} +1 -1
  91. package/dist/{use-record-subtitle-bfwIoUme.cjs → use-record-subtitle-B4lTR7pf.cjs} +5 -8
  92. package/dist/use-record-subtitle-B4lTR7pf.cjs.map +1 -0
  93. package/dist/{use-record-subtitle-gIaCGgbI.js → use-record-subtitle-pWMBqQx9.js} +5 -8
  94. package/dist/use-record-subtitle-pWMBqQx9.js.map +1 -0
  95. package/package.json +9 -9
  96. package/dist/bulk-summary-2HLgz7Il.cjs.map +0 -1
  97. package/dist/bulk-summary-D6A0ua21.js.map +0 -1
  98. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +0 -1
  99. package/dist/filter-builder-ByGhxLf_.cjs.map +0 -1
  100. package/dist/filter-builder-z3Axzf9Y.js.map +0 -1
  101. package/dist/flow-screen-3fk6J33y.js.map +0 -1
  102. package/dist/flow-screen-6y5E_HT6.cjs.map +0 -1
  103. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +0 -1
  104. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +0 -1
  105. package/dist/page-flow-route-CtEfqAvU.js.map +0 -1
  106. package/dist/page-flow-route-DEjAxHe3.cjs.map +0 -1
  107. package/dist/place-node-BdxT-30h.js.map +0 -1
  108. package/dist/place-node-Bhl8B3fF.cjs.map +0 -1
  109. package/dist/related-list-timeline-DCrwN9oO.js.map +0 -1
  110. package/dist/related-list-timeline-k0DtnxTs.cjs.map +0 -1
  111. package/dist/relation-create-C43cdPQt.js.map +0 -1
  112. package/dist/relation-create-DDEYcKfw.cjs.map +0 -1
  113. package/dist/relation-many-picker-BlrGRwMV.js.map +0 -1
  114. package/dist/relation-many-picker-Br-zBHxT.cjs.map +0 -1
  115. package/dist/relation-picker-ADjxI_TI.cjs.map +0 -1
  116. package/dist/relation-picker-dialog-CyKliBkN.js.map +0 -1
  117. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +0 -1
  118. package/dist/relation-picker-x7P5HKie.js.map +0 -1
  119. package/dist/resource-page-router-BRpUnhay.js +0 -1958
  120. package/dist/resource-page-router-BRpUnhay.js.map +0 -1
  121. package/dist/resource-page-router-qEd2xb55.cjs +0 -2167
  122. package/dist/resource-page-router-qEd2xb55.cjs.map +0 -1
  123. package/dist/success-notices-Dl3M1fZP.cjs.map +0 -1
  124. package/dist/success-notices-LDEgEkc4.js.map +0 -1
  125. package/dist/use-record-subtitle-bfwIoUme.cjs.map +0 -1
  126. package/dist/use-record-subtitle-gIaCGgbI.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-builder-DX-xC3Yd.js","names":[],"sources":["../src/layout/language-storage.ts","../src/fields/legacy-field.ts","../src/fields/resolved-field.tsx","../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/fields/field-time-zone.ts","../src/fields/local-date-time.ts","../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/data-table/data-table-faceted-filter.tsx","../src/list-kit/filter-builder/preset-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","import { declaredText, type FieldDeclaration, type FieldOption } from '@burojs/core';\n\n/**\n * The native builder's historical `kind` is inert declaration data. During\n * migration, lower it once to the same semantic shape that a raw config uses;\n * consumers must not each invent a renderer-specific fallback.\n */\nexport interface LegacyFieldLike {\n readonly declaration?: FieldDeclaration;\n readonly type?: string;\n readonly kind?: string;\n /**\n * `opts` and `ui` are opaque host-local bags: the lowering below reads them\n * only through own-property descriptors (`ownData`), never by index. They\n * are typed `object` rather than `Record<string, unknown>` on purpose — an\n * index signature would reject every real field type (`FileFieldOpts`,\n * `FieldUiSelection`, …), which is what forced `as LegacyFieldLike` casts\n * on callers passing the framework's own declarations.\n */\n readonly opts?: object;\n readonly format?: string;\n readonly items?: FieldDeclaration;\n readonly meta?: Readonly<Record<string, unknown>>;\n readonly ui?: object;\n readonly required?: boolean;\n readonly nullable?: boolean;\n}\n\n/**\n * A semantic declaration plus UI-only hints retained at the rendering edge.\n * `ui` remains outside `FieldDeclaration`: it participates in host renderer\n * selection/props, never in portable model compilation or runtime attachment\n * registration.\n */\nexport type LoweredLegacyFieldSource = FieldDeclaration &\n Readonly<{\n ui?: Readonly<Record<string, unknown>>;\n }>;\n\ntype LegacyMapping = Readonly<{\n type: string;\n format?: string;\n items?: FieldDeclaration;\n}>;\n\nconst STRING_ITEM: FieldDeclaration = Object.freeze({ type: 'string' });\n\nconst LEGACY_KIND_DECLARATIONS: Readonly<Record<string, LegacyMapping>> = Object.freeze({\n string: { type: 'string' },\n text: { type: 'string', format: 'text' },\n textarea: { type: 'string', format: 'textarea' },\n code: { type: 'string', format: 'code' },\n email: { type: 'string', format: 'email' },\n url: { type: 'string', format: 'url' },\n uuid: { type: 'string', format: 'uuid' },\n number: { type: 'number' },\n boolean: { type: 'boolean' },\n date: { type: 'string', format: 'date' },\n dateTime: { type: 'string', format: 'date-time' },\n enum: { type: 'string', format: 'enum' },\n json: { type: 'object', format: 'json' },\n richText: { type: 'string', format: 'rich-text' },\n file: { type: 'array', format: 'file', items: STRING_ITEM },\n relation: { type: 'relation' },\n relationMany: { type: 'array', format: 'relation', items: STRING_ITEM },\n});\n\nconst SEMANTIC_KEYS = Object.freeze([\n 'type',\n 'format',\n 'meta',\n 'required',\n 'nullable',\n 'empty',\n 'default',\n 'constraints',\n // A date field's zone (REF-118 §4.2): a renderer resolves it with the record or the form.\n 'zone',\n 'texts',\n 'options',\n 'items',\n 'properties',\n 'additionalProperties',\n 'variants',\n] as const);\n\ntype SemanticKey = (typeof SEMANTIC_KEYS)[number];\ntype OwnData = Readonly<{ readonly present: boolean; readonly value?: unknown }>;\n\n/** Read only own data properties: getters and inherited legacy attachments never cross the host boundary. */\nfunction ownData(value: unknown, key: PropertyKey): OwnData {\n if (value === null || (typeof value !== 'object' && typeof value !== 'function'))\n return { present: false };\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (\n descriptor === undefined ||\n !Object.prototype.propertyIsEnumerable.call(value, key) ||\n !('value' in descriptor)\n ) {\n return { present: false };\n }\n return { present: true, value: descriptor.value };\n}\n\nfunction ownString(value: unknown, key: PropertyKey): string | undefined {\n const candidate = ownData(value, key).value;\n return typeof candidate === 'string' && candidate.length > 0 ? candidate : undefined;\n}\n\n/**\n * A `ui.input` / `ui.display` selection as declared: an id, or its object form (`{ use, … }`,\n * REF-117), kept whole so `resolveSurfaceUi` reads the same layer the checks judged.\n */\nfunction ownSelection(value: unknown, key: PropertyKey): string | object | undefined {\n const candidate = ownData(value, key).value;\n if (typeof candidate === 'string') return candidate.length > 0 ? candidate : undefined;\n return candidate !== null && typeof candidate === 'object' && !Array.isArray(candidate)\n ? candidate\n : undefined;\n}\n\n/**\n * Keep only portable implementation selections and the group or element layout (T2 §5.5, whose\n * structural fields read it at render time) in a semantic declaration.\n */\nfunction projectUiSelection(input: unknown): FieldDeclaration['ui'] | undefined {\n const selection: { input?: string | object; display?: string | object; layout?: unknown } = {};\n const inputId = ownSelection(input, 'input');\n const displayId = ownSelection(input, 'display');\n const layout = ownData(input, 'layout').value;\n if (inputId !== undefined) selection.input = inputId;\n if (displayId !== undefined) selection.display = displayId;\n if (Array.isArray(layout)) selection.layout = layout;\n return Object.keys(selection).length === 0\n ? undefined\n : (Object.freeze(selection) as FieldDeclaration['ui']);\n}\n\n/**\n * Strictly project a native/legacy object into `FieldDeclaration`. This is a\n * boundary projection, not declaration validation: it intentionally drops\n * builder-only `schema`, `opts`, `kind`, codecs, and arbitrary attachments.\n */\nfunction projectFieldDeclaration(input: unknown): FieldDeclaration | undefined {\n const type = ownString(input, 'type');\n if (type === undefined) return undefined;\n const projected: Record<SemanticKey | 'ui', unknown> = { type } as Record<\n SemanticKey | 'ui',\n unknown\n >;\n for (const key of SEMANTIC_KEYS) {\n if (key === 'type' || key === 'items' || key === 'properties' || key === 'variants') continue;\n const candidate = ownData(input, key);\n if (candidate.present && candidate.value !== undefined) projected[key] = candidate.value;\n }\n const ui = projectUiSelection(ownData(input, 'ui').value);\n if (ui !== undefined) projected.ui = ui;\n if (type === 'array') {\n const item = projectFieldDeclaration(ownData(input, 'items').value);\n if (item !== undefined) projected.items = item;\n }\n if (type === 'object') {\n const properties = ownData(input, 'properties').value;\n if (Array.isArray(properties)) {\n // The portable ordered form (`f.object` writes it): `{ name, … }` entries, in order.\n const output: Record<string, FieldDeclaration> = {};\n for (let index = 0; index < properties.length; index += 1) {\n const entry = ownData(properties, index).value;\n const name = ownString(entry, 'name');\n const child = projectFieldDeclaration(entry);\n if (name !== undefined && child !== undefined) output[name] = child;\n }\n projected.properties = Object.freeze(output);\n } else if (properties !== null && typeof properties === 'object') {\n const output: Record<string, FieldDeclaration> = {};\n for (const key of Object.keys(properties)) {\n const child = projectFieldDeclaration(ownData(properties, key).value);\n if (child !== undefined) output[key] = child;\n }\n projected.properties = Object.freeze(output);\n }\n }\n if (type === 'union') {\n const variants = ownData(input, 'variants').value;\n if (Array.isArray(variants)) {\n const output: FieldDeclaration[] = [];\n for (let index = 0; index < variants.length; index += 1) {\n const child = projectFieldDeclaration(ownData(variants, index).value);\n if (child !== undefined) output.push(child);\n }\n projected.variants = Object.freeze(output);\n }\n }\n return Object.freeze(projected) as FieldDeclaration;\n}\n\n/** Preserve only original, enumerable own UI hints for a host implementation. */\nfunction projectUiHints(input: unknown): Readonly<Record<string, unknown>> | undefined {\n if (input === null || typeof input !== 'object' || Array.isArray(input)) return undefined;\n const projected: Record<string, unknown> = {};\n for (const key of Object.keys(input)) {\n const candidate = ownData(input, key);\n if (candidate.present) projected[key] = candidate.value;\n }\n return Object.freeze(projected);\n}\n\n/**\n * The choices of a legacy `f.enum([...])` builder, as canonical options. The builder keeps them in\n * `values` (and `ui.values`), which the canonical declaration has no slot for; without this the\n * form's select had nothing to offer. Each option is its value: labels come from the field's\n * `fields.<name>.options.<value>` slot, as for any option without an inline label.\n */\nfunction enumValueOptions(field: LegacyFieldLike): readonly FieldOption[] | undefined {\n const own = ownData(field, 'values').value;\n const values = Array.isArray(own) ? own : ownData(ownData(field, 'ui').value, 'values').value;\n if (!Array.isArray(values)) return undefined;\n const options = values\n .filter((value): value is string => typeof value === 'string')\n .map((value) => Object.freeze({ value }));\n return options.length === 0 ? undefined : Object.freeze(options);\n}\n\n/**\n * Return an immutable semantic field declaration for either native semantic\n * data or a legacy builder kind. Unknown kinds retain their name as a format\n * on an `unknown` base, so a host catalog may still handle them without\n * implying a primitive value type.\n */\nexport function lowerLegacyField(field: LegacyFieldLike): FieldDeclaration {\n const declaration = projectFieldDeclaration(ownData(field, 'declaration').value);\n if (declaration !== undefined) return declaration;\n const direct = projectFieldDeclaration(field);\n const kind = ownString(field, 'kind');\n const mapping = kind === undefined ? undefined : LEGACY_KIND_DECLARATIONS[kind];\n // A number of native-era builders already carry their primitive shape\n // (`type`, `items`, `meta`) alongside the former routing `kind`. Keep that\n // shape, but lower the kind into the semantic format so an app-local host\n // catalog can still select its custom implementation. An explicit format\n // remains authoritative.\n if (direct !== undefined) {\n const format = direct.format ?? mapping?.format ?? kind;\n return format === undefined\n ? direct\n : (Object.freeze({ ...direct, format }) as FieldDeclaration);\n }\n\n // Legacy `f.file()` is a one-file field unless its builder option explicitly\n // says `multiple: true`. The renderer contract follows that cardinality:\n // only a semantic array selects `CollectionInput`, whose public API always\n // carries `multiple: true`. Preserve the file format for both variants so\n // they share the same host implementation and full-width layout.\n const ui = projectUiHints(ownData(field, 'ui').value);\n const legacySingleFile = kind === 'file' && ui?.multiple !== true;\n const type = legacySingleFile ? 'unknown' : (mapping?.type ?? 'unknown');\n const format = ownString(field, 'format') ?? mapping?.format ?? kind;\n const items =\n type === 'array'\n ? (projectFieldDeclaration(ownData(field, 'items').value) ?? mapping?.items ?? STRING_ITEM)\n : undefined;\n const meta = ownData(field, 'meta');\n const texts = ownData(field, 'texts');\n const zone = ownData(field, 'zone');\n const options = kind === 'enum' ? enumValueOptions(field) : undefined;\n const required = ownData(field, 'required');\n const nullable = ownData(field, 'nullable');\n // Native builders retain their declaration-time semantics under `opts`.\n // Prefer an explicit top-level value when present, then project only the\n // portable boolean flags; functions and arbitrary builder options remain\n // host-local implementation details.\n const opts = ownData(field, 'opts').value;\n const optionRequired = ownData(opts, 'required');\n const optionNullable = ownData(opts, 'nullable');\n const requiredValue = required.present ? required.value : optionRequired.value;\n const nullableValue = nullable.present ? nullable.value : optionNullable.value;\n return Object.freeze({\n type,\n ...(format === undefined ? {} : { format }),\n ...(items === undefined ? {} : { items }),\n ...(meta.present && meta.value !== undefined ? { meta: meta.value } : {}),\n ...(options === undefined ? {} : { options }),\n ...(texts.present && texts.value !== undefined ? { texts: texts.value } : {}),\n ...(zone.present && zone.value !== undefined ? { zone: zone.value } : {}),\n ...(requiredValue === true ? { required: true } : {}),\n ...(nullableValue === true ? { nullable: true } : {}),\n }) as FieldDeclaration;\n}\n\n/**\n * Build the safe source accepted by a React field surface. Use this when a\n * legacy field must be lowered before it reaches `ResolvedField*` (for\n * example action-modal fields): semantic data comes from the shared lowering,\n * while inert UI hints such as file storage/accept/cardinality stay available\n * to the host implementation.\n */\nexport function lowerLegacyFieldSource(field: LegacyFieldLike): LoweredLegacyFieldSource {\n const declaration = lowerLegacyField(field);\n const ui = projectUiHints(ownData(field, 'ui').value);\n if (ui === undefined) return declaration as LoweredLegacyFieldSource;\n return Object.freeze({ ...declaration, ui }) as LoweredLegacyFieldSource;\n}\n\n/**\n * A field's description, the hint under its input: its `texts.description` (the field's\n * `declaration` is asked first, then the field itself). It is the code default of the one slot\n * `fields.<name>.description`, which wins at render; core's collector reads the same slot.\n */\nexport function fieldDescription(field: unknown): string | undefined {\n const declaration = (field as { readonly declaration?: unknown } | undefined)?.declaration;\n return (\n declaredText('field', declaration, 'description') ?? declaredText('field', field, 'description')\n );\n}\n","'use client';\nimport {\n formatEntityRef,\n canonicalResourceKey,\n resolveSurfaceUi,\n type EntityRef,\n type FieldContextSources,\n type FieldDeclaration,\n type FieldOption,\n type FieldStatus,\n type FixedFilterInput,\n type ScreenSources,\n type UiSurface,\n type ValidationIssues,\n} from '@burojs/core';\nimport {\n scopeValueOf,\n useFieldDisplayText,\n useFieldImplementation,\n useFieldMeta,\n useFieldMetaSources,\n useI18nCaps,\n useImplementationTexts,\n useLocale,\n useOptionalT,\n useResolvedFieldOptions,\n type FieldBinding,\n type FieldI18n,\n type FieldLinkState,\n type FieldMetaState,\n type FieldPresentation,\n type FieldRenderContext,\n} from '@burojs/react';\nimport {\n createElement,\n useEffect,\n useMemo,\n useRef,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Skeleton } from '../primitives/skeleton.js';\nimport { lowerLegacyField, lowerLegacyFieldSource, type LegacyFieldLike } from './legacy-field.js';\n\ntype FieldLike = LegacyFieldLike & {\n readonly format?: string;\n readonly formatSemantics?: { readonly id?: string; readonly fallback?: string };\n readonly items?: FieldDeclaration;\n readonly meta?: Readonly<Record<string, unknown>>;\n /** See `LegacyFieldLike.ui` — an opaque bag, so a canonical `FieldDeclaration` stays assignable. */\n readonly ui?: object;\n readonly options?: FieldDeclaration['options'];\n readonly required?: boolean;\n};\n\nconst EMPTY = Object.freeze({});\nconst EMPTY_ISSUES = Object.freeze([]);\nconst EMPTY_OPTIONS = Object.freeze([]) as readonly FieldOption[];\n\nfunction canonical(field: FieldLike): FieldDeclaration {\n return lowerLegacyField(field);\n}\n\n/**\n * The field lowered once per source object: its edge projection (the whole `ui` kept), the\n * canonical declaration, and the `ui` bag. Stable identities across renders, so the surface's\n * params, the merged `meta ⊕ params` record and the validation cache keep hitting (C23).\n */\nfunction useLoweredField(source: FieldLike) {\n return useMemo(() => {\n const sourceAtEdge = lowerLegacyFieldSource(source);\n return {\n sourceAtEdge,\n field: canonical(sourceAtEdge),\n ui: (sourceAtEdge.ui ?? EMPTY) as Readonly<Record<string, unknown>>,\n };\n }, [source]);\n}\n\nconst warnedObjectLabels = new Set<string>();\n\n/**\n * Canonical `options` win. During the compatibility migration, value-enum\n * builders still carry their choices in `meta.options`; normalize that trusted\n * legacy shape once here so every resolver surface sees the same contract. An\n * entry whose label is not a string (a JavaScript caller's `{ ru, en }`) is\n * kept under its raw value, with one development warning naming the fix.\n */\nfunction optionsFor(field: FieldDeclaration, name: string): readonly FieldOption[] {\n if (Array.isArray(field.options)) return field.options;\n const candidate = field.meta?.options;\n if (!Array.isArray(candidate)) return EMPTY_OPTIONS;\n const output: FieldOption[] = [];\n for (const item of candidate) {\n if (item === null || typeof item !== 'object' || Array.isArray(item)) continue;\n const raw = item as Record<string, unknown>;\n if (\n typeof raw.value !== 'string' &&\n typeof raw.value !== 'number' &&\n typeof raw.value !== 'boolean' &&\n raw.value !== null\n )\n continue;\n const { value, label, description, ...legacyMeta } = raw;\n if (\n typeof label !== 'string' &&\n process.env.NODE_ENV !== 'production' &&\n !warnedObjectLabels.has(`${name}:${value}`)\n ) {\n warnedObjectLabels.add(`${name}:${value}`);\n // eslint-disable-next-line no-console\n console.warn(\n `[buro/ui-shadcn] field '${name}': option '${value}' has a non-string label, so it renders its raw value. Declare a plain string label and put the translations under fields.${name}.options.${value} (see docs/i18n-guide.md).`,\n );\n }\n output.push(\n Object.freeze({\n value,\n label: typeof label === 'string' ? label : String(value),\n ...(typeof description === 'string' ? { description } : {}),\n ...(Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }),\n }) as FieldOption,\n );\n }\n return Object.freeze(output);\n}\n\n/**\n * The context's options, resolved for the current locale from the same name, `meta`, `ui` and\n * `texts` the built-in enum controls read through the control adapter, so a custom\n * implementation's `label` is the built-in display's text.\n */\nfunction useContextOptions(\n field: FieldDeclaration,\n name: string,\n meta: Readonly<Record<string, unknown>> | undefined,\n ui: Readonly<Record<string, unknown>>,\n resource: EntityRef,\n) {\n // `optionsFor` reads only these two arrays; a legacy `meta.options` is normalised into a fresh\n // array, so keep it while they are the same.\n const legacy = field.meta?.options;\n // biome-ignore lint/correctness/useExhaustiveDependencies: `optionsFor` reads only `field.options` and `field.meta.options`\n const options = useMemo(() => optionsFor(field, name), [field.options, legacy, name]);\n return useResolvedFieldOptions(\n { name, meta, ui, texts: (field as { readonly texts?: unknown }).texts },\n options,\n resource.id,\n );\n}\n\n/**\n * The context's text slots: the resolved implementation's `texts`, read for the current locale\n * under the field's name and declared `texts.impl`, with the caps its options are read with.\n */\nfunction useContextTexts(\n field: FieldDeclaration,\n name: string,\n resolved: {\n readonly id?: string;\n readonly implementation?: { readonly texts?: Readonly<Record<string, string>> };\n },\n resource: EntityRef,\n) {\n return useImplementationTexts(\n {\n fieldName: name,\n fieldTexts: (field as { readonly texts?: unknown }).texts,\n id: resolved.id,\n implementation: resolved.implementation,\n },\n resource.id,\n );\n}\n\nfunction useFieldRuntimeContext(\n resource: EntityRef,\n record: Readonly<Record<string, unknown>> | undefined,\n values: Readonly<Record<string, unknown>> | undefined,\n metaSources: ScreenSources | undefined,\n): Readonly<{\n readonly sources: FieldContextSources;\n readonly i18n: FieldI18n;\n readonly metaSources: ScreenSources;\n}> {\n const locale = useLocale();\n // `resource.id` alone aliases `hasura:user` and `supabase:user`, so a non-default namespace\n // keeps `ns:id`. The default namespace is the short name, the spelling the bundle and the\n // declaration layer write (`resources.doc.*`); `default:doc` would read a key nothing holds.\n const caps = useI18nCaps(canonicalResourceKey(resource));\n // What a field's `meta` refs read (P7 3.2): the owner's projected states when it passes them,\n // else this field's own `record` and `values` props. Never anything else.\n const metaSourcesOut = useFieldMetaSources({ record, values, extra: metaSources });\n // The scope an implementation resolves its option refs over is the one its `meta` reads (7d Q3,\n // `useScopeSource`): the owner's, or this field's own.\n const scopeState = metaSourcesOut.scope;\n const scope = (scopeState === undefined ? undefined : scopeValueOf(scopeState)) as\n | Readonly<Record<string, unknown>>\n | undefined;\n return useMemo(\n () =>\n Object.freeze({\n i18n: Object.freeze({ locale, has: caps.has, t: caps.t }),\n sources: Object.freeze({\n record: record ?? EMPTY,\n values: values ?? EMPTY,\n scope: scope ?? EMPTY,\n }),\n metaSources: metaSourcesOut,\n }),\n [caps, locale, record, scope, values, metaSourcesOut],\n );\n}\n\n/**\n * One development message per mounted field and refusal (P7 3.3): which field, which\n * implementation and why. Never in production, where the field only draws as misconfigured.\n */\nexport function useFieldMetaDiagnostic(\n state: FieldMetaState,\n resource: EntityRef,\n name: string,\n field: { readonly ui?: unknown; readonly format?: string; readonly type?: string },\n surface: UiSurface,\n entry?: { readonly display?: unknown },\n): void {\n const reported = useRef<string | undefined>(undefined);\n const reason = state.status === 'failed' ? state.reason : undefined;\n useEffect(() => {\n if (\n reason === undefined ||\n reported.current === reason ||\n process.env.NODE_ENV === 'production'\n )\n return;\n reported.current = reason;\n const implementation =\n resolveSurfaceUi(field, surface, entry).use ?? field.format ?? field.type;\n // eslint-disable-next-line no-console\n console.error(\n `[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`,\n );\n }, [reason, resource, name, field, surface, entry]);\n}\n\n/**\n * What a field draws while its `meta` is not ready (P7 3.2): a skeleton while a source or an\n * asynchronous schema is pending, an inert labelled control once it was refused. Never the\n * implementation, which would read a `meta` it was not given.\n */\nexport function MetaPlaceholder({ state }: { readonly state: FieldMetaState }): ReactElement {\n const t = useOptionalT();\n if (state.status === 'pending')\n return <Skeleton data-buro-skeleton=\"\" data-buro-field-meta=\"pending\" className=\"h-9 w-full\" />;\n return (\n <div\n role=\"alert\"\n data-buro-field-meta=\"failed\"\n className=\"rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground\"\n >\n {t('field.misconfigured', { defaultValue: 'This field is misconfigured' })}\n </div>\n );\n}\n\n/** Report a fail-soft resolver miss once per mounted field and diagnostic. */\nfunction useFieldImplementationDiagnostic(diagnostic: unknown): void {\n const reported = useRef<unknown>(undefined);\n useEffect(() => {\n if (\n diagnostic === undefined ||\n reported.current === diagnostic ||\n process.env.NODE_ENV === 'production'\n )\n return;\n reported.current = diagnostic;\n // eslint-disable-next-line no-console\n console.warn('[buro/ui-shadcn] missing-field-implementation; rendering fallback.', diagnostic);\n }, [diagnostic]);\n}\n\nexport interface ResolvedFieldDisplayProps {\n readonly resource: EntityRef;\n readonly name: string;\n readonly field: FieldLike;\n readonly value: unknown;\n readonly record?: Readonly<Record<string, unknown>>;\n readonly testId?: string;\n readonly fallback: ReactNode;\n readonly presentation?: FieldPresentation;\n /**\n * The owner's own source states, by name (P7 3.2): a form controller's projected `record` and\n * `values`, a flow's `with` and `args`. They replace the sources built from `record` and\n * `form`, so a field P5 withholds stays pending in a `meta` ref as it does everywhere else.\n */\n readonly metaSources?: ScreenSources;\n /**\n * The surface the display is drawn on (REF-117 §4.13): `'display'` (the default) for record\n * screens, `'column'` for a table cell, `'card'` for the default card item. Its layers select\n * the implementation, its params and its transform.\n */\n readonly uiSurface?: Exclude<UiSurface, 'input'>;\n /** A table cell's list entry, the last layer of the `column` surface. */\n readonly columnEntry?: { readonly display?: unknown };\n /**\n * The object property paths the form's plan holds absent by a `when` (REF-132 1a-9,\n * `ScreenPlan.absentPaths`), dotted from the record root (`'head.vsd'`), at or below this field.\n * An object group draws no property at or below one of them. Absent: every property is drawn.\n */\n readonly absentPaths?: readonly string[];\n /**\n * Each object path below this field whose status path rules decide (REF-131 S1,\n * `ScreenPlan.fieldPaths`), dotted from the record root (`'head.y'`). An object group draws an\n * `inactive` part not at all and any other part that is not `active` read-only. Absent: every\n * part follows the field.\n */\n readonly pathStatus?: Readonly<Record<string, FieldStatus>>;\n}\n\nexport interface ResolvedFieldInputProps\n extends Omit<ResolvedFieldDisplayProps, 'fallback' | 'uiSurface' | 'columnEntry'> {\n readonly inputId?: string;\n readonly onChange: (value: unknown) => void;\n /** Live form values, distinct from the edit record baseline. */\n readonly form?: Readonly<Record<string, unknown>>;\n /**\n * What a `changed` operand compares against: the form controller's starting values (seeded on a\n * create). An enum picker reads its available set against them. Absent: `record`.\n */\n readonly baseline?: Readonly<Record<string, unknown>>;\n readonly onBlur?: () => void;\n readonly disabled?: boolean;\n readonly required?: boolean;\n readonly placeholder?: string;\n readonly descriptionId?: string;\n /**\n * Id of the label the surface renders for this field. A control that a\n * `<label for>` cannot name (an enum's radio group) uses it as its\n * `aria-labelledby`.\n */\n readonly labelId?: string;\n readonly errorId?: string;\n readonly errorMessage?: string | null;\n /** Current lifecycle state from the enclosing form. */\n readonly pending?: boolean;\n readonly touched?: boolean;\n /** Optional canonical issues; localized errorMessage remains the fallback. */\n readonly issues?: ValidationIssues;\n /**\n * A relation select's create seeds (P6-31), from the form controller's binding. A separate\n * channel from `field.meta`: the seeds' provenance is the code registry, and `meta` can be\n * written by a layer.\n */\n readonly relationSeeds?: Readonly<Record<string, unknown>>;\n /**\n * A relation select's extra filters from the form's `filter` function (P7 3.4): code, handed\n * down by the form controller on its own channel, applied on top of the field's `candidates`.\n * Never read from `field.meta`, which a layer can write.\n */\n readonly fixedFilters?: readonly FixedFilterInput[];\n /**\n * A relation select's option subtitle (the field's `optionSubtitle`, REF-136): code, handed down\n * by the form controller on its own channel, as `fixedFilters` is.\n */\n readonly optionSubtitle?: FieldBinding['optionSubtitle'];\n /** An inline link field's rights, hidden keys and load state, from the form controller. */\n readonly link?: FieldLinkState;\n readonly fallback: ReactNode;\n}\n\n/** Render a display through the sole app-scoped canonical implementation resolver. */\nexport function ResolvedFieldDisplay({\n resource,\n name,\n field: source,\n value,\n record,\n testId,\n fallback,\n presentation,\n metaSources,\n uiSurface = 'display',\n columnEntry,\n absentPaths,\n pathStatus,\n}: ResolvedFieldDisplayProps): ReactElement {\n const { sourceAtEdge, field, ui } = useLoweredField(source);\n // The resolver and the text read every layer of the surface (`ui.column`, `ui.card`), which\n // the canonical declaration does not carry: the field with its whole `ui`.\n const surfaceField = useMemo(\n () => (sourceAtEdge.ui === undefined ? field : Object.freeze({ ...field, ui })),\n [sourceAtEdge, field, ui],\n );\n const resolved = useFieldImplementation({\n field: surfaceField,\n surface: 'display',\n uiSurface,\n ...(columnEntry === undefined ? {} : { uiEntry: columnEntry }),\n });\n // Spec 6.1: in show there is no form, so `values` is the record itself.\n const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);\n // The surface's params merge over `meta` (§4.12); `resolved.params` is absent when a `use`\n // did not resolve, so a fallback is judged on `meta` alone.\n const metaState = useFieldMeta(\n field,\n resolved.implementation,\n runtimeContext.metaSources,\n resolved.params,\n );\n const textField = useMemo(() => Object.freeze({ ...surfaceField, name }), [surfaceField, name]);\n const text = useFieldDisplayText(textField, value, {\n surface: uiSurface,\n ...(columnEntry === undefined ? {} : { entry: columnEntry }),\n resource: canonicalResourceKey(resource),\n // A `ref('record', …)` zone reads the row.\n ...(record === undefined ? {} : { record }),\n });\n const options = useContextOptions(\n field,\n name,\n metaState.status === 'ready' ? metaState.meta : undefined,\n ui,\n resource,\n );\n const texts = useContextTexts(field, name, resolved, resource);\n // The date keys a column, card or list-entry layer sets (REF-118 §4.8) land on `ui.display`,\n // where a date renderer reads them: the context's `ui` carries the surface's effective keys.\n const dateDisplay = resolveSurfaceUi(surfaceField, uiSurface, columnEntry).dateDisplay;\n const contextUi = useMemo(\n () =>\n dateDisplay === undefined\n ? ui\n : Object.freeze({\n ...ui,\n display: Object.freeze({\n ...(typeof ui.display === 'object' && ui.display !== null ? ui.display : {}),\n ...dateDisplay,\n }),\n }),\n [ui, dateDisplay],\n );\n useFieldMetaDiagnostic(metaState, resource, name, surfaceField, uiSurface, columnEntry);\n useFieldImplementationDiagnostic(resolved.diagnostic);\n if (resolved.Component === undefined) return <>{fallback}</>;\n if (metaState.status !== 'ready') return <MetaPlaceholder state={metaState} />;\n const Component = resolved.Component;\n const context: FieldRenderContext = {\n owner: resource,\n resource,\n field,\n path: Object.freeze([name]),\n ...(absentPaths === undefined || absentPaths.length === 0 ? {} : { absentPaths }),\n ...(pathStatus === undefined ? {} : { pathStatus }),\n surface: 'display',\n presentation: presentation ?? 'default',\n record,\n meta: metaState.meta,\n ui: contextUi,\n options,\n texts,\n ...(text === undefined ? {} : { text }),\n i18n: runtimeContext.i18n,\n sources: runtimeContext.sources,\n pending: false,\n touched: false,\n issues: EMPTY_ISSUES,\n accessibility: EMPTY,\n testIds: testId === undefined ? EMPTY : Object.freeze({ display: testId }),\n };\n if (resolved.multiple) {\n return createElement(\n Component as never,\n { ...context, multiple: true, value: Array.isArray(value) ? value : [] } as never,\n );\n }\n return createElement(Component as never, { ...context, multiple: false, value } as never);\n}\n\n/** Render an editable field through the same app-scoped resolver as displays. */\nexport function ResolvedFieldInput({\n resource,\n name,\n field: source,\n value,\n record,\n form,\n testId,\n inputId,\n onChange,\n onBlur,\n disabled,\n required,\n placeholder,\n descriptionId,\n labelId,\n errorId,\n errorMessage,\n pending = false,\n touched = errorMessage != null,\n issues: suppliedIssues,\n relationSeeds,\n fixedFilters,\n optionSubtitle,\n link,\n metaSources,\n baseline,\n fallback,\n absentPaths,\n pathStatus,\n}: ResolvedFieldInputProps): ReactElement {\n const { sourceAtEdge, field: base } = useLoweredField(source);\n const field = useMemo(\n () =>\n required === undefined || required === base.required\n ? base\n : (Object.freeze({ ...base, required }) as FieldDeclaration),\n [base, required],\n );\n // One object per field and placeholder: the resolver's params are cached by `ui`'s identity,\n // and the merged meta by theirs (C23), so a fresh `ui` per render would re-validate each time.\n const ui = useMemo(\n () =>\n Object.freeze({\n ...(sourceAtEdge.ui ?? EMPTY),\n ...(placeholder === undefined ? {} : { placeholder }),\n }),\n [sourceAtEdge, placeholder],\n );\n const inputField = useMemo(() => ({ ...field, ui }), [field, ui]);\n const resolved = useFieldImplementation({ field: inputField, surface: 'input' });\n const runtimeContext = useFieldRuntimeContext(resource, record, form, metaSources);\n // The input's params merge over `meta` (§4.12), as the planner judged the leaf.\n const metaState = useFieldMeta(\n field,\n resolved.implementation,\n runtimeContext.metaSources,\n resolved.params,\n );\n const options = useContextOptions(\n field,\n name,\n metaState.status === 'ready' ? metaState.meta : undefined,\n ui,\n resource,\n );\n const texts = useContextTexts(field, name, resolved, resource);\n useFieldMetaDiagnostic(metaState, resource, name, inputField, 'input');\n useFieldImplementationDiagnostic(resolved.diagnostic);\n if (resolved.Component === undefined) return <>{fallback}</>;\n if (metaState.status !== 'ready') return <MetaPlaceholder state={metaState} />;\n const Component = resolved.Component;\n // `errorMessage` is a bare string with no severity of its own — by\n // contract (the prop's name) it always denotes a blocking ERROR, so this\n // is the only severity this synthesis path may ever produce. A caller with\n // non-error diagnostics (severity 'warning' | 'info') must not funnel them\n // through `errorMessage`; it must supply them via `issues` (below) instead,\n // where they pass through untouched and keep their real severity — that is\n // what lets `default-renderers.ts`'s `ariaInvalid` (error-only) and any\n // consumer's non-blocking warning UI tell them apart from a real error.\n const issues =\n suppliedIssues ??\n (errorMessage == null\n ? EMPTY_ISSUES\n : Object.freeze([\n Object.freeze({\n code: 'ui.field-error',\n message: { key: 'ui.field-error', default: errorMessage },\n path: Object.freeze([]),\n source: 'field' as const,\n severity: 'error' as const,\n }),\n ]));\n const context: FieldRenderContext = {\n owner: resource,\n resource,\n field,\n path: Object.freeze([name]),\n ...(absentPaths === undefined || absentPaths.length === 0 ? {} : { absentPaths }),\n ...(pathStatus === undefined ? {} : { pathStatus }),\n surface: 'input',\n // Inputs always render in a surface we control; the axis exists on the\n // shared context so displays can read it, not because inputs vary yet.\n presentation: 'default',\n record,\n form,\n meta: metaState.meta,\n ui,\n options,\n texts,\n i18n: runtimeContext.i18n,\n sources: runtimeContext.sources,\n pending,\n touched,\n issues,\n accessibility: Object.freeze({\n input: inputId ?? name,\n label: labelId,\n description: descriptionId,\n error: errorId,\n }),\n testIds: testId === undefined ? EMPTY : Object.freeze({ input: testId }),\n };\n if (resolved.multiple) {\n return createElement(\n Component as never,\n {\n ...context,\n multiple: true,\n value: Array.isArray(value) ? value : [],\n onChange,\n onBlur,\n disabled,\n relationSeeds,\n fixedFilters,\n optionSubtitle,\n link,\n metaSources: runtimeContext.metaSources,\n ...(baseline === undefined ? {} : { baseline }),\n } as never,\n );\n }\n return createElement(\n Component as never,\n {\n ...context,\n multiple: false,\n value,\n onChange,\n onBlur,\n disabled,\n relationSeeds,\n fixedFilters,\n optionSubtitle,\n link,\n metaSources: runtimeContext.metaSources,\n ...(baseline === undefined ? {} : { baseline }),\n } as never,\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\nexport type SelectProps = React.ComponentProps<typeof SelectPrimitive.Root>;\n\n/**\n * Radix's `Root`, without its echo of a value it cannot show. Inside a `<form>`, Radix mirrors\n * the value into a hidden native `<select>`: when the `value` prop changes it writes the new\n * value there, dispatches `change`, and reports what the native select reads to `onValueChange`.\n * When the new value's `<option>` is not mounted yet — a loader enum's items mount in the same\n * commit as its stored value, and so does the \"current\" item of a value the select does not\n * offer — the native select reads '', and a stored value came back as a change to ''. No item can\n * carry '' (Radix refuses `<SelectItem value=\"\">`), so '' is never a pick and is not reported.\n */\nfunction Select({ onValueChange, ...props }: SelectProps): React.ReactElement {\n return (\n <SelectPrimitive.Root\n {...props}\n {...(onValueChange === undefined\n ? {}\n : {\n onValueChange: (next: string) => {\n if (next !== '') onValueChange(next);\n },\n })}\n />\n );\n}\nSelect.displayName = SelectPrimitive.Root.displayName;\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]:block [&>span]:truncate [&>span]:min-w-0 [&>span]:max-h-[1lh] [&>span]:overflow-hidden [&>span]:text-left',\n className,\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <IconChevronDown className=\"h-4 w-4 shrink-0 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 /**\n * Secondary text rendered under the label. It sits outside `ItemText` on\n * purpose: Radix clones everything inside `ItemText` into the closed trigger,\n * so a description placed there would be glued onto the selected value.\n */\n description?: React.ReactNode;\n};\n\nconst SelectItem = React.forwardRef<React.ElementRef<typeof SelectPrimitive.Item>, SelectItemProps>(\n ({ className, children, description, ...props }, ref) => {\n const hasDescription = description !== undefined && description !== null;\n return (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex w-full cursor-default select-none 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 hasDescription ? 'items-start' : 'items-center',\n className,\n )}\n {...props}\n >\n <span\n className={cn(\n 'absolute left-2 flex h-3.5 w-3.5 items-center justify-center',\n // Aligned with the label's first line (6px padding + half of the 20px line - 7px), not\n // centred across label and summary.\n hasDescription && 'top-[9px]',\n )}\n >\n <SelectPrimitive.ItemIndicator>\n <IconCheck className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n {!hasDescription ? (\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n ) : (\n <span className=\"flex min-w-0 flex-col gap-0.5\">\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <span className=\"text-xs leading-snug text-muted-foreground\">{description}</span>\n </span>\n )}\n </SelectPrimitive.Item>\n );\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 */\nexport function localeTag(locale?: string): string {\n return locale || loadLocale() || 'en-US';\n}\n\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 * 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 { FieldZone, 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. Every key it reads is kit chrome, so\n * the caller adapts `useChromeT` (unscoped), passed here as `chromeT`.\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 = (\n kind: FilterKind,\n value: PortableValue,\n /** The field's own zone (`FilterFieldSpec.zone`), for a date-time value. */\n zone?: FieldZone,\n) => string | undefined;\n\nfunction describeValue(\n value: PortableValue | undefined,\n chromeT: 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 chromeT('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, spec.zone);\n if (formatted !== undefined) return formatted;\n }\n return String(value);\n}\n\nfunction describeAt(\n row: FilterGroupRow,\n available: readonly FilterFieldSpec[],\n chromeT: DescribeRowTranslate,\n depth: number,\n formatValue: FormatValue | undefined,\n): string {\n if (isFilterGroup(row)) {\n if (depth > MAX_DESCRIBE_DEPTH) return '…';\n const joinLabel = chromeT(`filters.${row.join}`, undefined, row.join);\n const inner = row.rows\n .map((child) => describeAt(child, available, chromeT, 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 = chromeT(`filters.operators.${row.op}`, undefined, row.op);\n const valueText =\n opArity(row.op) === 'none' ? null : describeValue(row.value, chromeT, 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 chromeT: DescribeRowTranslate,\n formatValue?: FormatValue,\n): string {\n return describeAt(row, available, chromeT, 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, { recordTitle: true });\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 { formatTimeValue, isFilterGroup, resolveTimeZone } from '@burojs/core';\nimport type {\n ErasedResourceHandle,\n FilterExpression,\n FilterGroup,\n FilterGroupRow,\n ResolvedFixedFilter,\n} from '@burojs/core';\nimport { useChromeT, useOptionalLocale, useTimeContext, useTimeZone } from '@burojs/react';\nimport type { FilterFieldSpec } from '@burojs/react';\nimport { IconX } from '@tabler/icons-react';\nimport { useMemo } from 'react';\nimport { localeTag } 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 */\n// `Badge` wraps its children in a non-flex span (for text truncation), so a badge's own `gap` and\n// `items-center` never reach the label and the ×: this inner row lays them out.\nconst BADGE_ROW = 'inline-flex max-w-full items-center gap-1 align-middle';\n// A round hit area on the badge's own fill: `hover:bg-accent` is the same token as `secondary` in\n// the default theme, so the old hover drew nothing and the × blended into the badge.\nconst REMOVE_BUTTON =\n 'inline-flex size-4 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-foreground/10 hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring';\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 chromeT = useChromeT();\n // The live locale for the temporal formatters below — same subscription as\n // `DateDisplay`, so a badge re-formats on a language switch.\n const locale = useOptionalLocale();\n // The viewer's own zone, for the zone name; a chip reads the field's zone through the context.\n const viewer = useTimeZone();\n const timeContext = useTimeContext();\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 `chromeT` as the count, where it also interpolates.\n const tt: DescribeRowTranslate = (key, params, fallback) =>\n chromeT(key, {\n params,\n ...(typeof params?.count === 'number' ? { count: params.count } : {}),\n defaultValue: fallback,\n });\n const removeLabel = chromeT('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, fieldZone) => {\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 — an ISO instant for a date-time, a plain\n // `YYYY-MM-DD` for a calendar date. Printed verbatim, the badge hands the reader an ISO string\n // directly beside the control they picked a wall time in. The same formatter the list cells\n // and show panels use, so a filtered-on value reads exactly like the column it filters. A\n // calendar date is never handed to `new Date` (it would land on the day before west of UTC).\n // `undefined` for text the kind does not parse (a hand-edited URL, a half-typed value) falls\n // through to the raw text in `describeValue`.\n if (kind.kind === 'date' && viewer.status === 'ready') {\n // A filter has no row, so a `ref('record', …)` zone reads nothing and falls through.\n const zone = resolveTimeZone(timeContext.levels, fieldZone, timeContext.sources);\n if (zone.status === 'pending') return undefined;\n return formatTimeValue(kind.time ? 'dateTime' : 'date', String(value), {\n zone: zone.zone,\n viewerZone: viewer.zone,\n locale: localeTag(locale),\n params: {},\n });\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=\"py-1 pr-1\">\n <span className={BADGE_ROW}>\n <button\n type=\"button\"\n data-testid={`${testIdBase}:badge:${index}`}\n onClick={() => onOpenAt([index])}\n className=\"min-w-0 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={REMOVE_BUTTON}\n >\n <IconX className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </span>\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=\"py-1 pr-1\">\n <span className={BADGE_ROW}>\n <span data-testid={`${testIdBase}:badge:pinned:${index}`} className=\"min-w-0 truncate\">\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={REMOVE_BUTTON}\n >\n <IconX className=\"size-3\" aria-hidden=\"true\" />\n </button>\n </span>\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 { useChromeT, 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 const chromeT = useChromeT();\n\n // SP2 final-review finding 7: include the field's own label when given.\n const ariaLabel = fieldLabel\n ? chromeT('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : chromeT('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 = chromeT('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, props.placeholder ?? 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 { useChromeT, 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 /** The trigger's text while nothing is selected (`FilterValueControlProps.placeholder`). */\n readonly placeholder?: 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 const chromeT = useChromeT();\n\n const ariaLabel = fieldLabel\n ? chromeT('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : chromeT('filters.value', { defaultValue: 'Value' });\n const loadingLabel = t('table.loading', { defaultValue: 'Loading…' });\n const clearLabel = chromeT('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, props.placeholder ?? 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 placeholder={props.placeholder}\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, { recordTitle: true });\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, useChromeT, 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 chromeT = useChromeT();\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, { recordTitle: true });\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 ? chromeT('filters.valueFor', {\n defaultValue: `Value for ${fieldLabel}`,\n params: { field: fieldLabel },\n })\n : chromeT('filters.value', { defaultValue: 'Value' });\n const clearLabel = chromeT('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, props.placeholder ?? 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","import {\n type DateDisplayParams,\n type FieldZone,\n type ScreenSources,\n type ZoneResolution,\n} from '@burojs/core';\nimport { normaliseFieldZone } from '@burojs/core/internal';\nimport { useTimeContext, useTimeZone } from '@burojs/react';\nimport { useMemo } from 'react';\n\n/**\n * A date field's resolved zone (REF-118 §4.2): the field's own `zone`, then the user's, the app's\n * and the host's. A `ref('record', …)` zone reads `row`: the record on a display, the form's live\n * values on an input (C5), so changing the zone column re-resolves the field. `pending` while a\n * source the zone reads is pending.\n */\nexport function useFieldTimeZone(\n zone: unknown,\n row: Readonly<Record<string, unknown>> | undefined,\n): ZoneResolution {\n const { sources: session } = useTimeContext();\n const sources = useMemo<ScreenSources>(\n () =>\n row === undefined\n ? session\n : { ...session, record: { status: 'ready', value: row, refreshing: false } },\n [session, row],\n );\n const fieldZone = useMemo<FieldZone | undefined>(() => normaliseFieldZone(zone), [zone]);\n return useTimeZone(fieldZone, sources);\n}\n\nconst DATE_KEYS = ['dateStyle', 'timeStyle', 'zoneName'] as const;\n\n/**\n * The date display params a field declares on its display layer (`ui.display.dateStyle`,\n * `timeStyle`, `zoneName`), as the renderer receives them through `FieldUiHints`.\n */\nexport function dateDisplayParams(\n ui: Readonly<Record<string, unknown>> | undefined,\n): DateDisplayParams {\n const display = ui?.display;\n if (display === null || typeof display !== 'object') return {};\n const params: Record<string, unknown> = {};\n for (const key of DATE_KEYS) {\n const value = (display as Record<string, unknown>)[key];\n if (value !== undefined) params[key] = value;\n }\n return params as DateDisplayParams;\n}\n","import {\n type TimeZoneName,\n canonicalInstant,\n instantToWall,\n isCalendarDate,\n isWallTime,\n wallToInstant,\n} from '@burojs/core';\n\n/**\n * The two directions between a stored date-time instant and the text of a\n * `datetime-local` box, shared by the date-time field input and the filter\n * builder's date-time value control.\n *\n * A date-time is stored as an ISO instant, but a `datetime-local` box speaks\n * wall 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 is projected into the field's\n * resolved zone on the way in and resolved back to an instant on the way out.\n * The host's own zone never takes part (REF-118 §4.8).\n */\n\n/**\n * The stored instant as a `datetime-local` box wants to see it, in `zone`. An empty or\n * unparseable value renders as empty rather than as garbage the box would silently reject anyway.\n */\nexport function toLocalDateTimeText(value: unknown, zone: TimeZoneName): string {\n if (value === undefined || value === null || value === '') return '';\n const instant = canonicalInstant(value instanceof Date ? value.toISOString() : String(value));\n if (instant === undefined) return '';\n const { date, time } = instantToWall(instant, zone);\n return `${date}T${time.slice(0, 5)}`;\n}\n\n/**\n * The box's text resolved to a canonical instant, reading it as the wall time in `zone`;\n * `undefined` for empty or unparseable text. The raw text carries no zone at all, and a backend\n * reading it as UTC (a `timestamptz` column does) would shift the value by the zone's offset — so\n * the wall time is resolved here, once, where the zone is actually known. A wall time that falls\n * in a gap moves forward by the gap's length.\n */\nexport function fromLocalDateTimeText(text: string, zone: TimeZoneName): string | undefined {\n const [date, time, ...rest] = text.split('T');\n if (date === undefined || time === undefined || rest.length > 0) return undefined;\n if (!isCalendarDate(date) || !isWallTime(time)) return undefined;\n return wallToInstant(date, time, zone);\n}\n","'use client';\nimport type { FieldZone, PortableValue, TimeZoneName } from '@burojs/core';\nimport { useChromeT } from '@burojs/react';\nimport { IconX } from '@tabler/icons-react';\nimport { useEffect, useRef, useState } from 'react';\nimport { useFieldTimeZone } from '../../../fields/field-time-zone.js';\nimport { fromLocalDateTimeText, toLocalDateTimeText } from '../../../fields/local-date-time.js';\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/**\n * The zone a date-time filter box is read and written in: the field's own, else the user's, the\n * app's and the host's (REF-118 §4.2). A filter control has no record, so a `ref('record', …)`\n * zone reads nothing and falls through.\n */\nfunction useFilterZone(fieldZone: FieldZone | undefined): TimeZoneName {\n const zone = useFieldTimeZone(fieldZone, undefined);\n return zone.status === 'ready' ? zone.zone : 'UTC';\n}\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, projected into `zone` for the `datetime-local` box (see\n * `local-date-time.ts`).\n */\nfunction toText(kind: Kind, value: PortableValue | undefined, zone: TimeZoneName): string {\n if (isDateTime(kind)) return toLocalDateTimeText(value, zone);\n if (value === undefined || value === null || value === '') return '';\n return String(value);\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, zone: TimeZoneName): 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)) return fromLocalDateTimeText(raw, zone);\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 chromeT = useChromeT();\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 ? chromeT('filters.valueFor', {\n defaultValue: `Value for ${props.fieldLabel}`,\n params: { field: props.fieldLabel },\n })\n : chromeT('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 zone = useFilterZone(props.zone);\n const storedText = toText(kind, value, zone);\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, zone);\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, zone);\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 const zone = useFilterZone(props.zone);\n\n const addChip = () => {\n const parsed = parseOne(kind, draft.trim(), zone);\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 { FieldZone, 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`), and the empty\n * text of a select-like trigger (enum, options, relation, boolean), whose\n * accessible name stays \"Value for <field>\". 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 * `FilterFieldSpec.zone`: the zone a date-time box reads and writes in, before the user's, the\n * app's and the host's (REF-118 §4.2). Other kinds ignore it.\n */\n readonly zone?: FieldZone;\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 placeholder={props.placeholder}\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 placeholder={props.placeholder}\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 { useChromeT } 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 chromeT = useChromeT();\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 = chromeT('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 = chromeT('filters.field', { defaultValue: 'Field' });\n const operatorAriaLabel = chromeT('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 {chromeT(`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 {...(spec.zone === undefined ? {} : { zone: spec.zone })}\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 { useChromeT } 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 chromeT = useChromeT();\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 =>\n chromeT(`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 && chromeT('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={chromeT('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 {chromeT('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 {chromeT('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 {chromeT('filters.removeGroup', { defaultValue: 'Remove group' })}\n </Button>\n )}\n </div>\n </div>\n );\n}\n","'use client';\nimport type { FilterGroup } from '@burojs/core';\nimport { useChromeT } 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 chromeT = useChromeT();\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 {chromeT('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 {chromeT('filters.reset', { defaultValue: 'Reset' })}\n </Button>\n <Button type=\"button\" size=\"sm\" data-testid={`${testIdBase}:apply`} onClick={handleApply}>\n {chromeT('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 {...(spec.zone === undefined ? {} : { zone: spec.zone })}\n />\n );\n}\n","'use client';\nimport type { Column } from '@tanstack/react-table';\nimport { useChromeT, useOptionalT } from '@burojs/react';\nimport { IconCheck, IconCirclePlus } from '@tabler/icons-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { cn } from '../lib/cn.js';\nimport { Badge } from '../primitives/badge.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';\n\n/**\n * Multi-select faceted filter for a single TanStack Table column, built\n * on `cmdk` (`Command`) for keyboard-navigable search-while-typing.\n *\n * Design decisions:\n * - cmdk's default client-side filter (`shouldFilter`) narrows options by\n * each item's `value` (the option label) as the user types in the\n * `<CommandInput>`. For static option sets that is exactly what we want;\n * async/relation-backed options (Task F2/F3) flip to\n * `shouldFilter={false}` and drive the list from server results.\n * - NO facet counts are rendered. Under server-side pagination the counts\n * from `getFacetedUniqueValues` reflect only the current page and are\n * therefore misleading, so the spec drops them entirely.\n * - Selected values that are not present in `options` (e.g. async relation\n * ids resolved lazily) get their trigger label from `selectedLabelFor`,\n * falling back to the raw value.\n *\n * Selection state is owned by TanStack via `column.getFilterValue()` /\n * `column.setFilterValue()`; the value is always an array (or undefined\n * when empty). The hook bridges this to the parent's\n * `onColumnFiltersChange` (server-side filters via `useBuroDataTable`).\n *\n * Required column wiring (set on the resource's columnDef):\n * filterFn: 'arrIncludesSome' or a custom (row, id, value) =>\n * value.includes(row.getValue(id))\n *\n * Testids: resource-agnostic, supplied by the caller via `getTestId(role)`\n * where role is one of 'trigger' | 'option' | 'clear' | 'search'. For\n * per-option testids the role is suffixed with the option value:\n * `getTestId('option', value)`.\n */\nexport interface FacetedFilterOption {\n label: string;\n value: string;\n /** Optional icon rendered inline next to the label. */\n icon?: React.ComponentType<{ className?: string }>;\n /** Optional secondary line drawn under the label (an option filter's `doc.summary`). */\n summary?: string;\n}\n\n/**\n * The column-free multi-select under {@link DataTableFacetedFilter}: the selection is a plain\n * list of option values the caller owns. Anything that is not a table column mounts it directly:\n * an option filter of a list (T4 D3) drives it from `filters.presets`, a scope dimension picker\n * from the dimension's selection.\n */\nexport interface FacetedFilterPickerProps {\n /** The selected option values. */\n selected: readonly string[];\n /** Called with the whole next selection (empty for Clear or the last deselect). */\n onChange: (next: string[]) => void;\n /** A hint for the control: the trigger's tooltip and a line at the top of the popover. */\n hint?: string;\n /** Shown beside the title while nothing is selected, e.g. \"All\". */\n emptyLabel?: string;\n /** Whether the footer offers to clear the selection. Default `true`. */\n clearable?: boolean;\n title: string;\n options?: FacetedFilterOption[];\n\n /**\n * Async option loader for server-side / relation-backed filters (Task F2/F3).\n * When provided, the component switches to `shouldFilter={false}` (cmdk\n * delegates filtering to the server), debounces the search input by 250ms,\n * and replaces the static `options` list with the resolved results.\n * `options` is ignored when `loadOptions` is set.\n */\n loadOptions?: (search: string) => Promise<FacetedFilterOption[]>;\n\n /**\n * Resolve a display label for a selected value that is NOT present in\n * `options`. Used by async relation filters (Task F2/F3) where the\n * selected ids are known but their labels are loaded lazily. Returning\n * `undefined` falls back to the raw value.\n */\n selectedLabelFor?: (value: string) => string | undefined;\n\n /** Placeholder for the search input. Defaults to `title`. */\n searchPlaceholder?: string;\n /** Empty-state text when no options match the search. Defaults to \"No results.\". */\n emptyText?: string;\n\n /**\n * Resource-agnostic testid generator. Roles:\n * 'trigger' → the popover trigger button\n * 'search' → the cmdk search input\n * 'option' → option item (value passed as second arg)\n * 'clear' → the Clear-filters footer item\n * 'hint' → the hint line at the top of the popover\n */\n getTestId?: (\n role: 'trigger' | 'option' | 'clear' | 'search' | 'hint',\n value?: string,\n ) => string | undefined;\n\n className?: string;\n}\n\nexport interface DataTableFacetedFilterProps<TData, TValue>\n extends Omit<FacetedFilterPickerProps, 'selected' | 'onChange' | 'hint' | 'getTestId'> {\n column: Column<TData, TValue>;\n /** As `FacetedFilterPickerProps.getTestId`, without `'hint'`: a column filter has no hint. */\n getTestId?: (\n role: 'trigger' | 'option' | 'clear' | 'search',\n value?: string,\n ) => string | undefined;\n}\n\n/** A faceted filter bound to a TanStack column: the column's filter value is the selection. */\nexport function DataTableFacetedFilter<TData, TValue>({\n column,\n getTestId,\n ...rest\n}: DataTableFacetedFilterProps<TData, TValue>): React.ReactElement {\n // The column filter value is an array of selected option values for\n // multi-select; forward consistent shapes back into TanStack. An empty\n // selection is `undefined`, never `[]`.\n const filterValue = column.getFilterValue();\n return (\n <FacetedFilterPicker\n {...rest}\n {...(getTestId === undefined\n ? {}\n : { getTestId: (role, value) => (role === 'hint' ? undefined : getTestId(role, value)) })}\n selected={Array.isArray(filterValue) ? filterValue.map(String) : []}\n onChange={(next) => column.setFilterValue(next.length === 0 ? undefined : next)}\n />\n );\n}\n\n/** The picker behind `DataTableFacetedFilter`, controlled by a plain selection. */\nexport function FacetedFilterPicker({\n title,\n options = [],\n loadOptions,\n selectedLabelFor,\n searchPlaceholder,\n emptyText,\n getTestId,\n className,\n selected: selectedList,\n onChange,\n hint,\n emptyLabel,\n clearable = true,\n}: FacetedFilterPickerProps): React.ReactElement {\n // `useOptionalT`, not `useT`: this filter is a composable the adopter can\n // mount outside a BuroProvider, and the framework's own tests do.\n const t = useOptionalT();\n const chromeT = useChromeT();\n // Normalise to a Set for O(1) membership checks.\n const selected = useMemo<Set<string>>(() => new Set(selectedList), [selectedList]);\n\n // ── Async / server-driven option loading ──────────────────────────────\n // When `loadOptions` is provided we control the search value ourselves,\n // debounce it 250ms, call the loader, and drive cmdk with shouldFilter=false.\n const [search, setSearch] = useState('');\n const [asyncOptions, setAsyncOptions] = useState<FacetedFilterOption[]>([]);\n const [loading, setLoading] = useState(false);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Keep a stable ref to the latest loadOptions so that callers who pass an\n // inline arrow function (new identity each render) do NOT trigger the effect\n // on every render — only `search` changes drive a refetch.\n const loadOptionsRef = useRef(loadOptions);\n useEffect(() => {\n loadOptionsRef.current = loadOptions;\n });\n\n useEffect(() => {\n if (!loadOptionsRef.current) return;\n // Show spinner immediately on keystroke (before the debounce fires).\n setLoading(true);\n // Clear any pending debounce timer.\n if (debounceRef.current !== null) clearTimeout(debounceRef.current);\n let cancelled = false;\n debounceRef.current = setTimeout(() => {\n const loader = loadOptionsRef.current;\n if (!loader) return;\n loader(search)\n .then((results) => {\n if (!cancelled) {\n setAsyncOptions(results);\n setLoading(false);\n }\n })\n .catch(() => {\n if (!cancelled) setLoading(false);\n });\n }, 250);\n return () => {\n cancelled = true;\n if (debounceRef.current !== null) clearTimeout(debounceRef.current);\n };\n }, [search]);\n\n // The effective list shown in the command palette.\n const effectiveOptions = loadOptions ? asyncOptions : options;\n\n // Resolve a display label for the trigger chips: effectiveOptions first,\n // then the caller-supplied resolver (async relations), then the raw value.\n const labelFor = (value: string): string => {\n const opt = effectiveOptions.find((o) => o.value === value);\n if (opt) return opt.label;\n return selectedLabelFor?.(value) ?? value;\n };\n\n const toggle = (value: string) => {\n const next = new Set(selected);\n if (next.has(value)) next.delete(value);\n else next.add(value);\n onChange(Array.from(next));\n };\n\n const selectedValues = Array.from(selected);\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-testid={getTestId?.('trigger')}\n title={hint}\n className={cn('h-8 border-dashed', className)}\n >\n <IconCirclePlus className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n {title}\n {selected.size === 0 && emptyLabel !== undefined && (\n <>\n <span className=\"mx-2 h-4 w-px bg-border\" aria-hidden=\"true\" />\n <Badge variant=\"secondary\" className=\"rounded-sm px-1 font-normal\">\n {emptyLabel}\n </Badge>\n </>\n )}\n {selected.size > 0 && (\n <>\n <span className=\"mx-2 h-4 w-px bg-border\" aria-hidden=\"true\" />\n <Badge variant=\"secondary\" className=\"rounded-sm px-1 font-normal lg:hidden\">\n {selected.size}\n </Badge>\n <div className=\"hidden gap-1 lg:flex\">\n {selected.size > 2 ? (\n <Badge variant=\"secondary\" className=\"rounded-sm px-1 font-normal\">\n {t('table.selectedCount', {\n count: selected.size,\n defaultValue: '{{count}} selected',\n })}\n </Badge>\n ) : (\n selectedValues.map((value) => (\n <Badge variant=\"secondary\" key={value} className=\"rounded-sm px-1 font-normal\">\n {labelFor(value)}\n </Badge>\n ))\n )}\n </div>\n </>\n )}\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[220px] p-0\" align=\"start\">\n <Command shouldFilter={!loadOptions}>\n {hint !== undefined && (\n <p\n data-testid={getTestId?.('hint')}\n className=\"border-b px-3 py-2 text-xs text-muted-foreground\"\n >\n {hint}\n </p>\n )}\n <CommandInput\n placeholder={searchPlaceholder ?? title}\n data-testid={getTestId?.('search')}\n {...(loadOptions ? { value: search, onValueChange: setSearch } : {})}\n />\n <CommandList>\n {loading && (\n <div className=\"py-6 text-center text-sm text-muted-foreground\">\n {t('list.loadingShort', { defaultValue: 'Loading…' })}\n </div>\n )}\n <CommandEmpty>\n {emptyText ?? t('relation.noResults', { defaultValue: 'No results.' })}\n </CommandEmpty>\n <CommandGroup>\n {effectiveOptions.map((opt) => {\n const isSelected = selected.has(opt.value);\n const Icon = opt.icon;\n return (\n <CommandItem\n key={opt.value}\n value={opt.label}\n data-testid={getTestId?.('option', opt.value)}\n // biome-ignore lint/a11y/useSemanticElements: cmdk renders as div; role=\"option\" + aria-checked are intentional ARIA-1.2 multi-select semantics\n role=\"option\"\n aria-selected={isSelected}\n aria-checked={isSelected}\n onSelect={() => toggle(opt.value)}\n >\n <span\n className={cn(\n 'mr-2 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border',\n isSelected\n ? 'border-primary bg-primary text-primary-foreground'\n : 'border-input opacity-50',\n )}\n aria-hidden=\"true\"\n >\n {isSelected && <IconCheck className=\"h-3.5 w-3.5\" />}\n </span>\n {Icon && (\n <Icon className=\"mr-2 h-4 w-4 text-muted-foreground\" aria-hidden=\"true\" />\n )}\n <span className=\"flex min-w-0 flex-1 flex-col text-left\">\n <span>{opt.label}</span>\n {opt.summary !== undefined && (\n <span className=\"text-xs text-muted-foreground\">{opt.summary}</span>\n )}\n </span>\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n {clearable && selected.size > 0 && (\n <button\n type=\"button\"\n data-testid={getTestId?.('clear')}\n onClick={() => onChange([])}\n className=\"flex w-full items-center justify-center border-t px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground\"\n >\n {chromeT('filters.clearAll', { defaultValue: 'Clear filters' })}\n </button>\n )}\n </Command>\n </PopoverContent>\n </Popover>\n );\n}\n","'use client';\nimport type { ListOptionFilter } from '@burojs/react';\nimport { FacetedFilterPicker } from '../../data-table/data-table-faceted-filter.js';\n\nexport interface PresetFilterControlsProps {\n /** The list's option filters (`filters.presets`). */\n readonly presets: readonly ListOptionFilter[];\n /**\n * `filters.setPreset`: it writes the whole selection of one filter and resets the page to 1\n * itself, so this component does not.\n */\n readonly setPreset: (id: string, optionIds: readonly string[]) => void;\n readonly testIdBase: string;\n}\n\n/**\n * One faceted multi-select per option filter (T4 D3, D4): the filter's label is the trigger, its\n * `doc.summary` the hint, each option shows its label with its `doc.summary` under it. The\n * trigger doubles as the chip: it lists the selected options, and deselecting the last one (or\n * the picker's own Clear) writes an empty selection, which the URL keeps as `?<ns>.<id>=` so a\n * reload keeps the initial option removed.\n *\n * Test ids: `<base>:preset:<id>:trigger | :option:<optionId> | :clear | :search | :hint`.\n */\nexport function PresetFilterControls(props: PresetFilterControlsProps): React.ReactElement {\n const { presets, setPreset, testIdBase } = props;\n return (\n <>\n {presets.map((filter) => (\n <FacetedFilterPicker\n key={filter.id}\n title={filter.label}\n {...(filter.hint === undefined ? {} : { hint: filter.hint })}\n options={filter.options.map((option) => ({\n value: option.id,\n label: option.label,\n ...(option.summary === undefined ? {} : { summary: option.summary }),\n }))}\n selected={filter.selected}\n // A selection is a set kept in the options' declaration order (spec §3.3).\n onChange={(next) =>\n setPreset(\n filter.id,\n filter.options.map((option) => option.id).filter((id) => next.includes(id)),\n )\n }\n getTestId={(role, value) =>\n `${testIdBase}:preset:${filter.id}:${role}${value === undefined ? '' : `:${value}`}`\n }\n />\n ))}\n </>\n );\n}\n","'use client';\nimport type {\n ErasedResourceHandle,\n FilterExpression,\n FilterGroup,\n ResolvedFixedFilter,\n} from '@burojs/core';\nimport type { FilterFieldSpec, ListOptionFilter } 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';\nimport { PresetFilterControls } from './preset-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 /**\n * The list's option filters (`filters.presets`), drawn as faceted pickers before the pinned\n * controls. Absent where there are none (the relation picker's dialog).\n */\n readonly presets?: readonly ListOptionFilter[];\n /** `filters.setPreset`: it resets the page itself, so no `setPage(1)` is composed here. */\n readonly setPreset?: (id: string, optionIds: readonly string[]) => 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 presets,\n setPreset,\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 {presets !== undefined && presets.length > 0 && setPreset !== undefined && (\n <PresetFilterControls presets={presets} setPreset={setPreset} testIdBase={testIdBase} />\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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"}