@burojs/ui-shadcn 0.6.0 → 0.8.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 (158) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-2HLgz7Il.cjs +169 -0
  3. package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
  4. package/dist/bulk-summary-D6A0ua21.js +169 -0
  5. package/dist/bulk-summary-D6A0ua21.js.map +1 -0
  6. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-C47mF260.cjs} +4 -4
  7. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
  8. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CGXItMN0.js} +3 -3
  9. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
  10. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-BT04Mieo.cjs} +475 -107
  11. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
  12. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-CtiRzOAt.js} +391 -108
  13. package/dist/check-confirm-dialog-CtiRzOAt.js.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-ByGhxLf_.cjs} +1021 -154
  19. package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-z3Axzf9Y.js} +929 -140
  21. package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BVFjMU5-.cjs} +64 -29
  23. package/dist/flow-runtime-BVFjMU5-.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DrUWyTS0.js} +65 -30
  25. package/dist/flow-runtime-DrUWyTS0.js.map +1 -0
  26. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-3fk6J33y.js} +130 -87
  27. package/dist/flow-screen-3fk6J33y.js.map +1 -0
  28. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-6y5E_HT6.cjs} +130 -87
  29. package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
  30. package/dist/index.cjs +1186 -950
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +631 -322
  33. package/dist/index.d.ts +631 -322
  34. package/dist/index.js +1100 -870
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-C4KmZIYE.cjs} +5 -5
  37. package/dist/inline-flow-host-C4KmZIYE.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-YnnvWjdU.js} +5 -5
  39. package/dist/inline-flow-host-YnnvWjdU.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-DN4bvQfg.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-CtEfqAvU.js} +35 -23
  45. package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DEjAxHe3.cjs} +42 -30
  47. package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
  48. package/dist/{place-node-NraNYKPx.js → place-node-BdxT-30h.js} +1980 -1041
  49. package/dist/place-node-BdxT-30h.js.map +1 -0
  50. package/dist/{place-node-C5SmIAYU.cjs → place-node-Bhl8B3fF.cjs} +2038 -1087
  51. package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
  52. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-Bc_vfCE_.cjs} +9 -4
  53. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
  54. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BvkD5OcC.js} +9 -4
  55. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
  56. package/dist/related-list-timeline-DCrwN9oO.js +167 -0
  57. package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
  58. package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
  59. package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
  60. package/dist/{related-rows-ea43qhhy.cjs → related-rows-BW9Jr07T.cjs} +27 -16
  61. package/dist/related-rows-BW9Jr07T.cjs.map +1 -0
  62. package/dist/{related-rows-o0_t-Yce.js → related-rows-CPE-9FFr.js} +25 -14
  63. package/dist/related-rows-CPE-9FFr.js.map +1 -0
  64. package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
  65. package/dist/relation-create-C43cdPQt.js.map +1 -0
  66. package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
  67. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  68. package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BlrGRwMV.js} +210 -104
  69. package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-Br-zBHxT.cjs} +218 -112
  71. package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
  72. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-ADjxI_TI.cjs} +36 -73
  73. package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
  74. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-CyKliBkN.js} +134 -36
  75. package/dist/relation-picker-dialog-CyKliBkN.js.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-DimVQF9Y.cjs} +133 -35
  77. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +1 -0
  78. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-x7P5HKie.js} +30 -61
  79. package/dist/relation-picker-x7P5HKie.js.map +1 -0
  80. package/dist/relation-select-B8Fq4o-t.js +48 -0
  81. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  82. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  83. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  84. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-BRpUnhay.js} +634 -264
  85. package/dist/resource-page-router-BRpUnhay.js.map +1 -0
  86. package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-qEd2xb55.cjs} +694 -312
  87. package/dist/resource-page-router-qEd2xb55.cjs.map +1 -0
  88. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  89. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  90. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  91. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  92. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  93. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  94. package/dist/success-notices-LDEgEkc4.js +41 -0
  95. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  96. package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
  97. package/dist/toaster-BbByOytP.cjs.map +1 -0
  98. package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
  99. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  100. package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  101. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  102. package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  103. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  104. package/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
  105. package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
  106. package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
  107. package/dist/use-record-subtitle-gIaCGgbI.js.map +1 -0
  108. package/package.json +11 -11
  109. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  110. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  111. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  112. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  113. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  114. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  115. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  116. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  117. package/dist/dialog-B_rt9S1c.js.map +0 -1
  118. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  119. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  120. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  121. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  122. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  123. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  124. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  125. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  126. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  127. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  128. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  129. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  130. package/dist/place-node-NraNYKPx.js.map +0 -1
  131. package/dist/related-list-timeline-CCzl71aA.cjs +0 -142
  132. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  133. package/dist/related-list-timeline-yrPsjA3F.js +0 -142
  134. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  135. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  136. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  137. package/dist/relation-create-IA40t1hi.cjs +0 -364
  138. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  139. package/dist/relation-create-h6F9B4C4.js +0 -339
  140. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  141. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  142. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  143. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  144. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  145. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  146. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  147. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  148. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  149. package/dist/search-access-CPPmfuvy.js.map +0 -1
  150. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  151. package/dist/shell-BPlk8hEa.js +0 -205
  152. package/dist/shell-BPlk8hEa.js.map +0 -1
  153. package/dist/shell-DxhlsPwm.cjs +0 -271
  154. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  155. package/dist/toaster-C02XbB9r.js.map +0 -1
  156. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  157. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  158. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
@@ -1,16 +1,29 @@
1
1
  import { l as cn } from "./use-theme-dnE3Od3I.js";
2
- import { d as PopoverTrigger, s as Popover, u as PopoverContent } from "./toaster-C02XbB9r.js";
2
+ import { m as PopoverTrigger, p as PopoverContent, u as Popover } from "./toaster-Ct_YHGs6.js";
3
3
  import { t as Button } from "./button-MWVeZaqj.js";
4
- import { _ as CommandEmpty, b as CommandItem, d as useRowTitle, h as Command, n as narrowSearchDecl, t as namesHiddenField, x as CommandList, y as CommandInput } from "./search-access-CPPmfuvy.js";
5
- import { n as useCandidatesGate, r as LazyRelationPickerDialog } from "./candidates-gate-Bb70LPAy.js";
6
- import { i as selectKey, n as useSelectCreate, r as RelationViewButton, t as RelationCreateButton } from "./relation-create-h6F9B4C4.js";
4
+ import { C as CommandEmpty, D as CommandList, E as CommandItem, T as CommandInput, a as namesHiddenField, i as selectKey, m as useRowTitle, n as useSelectCreate, o as narrowSearchDecl, r as RelationViewButton, t as RelationCreateButton, x as Command, y as LoadError } from "./relation-create-C43cdPQt.js";
5
+ import { r as useCandidatesGate, t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-DN4bvQfg.js";
6
+ import { r as useRowSubtitle, t as optionSubtitle } from "./use-record-subtitle-gIaCGgbI.js";
7
7
  import { composeListFilter, normalizeFixedFilters, relationOpenOf, resolveResourceLabel, resourceKey } from "@burojs/core";
8
- import { useApp, useI18nCaps, useList, useListAccess, useOptionalT, useRelatedRecord, useTestIds } from "@burojs/react";
8
+ import { resolveErrorText, useApp, useI18nCaps, useList, useListAccess, useOptionalT, useRelatedRecord, useTestIds } from "@burojs/react";
9
9
  import { IconCheck, IconChevronDown, IconLoader2, IconX } from "@tabler/icons-react";
10
10
  import * as React from "react";
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
12
+ //#region src/lib/error-title.ts
13
+ function errorTitle(error, caps) {
14
+ if (error instanceof Error) return error.message;
15
+ if (error !== null && typeof error === "object") {
16
+ const { code, message } = error;
17
+ if (typeof code === "string") return resolveErrorText({
18
+ code,
19
+ ...message !== null && typeof message === "object" ? { message } : {}
20
+ }, caps);
21
+ }
22
+ }
23
+ //#endregion
12
24
  //#region src/fields/relation-many-picker.tsx
13
25
  const DEBOUNCE_MS = 250;
26
+ const NO_KEYS = Object.freeze([]);
14
27
  function keysOf(value) {
15
28
  if (!Array.isArray(value)) return [];
16
29
  const seen = new Set();
@@ -24,7 +37,7 @@ function keysOf(value) {
24
37
  return out;
25
38
  }
26
39
  function RelationManyPickerInput(props) {
27
- const { name, id, value, onChange, testid, field, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, relationSeeds, fixedFilters, metaSources, resource } = props;
40
+ const { name, id, value, onChange, testid, field, disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage, relationSeeds, fixedFilters, metaSources, resource, link } = props;
28
41
  const meta = field?.meta;
29
42
  let to;
30
43
  try {
@@ -52,13 +65,34 @@ function RelationManyPickerInput(props) {
52
65
  children: "—"
53
66
  })
54
67
  });
68
+ const linkRights = link?.rights;
69
+ const notAtomic = linkRights !== void 0 && [
70
+ linkRights.edit,
71
+ linkRights.attach,
72
+ linkRights.detach
73
+ ].some((right) => !right.allowed && right.reason === "not-atomic");
74
+ const denied = linkRights !== void 0 && !notAtomic && !linkRights.edit.allowed && [
75
+ "field-readonly",
76
+ "rule-denied",
77
+ "operation-absent"
78
+ ].includes(linkRights.edit.reason);
79
+ const keyRead = link?.keys;
80
+ const keyNotice = link === void 0 ? void 0 : keyRead?.error ? {
81
+ kind: "error",
82
+ error: keyRead.error
83
+ } : keyRead !== void 0 && !keyRead.isLoading && keyRead.keys !== void 0 && !keyRead.complete ? {
84
+ kind: "incomplete",
85
+ count: keyRead.keys.length
86
+ } : !link.ready ? { kind: "pending" } : void 0;
55
87
  const filters = gate.filters.length === 0 ? fixedFilters : [...gate.filters, ...fixedFilters ?? []];
88
+ const subtitle = props.optionSubtitle;
56
89
  return jsx(ManyPicker, {
57
90
  to,
58
91
  keys: keysOf(value),
59
92
  onChange: (next) => onChange(next),
60
93
  ...typeof meta?.by === "string" ? { by: meta.by } : {},
61
94
  searchField: meta?.searchField,
95
+ ...meta?.scope === void 0 ? {} : { scope: meta.scope },
62
96
  fixedFilters: filters,
63
97
  ...gate.gate === void 0 ? {} : { gate: gate.gate },
64
98
  createSeeds: relationSeeds,
@@ -68,6 +102,15 @@ function RelationManyPickerInput(props) {
68
102
  resource: resource ?? "",
69
103
  fieldName: field?.name ?? name,
70
104
  open: relationOpenOf(field ?? {}),
105
+ ...subtitle === void 0 ? {} : { optionSubtitle: subtitle },
106
+ ...linkRights === void 0 ? {} : {
107
+ canAdd: linkRights.attach.allowed,
108
+ canRemove: linkRights.detach.allowed,
109
+ hidden: link?.hidden ?? [],
110
+ notAtomic,
111
+ readOnly: denied,
112
+ ...keyNotice === void 0 ? {} : { keyNotice }
113
+ },
71
114
  disabled,
72
115
  required,
73
116
  ariaInvalid,
@@ -75,13 +118,14 @@ function RelationManyPickerInput(props) {
75
118
  ariaErrorMessage
76
119
  });
77
120
  }
78
- function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, createSeeds, name, id, testid, resource, fieldName, open: openMode, scope = "context", disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
121
+ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, createSeeds, name, id, testid, resource, fieldName, open: openMode, optionSubtitle: optionSubtitle$1, canAdd = true, canRemove = true, hidden = NO_KEYS, notAtomic = false, readOnly = false, keyNotice, scope = "context", disabled, required, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
79
122
  const app = useApp();
80
123
  const caps = useI18nCaps();
81
124
  const t = useOptionalT();
82
125
  const tid = useTestIds();
83
126
  const targetDef = app.registry.get(to);
84
127
  const rowTitle = useRowTitle(to, targetDef?.displayField);
128
+ const rowSubtitle = useRowSubtitle(to, optionSubtitle$1);
85
129
  const targetLabel = resolveResourceLabel(caps, to, "single", targetDef).toLowerCase();
86
130
  const childPrefix = `relation-picker:${to}`;
87
131
  const effectiveSearchField = searchField ?? targetDef?.displayField ?? "name";
@@ -135,13 +179,28 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
135
179
  targetDef
136
180
  ]);
137
181
  const blocked = !accessReady || gate !== void 0 || namesHiddenField(filter, (field) => access.fieldVisible(field));
182
+ const hiddenSet = React.useMemo(() => new Set(hidden.map(String)), [hidden]);
183
+ const visibleKeys = React.useMemo(() => keys.filter((key) => !hiddenSet.has(String(key))), [keys, hiddenSet]);
184
+ const hiddenHeld = keys.length - visibleKeys.length;
138
185
  const append = React.useCallback((next) => {
139
- if (keys.some((key) => String(key) === String(next))) return;
186
+ if (!canAdd || keys.some((key) => String(key) === String(next))) return;
140
187
  onChange([...keys, next]);
141
- }, [keys, onChange]);
188
+ }, [
189
+ keys,
190
+ onChange,
191
+ canAdd
192
+ ]);
142
193
  const toggle = React.useCallback((next) => {
143
- onChange(keys.some((key) => String(key) === String(next)) ? keys.filter((key) => String(key) !== String(next)) : [...keys, next]);
144
- }, [keys, onChange]);
194
+ const held = keys.some((key) => String(key) === String(next));
195
+ if (held ? !canRemove || hiddenSet.has(String(next)) : !canAdd) return;
196
+ onChange(held ? keys.filter((key) => String(key) !== String(next)) : [...keys, next]);
197
+ }, [
198
+ keys,
199
+ onChange,
200
+ canAdd,
201
+ canRemove,
202
+ hiddenSet
203
+ ]);
145
204
  const { create, reader: createReader } = useSelectCreate({
146
205
  to,
147
206
  by,
@@ -192,45 +251,87 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
192
251
  params: { name: targetLabel },
193
252
  defaultValue: `Select ${targetLabel}…`
194
253
  });
195
- const chips = jsx("div", {
254
+ const chips = visibleKeys.length === 0 && hiddenHeld === 0 ? null : jsxs("div", {
196
255
  className: "flex flex-wrap items-center gap-1",
197
256
  "data-testid": `${testid}:chips`,
198
- children: keys.map((key) => jsx(Chip, {
199
- to,
200
- keyValue: key,
201
- by,
202
- resource,
203
- fieldName,
204
- open: openMode,
205
- testId: tid.relationManyChip(resource, fieldName, String(key)),
206
- disabled: disabled === true,
207
- focusTrigger,
208
- onRemove: () => onChange(keys.filter((entry) => String(entry) !== String(key)))
209
- }, String(key)))
257
+ children: [
258
+ visibleKeys.map((key) => jsx(Chip, {
259
+ to,
260
+ keyValue: key,
261
+ by,
262
+ resource,
263
+ fieldName,
264
+ open: openMode,
265
+ testId: tid.relationManyChip(resource, fieldName, String(key)),
266
+ disabled: disabled === true || !canRemove,
267
+ focusTrigger,
268
+ onRemove: () => onChange(keys.filter((entry) => String(entry) !== String(key)))
269
+ }, String(key))),
270
+ hiddenHeld > 0 ? jsx("span", {
271
+ "data-testid": tid.linkHidden(resource, fieldName),
272
+ className: "inline-flex items-center rounded-md border bg-secondary px-2 py-0.5 text-sm text-muted-foreground",
273
+ children: t("field.link.hidden", {
274
+ count: hiddenHeld,
275
+ defaultValue: "{{count}} not visible"
276
+ })
277
+ }) : null,
278
+ canRemove && disabled !== true && visibleKeys.length > 0 ? jsx("button", {
279
+ type: "button",
280
+ onClick: () => {
281
+ focusTrigger();
282
+ onChange(keys.filter((key) => hiddenSet.has(String(key))));
283
+ },
284
+ "data-testid": `${testid}:clear-all`,
285
+ className: "rounded-sm px-1 text-xs text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
286
+ children: t("relationPicker.clearAll", { defaultValue: "Clear all" })
287
+ }) : null
288
+ ]
210
289
  });
290
+ const notice = keyNotice ? jsx("p", {
291
+ "data-testid": `${testid}:keys-${keyNotice.kind}`,
292
+ role: keyNotice.kind === "error" ? "status" : void 0,
293
+ className: cn("text-xs", keyNotice.kind === "error" ? "text-destructive" : "text-muted-foreground"),
294
+ children: keyNotice.kind === "error" ? `${t("field.link.failed", { defaultValue: "Links could not be loaded" })}: ${errorTitle(keyNotice.error, caps) ?? ""}` : keyNotice.kind === "incomplete" ? t("field.link.incomplete", {
295
+ count: keyNotice.count,
296
+ defaultValue: "More than {{count}} links"
297
+ }) : t("table.loading", { defaultValue: "Loading…" })
298
+ }) : readOnly ? jsx("p", {
299
+ "data-testid": `${testid}:read-only`,
300
+ className: "text-xs text-muted-foreground",
301
+ children: t("field.link.readOnly", { defaultValue: "You cannot change this link." })
302
+ }) : notAtomic ? jsx("p", {
303
+ "data-testid": `${testid}:not-atomic`,
304
+ className: "text-xs text-muted-foreground",
305
+ children: t("field.link.notAtomic", { defaultValue: "This link cannot be edited here: its data source does not save it with the record." })
306
+ }) : null;
211
307
  if (gate?.kind === "pending") return jsxs("div", {
212
308
  className: "flex flex-col gap-1.5",
213
- children: [chips, jsxs(Button, {
214
- type: "button",
215
- variant: "outline",
216
- disabled: true,
217
- "aria-busy": "true",
218
- "data-testid": testid,
219
- "data-buro-candidates": "pending",
220
- className: "w-full justify-between font-normal",
221
- children: [jsx("span", {
222
- className: "truncate text-muted-foreground",
223
- children: placeholder
224
- }), jsx(IconLoader2, {
225
- className: "h-4 w-4 shrink-0 animate-spin opacity-50",
226
- "aria-hidden": true
227
- })]
228
- })]
309
+ children: [
310
+ chips,
311
+ notice,
312
+ jsxs(Button, {
313
+ type: "button",
314
+ variant: "outline",
315
+ disabled: true,
316
+ "aria-busy": "true",
317
+ "data-testid": testid,
318
+ "data-buro-candidates": "pending",
319
+ className: "w-full justify-between font-normal",
320
+ children: [jsx("span", {
321
+ className: "truncate text-muted-foreground",
322
+ children: placeholder
323
+ }), jsx(IconLoader2, {
324
+ className: "h-4 w-4 shrink-0 animate-spin opacity-50",
325
+ "aria-hidden": true
326
+ })]
327
+ })
328
+ ]
229
329
  });
230
330
  if (gate?.kind === "forbidden") return jsxs("div", {
231
331
  className: "flex flex-col gap-1.5",
232
332
  children: [
233
333
  chips,
334
+ notice,
234
335
  jsxs(Button, {
235
336
  type: "button",
236
337
  variant: "outline",
@@ -261,33 +362,37 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
261
362
  children: [
262
363
  jsxs("div", {
263
364
  className: "flex flex-col gap-1.5",
264
- children: [chips, jsx(PopoverTrigger, {
265
- asChild: true,
266
- children: jsxs(Button, {
267
- ref: triggerRef,
268
- type: "button",
269
- variant: "outline",
270
- role: "combobox",
271
- "aria-expanded": open,
272
- "aria-haspopup": "listbox",
273
- "aria-required": required,
274
- "aria-invalid": ariaInvalid || void 0,
275
- "aria-describedby": ariaDescribedBy,
276
- ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
277
- disabled,
278
- "data-testid": testid,
279
- name,
280
- id,
281
- className: "w-full justify-between font-normal",
282
- children: [jsx("span", {
283
- className: "truncate text-left text-muted-foreground",
284
- children: placeholder
285
- }), jsx(IconChevronDown, {
286
- className: "h-4 w-4 shrink-0 opacity-50",
287
- "aria-hidden": true
288
- })]
365
+ children: [
366
+ chips,
367
+ notice,
368
+ jsx(PopoverTrigger, {
369
+ asChild: true,
370
+ children: jsxs(Button, {
371
+ ref: triggerRef,
372
+ type: "button",
373
+ variant: "outline",
374
+ role: "combobox",
375
+ "aria-expanded": open,
376
+ "aria-haspopup": "listbox",
377
+ "aria-required": required,
378
+ "aria-invalid": ariaInvalid || void 0,
379
+ "aria-describedby": ariaDescribedBy,
380
+ ...ariaErrorMessage ? { "aria-errormessage": ariaErrorMessage } : {},
381
+ disabled,
382
+ "data-testid": testid,
383
+ name,
384
+ id,
385
+ className: "w-full justify-between font-normal",
386
+ children: [jsx("span", {
387
+ className: "truncate text-left text-muted-foreground",
388
+ children: placeholder
389
+ }), jsx(IconChevronDown, {
390
+ className: "h-4 w-4 shrink-0 opacity-50",
391
+ "aria-hidden": true
392
+ })]
393
+ })
289
394
  })
290
- })]
395
+ ]
291
396
  }),
292
397
  jsx(PopoverContent, {
293
398
  className: "w-[var(--radix-popover-trigger-width)] p-0",
@@ -299,7 +404,7 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
299
404
  }) : jsxs(Command, {
300
405
  shouldFilter: false,
301
406
  children: [
302
- searchDecl !== void 0 && jsx(CommandInput, {
407
+ searchDecl !== void 0 && canAdd && jsx(CommandInput, {
303
408
  value: search,
304
409
  onValueChange: setSearch,
305
410
  placeholder: t("toolbar.search", {
@@ -312,9 +417,10 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
312
417
  defaultValue: `Search ${targetLabel}`
313
418
  }),
314
419
  onKeyDown: (event) => {
315
- if (event.key === "Backspace" && search === "" && keys.length > 0) {
420
+ if (event.key === "Backspace" && search === "" && canRemove) {
316
421
  event.preventDefault();
317
- onChange(keys.slice(0, -1));
422
+ const last = visibleKeys.at(-1);
423
+ if (last !== void 0) onChange(keys.filter((key) => String(key) !== String(last)));
318
424
  }
319
425
  if (event.key === " " && search === "") {
320
426
  event.preventDefault();
@@ -335,11 +441,15 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
335
441
  "aria-hidden": true
336
442
  }), jsx("span", { children: t("table.loading", { defaultValue: "Loading…" }) })]
337
443
  }),
338
- !isLoading && error && jsx("div", {
339
- className: "px-3 py-6 text-center text-sm text-muted-foreground",
340
- "data-testid": `${childPrefix}:error`,
341
- title: error.message,
342
- children: t("relation.failed", { defaultValue: "Failed to load options." })
444
+ !isLoading && error && jsx(LoadError, {
445
+ surface: "picker",
446
+ error,
447
+ testId: `${childPrefix}:error`,
448
+ className: "block px-3 py-6 text-center text-sm text-muted-foreground",
449
+ fallback: {
450
+ key: "relation.failed",
451
+ default: "Failed to load options."
452
+ }
343
453
  }),
344
454
  !isLoading && !error && rows.length === 0 && jsx(CommandEmpty, {
345
455
  "data-testid": `${childPrefix}:empty`,
@@ -350,11 +460,13 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
350
460
  if (rowValue === null || rowValue === void 0 || rowValue === "") return null;
351
461
  const idStr = String(rowValue);
352
462
  const label = rowTitle(row);
463
+ const subtitle = optionSubtitle(rowSubtitle(row), label, idStr);
353
464
  const chosen = keys.some((key) => String(key) === idStr);
354
465
  return jsxs(CommandItem, {
355
466
  value: `${label} ${idStr}`,
356
467
  "data-testid": `${childPrefix}:option:${idStr}`,
357
468
  "aria-checked": chosen,
469
+ disabled: chosen ? !canRemove : !canAdd,
358
470
  onSelect: () => toggle(rowValue),
359
471
  children: [
360
472
  jsx(IconCheck, {
@@ -365,9 +477,9 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
365
477
  className: "truncate",
366
478
  children: label
367
479
  }),
368
- label === idStr ? null : jsxs("span", {
480
+ subtitle === null ? null : jsx("span", {
369
481
  className: "ml-auto shrink-0 pl-2 text-xs text-muted-foreground",
370
- children: ["#", idStr]
482
+ children: subtitle
371
483
  })
372
484
  ]
373
485
  }, idStr);
@@ -376,7 +488,7 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
376
488
  }),
377
489
  jsxs("div", {
378
490
  className: "flex items-center justify-between gap-2 border-t px-3 py-2 text-sm",
379
- children: [jsx("button", {
491
+ children: [canAdd ? jsx("button", {
380
492
  type: "button",
381
493
  onClick: () => {
382
494
  setOpen(false);
@@ -385,7 +497,7 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
385
497
  "data-testid": `${childPrefix}:advanced`,
386
498
  className: "text-foreground hover:underline focus:outline-hidden focus-visible:underline",
387
499
  children: t("relationPicker.advancedSearch", { defaultValue: "Advanced search" })
388
- }), !disabled && jsx(RelationCreateButton, {
500
+ }) : jsx("span", {}), !disabled && canAdd && jsx(RelationCreateButton, {
389
501
  to,
390
502
  fieldName,
391
503
  create,
@@ -401,7 +513,7 @@ function ManyPicker({ to, keys, onChange, by, searchField, fixedFilters, gate, c
401
513
  ]
402
514
  })
403
515
  }),
404
- dialogMounted && !gated && jsx(LazyRelationPickerDialog, {
516
+ dialogMounted && !gated && canAdd && jsx(LazyRelationPickerDialog, {
405
517
  to,
406
518
  open: dialogOpen,
407
519
  onOpenChange: setDialogOpen,
@@ -433,34 +545,28 @@ function Chip({ to, keyValue, by, resource, fieldName, testId, open: openMode, d
433
545
  "data-testid": testId,
434
546
  "data-resource": resource,
435
547
  className: "inline-flex items-center gap-0.5 rounded-md border bg-secondary pl-2 text-sm text-secondary-foreground",
436
- children: [
437
- jsx("span", {
438
- className: "max-w-[16rem] truncate",
439
- children: isLoading ? t("table.loading", { defaultValue: "Loading…" }) : title
440
- }),
441
- jsx(RelationViewButton, {
442
- to,
443
- value: keyValue,
444
- by,
445
- fieldName: `${fieldName}:${String(keyValue)}`,
446
- beforeOpen: focusTrigger,
447
- open: openMode
448
- }),
449
- !disabled && jsx("button", {
450
- type: "button",
451
- onClick: onRemove,
452
- "data-testid": `${testId}:remove`,
453
- "aria-label": `${t("relationPicker.clear", { defaultValue: "Clear" })} ${title}`,
454
- className: "inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
455
- children: jsx(IconX, {
456
- className: "size-3.5",
457
- "aria-hidden": true
458
- })
548
+ children: [jsx(RelationViewButton, {
549
+ to,
550
+ value: keyValue,
551
+ by,
552
+ fieldName: `${fieldName}:${String(keyValue)}`,
553
+ beforeOpen: focusTrigger,
554
+ open: openMode,
555
+ title: isLoading ? t("table.loading", { defaultValue: "Loading…" }) : title
556
+ }), !disabled && jsx("button", {
557
+ type: "button",
558
+ onClick: onRemove,
559
+ "data-testid": `${testId}:remove`,
560
+ "aria-label": `${t("relationPicker.clear", { defaultValue: "Clear" })} ${title}`,
561
+ className: "inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
562
+ children: jsx(IconX, {
563
+ className: "size-3.5",
564
+ "aria-hidden": true
459
565
  })
460
- ]
566
+ })]
461
567
  });
462
568
  }
463
569
  //#endregion
464
570
  export { RelationManyPickerInput };
465
571
 
466
- //# sourceMappingURL=relation-many-picker-bvkjkv-k.js.map
572
+ //# sourceMappingURL=relation-many-picker-BlrGRwMV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-many-picker-BlrGRwMV.js","names":["optionSubtitle","optionSubtitleText"],"sources":["../src/lib/error-title.ts","../src/fields/relation-many-picker.tsx"],"sourcesContent":["import { type ErrorTextCaps, resolveErrorText } from '@burojs/react';\n\n/**\n * The text a user may read for a failure a hook handed to the kit as an `Error`-typed value.\n * The value is a `DataError` at runtime when it came from a data provider (its `message` is a\n * reference, so it is resolved through the bundle) and a plain `Error` when the framework raised\n * it (its `message` is already a sentence). Anything else has nothing to show.\n */\nexport function errorTitle(error: unknown, caps: ErrorTextCaps): string | undefined {\n if (error instanceof Error) return error.message;\n if (error !== null && typeof error === 'object') {\n const { code, message } = error as { readonly code?: unknown; readonly message?: unknown };\n if (typeof code === 'string') {\n return resolveErrorText(\n {\n code,\n ...(message !== null && typeof message === 'object' ? { message } : {}),\n } as Parameters<typeof resolveErrorText>[0],\n caps,\n );\n }\n }\n return undefined;\n}\n","'use client';\n\nimport {\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n type RelationCandidate,\n type RelationOpen,\n relationOpenOf,\n type ResourceRefInput,\n type ScopeSource,\n type SearchDecl,\n composeListFilter,\n normalizeFixedFilters,\n resolveResourceLabel,\n resourceKey,\n} from '@burojs/core';\nimport {\n useApp,\n useI18nCaps,\n useList,\n useListAccess,\n useOptionalT,\n useRelatedRecord,\n useTestIds,\n} from '@burojs/react';\nimport { errorTitle } from '../lib/error-title.js';\nimport { LoadError } from '../load-error/load-error-view.js';\nimport { IconCheck, IconChevronDown, IconLoader2, IconX } from '@tabler/icons-react';\nimport * as React 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 {\n optionSubtitle as optionSubtitleText,\n type RecordSubtitleFn,\n useRowSubtitle,\n} from '../resource/use-record-subtitle.js';\nimport { useRowTitle } from '../resource/use-row-title.js';\nimport { useCandidatesGate } from './candidates-gate.js';\nimport { RelationCreateButton, useSelectCreate } from './relation-create.js';\nimport { LazyRelationPickerDialog } from './lazy-relation-picker-dialog.js';\nimport { RelationViewButton, selectKey } from './relation-view-button.js';\nimport type { FieldInputProps } from './registry.js';\nimport { namesHiddenField, narrowSearchDecl } from './search-access.js';\n\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\ntype Key = string | number;\n\nconst DEBOUNCE_MS = 250;\nconst NO_KEYS: readonly Key[] = Object.freeze([]);\n\n/** The keys a to-many field holds: strings and finite numbers, the user's order, no repeats. */\nfunction keysOf(value: unknown): readonly Key[] {\n if (!Array.isArray(value)) return [];\n const seen = new Set<string>();\n const out: Key[] = [];\n for (const entry of value) {\n const key = selectKey(entry);\n if (key === null || seen.has(key)) continue;\n seen.add(key);\n out.push(entry as Key);\n }\n return out;\n}\n\n/**\n * Form input of `f.relationMany` (P7 3.5): the chosen rows as chips (titles through the relation\n * aggregator, one batched read), a popover that searches the same option query a single select\n * does (candidates, field access and the create hand-back included), \"Advanced search\" in a\n * multiple-choice dialog, a \"view\" on every chip and \"+ create\" that appends what it created.\n * The value is `row[by ?? id]` for each chosen row. Loaded lazily: it is not on any screen's\n * first paint.\n */\nexport function RelationManyPickerInput(props: FieldInputProps): React.ReactElement {\n const {\n name,\n id,\n value,\n onChange,\n testid,\n field,\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n relationSeeds,\n fixedFilters,\n metaSources,\n resource,\n link,\n } = props;\n const meta = field?.meta as\n | {\n to?: ResourceRefInput;\n by?: unknown;\n searchField?: string;\n candidates?: unknown;\n scope?: ScopeSource;\n }\n | undefined;\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const gate = useCandidatesGate({\n to,\n candidates: Array.isArray(meta?.candidates)\n ? (meta.candidates as readonly RelationCandidate[])\n : undefined,\n sources: metaSources,\n resource: resource ?? '',\n fieldName: field?.name ?? name,\n });\n if (!to) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${testid ?? `relation-many:${name}`}:no-target`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">—</span>\n </Button>\n );\n }\n // An inline link: the form controller's rights decide what the select offers, and the keys the\n // user cannot see ride along in the value. A link denied `not-atomic` is read-only with a notice.\n const linkRights = link?.rights;\n const notAtomic =\n linkRights !== undefined &&\n [linkRights.edit, linkRights.attach, linkRights.detach].some(\n (right) => !right.allowed && right.reason === 'not-atomic',\n );\n const denied =\n linkRights !== undefined &&\n !notAtomic &&\n !linkRights.edit.allowed &&\n ['field-readonly', 'rule-denied', 'operation-absent'].includes(linkRights.edit.reason);\n // The form holds a link's keys only once they are read whole: a failed or capped read, or one\n // still running, leaves the field read-only, and the user is told why.\n const keyRead = link?.keys;\n const keyNotice: KeyNotice | undefined =\n link === undefined\n ? undefined\n : keyRead?.error\n ? { kind: 'error', error: keyRead.error }\n : keyRead !== undefined &&\n !keyRead.isLoading &&\n keyRead.keys !== undefined &&\n !keyRead.complete\n ? { kind: 'incomplete', count: keyRead.keys.length }\n : !link.ready\n ? { kind: 'pending' }\n : undefined;\n const filters =\n gate.filters.length === 0 ? fixedFilters : [...gate.filters, ...(fixedFilters ?? [])];\n const subtitle = props.optionSubtitle;\n return (\n <ManyPicker\n to={to}\n keys={keysOf(value)}\n onChange={(next) => onChange(next)}\n {...(typeof meta?.by === 'string' ? { by: meta.by } : {})}\n searchField={meta?.searchField}\n {...(meta?.scope === undefined ? {} : { scope: meta.scope })}\n fixedFilters={filters}\n {...(gate.gate === undefined ? {} : { gate: gate.gate })}\n createSeeds={relationSeeds}\n name={name}\n id={id}\n testid={testid ?? `relation-many:${to}`}\n resource={resource ?? ''}\n fieldName={field?.name ?? name}\n open={relationOpenOf(field ?? {})}\n {...(subtitle === undefined ? {} : { optionSubtitle: subtitle })}\n {...(linkRights === undefined\n ? {}\n : {\n canAdd: linkRights.attach.allowed,\n canRemove: linkRights.detach.allowed,\n hidden: link?.hidden ?? [],\n notAtomic,\n readOnly: denied,\n ...(keyNotice === undefined ? {} : { keyNotice }),\n })}\n disabled={disabled}\n required={required}\n ariaInvalid={ariaInvalid}\n ariaDescribedBy={ariaDescribedBy}\n ariaErrorMessage={ariaErrorMessage}\n />\n );\n}\n\n/** Why a link whose keys the form does not hold whole is read-only. */\ntype KeyNotice =\n | { readonly kind: 'error'; readonly error: unknown }\n | { readonly kind: 'incomplete'; readonly count: number }\n | { readonly kind: 'pending' };\n\ninterface ManyPickerProps {\n readonly to: string;\n readonly keys: readonly Key[];\n readonly onChange: (next: readonly Key[]) => void;\n readonly by?: string;\n readonly searchField?: string | undefined;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n readonly gate?: import('./candidates-gate.js').CandidatesGate;\n readonly createSeeds: Readonly<Record<string, unknown>> | undefined;\n readonly name: string;\n readonly id: string | undefined;\n readonly testid: string;\n readonly resource: string;\n readonly fieldName: string;\n readonly open: RelationOpen;\n /** The options' second line (the field's `optionSubtitle`); see `RelationPickerProps`. */\n readonly optionSubtitle?: RecordSubtitleFn;\n /** False: no search box, no \"Advanced search\", no create, and no key can be added. Default true. */\n readonly canAdd?: boolean;\n /** False: no chip remove button, no Backspace removal, no clear-all. Default true. */\n readonly canRemove?: boolean;\n /**\n * Keys of `keys` whose target the user cannot see: one \"N not visible\" chip stands for them, it\n * has no remove button, and neither Backspace nor clear-all drops them.\n */\n readonly hidden?: readonly Key[];\n /** The link is edited with the record but its provider does not write it so: read-only, say so. */\n readonly notAtomic?: boolean;\n /** The user may see the link but not change it: say so. */\n readonly readOnly?: boolean;\n /** The link's keys are not held whole (failed, cut at the read cap, or still loading). */\n readonly keyNotice?: KeyNotice;\n /**\n * The read scope of the option reads (popover, dialog, create hand-back). Default `'context'`:\n * the record around the select narrows them, except by the field the select writes.\n */\n readonly scope?: ScopeSource;\n readonly disabled: boolean | undefined;\n readonly required: boolean | undefined;\n readonly ariaInvalid: boolean | undefined;\n readonly ariaDescribedBy: string | undefined;\n readonly ariaErrorMessage: string | undefined;\n}\n\nfunction ManyPicker({\n to,\n keys,\n onChange,\n by,\n searchField,\n fixedFilters,\n gate,\n createSeeds,\n name,\n id,\n testid,\n resource,\n fieldName,\n open: openMode,\n optionSubtitle,\n canAdd = true,\n canRemove = true,\n hidden = NO_KEYS,\n notAtomic = false,\n readOnly = false,\n keyNotice,\n scope = 'context',\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n}: ManyPickerProps): React.ReactElement {\n const app = useApp();\n const caps = useI18nCaps();\n const t = useOptionalT();\n const tid = useTestIds();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const rowSubtitle = useRowSubtitle(to, optionSubtitle);\n const targetLabel = resolveResourceLabel(caps, to, 'single', targetDef).toLowerCase();\n const childPrefix = `relation-picker:${to}`;\n const effectiveSearchField = searchField ?? targetDef?.displayField ?? 'name';\n const [open, setOpen] = React.useState(false);\n const [dialogOpen, setDialogOpen] = React.useState(false);\n // Mounted on the first open, and only while no candidates gate holds the read: without the\n // candidate filters the dialog would list rows the field must not offer.\n const gated = gate !== undefined;\n const [dialogMounted, setDialogMounted] = React.useState(false);\n React.useEffect(() => {\n if (gated) setDialogOpen(false);\n }, [gated]);\n React.useEffect(() => {\n if (dialogOpen) setDialogMounted(true);\n }, [dialogOpen]);\n const [search, setSearch] = React.useState('');\n const [debouncedSearch, setDebouncedSearch] = React.useState('');\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const focusTrigger = React.useCallback(() => triggerRef.current?.focus(), []);\n\n React.useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(search), DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n React.useEffect(() => {\n if (!open) {\n setSearch('');\n setDebouncedSearch('');\n }\n }, [open]);\n\n const targetIdField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const idField =\n typeof targetIdField === 'string' && targetIdField.length > 0 ? targetIdField : 'id';\n const valueField = by ?? idField;\n const access = useListAccess(to);\n const accessReady = access.status === 'ready';\n const searchDecl = React.useMemo<SearchDecl | undefined>(\n () =>\n narrowSearchDecl(\n targetDef?.views?.list?.search ?? { fields: [effectiveSearchField] },\n (field) => access.fieldVisible(field),\n ),\n [targetDef, effectiveSearchField, access],\n );\n const filter = React.useMemo<ErasedExpression | undefined>(\n () =>\n composeListFilter({\n fixed: normalizeFixedFilters(fixedFilters),\n search: debouncedSearch,\n searchDecl,\n searchContext: { resource: to, def: targetDef },\n }),\n [fixedFilters, debouncedSearch, searchDecl, to, targetDef],\n );\n const blocked =\n !accessReady ||\n gate !== undefined ||\n namesHiddenField(filter, (field) => access.fieldVisible(field));\n\n const hiddenSet = React.useMemo(() => new Set(hidden.map(String)), [hidden]);\n const visibleKeys = React.useMemo(\n () => keys.filter((key) => !hiddenSet.has(String(key))),\n [keys, hiddenSet],\n );\n const hiddenHeld = keys.length - visibleKeys.length;\n const append = React.useCallback(\n (next: Key) => {\n if (!canAdd || keys.some((key) => String(key) === String(next))) return;\n onChange([...keys, next]);\n },\n [keys, onChange, canAdd],\n );\n const toggle = React.useCallback(\n (next: Key) => {\n const held = keys.some((key) => String(key) === String(next));\n if (held ? !canRemove || hiddenSet.has(String(next)) : !canAdd) return;\n onChange(held ? keys.filter((key) => String(key) !== String(next)) : [...keys, next]);\n },\n [keys, onChange, canAdd, canRemove, hiddenSet],\n );\n const { create, reader: createReader } = useSelectCreate({\n to,\n by,\n valueField,\n idField,\n fixedFilters,\n scope,\n scopeField: fieldName,\n held: gated,\n searchDecl,\n searchContext: { resource: to, def: targetDef },\n // What was created and fits is appended, as a pick would be; the dialog it came from is done.\n onValue: (picked) => {\n append(picked);\n setDialogOpen(false);\n },\n });\n\n const listQuery = useList(to, {\n ...(filter ? { filter } : {}),\n pagination: { kind: 'offset', page: 1, pageSize: 10 },\n scope,\n scopeField: fieldName,\n enabled: open && targetDef != null && !blocked,\n });\n const rows = (listQuery.data?.data ?? []) as Array<Record<string, unknown>>;\n const isLoading = listQuery.isLoading || listQuery.isFetching;\n const error = listQuery.isError ? (listQuery.error as unknown as Error) : null;\n\n if (!targetDef) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${childPrefix}:unregistered`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">{keys.map(String).join(', ') || '—'}</span>\n </Button>\n );\n }\n\n const placeholder = t('relation.select', {\n params: { name: targetLabel },\n defaultValue: `Select ${targetLabel}…`,\n });\n // Nothing held, nothing drawn: an empty row would still take the column's gap above the trigger.\n const chips =\n visibleKeys.length === 0 && hiddenHeld === 0 ? null : (\n <div className=\"flex flex-wrap items-center gap-1\" data-testid={`${testid}:chips`}>\n {visibleKeys.map((key) => (\n <Chip\n key={String(key)}\n to={to}\n keyValue={key}\n by={by}\n resource={resource}\n fieldName={fieldName}\n open={openMode}\n testId={tid.relationManyChip(resource, fieldName, String(key))}\n disabled={disabled === true || !canRemove}\n focusTrigger={focusTrigger}\n onRemove={() => onChange(keys.filter((entry) => String(entry) !== String(key)))}\n />\n ))}\n {hiddenHeld > 0 ? (\n <span\n data-testid={tid.linkHidden(resource, fieldName)}\n className=\"inline-flex items-center rounded-md border bg-secondary px-2 py-0.5 text-sm text-muted-foreground\"\n >\n {t('field.link.hidden', {\n count: hiddenHeld,\n defaultValue: '{{count}} not visible',\n })}\n </span>\n ) : null}\n {canRemove && disabled !== true && visibleKeys.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n // The button leaves with the chips it clears: focus goes where chip removal sends it.\n focusTrigger();\n onChange(keys.filter((key) => hiddenSet.has(String(key))));\n }}\n data-testid={`${testid}:clear-all`}\n className=\"rounded-sm px-1 text-xs text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t('relationPicker.clearAll', { defaultValue: 'Clear all' })}\n </button>\n ) : null}\n </div>\n );\n const notice = keyNotice ? (\n <p\n data-testid={`${testid}:keys-${keyNotice.kind}`}\n role={keyNotice.kind === 'error' ? 'status' : undefined}\n className={cn(\n 'text-xs',\n keyNotice.kind === 'error' ? 'text-destructive' : 'text-muted-foreground',\n )}\n >\n {keyNotice.kind === 'error'\n ? `${t('field.link.failed', { defaultValue: 'Links could not be loaded' })}: ${errorTitle(keyNotice.error, caps) ?? ''}`\n : keyNotice.kind === 'incomplete'\n ? t('field.link.incomplete', {\n count: keyNotice.count,\n defaultValue: 'More than {{count}} links',\n })\n : t('table.loading', { defaultValue: 'Loading…' })}\n </p>\n ) : readOnly ? (\n <p data-testid={`${testid}:read-only`} className=\"text-xs text-muted-foreground\">\n {t('field.link.readOnly', { defaultValue: 'You cannot change this link.' })}\n </p>\n ) : notAtomic ? (\n <p data-testid={`${testid}:not-atomic`} className=\"text-xs text-muted-foreground\">\n {t('field.link.notAtomic', {\n defaultValue:\n 'This link cannot be edited here: its data source does not save it with the record.',\n })}\n </p>\n ) : null;\n\n if (gate?.kind === 'pending')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n aria-busy=\"true\"\n data-testid={testid}\n data-buro-candidates=\"pending\"\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconLoader2 className=\"h-4 w-4 shrink-0 animate-spin opacity-50\" aria-hidden />\n </Button>\n </div>\n );\n if (gate?.kind === 'forbidden')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={testid}\n data-buro-candidates=\"forbidden\"\n aria-describedby={gate.testId}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n <p id={gate.testId} data-testid={gate.testId} className=\"text-xs text-muted-foreground\">\n {gate.message}\n </p>\n </div>\n );\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <PopoverTrigger asChild>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-required={required}\n aria-invalid={ariaInvalid || undefined}\n aria-describedby={ariaDescribedBy}\n {...(ariaErrorMessage ? { 'aria-errormessage': ariaErrorMessage } : {})}\n disabled={disabled}\n data-testid={testid}\n name={name}\n id={id}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-left text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n </PopoverTrigger>\n </div>\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n {gate?.kind === 'empty' ? (\n <p\n className=\"px-3 py-6 text-center text-sm text-muted-foreground\"\n data-testid={gate.testId}\n >\n {gate.message}\n </p>\n ) : (\n <Command shouldFilter={false}>\n {searchDecl !== undefined && canAdd && (\n <CommandInput\n value={search}\n onValueChange={setSearch}\n placeholder={t('toolbar.search', {\n params: { name: targetLabel },\n defaultValue: `Search ${targetLabel}…`,\n })}\n data-testid={`${childPrefix}:search`}\n aria-label={t('toolbar.search', {\n params: { name: targetLabel },\n defaultValue: `Search ${targetLabel}`,\n })}\n onKeyDown={(event) => {\n // Backspace in an empty search takes the last chip off.\n if (event.key === 'Backspace' && search === '' && canRemove) {\n event.preventDefault();\n const last = visibleKeys.at(-1);\n if (last !== undefined)\n onChange(keys.filter((key) => String(key) !== String(last)));\n }\n // Space toggles the highlighted option while nothing is typed.\n if (event.key === ' ' && search === '') {\n event.preventDefault();\n document\n .querySelector<HTMLElement>(\n `[data-testid=\"${childPrefix}:list\"] [aria-selected=\"true\"]`,\n )\n ?.click();\n }\n }}\n />\n )}\n <CommandList\n data-testid={`${childPrefix}:list`}\n aria-multiselectable=\"true\"\n aria-label={targetLabel}\n >\n {isLoading && (\n <div\n className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\"\n data-testid={`${childPrefix}:loading`}\n >\n <IconLoader2 className=\"h-4 w-4 animate-spin\" aria-hidden />\n <span>{t('table.loading', { defaultValue: 'Loading…' })}</span>\n </div>\n )}\n {!isLoading && error && (\n <LoadError\n surface=\"picker\"\n error={error}\n testId={`${childPrefix}:error`}\n className=\"block px-3 py-6 text-center text-sm text-muted-foreground\"\n fallback={{ key: 'relation.failed', default: 'Failed to load options.' }}\n />\n )}\n {!isLoading && !error && rows.length === 0 && (\n <CommandEmpty data-testid={`${childPrefix}:empty`}>\n {t('relation.noResults', { defaultValue: 'No results.' })}\n </CommandEmpty>\n )}\n {!isLoading &&\n !error &&\n rows.map((row) => {\n const rowValue = row[valueField];\n if (rowValue === null || rowValue === undefined || rowValue === '') return null;\n const idStr = String(rowValue);\n const label = rowTitle(row);\n const subtitle = optionSubtitleText(rowSubtitle(row), label, idStr);\n const chosen = keys.some((key) => String(key) === idStr);\n return (\n <CommandItem\n key={idStr}\n value={`${label} ${idStr}`}\n data-testid={`${childPrefix}:option:${idStr}`}\n aria-checked={chosen}\n disabled={chosen ? !canRemove : !canAdd}\n onSelect={() => toggle(rowValue as Key)}\n >\n <IconCheck\n className={cn(\n 'mr-2 h-4 w-4 shrink-0',\n chosen ? 'opacity-100' : 'opacity-0',\n )}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n {/* The declared subtitle, else `#<id>` unless the label already is the id\n (REF-40). */}\n {subtitle === null ? null : (\n <span className=\"ml-auto shrink-0 pl-2 text-xs text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandList>\n <div className=\"flex items-center justify-between gap-2 border-t px-3 py-2 text-sm\">\n {canAdd ? (\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n setDialogOpen(true);\n }}\n data-testid={`${childPrefix}:advanced`}\n className=\"text-foreground hover:underline focus:outline-hidden focus-visible:underline\"\n >\n {t('relationPicker.advancedSearch', { defaultValue: 'Advanced search' })}\n </button>\n ) : (\n <span />\n )}\n {!disabled && canAdd && (\n <RelationCreateButton\n to={to}\n fieldName={fieldName}\n create={create}\n seeds={createSeeds}\n search={search}\n beforeOpen={() => {\n setOpen(false);\n focusTrigger();\n }}\n className=\"text-foreground hover:underline focus:outline-hidden focus-visible:underline disabled:opacity-50\"\n />\n )}\n </div>\n </Command>\n )}\n </PopoverContent>\n {dialogMounted && !gated && canAdd && (\n <LazyRelationPickerDialog\n to={to}\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onSelect={() => undefined}\n multiple\n selected={keys}\n onToggle={toggle}\n valueField={valueField}\n searchField={searchField}\n fixedFilters={fixedFilters}\n scope={scope}\n scopeField={fieldName}\n {...(disabled ? {} : { create })}\n createSeeds={createSeeds}\n fieldName={fieldName}\n onBeforeCreate={focusTrigger}\n />\n )}\n {createReader}\n </Popover>\n );\n}\n\n/** One chosen row: its title (the aggregator batches every chip's read), which views it, and remove. */\nfunction Chip({\n to,\n keyValue,\n by,\n resource,\n fieldName,\n testId,\n open: openMode,\n disabled,\n focusTrigger,\n onRemove,\n}: {\n readonly to: string;\n readonly keyValue: Key;\n readonly by: string | undefined;\n readonly resource: string;\n readonly fieldName: string;\n readonly testId: string;\n readonly open: RelationOpen;\n readonly disabled: boolean;\n readonly focusTrigger: () => void;\n readonly onRemove: () => void;\n}): React.ReactElement {\n const t = useOptionalT();\n const app = useApp();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const { record, isLoading } = useRelatedRecord(\n to,\n keyValue,\n by === undefined ? undefined : { by },\n );\n const title = record ? rowTitle(record as Record<string, unknown>) : String(keyValue);\n return (\n <span\n data-testid={testId}\n data-resource={resource}\n className=\"inline-flex items-center gap-0.5 rounded-md border bg-secondary pl-2 text-sm text-secondary-foreground\"\n >\n <RelationViewButton\n to={to}\n value={keyValue}\n by={by}\n fieldName={`${fieldName}:${String(keyValue)}`}\n beforeOpen={focusTrigger}\n open={openMode}\n title={isLoading ? t('table.loading', { defaultValue: 'Loading…' }) : title}\n />\n {!disabled && (\n <button\n type=\"button\"\n onClick={onRemove}\n data-testid={`${testId}:remove`}\n aria-label={`${t('relationPicker.clear', { defaultValue: 'Clear' })} ${title}`}\n className=\"inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <IconX className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </span>\n );\n}\n"],"mappings":"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"}