@burojs/ui-shadcn 0.6.0 → 0.7.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 (152) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-BWSAS7k2.js +143 -0
  3. package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
  4. package/dist/bulk-summary-CrEij5vq.cjs +143 -0
  5. package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
  6. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CS_JZ4Fd.js} +3 -3
  7. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CS_JZ4Fd.js.map} +1 -1
  8. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-y5M6_oe4.cjs} +4 -4
  9. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-y5M6_oe4.cjs.map} +1 -1
  10. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-2j0gOd7x.js} +384 -108
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-C-NHgMST.cjs} +468 -107
  13. package/dist/check-confirm-dialog-C-NHgMST.cjs.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-DFu0JP-1.cjs} +923 -120
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-DaWHUZuV.js} +839 -108
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-Bcz9NF2V.cjs} +128 -87
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-DCdli6Ha.js} +128 -87
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +731 -906
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +455 -247
  33. package/dist/index.d.ts +455 -247
  34. package/dist/index.js +647 -828
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
  37. package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-Bt8lrEIu.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-CAScLPb2.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-BHUEpn0I.js} +36 -23
  45. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  47. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  48. package/dist/{place-node-C5SmIAYU.cjs → place-node-CNb3w-c7.cjs} +1869 -947
  49. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  50. package/dist/{place-node-NraNYKPx.js → place-node-CfzQvh4Y.js} +1824 -926
  51. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  52. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BIqqY69Y.js} +9 -4
  53. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  54. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  55. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  56. package/dist/{related-list-timeline-yrPsjA3F.js → related-list-timeline-CRh7xwLD.js} +6 -7
  57. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  58. package/dist/{related-list-timeline-CCzl71aA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  59. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  60. package/dist/{related-rows-o0_t-Yce.js → related-rows--uP2iKQC.js} +25 -14
  61. package/dist/related-rows--uP2iKQC.js.map +1 -0
  62. package/dist/{related-rows-ea43qhhy.cjs → related-rows-MPnLCmPF.cjs} +27 -16
  63. package/dist/related-rows-MPnLCmPF.cjs.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-BJhaEqxz.js} +203 -102
  69. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-DzuGJpEl.cjs} +211 -110
  71. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  72. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-D-tBBZW_.js} +23 -59
  73. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  74. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-DaIMzf2H.cjs} +29 -71
  75. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +133 -35
  77. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  78. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-DoEY8EkH.js} +134 -36
  79. package/dist/relation-picker-dialog-DoEY8EkH.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-PXfMW9nh.cjs → resource-page-router-C_kGW2o_.cjs} +543 -300
  85. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  86. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-Diw6S6TK.js} +498 -255
  87. package/dist/resource-page-router-Diw6S6TK.js.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/package.json +11 -11
  105. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  106. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  107. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  108. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  109. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  110. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  111. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  112. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  113. package/dist/dialog-B_rt9S1c.js.map +0 -1
  114. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  115. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  116. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  117. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  118. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  119. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  120. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  121. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  122. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  123. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  124. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  125. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  126. package/dist/place-node-NraNYKPx.js.map +0 -1
  127. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  128. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  129. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  130. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  131. package/dist/relation-create-IA40t1hi.cjs +0 -364
  132. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  133. package/dist/relation-create-h6F9B4C4.js +0 -339
  134. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  135. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  136. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  137. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  138. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  139. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  140. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  141. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  142. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  143. package/dist/search-access-CPPmfuvy.js.map +0 -1
  144. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  145. package/dist/shell-BPlk8hEa.js +0 -205
  146. package/dist/shell-BPlk8hEa.js.map +0 -1
  147. package/dist/shell-DxhlsPwm.cjs +0 -271
  148. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  149. package/dist/toaster-C02XbB9r.js.map +0 -1
  150. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  151. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  152. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
@@ -1,13 +1,15 @@
1
1
  const require_use_theme = require("./use-theme-DFlpXils.cjs");
2
- const require_toaster = require("./toaster-Cr51X7bz.cjs");
3
- const require_check_confirm_dialog = require("./check-confirm-dialog--gPFnEeg.cjs");
2
+ const require_toaster = require("./toaster-BbByOytP.cjs");
3
+ const require_check_confirm_dialog = require("./check-confirm-dialog-C-NHgMST.cjs");
4
4
  const require_button = require("./button-BxnnE28H.cjs");
5
5
  const require_input = require("./input-BI0czGyI.cjs");
6
- const require_dialog = require("./dialog-DRNUq9EA.cjs");
7
- const require_search_access = require("./search-access-Dugvttlg.cjs");
6
+ const require_dialog = require("./dialog-vmMdfUWs.cjs");
7
+ const require_relation_create = require("./relation-create-DDEYcKfw.cjs");
8
8
  const require_skeleton = require("./skeleton-_1gP7wan.cjs");
9
- const require_filter_builder = require("./filter-builder-nXqtM_h4.cjs");
9
+ const require_filter_builder = require("./filter-builder-DFu0JP-1.cjs");
10
10
  const require_label = require("./label-w6JYf2cF.cjs");
11
+ const require_lazy_relation_picker_dialog = require("./lazy-relation-picker-dialog-Bt8lrEIu.cjs");
12
+ const require_relation_select = require("./relation-select-Cr3CfNLy.cjs");
11
13
  let _burojs_core = require("@burojs/core");
12
14
  let _burojs_react = require("@burojs/react");
13
15
  let _tabler_icons_react = require("@tabler/icons-react");
@@ -120,428 +122,91 @@ function toDisplayText(value) {
120
122
  }
121
123
  }
122
124
  //#endregion
123
- //#region src/fields/legacy-field.ts
124
- const STRING_ITEM = Object.freeze({ type: "string" });
125
- const LEGACY_KIND_DECLARATIONS = Object.freeze({
126
- string: { type: "string" },
127
- text: {
128
- type: "string",
129
- format: "text"
130
- },
131
- textarea: {
132
- type: "string",
133
- format: "textarea"
134
- },
135
- code: {
136
- type: "string",
137
- format: "code"
138
- },
139
- email: {
140
- type: "string",
141
- format: "email"
142
- },
143
- url: {
144
- type: "string",
145
- format: "url"
146
- },
147
- uuid: {
148
- type: "string",
149
- format: "uuid"
150
- },
151
- number: { type: "number" },
152
- boolean: { type: "boolean" },
153
- date: {
154
- type: "string",
155
- format: "date"
156
- },
157
- dateTime: {
158
- type: "string",
159
- format: "date-time"
160
- },
161
- enum: {
162
- type: "string",
163
- format: "enum"
164
- },
165
- json: {
166
- type: "object",
167
- format: "json"
168
- },
169
- richText: {
170
- type: "string",
171
- format: "rich-text"
172
- },
173
- file: {
174
- type: "array",
175
- format: "file",
176
- items: STRING_ITEM
177
- },
178
- relation: { type: "relation" },
179
- relationMany: {
180
- type: "array",
181
- format: "relation",
182
- items: STRING_ITEM
183
- }
184
- });
185
- const SEMANTIC_KEYS = Object.freeze([
186
- "type",
187
- "format",
188
- "meta",
189
- "required",
190
- "nullable",
191
- "empty",
192
- "default",
193
- "constraints",
194
- "texts",
195
- "options",
196
- "items",
197
- "properties",
198
- "additionalProperties",
199
- "variants"
200
- ]);
201
- function ownData(value, key) {
202
- if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
203
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
204
- if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
205
- return {
206
- present: true,
207
- value: descriptor.value
208
- };
209
- }
210
- function ownString(value, key) {
211
- const candidate = ownData(value, key).value;
212
- return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
213
- }
214
- function projectUiSelection(input) {
215
- const selection = {};
216
- const inputId = ownString(input, "input");
217
- const displayId = ownString(input, "display");
218
- if (inputId !== void 0) selection.input = inputId;
219
- if (displayId !== void 0) selection.display = displayId;
220
- return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
221
- }
222
- function projectFieldDeclaration(input) {
223
- const type = ownString(input, "type");
224
- if (type === void 0) return void 0;
225
- const projected = { type };
226
- for (const key of SEMANTIC_KEYS) {
227
- if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
228
- const candidate = ownData(input, key);
229
- if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
230
- }
231
- const ui = projectUiSelection(ownData(input, "ui").value);
232
- if (ui !== void 0) projected.ui = ui;
233
- if (type === "array") {
234
- const item = projectFieldDeclaration(ownData(input, "items").value);
235
- if (item !== void 0) projected.items = item;
236
- }
237
- if (type === "object") {
238
- const properties = ownData(input, "properties").value;
239
- if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
240
- const output = {};
241
- for (const key of Object.keys(properties)) {
242
- const child = projectFieldDeclaration(ownData(properties, key).value);
243
- if (child !== void 0) output[key] = child;
244
- }
245
- projected.properties = Object.freeze(output);
246
- }
247
- }
248
- if (type === "union") {
249
- const variants = ownData(input, "variants").value;
250
- if (Array.isArray(variants)) {
251
- const output = [];
252
- for (let index = 0; index < variants.length; index += 1) {
253
- const child = projectFieldDeclaration(ownData(variants, index).value);
254
- if (child !== void 0) output.push(child);
255
- }
256
- projected.variants = Object.freeze(output);
257
- }
258
- }
259
- return Object.freeze(projected);
260
- }
261
- function projectUiHints(input) {
262
- if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
263
- const projected = {};
264
- for (const key of Object.keys(input)) {
265
- const candidate = ownData(input, key);
266
- if (candidate.present) projected[key] = candidate.value;
267
- }
268
- return Object.freeze(projected);
269
- }
270
- function lowerLegacyField(field) {
271
- const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
272
- if (declaration !== void 0) return declaration;
273
- const direct = projectFieldDeclaration(field);
274
- const kind = ownString(field, "kind");
275
- const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
276
- if (direct !== void 0) {
277
- const format = direct.format ?? mapping?.format ?? kind;
278
- return format === void 0 ? direct : Object.freeze({
279
- ...direct,
280
- format
281
- });
282
- }
283
- const ui = projectUiHints(ownData(field, "ui").value);
284
- const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
285
- const format = ownString(field, "format") ?? mapping?.format ?? kind;
286
- const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
287
- const meta = ownData(field, "meta");
288
- const texts = ownData(field, "texts");
289
- const required = ownData(field, "required");
290
- const nullable = ownData(field, "nullable");
291
- const opts = ownData(field, "opts").value;
292
- const optionRequired = ownData(opts, "required");
293
- const optionNullable = ownData(opts, "nullable");
294
- const requiredValue = required.present ? required.value : optionRequired.value;
295
- const nullableValue = nullable.present ? nullable.value : optionNullable.value;
296
- return Object.freeze({
297
- type,
298
- ...format === void 0 ? {} : { format },
299
- ...items === void 0 ? {} : { items },
300
- ...meta.present && meta.value !== void 0 ? { meta: meta.value } : {},
301
- ...texts.present && texts.value !== void 0 ? { texts: texts.value } : {},
302
- ...requiredValue === true ? { required: true } : {},
303
- ...nullableValue === true ? { nullable: true } : {}
304
- });
305
- }
306
- function lowerLegacyFieldSource(field) {
307
- const declaration = lowerLegacyField(field);
308
- const ui = projectUiHints(ownData(field, "ui").value);
309
- if (ui === void 0) return declaration;
310
- return Object.freeze({
311
- ...declaration,
312
- ui
313
- });
314
- }
315
- function fieldDescription(field) {
316
- const declaration = field?.declaration;
317
- return (0, _burojs_core.declaredText)("field", declaration, "description") ?? (0, _burojs_core.declaredText)("field", field, "description");
125
+ //#region src/data-table/annotate-if-clipped.ts
126
+ function annotateIfClipped(e) {
127
+ const el = e.currentTarget;
128
+ const text = el.scrollWidth > el.clientWidth || Array.from(el.querySelectorAll("*")).some((c) => c.scrollWidth > c.clientWidth) ? (el.textContent ?? "").trim() : "";
129
+ if (text) el.setAttribute("title", text);
130
+ else el.removeAttribute("title");
318
131
  }
319
132
  //#endregion
320
- //#region src/fields/resolved-field.tsx
321
- const EMPTY = Object.freeze({});
322
- const EMPTY_ISSUES$1 = Object.freeze([]);
323
- const EMPTY_OPTIONS = Object.freeze([]);
324
- function canonical(field) {
325
- return lowerLegacyField(field);
326
- }
327
- const warnedObjectLabels = new Set();
328
- function optionsFor(field, name) {
329
- if (Array.isArray(field.options)) return field.options;
330
- const candidate = field.meta?.options;
331
- if (!Array.isArray(candidate)) return EMPTY_OPTIONS;
332
- const output = [];
333
- for (const item of candidate) {
334
- if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
335
- const raw = item;
336
- if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
337
- const { value, label, description, ...legacyMeta } = raw;
338
- if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
339
- warnedObjectLabels.add(`${name}:${value}`);
340
- console.warn(`[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).`);
341
- }
342
- output.push(Object.freeze({
343
- value,
344
- label: typeof label === "string" ? label : String(value),
345
- ...typeof description === "string" ? { description } : {},
346
- ...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
347
- }));
348
- }
349
- return Object.freeze(output);
350
- }
351
- const readScope = (ctx) => ctx.scope;
352
- function useFieldRuntimeContext(resource, record, values, metaSources) {
353
- const scope = (0, _burojs_react.useCtx)(readScope);
354
- const locale = (0, _burojs_react.useLocale)();
355
- const caps = (0, _burojs_react.useI18nCaps)((0, _burojs_core.formatEntityRef)(resource));
356
- const metaSourcesOut = (0, _burojs_react.useFieldMetaSources)({
357
- record,
358
- values,
359
- extra: metaSources
360
- });
361
- return (0, react.useMemo)(() => Object.freeze({
362
- i18n: Object.freeze({
363
- locale,
364
- has: caps.has,
365
- t: caps.t
366
- }),
367
- sources: Object.freeze({
368
- record: record ?? EMPTY,
369
- values: values ?? EMPTY,
370
- scope: scope ?? EMPTY
371
- }),
372
- metaSources: metaSourcesOut
373
- }), [
374
- caps,
375
- locale,
376
- record,
377
- scope,
378
- values,
379
- metaSourcesOut
380
- ]);
381
- }
382
- function useFieldMetaDiagnostic(state, resource, name, field, surface) {
383
- const reported = (0, react.useRef)(void 0);
384
- const reason = state.status === "failed" ? state.reason : void 0;
385
- (0, react.useEffect)(() => {
386
- if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
387
- reported.current = reason;
388
- const named = field.ui?.[surface];
389
- const implementation = typeof named === "string" ? named : field.format ?? field.type;
390
- console.error(`[burojs] field.meta-invalid: ${(0, _burojs_core.formatEntityRef)(resource)}.${name} (${implementation}): ${reason}`);
391
- }, [
392
- reason,
393
- resource,
394
- name,
395
- field,
396
- surface
397
- ]);
398
- }
399
- function MetaPlaceholder({ state }) {
133
+ //#region src/fields/field-value-frame.tsx
134
+ const COPY_CLASSES = [
135
+ "string",
136
+ "number",
137
+ "date"
138
+ ];
139
+ const COPIED_MS = 2e3;
140
+ const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
141
+ const isEmpty = (value) => value === null || value === void 0 || value === "";
142
+ function copyText(value) {
143
+ if (value instanceof Date) return value.toISOString();
144
+ return typeof value === "string" ? value : String(value);
145
+ }
146
+ function FieldValueFrame({ field, value, surface, entry, children }) {
400
147
  const t = (0, _burojs_react.useOptionalT)();
401
- if (state.status === "pending") return (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, {
402
- "data-buro-skeleton": "",
403
- "data-buro-field-meta": "pending",
404
- className: "h-9 w-full"
405
- });
406
- return (0, react_jsx_runtime.jsx)("div", {
407
- role: "alert",
408
- "data-buro-field-meta": "failed",
409
- className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
410
- children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
411
- });
412
- }
413
- function useFieldImplementationDiagnostic(diagnostic) {
414
- const reported = (0, react.useRef)(void 0);
148
+ const clipboard = (0, _burojs_react.useClipboard)();
149
+ const [copied, setCopied] = (0, react.useState)(false);
415
150
  (0, react.useEffect)(() => {
416
- if (diagnostic === void 0 || reported.current === diagnostic || process.env.NODE_ENV === "production") return;
417
- reported.current = diagnostic;
418
- console.warn("[buro/ui-shadcn] missing-field-implementation; rendering fallback.", diagnostic);
419
- }, [diagnostic]);
420
- }
421
- function ResolvedFieldDisplay({ resource, name, field: source, value, record, testId, fallback, presentation, metaSources }) {
422
- const sourceAtEdge = lowerLegacyFieldSource(source);
423
- const field = canonical(sourceAtEdge);
424
- const resolved = (0, _burojs_react.useFieldImplementation)({
425
- field,
426
- surface: "display"
427
- });
428
- const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
429
- const metaState = (0, _burojs_react.useFieldMeta)(field, resolved.implementation, runtimeContext.metaSources);
430
- useFieldMetaDiagnostic(metaState, resource, name, field, "display");
431
- useFieldImplementationDiagnostic(resolved.diagnostic);
432
- if (resolved.Component === void 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: fallback });
433
- if (metaState.status !== "ready") return (0, react_jsx_runtime.jsx)(MetaPlaceholder, { state: metaState });
434
- const Component = resolved.Component;
435
- const context = {
436
- owner: resource,
437
- resource,
151
+ if (!copied) return;
152
+ const timer = setTimeout(() => setCopied(false), COPIED_MS);
153
+ return () => clearTimeout(timer);
154
+ }, [copied]);
155
+ const ui = isRecord$1(field.ui) ? field.ui : void 0;
156
+ const asksCopy = ui?.copy === true;
157
+ const valueClass = (0, react.useMemo)(() => asksCopy ? (0, _burojs_core.displayClassOf)({
158
+ ...require_filter_builder.lowerLegacyField(field),
159
+ ...ui === void 0 ? {} : { ui }
160
+ }) : void 0, [
161
+ asksCopy,
438
162
  field,
439
- path: Object.freeze([name]),
440
- surface: "display",
441
- presentation: presentation ?? "default",
442
- record,
443
- meta: metaState.meta,
444
- ui: sourceAtEdge.ui ?? EMPTY,
445
- options: optionsFor(field, name),
446
- i18n: runtimeContext.i18n,
447
- sources: runtimeContext.sources,
448
- pending: false,
449
- touched: false,
450
- issues: EMPTY_ISSUES$1,
451
- accessibility: EMPTY,
452
- testIds: testId === void 0 ? EMPTY : Object.freeze({ display: testId })
453
- };
454
- if (resolved.multiple) return (0, react.createElement)(Component, {
455
- ...context,
456
- multiple: true,
457
- value: Array.isArray(value) ? value : []
458
- });
459
- return (0, react.createElement)(Component, {
460
- ...context,
461
- multiple: false,
462
- value
463
- });
464
- }
465
- function ResolvedFieldInput({ resource, name, field: source, value, record, form, testId, inputId, onChange, onBlur, disabled, required, placeholder, descriptionId, errorId, errorMessage, pending = false, touched = errorMessage != null, issues: suppliedIssues, relationSeeds, fixedFilters, metaSources, fallback }) {
466
- const sourceAtEdge = lowerLegacyFieldSource(source);
467
- const base = canonical(sourceAtEdge);
468
- const field = required === void 0 || required === base.required ? base : Object.freeze({
469
- ...base,
470
- required
471
- });
472
- const ui = Object.freeze({
473
- ...sourceAtEdge.ui ?? EMPTY,
474
- ...placeholder === void 0 ? {} : { placeholder }
475
- });
476
- const resolved = (0, _burojs_react.useFieldImplementation)({
477
- field: {
478
- ...field,
479
- ui
480
- },
481
- surface: "input"
163
+ ui
164
+ ]);
165
+ const { overflow: declared } = (0, _burojs_core.resolveSurfaceUi)(field, surface, entry);
166
+ const copyable = asksCopy && clipboard !== void 0 && !isEmpty(value) && valueClass !== void 0 && COPY_CLASSES.includes(valueClass);
167
+ if (!copyable && declared === void 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
168
+ const body = (declared ?? (surface === "column" ? "truncate" : "wrap")) === "wrap" ? (0, react_jsx_runtime.jsx)("span", {
169
+ className: "min-w-0 whitespace-normal [overflow-wrap:anywhere]",
170
+ "data-buro-overflow": "wrap",
171
+ children
172
+ }) : (0, react_jsx_runtime.jsx)("span", {
173
+ className: "inline-block min-w-0 max-w-full truncate align-bottom",
174
+ "data-buro-overflow": "truncate",
175
+ onPointerEnter: annotateIfClipped,
176
+ children
482
177
  });
483
- const runtimeContext = useFieldRuntimeContext(resource, record, form, metaSources);
484
- const metaState = (0, _burojs_react.useFieldMeta)(field, resolved.implementation, runtimeContext.metaSources);
485
- useFieldMetaDiagnostic(metaState, resource, name, field, "input");
486
- useFieldImplementationDiagnostic(resolved.diagnostic);
487
- if (resolved.Component === void 0) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: fallback });
488
- if (metaState.status !== "ready") return (0, react_jsx_runtime.jsx)(MetaPlaceholder, { state: metaState });
489
- const Component = resolved.Component;
490
- const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES$1 : Object.freeze([Object.freeze({
491
- code: "ui.field-error",
492
- message: {
493
- key: "ui.field-error",
494
- default: errorMessage
495
- },
496
- path: Object.freeze([]),
497
- source: "field",
498
- severity: "error"
499
- })]));
500
- const context = {
501
- owner: resource,
502
- resource,
503
- field,
504
- path: Object.freeze([name]),
505
- surface: "input",
506
- presentation: "default",
507
- record,
508
- form,
509
- meta: metaState.meta,
510
- ui,
511
- options: optionsFor(field, name),
512
- i18n: runtimeContext.i18n,
513
- sources: runtimeContext.sources,
514
- pending,
515
- touched,
516
- issues,
517
- accessibility: Object.freeze({
518
- input: inputId ?? name,
519
- description: descriptionId,
520
- error: errorId
521
- }),
522
- testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
178
+ if (!copyable) return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: body });
179
+ const label = t("field.copy", { defaultValue: "Copy" });
180
+ const copy = (event) => {
181
+ event.preventDefault();
182
+ event.stopPropagation();
183
+ clipboard(copyText(value)).then(() => setCopied(true), () => void 0);
523
184
  };
524
- if (resolved.multiple) return (0, react.createElement)(Component, {
525
- ...context,
526
- multiple: true,
527
- value: Array.isArray(value) ? value : [],
528
- onChange,
529
- onBlur,
530
- disabled,
531
- relationSeeds,
532
- fixedFilters,
533
- metaSources: runtimeContext.metaSources
534
- });
535
- return (0, react.createElement)(Component, {
536
- ...context,
537
- multiple: false,
538
- value,
539
- onChange,
540
- onBlur,
541
- disabled,
542
- relationSeeds,
543
- fixedFilters,
544
- metaSources: runtimeContext.metaSources
185
+ return (0, react_jsx_runtime.jsxs)("span", {
186
+ className: "inline-flex min-w-0 max-w-full items-center gap-1 align-bottom",
187
+ children: [
188
+ body,
189
+ (0, react_jsx_runtime.jsx)("button", {
190
+ type: "button",
191
+ "data-testid": `field-copy:${field.name}`,
192
+ "aria-label": label,
193
+ title: label,
194
+ onClick: copy,
195
+ className: "inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
196
+ children: copied ? (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, {
197
+ "aria-hidden": "true",
198
+ className: "size-3.5"
199
+ }) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCopy, {
200
+ "aria-hidden": "true",
201
+ className: "size-3.5"
202
+ })
203
+ }),
204
+ (0, react_jsx_runtime.jsx)("span", {
205
+ "aria-live": "polite",
206
+ className: "sr-only",
207
+ children: copied ? t("field.copied", { defaultValue: "Copied" }) : ""
208
+ })
209
+ ]
545
210
  });
546
211
  }
547
212
  //#endregion
@@ -555,7 +220,7 @@ function resolveRef(resource, i18nPrefix) {
555
220
  };
556
221
  }
557
222
  function Field({ resource, i18nPrefix, fallback, fixedFilters, field, doc, ...rest }) {
558
- const input = (0, react_jsx_runtime.jsx)(ResolvedFieldInput, {
223
+ const input = (0, react_jsx_runtime.jsx)(require_filter_builder.ResolvedFieldInput, {
559
224
  ...rest,
560
225
  field,
561
226
  ...fixedFilters === void 0 ? {} : { fixedFilters },
@@ -581,20 +246,30 @@ function Field({ resource, i18nPrefix, fallback, fixedFilters, field, doc, ...re
581
246
  });
582
247
  }
583
248
  function FieldDisplay({ resource, i18nPrefix, fallback, value, ...rest }) {
584
- return (0, react_jsx_runtime.jsx)(ResolvedFieldDisplay, {
585
- ...rest,
249
+ const frameField = (0, react.useMemo)(() => ({
250
+ ...rest.field,
251
+ name: rest.name
252
+ }), [rest.field, rest.name]);
253
+ return (0, react_jsx_runtime.jsx)(FieldValueFrame, {
254
+ field: frameField,
586
255
  value,
587
- resource: resolveRef(resource, i18nPrefix),
588
- fallback: fallback ?? (0, react_jsx_runtime.jsx)("span", { children: value == null || value === "" ? "—" : String(value) })
256
+ surface: "display",
257
+ children: (0, react_jsx_runtime.jsx)(require_filter_builder.ResolvedFieldDisplay, {
258
+ ...rest,
259
+ value,
260
+ resource: resolveRef(resource, i18nPrefix),
261
+ fallback: fallback ?? (0, react_jsx_runtime.jsx)("span", { children: value == null || value === "" ? "—" : String(value) })
262
+ })
589
263
  });
590
264
  }
591
265
  //#endregion
592
266
  //#region src/screen/field-leaf.tsx
593
267
  const SIZES = new Set(_burojs_core.LAYOUT_SIZES);
268
+ const SHOWN_ISSUES = 3;
594
269
  const FOCUSABLE = "input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])";
595
270
  function useLeafSize(field) {
596
271
  const implementation = (0, _burojs_react.useFieldImplementation)({
597
- field: lowerLegacyField(field),
272
+ field: require_filter_builder.lowerLegacyField(field),
598
273
  surface: "input"
599
274
  });
600
275
  const declared = field.ui?.size;
@@ -625,7 +300,7 @@ function MisconfiguredLeaf({ node }) {
625
300
  const binding = controller.field(name);
626
301
  const caps = (0, _burojs_react.useI18nCaps)(resource);
627
302
  const tid = (0, _burojs_react.useTestIds)();
628
- const field = lowerLegacyField(binding.field);
303
+ const field = require_filter_builder.lowerLegacyField(binding.field);
629
304
  const surface = mode === "show" ? "display" : "input";
630
305
  const resolved = (0, _burojs_react.useFieldImplementation)({
631
306
  field,
@@ -636,8 +311,8 @@ function MisconfiguredLeaf({ node }) {
636
311
  values: controller.values,
637
312
  extra: controller.sources
638
313
  });
639
- const state = (0, _burojs_react.useFieldMeta)(field, resolved.implementation, sources);
640
- useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
314
+ const state = (0, _burojs_react.useFieldMeta)(field, resolved.implementation, sources, resolved.params);
315
+ require_filter_builder.useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
641
316
  const label = (0, _burojs_core.resolveFieldLabel)(caps, name, (0, _burojs_core.declaredText)("field", binding.field, "label"));
642
317
  const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
643
318
  return (0, react_jsx_runtime.jsxs)("div", {
@@ -651,7 +326,7 @@ function MisconfiguredLeaf({ node }) {
651
326
  children: label
652
327
  }), (0, react_jsx_runtime.jsx)("div", {
653
328
  "data-buro-region": "field-control",
654
- children: (0, react_jsx_runtime.jsx)(MetaPlaceholder, { state })
329
+ children: (0, react_jsx_runtime.jsx)(require_filter_builder.MetaPlaceholder, { state })
655
330
  })]
656
331
  });
657
332
  }
@@ -668,10 +343,7 @@ function RecordLeaf({ node }) {
668
343
  const t = (0, _burojs_react.useOptionalT)();
669
344
  if (node.restricted === true) {
670
345
  const rawReason = node.restrictedReason;
671
- const reason = rawReason === void 0 || rawReason === "" ? void 0 : (0, _burojs_core.resolveContentSlot)(caps, [rawReason], {
672
- resource,
673
- default: rawReason
674
- });
346
+ const reason = rawReason === void 0 || rawReason === "" ? void 0 : require_check_confirm_dialog.reasonText(caps, rawReason, resource);
675
347
  const restrictedId = tid.formFieldRestricted(resource, mode, name);
676
348
  return (0, react_jsx_runtime.jsxs)("div", {
677
349
  ...attributes,
@@ -734,10 +406,7 @@ function RecordLeaf({ node }) {
734
406
  });
735
407
  if (node.editable !== true) {
736
408
  const rawReason = node.lockReason;
737
- const reason = rawReason === void 0 ? void 0 : (0, _burojs_core.resolveContentSlot)(caps, [rawReason], {
738
- resource,
739
- default: rawReason
740
- });
409
+ const reason = rawReason === void 0 ? void 0 : require_check_confirm_dialog.reasonText(caps, rawReason, resource);
741
410
  const locked = tid.formFieldLocked(resource, mode, name);
742
411
  return (0, react_jsx_runtime.jsxs)("div", {
743
412
  ...attributes,
@@ -789,7 +458,6 @@ function RecordLeaf({ node }) {
789
458
  function ActiveLeaf({ node, binding, label, attributes, inputId }) {
790
459
  const { resource, mode, controller, disabled: locked } = useScreenTree();
791
460
  const caps = (0, _burojs_react.useI18nCaps)(resource);
792
- const t = (0, _burojs_react.useOptionalT)();
793
461
  const tid = (0, _burojs_react.useTestIds)();
794
462
  const name = binding.name;
795
463
  const record = controller.record;
@@ -804,25 +472,22 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
804
472
  });
805
473
  const messageId = `${mode}-${name}-msg`;
806
474
  const placeholder = (0, _burojs_core.resolveFieldSlot)(caps, name, "placeholder");
807
- const hint = (0, _burojs_core.resolveFieldSlot)(caps, name, "description", fieldDescription(binding.field));
475
+ const hint = (0, _burojs_core.resolveFieldSlot)(caps, name, "description", require_filter_builder.fieldDescription(binding.field));
476
+ const chromeT = (0, _burojs_react.useChromeT)();
808
477
  const fieldKey = `resources.${resource}.fields.${name}`;
809
- const errorText = binding.error === void 0 ? null : (0, _burojs_react.resolveValidationMessage)({
810
- code: binding.error.code ?? "",
811
- message: binding.error.message,
812
- ...binding.error.params === void 0 ? {} : { params: binding.error.params }
813
- }, {
814
- t,
815
- fieldKey
816
- });
817
- const warning = errorText === null ? binding.warnings[0] : void 0;
818
- const warningText = warning === void 0 ? null : (0, _burojs_react.resolveValidationMessage)({
819
- code: warning.code ?? "",
820
- message: warning.message,
821
- ...warning.params === void 0 ? {} : { params: warning.params }
478
+ const text = (issue) => (0, _burojs_react.resolveValidationMessage)({
479
+ code: issue.code ?? "",
480
+ message: issue.message,
481
+ ...issue.check === void 0 ? {} : { check: issue.check }
822
482
  }, {
823
- t,
483
+ has: caps.has,
484
+ t: caps.t,
824
485
  fieldKey
825
486
  });
487
+ const shown = binding.issues.slice(0, SHOWN_ISSUES);
488
+ const more = binding.issues.length - shown.length;
489
+ const firstError = shown.find((issue) => issue.severity === "error");
490
+ const errorText = firstError === void 0 ? null : text(firstError);
826
491
  const disabled = binding.disabled || controller.isSubmitting || locked === true;
827
492
  const describedBy = errorText === null && hint ? messageId : void 0;
828
493
  const errorMessageId = errorText === null ? void 0 : messageId;
@@ -836,6 +501,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
836
501
  "data-buro-region": "field-label",
837
502
  className: "flex items-center gap-2",
838
503
  children: [(0, react_jsx_runtime.jsx)(require_label.Label, {
504
+ id: `${inputId}-label`,
839
505
  htmlFor: inputId,
840
506
  required: binding.required,
841
507
  children: label
@@ -851,6 +517,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
851
517
  resource: binding.resource,
852
518
  name,
853
519
  inputId,
520
+ labelId: `${inputId}-label`,
854
521
  field: binding.field,
855
522
  value: binding.value,
856
523
  onChange: binding.onChange,
@@ -864,8 +531,10 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
864
531
  ...describedBy === void 0 ? {} : { descriptionId: describedBy },
865
532
  ...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
866
533
  errorMessage: errorText,
534
+ issues: binding.issues,
867
535
  ...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
868
536
  ...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
537
+ ...binding.link === void 0 ? {} : { link: binding.link },
869
538
  fallback: (0, react_jsx_runtime.jsx)(require_input.Input, {
870
539
  id: inputId,
871
540
  name: inputId,
@@ -880,18 +549,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
880
549
  ...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
881
550
  onChange: (event) => binding.onChange(event.target.value)
882
551
  })
883
- }), errorText !== null ? (0, react_jsx_runtime.jsx)("p", {
552
+ }), shown.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
884
553
  id: messageId,
885
554
  "aria-live": "polite",
886
- className: "text-sm text-destructive",
887
- "data-testid": tid.formFieldError(resource, mode, name),
888
- children: errorText
889
- }) : warningText !== null ? (0, react_jsx_runtime.jsx)("p", {
890
- id: messageId,
891
- "aria-live": "polite",
892
- className: "text-sm text-warning-emphasis",
893
- "data-testid": `form-field-warning:${resource}:${mode}:${name}`,
894
- children: warningText
555
+ className: require_use_theme.cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
556
+ "data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
557
+ children: [shown.map((issue, index) => (0, react_jsx_runtime.jsx)("p", {
558
+ "data-buro-severity": issue.severity,
559
+ className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
560
+ children: text(issue)
561
+ }, index)), more > 0 ? (0, react_jsx_runtime.jsx)("p", {
562
+ className: "text-muted-foreground",
563
+ "data-testid": `form-field-more:${resource}:${mode}:${name}`,
564
+ children: chromeT("validation.moreIssues", {
565
+ count: more,
566
+ defaultValue: "+{{count}} more"
567
+ })
568
+ }) : null]
895
569
  }) : hint ? (0, react_jsx_runtime.jsx)("p", {
896
570
  id: messageId,
897
571
  "data-testid": tid.formFieldHelp(resource, mode, name),
@@ -911,7 +585,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
911
585
  const docRef = (0, _burojs_core.i18nPrefixToRef)(prefix);
912
586
  const docScope = docRef.namespace === "default" ? docRef.id : (0, _burojs_core.formatEntityRef)(docRef);
913
587
  const caps = (0, _burojs_react.useI18nCaps)(docScope);
914
- const t = (0, _burojs_react.useOptionalT)();
588
+ const issueCaps = (0, _burojs_react.useI18nCaps)();
915
589
  const uid = (0, react.useId)();
916
590
  const root = (0, react.useRef)(null);
917
591
  const inputId = `${inputIds ?? uid}-${name}`;
@@ -924,7 +598,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
924
598
  element.querySelector(FOCUSABLE)?.focus();
925
599
  });
926
600
  const label = (0, _burojs_core.resolveFieldLabel)(caps, name, (0, _burojs_core.declaredText)("field", binding.field, "label"));
927
- const hint = (0, _burojs_core.resolveFieldSlot)(caps, name, "description", fieldDescription(binding.field));
601
+ const hint = (0, _burojs_core.resolveFieldSlot)(caps, name, "description", require_filter_builder.fieldDescription(binding.field));
928
602
  const info = (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DocInfo, {
929
603
  address: `${docScope}/fields/${name}`,
930
604
  name: label,
@@ -965,9 +639,10 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
965
639
  const text = (issue) => (0, _burojs_react.resolveValidationMessage)({
966
640
  code: issue.code ?? "",
967
641
  message: issue.message,
968
- ...issue.params === void 0 ? {} : { params: issue.params }
642
+ ...issue.check === void 0 ? {} : { check: issue.check }
969
643
  }, {
970
- t,
644
+ has: issueCaps.has,
645
+ t: issueCaps.t,
971
646
  fieldKey
972
647
  });
973
648
  const defaultNode = (0, react_jsx_runtime.jsx)(Field, {
@@ -975,6 +650,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
975
650
  field: binding.field,
976
651
  metaSources: controller.sources,
977
652
  inputId,
653
+ labelId: `${inputId}-label`,
978
654
  i18nPrefix: prefix,
979
655
  disabled: disabled === true || binding.disabled
980
656
  });
@@ -990,6 +666,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
990
666
  "data-buro-region": "field-label",
991
667
  className: "flex items-center gap-2",
992
668
  children: [(0, react_jsx_runtime.jsx)(require_label.Label, {
669
+ id: `${inputId}-label`,
993
670
  htmlFor: inputId,
994
671
  required: binding.required,
995
672
  children: label
@@ -1030,9 +707,10 @@ function Notice({ children }) {
1030
707
  children
1031
708
  });
1032
709
  }
1033
- function FactsView({ props }) {
710
+ function FactsView({ props, empty }) {
1034
711
  const own = props;
1035
712
  const t = (0, _burojs_react.useOptionalT)();
713
+ const chromeT = (0, _burojs_react.useChromeT)();
1036
714
  const snapshot = (0, _burojs_react.useApplicationSnapshot)();
1037
715
  const queryId = (0, _burojs_core.formatEntityRef)(own.query);
1038
716
  const caps = (0, _burojs_react.useI18nCaps)();
@@ -1049,7 +727,10 @@ function FactsView({ props }) {
1049
727
  kind: "custom-query",
1050
728
  ref: own.query
1051
729
  }), [own.query]);
1052
- const result = (0, _burojs_react.useCustomQuery)(handle, blocked ? {} : input, { enabled: !blocked });
730
+ const result = (0, _burojs_react.useCustomQuery)(handle, blocked ? {} : input, {
731
+ enabled: !blocked,
732
+ ...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
733
+ });
1053
734
  const shown = (0, react.useMemo)(() => {
1054
735
  const byName = new Map(fields.map((field) => [field.name, field]));
1055
736
  return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
@@ -1057,7 +738,7 @@ function FactsView({ props }) {
1057
738
  return field === void 0 ? [] : [field];
1058
739
  });
1059
740
  }, [fields, own.rows]);
1060
- if (blocked) return (0, react_jsx_runtime.jsx)(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
741
+ if (blocked) return (0, react_jsx_runtime.jsx)(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1061
742
  if (result.isError) return (0, react_jsx_runtime.jsxs)("div", {
1062
743
  role: "alert",
1063
744
  className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
@@ -1066,7 +747,7 @@ function FactsView({ props }) {
1066
747
  variant: "outline",
1067
748
  size: "sm",
1068
749
  onClick: () => void result.refetch(),
1069
- children: t("actions.retry", { defaultValue: "Retry" })
750
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
1070
751
  })]
1071
752
  });
1072
753
  if (result.data === void 0) return own.view === "list" ? (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, {
@@ -1080,7 +761,7 @@ function FactsView({ props }) {
1080
761
  }, field.name))
1081
762
  });
1082
763
  const data = result.data;
1083
- if (data === null || typeof data !== "object" || shown.length === 0) return (0, react_jsx_runtime.jsx)(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
764
+ if (data === null || typeof data !== "object" || shown.length === 0) return (0, react_jsx_runtime.jsx)(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
1084
765
  const pairs = shown.map((field) => {
1085
766
  const value = data[field.name];
1086
767
  const label = (0, _burojs_core.resolveContentSlot)(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
@@ -1090,13 +771,18 @@ function FactsView({ props }) {
1090
771
  const body = value === null || value === void 0 ? (0, react_jsx_runtime.jsx)("span", {
1091
772
  className: "text-muted-foreground",
1092
773
  children: "—"
1093
- }) : (0, react_jsx_runtime.jsx)(ResolvedFieldDisplay, {
1094
- resource: own.query,
1095
- name: field.name,
774
+ }) : (0, react_jsx_runtime.jsx)(FieldValueFrame, {
1096
775
  field,
1097
776
  value,
1098
- record: data,
1099
- fallback: toDisplayText(value).text
777
+ surface: "display",
778
+ children: (0, react_jsx_runtime.jsx)(require_filter_builder.ResolvedFieldDisplay, {
779
+ resource: own.query,
780
+ name: field.name,
781
+ field,
782
+ value,
783
+ record: data,
784
+ fallback: toDisplayText(value).text
785
+ })
1100
786
  });
1101
787
  return {
1102
788
  name: field.name,
@@ -1150,6 +836,9 @@ const PlacementFrameContext = (0, react.createContext)(null);
1150
836
  function usePlacementFrame() {
1151
837
  return (0, react.useContext)(PlacementFrameContext);
1152
838
  }
839
+ function placementLayoutId(frame) {
840
+ return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
841
+ }
1153
842
  //#endregion
1154
843
  //#region src/flows/app-flow-button.tsx
1155
844
  function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
@@ -1269,7 +958,7 @@ function RecordInlineFlow({ name }) {
1269
958
  return (0, react_jsx_runtime.jsx)(InlineHostLoader, {
1270
959
  resource,
1271
960
  name: flowName,
1272
- record: row,
961
+ ...hostRecord((0, _burojs_core.flowRecordOf)(compiled, row)),
1273
962
  placementKey: key,
1274
963
  lease: kept,
1275
964
  stale: true,
@@ -1285,7 +974,7 @@ function RecordInlineFlow({ name }) {
1285
974
  if (control.state === "disabled" && kept !== void 0 && kept.dirty) return (0, react_jsx_runtime.jsx)(InlineHostLoader, {
1286
975
  resource,
1287
976
  name: flowName,
1288
- record: row,
977
+ ...hostRecord((0, _burojs_core.flowRecordOf)(compiled, row)),
1289
978
  placementKey: key,
1290
979
  lease: kept,
1291
980
  stale: true,
@@ -1297,11 +986,12 @@ function RecordInlineFlow({ name }) {
1297
986
  return (0, react_jsx_runtime.jsx)(InlineHostLoader, {
1298
987
  resource,
1299
988
  name: flowName,
1300
- record: control.record ?? row,
989
+ ...hostRecord((0, _burojs_core.flowRecordOf)(compiled, control.record ?? row)),
1301
990
  placementKey: key,
1302
991
  ...lease === void 0 ? {} : { lease }
1303
992
  });
1304
993
  }
994
+ const hostRecord = (record) => record === void 0 ? {} : { record };
1305
995
  function InlineHostLoader(props) {
1306
996
  const loaded = require_check_confirm_dialog.useLoaded(require_check_confirm_dialog.loadInlineFlowHost);
1307
997
  if (loaded.error !== void 0) return (0, react_jsx_runtime.jsx)("p", {
@@ -1517,6 +1207,7 @@ function placementSeeds(where, resolved, closure) {
1517
1207
  where.forEach((entry, index) => {
1518
1208
  const value = resolved[index]?.value;
1519
1209
  if (entry.op !== "eq" || value === null || value === void 0) return;
1210
+ if (typeof entry.field !== "string" || entry.field.includes(".")) return;
1520
1211
  if (resolved[index]?.field !== entry.field) return;
1521
1212
  const source = refSource(entry.value);
1522
1213
  if (source === null ? isLiteral(entry.value) : source === "record" || source === "user" || !UNTRUSTED.includes(source) && Object.hasOwn(closure, source) && !(closure[source] ?? []).some((input) => UNTRUSTED.includes(input))) seeds[entry.field] = value;
@@ -1534,7 +1225,8 @@ function BadgeBody({ icon, label }) {
1534
1225
  }) : null, label]
1535
1226
  });
1536
1227
  }
1537
- function EnumBadge({ enumName, value, testid, className, presentation }) {
1228
+ function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
1229
+ const enumName = (0, _burojs_core_internal.enumBindingKey)(enumInput) ?? "";
1538
1230
  const app = (0, _burojs_react.useApp)();
1539
1231
  const caps = (0, _burojs_react.useI18nCaps)();
1540
1232
  const def = app.registry.enums.get(enumName);
@@ -1680,6 +1372,7 @@ function useExportRunWatcher() {
1680
1372
  const runtime = (0, _burojs_react.useDataRuntime)();
1681
1373
  const runs = (0, _burojs_react.useExportRuns)();
1682
1374
  const t = (0, _burojs_react.useOptionalT)();
1375
+ const caps = (0, _burojs_react.useI18nCaps)();
1683
1376
  react.useEffect(() => {
1684
1377
  const announced = announcedRunsFor(runtime);
1685
1378
  const unsubscribes = runs.map((run) => {
@@ -1698,7 +1391,7 @@ function useExportRunWatcher() {
1698
1391
  label: t("export.toast.download"),
1699
1392
  onClick: () => downloadArtifact(artifact)
1700
1393
  } });
1701
- } else if (state.status === "failed") sonner.toast.error(t("export.toast.failed", { params: { message: state.error.message } }));
1394
+ } else if (state.status === "failed") sonner.toast.error(t("export.toast.failed", { params: { message: (0, _burojs_react.resolveErrorText)(state.error, caps) } }));
1702
1395
  };
1703
1396
  check();
1704
1397
  return run.handle.subscribe(check);
@@ -1709,24 +1402,27 @@ function useExportRunWatcher() {
1709
1402
  }, [
1710
1403
  runs,
1711
1404
  runtime,
1712
- t
1405
+ t,
1406
+ caps
1713
1407
  ]);
1714
1408
  }
1715
1409
  //#endregion
1716
1410
  //#region src/actions/action-overflow.tsx
1717
1411
  const RowActionsCollapseContext = (0, react.createContext)(false);
1718
- function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
1719
- const t = (0, _burojs_react.useOptionalT)();
1412
+ function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
1413
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
1414
+ const chromeT = (0, _burojs_react.useChromeT)();
1720
1415
  const tid = (0, _burojs_react.useTestIds)();
1416
+ const recordKey = String((0, _burojs_react.recordKeyOf)((0, _burojs_react.useApp)().registry.get(resource), record) ?? "");
1721
1417
  const collapsedRows = (0, react.useContext)(RowActionsCollapseContext);
1722
1418
  const [flowFor, setFlowFor] = (0, react.useState)(null);
1723
- const actions = useShownItemActions(resource, record, surface, hideFlows);
1419
+ const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
1724
1420
  if (actions.length === 0) return null;
1725
1421
  const isRow = surface === "row";
1726
1422
  const folded = isRow && (collapse ?? collapsedRows);
1727
1423
  const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
1728
1424
  const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
1729
- const menuTestId = isRow ? tid.listRowActionsMenu(resource, record.id) : tid.formActionsMenu(resource, surface);
1425
+ const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
1730
1426
  const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
1731
1427
  const overlays = (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: flowFor != null ? (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.RuntimeFlowRunner, {
1732
1428
  resource,
@@ -1740,6 +1436,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1740
1436
  actionName: a.name,
1741
1437
  record,
1742
1438
  scope: surface,
1439
+ ...sources === void 0 ? {} : { sources },
1743
1440
  density: a.density
1744
1441
  }, a.name));
1745
1442
  return (0, react_jsx_runtime.jsxs)("div", {
@@ -1763,7 +1460,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1763
1460
  size: "icon",
1764
1461
  className: "size-7",
1765
1462
  "data-testid": menuTestId,
1766
- "aria-label": t("actions.more", { defaultValue: "More" }),
1463
+ "aria-label": chromeT("actions.more", { defaultValue: "More" }),
1767
1464
  children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconDots, {
1768
1465
  className: "h-4 w-4",
1769
1466
  "aria-hidden": "true"
@@ -1777,15 +1474,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1777
1474
  children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconDots, {
1778
1475
  className: "h-4 w-4",
1779
1476
  "aria-hidden": "true"
1780
- }), t("actions.more", { defaultValue: "More" })]
1477
+ }), chromeT("actions.more", { defaultValue: "More" })]
1781
1478
  })
1782
1479
  }), (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DropdownMenuContent, {
1783
1480
  align: "end",
1784
- children: menu.map((a, index) => (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index === firstDestructive && index > 0 && (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${record.id}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.ActionButton, {
1481
+ children: menu.map((a, index) => (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [index === firstDestructive && index > 0 && (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${recordKey}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.ActionButton, {
1785
1482
  resource,
1786
1483
  actionName: a.name,
1787
1484
  record,
1788
1485
  scope: surface,
1486
+ ...sources === void 0 ? {} : { sources },
1789
1487
  asMenuItem: true
1790
1488
  }) : (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.FlowMenuItem, {
1791
1489
  resource,
@@ -1805,11 +1503,12 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
1805
1503
  ]
1806
1504
  });
1807
1505
  }
1808
- function useShownItemActions(resource, record, surface, hideFlows) {
1506
+ function useShownItemActions(resource, record, surface, hideFlows, sources) {
1809
1507
  const resolved = (0, _burojs_react.useItemActions)({
1810
1508
  resource,
1811
1509
  record,
1812
- surface
1510
+ surface,
1511
+ ...sources === void 0 ? {} : { sources }
1813
1512
  });
1814
1513
  const def = (0, _burojs_react.useApp)().registry.get(resource);
1815
1514
  const shown = def !== void 0 && (0, _burojs_core.isBuiltinItemAction)((def.actions?.item)?.find((action) => action.name === "edit"), "edit") && !(0, _burojs_core.recordEntries)(def).page.edit && (0, _burojs_core.recordEntries)(def).quick.edit === null ? resolved.filter((a) => a.name !== "edit") : resolved;
@@ -1902,8 +1601,14 @@ function DocumentTitleProvider({ routeKey, children }) {
1902
1601
  });
1903
1602
  }
1904
1603
  //#endregion
1604
+ //#region src/resource/resource-title.ts
1605
+ function resolveResourceTitle(caps, t, resource, slot, params) {
1606
+ return (0, _burojs_core.resolveContentSlot)(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
1607
+ }
1608
+ //#endregion
1905
1609
  //#region src/resource/page-header.tsx
1906
- function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1610
+ function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
1611
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
1907
1612
  const app = (0, _burojs_react.useApp)();
1908
1613
  const t = (0, _burojs_react.useT)();
1909
1614
  const def = app.registry.get(resource);
@@ -1912,10 +1617,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1912
1617
  const resourceSingle = (0, _burojs_core.resolveResourceLabel)(caps, resource, "single", def);
1913
1618
  const labelSingle = labelOverride ?? resourceSingle;
1914
1619
  const labelPlural = (0, _burojs_core.resolveResourceLabel)(caps, resource, "plural", def);
1915
- const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? t("pageHeader.editTitleNamed", { params: {
1620
+ const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
1916
1621
  label: labelSingle,
1917
1622
  name: recordName
1918
- } }) : t("pageHeader.editTitle", { params: { label: labelSingle } }) : t("pageHeader.createTitle", { params: { label: labelSingle } }));
1623
+ }) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
1919
1624
  useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
1920
1625
  const showBack = backLink && mode !== "list" && !breadcrumbs;
1921
1626
  const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
@@ -1946,9 +1651,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
1946
1651
  ]
1947
1652
  });
1948
1653
  }
1949
- function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1654
+ function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
1655
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
1950
1656
  const def = (0, _burojs_react.useApp)().registry.get(resource);
1951
- const recordName = (0, _burojs_react.useProjectedTitle)(resource, record?.id, record);
1657
+ const recordName = (0, _burojs_react.useProjectedTitle)(resource, (0, _burojs_react.recordKeyOf)(def, record), record);
1952
1658
  (0, _burojs_react.usePublishDocVariant)(resource, record);
1953
1659
  if (!def) return null;
1954
1660
  return (0, react_jsx_runtime.jsx)(PageHeader, {
@@ -1963,16 +1669,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
1963
1669
  }), extraActions] })
1964
1670
  });
1965
1671
  }
1966
- function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
1672
+ function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
1673
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
1967
1674
  const app = (0, _burojs_react.useApp)();
1968
1675
  const def = app.registry.get(resource);
1969
1676
  const caps = (0, _burojs_react.useI18nCaps)(resource);
1970
- const recordName = (0, _burojs_react.useProjectedTitle)(resource, record?.id, record);
1677
+ const recordKey = (0, _burojs_react.recordKeyOf)(def, record);
1678
+ const recordName = (0, _burojs_react.useProjectedTitle)(resource, recordKey, record);
1971
1679
  return (0, react_jsx_runtime.jsx)(PageHeader, {
1972
1680
  resource,
1973
1681
  mode: "edit",
1974
1682
  recordName,
1975
- backHref: editBackHref(app.registry, resource, String(record.id), backHref),
1683
+ backHref: editBackHref(app.registry, resource, String(recordKey ?? ""), backHref),
1976
1684
  breadcrumbs,
1977
1685
  backLabel: recordName ? `${(0, _burojs_core.resolveResourceLabel)(caps, resource, "single", def)}: ${recordName}` : void 0,
1978
1686
  actions: (0, react_jsx_runtime.jsx)(ActionOverflow, {
@@ -2172,7 +1880,8 @@ function ItemNode({ node, row, fallback }) {
2172
1880
  const viewProps = {
2173
1881
  props,
2174
1882
  data: {},
2175
- t: placement.viewProps?.t ?? ((name) => name)
1883
+ t: placement.viewProps?.t ?? ((name) => name),
1884
+ ...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
2176
1885
  };
2177
1886
  return (0, react_jsx_runtime.jsx)(View, { ...viewProps });
2178
1887
  }
@@ -2254,6 +1963,7 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
2254
1963
  const t = (0, _burojs_react.useOptionalT)();
2255
1964
  return (0, react_jsx_runtime.jsxs)("section", {
2256
1965
  ...nodeAttributes,
1966
+ ...title === void 0 ? {} : { "aria-labelledby": titleId },
2257
1967
  children: [
2258
1968
  title === void 0 && info === null && !collapsible ? null : (0, react_jsx_runtime.jsxs)("div", {
2259
1969
  "data-buro-region": "panel-title",
@@ -2313,7 +2023,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
2313
2023
  }), info]
2314
2024
  }), tabs.map((tab) => (0, react_jsx_runtime.jsx)(TabsContent, {
2315
2025
  value: tab.key,
2316
- forceMount: true,
2317
2026
  "data-buro-region": "tab-panel",
2318
2027
  "data-buro-key": tab.key,
2319
2028
  children: tab.panel
@@ -2401,17 +2110,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
2401
2110
  //#region src/primitives/checkbox.tsx
2402
2111
  const Checkbox = react.forwardRef(({ className, checked, ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_checkbox.Root, {
2403
2112
  ref,
2404
- className: require_use_theme.cn("peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
2113
+ className: require_use_theme.cn("peer inline-flex h-4 w-4 shrink-0 items-center justify-center align-middle rounded-[4px] border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
2405
2114
  checked,
2406
2115
  ...props,
2407
2116
  children: (0, react_jsx_runtime.jsx)(_radix_ui_react_checkbox.Indicator, {
2408
2117
  className: require_use_theme.cn("flex items-center justify-center text-current"),
2409
2118
  children: checked === "indeterminate" ? (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconMinus, {
2410
2119
  "data-icon": "minus",
2411
- className: "h-4 w-4"
2120
+ className: "size-3.5"
2412
2121
  }) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, {
2413
2122
  "data-icon": "check",
2414
- className: "h-4 w-4"
2123
+ className: "size-3.5"
2415
2124
  })
2416
2125
  })
2417
2126
  }));
@@ -2436,6 +2145,17 @@ function useContainerNarrow(maxRem = 40) {
2436
2145
  return [setElement, narrow];
2437
2146
  }
2438
2147
  //#endregion
2148
+ //#region src/data-table/fill-column.ts
2149
+ function fillColumnId(columns) {
2150
+ const eligible = columns.filter((c) => !c.pinned && !c.service);
2151
+ const declared = eligible.find((c) => c.fill);
2152
+ if (declared !== void 0) return declared.id;
2153
+ for (let i = eligible.length - 1; i >= 0; i--) {
2154
+ const c = eligible[i];
2155
+ if (c.resize && c.max === void 0) return c.id;
2156
+ }
2157
+ }
2158
+ //#endregion
2439
2159
  //#region src/data-table/data-table.tsx
2440
2160
  function pinEdgeClass(isLastLeft, isFirstRight) {
2441
2161
  if (isLastLeft) return "after:pointer-events-none after:absolute after:inset-y-0 after:left-full after:z-10 after:w-1.5 after:bg-gradient-to-r after:from-overlay/[0.07] after:to-transparent dark:after:from-overlay/30";
@@ -2447,7 +2167,8 @@ function pinEdgeClassFor(column, overflowing) {
2447
2167
  if (!pinned || !overflowing) return "";
2448
2168
  return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
2449
2169
  }
2450
- function getPinStyles(column, overflowing) {
2170
+ function getPinStyles(column, overflowing, fill = false) {
2171
+ if (fill) return {};
2451
2172
  const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
2452
2173
  const pinned = column.getIsPinned();
2453
2174
  if (!pinned || !overflowing) return explicitWidth;
@@ -2472,21 +2193,35 @@ function columnMetaNoRowNav(meta) {
2472
2193
  function columnMetaNoClip(meta) {
2473
2194
  return meta?.noClip === true;
2474
2195
  }
2475
- function annotateIfClipped(e) {
2476
- const el = e.currentTarget;
2477
- const text = el.scrollWidth > el.clientWidth || Array.from(el.querySelectorAll("*")).some((c) => c.scrollWidth > c.clientWidth) ? (el.textContent ?? "").trim() : "";
2478
- if (text) el.setAttribute("title", text);
2479
- else el.removeAttribute("title");
2196
+ function columnMetaWrap(meta) {
2197
+ return meta?.wrap === true;
2198
+ }
2199
+ function columnMetaFill(meta) {
2200
+ return meta?.fill === true;
2201
+ }
2202
+ function tableFillColumnId(table) {
2203
+ if (table.options.enableColumnResizing !== true) return void 0;
2204
+ return fillColumnId(table.getVisibleLeafColumns().map((c) => {
2205
+ const max = c.columnDef.maxSize;
2206
+ return {
2207
+ id: c.id,
2208
+ fill: columnMetaFill(c.columnDef.meta),
2209
+ resize: c.getCanResize(),
2210
+ ...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
2211
+ pinned: c.getIsPinned() !== false,
2212
+ service: c.id.startsWith("__")
2213
+ };
2214
+ }));
2480
2215
  }
2481
- function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, noRowNav }) {
2216
+ function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
2482
2217
  const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
2483
2218
  const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
2484
- const noClip = columnMetaNoClip(cell.column.columnDef.meta);
2219
+ const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
2485
2220
  return (0, react_jsx_runtime.jsx)(require_filter_builder.TableCell, {
2486
2221
  "data-testid": testId,
2487
2222
  "data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
2488
2223
  onClick: onCellClick,
2489
- style: getPinStyles(liveColumn, overflowing),
2224
+ style: getPinStyles(liveColumn, overflowing, fill),
2490
2225
  className: require_use_theme.cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
2491
2226
  children: resizable && !noClip ? (0, react_jsx_runtime.jsx)("div", {
2492
2227
  className: "truncate",
@@ -2498,7 +2233,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
2498
2233
  function MemoCellInner(props) {
2499
2234
  return renderCellMarkup(props);
2500
2235
  }
2501
- const MemoCell = (0, react.memo)(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.testId === next.testId);
2236
+ const MemoCell = (0, react.memo)(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.fill === next.fill && prev.testId === next.testId);
2502
2237
  function SelectionCellInner(props) {
2503
2238
  const row = props.cell.row;
2504
2239
  const toggle = (e) => {
@@ -2512,7 +2247,7 @@ function SelectionCellInner(props) {
2512
2247
  });
2513
2248
  }
2514
2249
  const SelectionCell = (0, react.memo)(SelectionCellInner);
2515
- function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2250
+ function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2516
2251
  const clickable = rowHref !== void 0 || onRowOpen !== void 0;
2517
2252
  return (0, react_jsx_runtime.jsx)(require_filter_builder.TableRow, {
2518
2253
  "data-state": isSelected ? "selected" : void 0,
@@ -2525,6 +2260,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2525
2260
  isSelected,
2526
2261
  resizable,
2527
2262
  overflowing,
2263
+ fill: cell.column.id === fillColumnId,
2528
2264
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2529
2265
  testId: getCellTestId?.(row.original, cell.column.id)
2530
2266
  }, cell.id) : (0, react_jsx_runtime.jsx)(MemoCell, {
@@ -2532,13 +2268,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
2532
2268
  rowOriginal: row.original,
2533
2269
  resizable,
2534
2270
  overflowing,
2271
+ fill: cell.column.id === fillColumnId,
2535
2272
  pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
2536
2273
  testId: getCellTestId?.(row.original, cell.column.id)
2537
2274
  }, cell.id))
2538
2275
  });
2539
2276
  }
2540
- const MemoRow = (0, react.memo)(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
2541
- function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2277
+ const MemoRow = (0, react.memo)(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
2278
+ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
2542
2279
  return (0, react_jsx_runtime.jsx)(require_filter_builder.TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => (0, react_jsx_runtime.jsx)(require_filter_builder.TableRow, {
2543
2280
  "aria-busy": "true",
2544
2281
  children: Array.from({ length: Math.max(1, visibleColumnCount) }).map((__, j) => (0, react_jsx_runtime.jsx)(require_filter_builder.TableCell, { children: (0, react_jsx_runtime.jsx)("div", { className: "h-4 w-3/4 animate-pulse rounded bg-muted" }) }, `skeleton-${i}-${j}`))
@@ -2555,6 +2292,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2555
2292
  resizable,
2556
2293
  overflowing,
2557
2294
  columnLayoutKey,
2295
+ fillColumnId,
2558
2296
  selectionColumnId,
2559
2297
  getCellTestId,
2560
2298
  getRowTestId,
@@ -2566,7 +2304,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
2566
2304
  }
2567
2305
  const DataTableBody = (0, react.memo)(DataTableBodyInner, (prev, next) => {
2568
2306
  if (!next.isResizing) return false;
2569
- return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId;
2307
+ return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId;
2570
2308
  });
2571
2309
  function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
2572
2310
  const t = (0, _burojs_react.useOptionalT)();
@@ -2596,8 +2334,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2596
2334
  const rows = table.getRowModel().rows;
2597
2335
  const headerGroups = table.getHeaderGroups();
2598
2336
  const visibleColumnCount = table.getVisibleLeafColumns().length;
2337
+ const fillId = tableFillColumnId(table);
2599
2338
  const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
2600
2339
  const resizable = table.options.enableColumnResizing === true;
2340
+ const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
2601
2341
  const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
2602
2342
  const sizingState = table.getState().columnSizing;
2603
2343
  const sizingInfo = table.getState().columnSizingInfo;
@@ -2689,7 +2429,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2689
2429
  className,
2690
2430
  style: resizable ? {
2691
2431
  ...columnSizeVars,
2692
- minWidth: "100%",
2432
+ width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
2693
2433
  tableLayout: "fixed"
2694
2434
  } : void 0,
2695
2435
  children: [(0, react_jsx_runtime.jsx)(require_filter_builder.TableHeader, { children: headerGroups.map((hg) => (0, react_jsx_runtime.jsx)(require_filter_builder.TableRow, { children: hg.headers.map((header) => {
@@ -2700,7 +2440,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2700
2440
  return (0, react_jsx_runtime.jsxs)(require_filter_builder.TableHead, {
2701
2441
  colSpan: header.colSpan,
2702
2442
  "aria-sort": ariaSort,
2703
- style: getPinStyles(header.column, overflowing),
2443
+ style: getPinStyles(header.column, overflowing, header.column.id === fillId),
2704
2444
  className: require_use_theme.cn("relative", resizable && !columnMetaNoClip(header.column.columnDef.meta) && (pinEdgeClassFor(header.column, overflowing) ? "[&>*:not([data-testid^=col-resize])]:truncate" : "overflow-hidden [&>*:not([data-testid^=col-resize])]:truncate"), canReorder && "cursor-grab", pinEdgeClassFor(header.column, overflowing), columnMetaClassName(header.column.columnDef.meta)),
2705
2445
  draggable: canReorder || void 0,
2706
2446
  onDragStart: canReorder ? (e) => {
@@ -2759,6 +2499,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
2759
2499
  resizable,
2760
2500
  overflowing,
2761
2501
  columnLayoutKey,
2502
+ fillColumnId: fillId,
2762
2503
  selectionColumnId,
2763
2504
  isResizing: Boolean(sizingInfo.isResizingColumn),
2764
2505
  getCellTestId,
@@ -2982,63 +2723,247 @@ function resolveUpdater(updater, prev) {
2982
2723
  }
2983
2724
  //#endregion
2984
2725
  //#region src/resource/list-error-state.tsx
2985
- function ListErrorState({ error, onRetry, resource, title, className }) {
2986
- const t = (0, _burojs_react.useT)();
2987
- const raw = error.message ?? "";
2988
- const friendly = require_toaster.toFriendlyError(raw, "", require_toaster.friendlyStringsFrom(t));
2989
- const friendlyError = friendly.replaced ? friendly.detail : raw;
2990
- return (0, react_jsx_runtime.jsxs)("div", {
2991
- role: "alert",
2992
- className: require_use_theme.cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-destructive/40 bg-destructive/5 text-center", className),
2993
- "data-testid": "error",
2994
- children: [
2995
- (0, react_jsx_runtime.jsx)("div", {
2996
- className: "rounded-full bg-destructive/10 p-3 text-destructive",
2997
- children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconAlertCircle, {
2998
- className: "size-6",
2999
- "aria-hidden": "true"
3000
- })
3001
- }),
3002
- (0, react_jsx_runtime.jsxs)("div", {
3003
- className: "space-y-1",
3004
- children: [(0, react_jsx_runtime.jsx)("h3", {
3005
- className: "text-base font-semibold tracking-tight text-destructive",
3006
- children: title ?? t("list.errorTitle")
3007
- }), (0, react_jsx_runtime.jsx)("p", {
3008
- className: "max-w-md text-sm text-muted-foreground",
3009
- children: friendlyError || t("list.errorBody")
3010
- })]
3011
- }),
3012
- raw && raw !== friendlyError ? (0, react_jsx_runtime.jsxs)("details", {
3013
- className: "max-w-md text-left",
3014
- children: [(0, react_jsx_runtime.jsx)("summary", {
3015
- className: "cursor-pointer text-xs text-muted-foreground hover:text-foreground",
3016
- children: t("list.errorDetails")
3017
- }), (0, react_jsx_runtime.jsx)("p", {
3018
- className: "mt-1.5 break-all font-mono text-xs text-muted-foreground",
3019
- "data-testid": "list-error-detail",
3020
- children: raw
3021
- })]
3022
- }) : null,
3023
- onRetry && (0, react_jsx_runtime.jsx)("div", {
3024
- className: "flex justify-center pt-2",
3025
- children: (0, react_jsx_runtime.jsxs)(require_button.Button, {
3026
- type: "button",
3027
- variant: "outline",
3028
- size: "sm",
3029
- "data-testid": resource ? `list-error-retry:${resource}` : "list-error-retry",
3030
- onClick: onRetry,
3031
- className: "gap-1.5",
3032
- children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconRefresh, {
3033
- className: "size-3.5",
3034
- "aria-hidden": "true"
3035
- }), t("actions.retry")]
3036
- })
3037
- })
3038
- ]
2726
+ function ListErrorState(props) {
2727
+ const { error, onRetry, resource, title, className } = props;
2728
+ return (0, react_jsx_runtime.jsx)(require_relation_create.LoadError, {
2729
+ surface: "list",
2730
+ error,
2731
+ ...onRetry === void 0 ? {} : { retry: onRetry },
2732
+ ...resource === void 0 ? {} : { resource },
2733
+ ...title === void 0 ? {} : { title },
2734
+ ...className === void 0 ? {} : { className }
3039
2735
  });
3040
2736
  }
3041
2737
  //#endregion
2738
+ //#region src/resource/use-table-layout.ts
2739
+ const WRITE_DEBOUNCE_MS = 200;
2740
+ function mutate(run, ok, then) {
2741
+ try {
2742
+ run().then((result) => {
2743
+ if (ok(result.status)) then();
2744
+ }, () => void 0);
2745
+ } catch {}
2746
+ }
2747
+ function resolve(updater, current) {
2748
+ return typeof updater === "function" ? updater(current) : updater;
2749
+ }
2750
+ function sameNames(a, b) {
2751
+ return a.length === b.length && a.every((name, index) => name === b[index]);
2752
+ }
2753
+ function readSizes(sizes, rule) {
2754
+ const out = {};
2755
+ for (const [column, size] of Object.entries(sizes)) {
2756
+ const of = rule(column);
2757
+ if (of === void 0 || !of.resize) continue;
2758
+ out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
2759
+ }
2760
+ return out;
2761
+ }
2762
+ function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
2763
+ const params = (0, react.useMemo)(() => layoutId === void 0 ? null : {
2764
+ resource,
2765
+ list: layoutId
2766
+ }, [resource, layoutId]);
2767
+ const settings = (0, _burojs_react.useOptionalUiSettings)(_burojs_react.tableLayoutSetting, params);
2768
+ const settingsRef = (0, react.useRef)(settings);
2769
+ settingsRef.current = settings;
2770
+ const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
2771
+ const fallback = (0, react.useMemo)(() => ({
2772
+ sizes: {},
2773
+ order: [],
2774
+ pinning: defaultPinning
2775
+ }), [defaultPinning]);
2776
+ const [local, setLocal] = (0, react.useState)();
2777
+ const stored = settings?.snapshot.entries[0]?.value;
2778
+ const settled = settings === void 0 || !settings.snapshot.pending;
2779
+ const held = (0, react.useRef)(null);
2780
+ const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
2781
+ const latest = (0, react.useRef)(layout);
2782
+ latest.current = layout;
2783
+ const sizing = (0, react.useMemo)(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
2784
+ const sizingRef = (0, react.useRef)(sizing);
2785
+ sizingRef.current = sizing;
2786
+ const generation = (0, react.useRef)(0);
2787
+ const timer = (0, react.useRef)(null);
2788
+ const pending = (0, react.useRef)(null);
2789
+ const confirm = (0, react.useCallback)((sent) => {
2790
+ if (generation.current === sent && pending.current === null) setLocal(void 0);
2791
+ }, []);
2792
+ const write = (0, react.useCallback)((target, value) => {
2793
+ const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
2794
+ if (scopeId === void 0) return;
2795
+ const sent = generation.current;
2796
+ mutate(() => target.set({
2797
+ scopeId,
2798
+ value
2799
+ }), (status) => status === "created" || status === "updated", () => confirm(sent));
2800
+ }, [confirm]);
2801
+ const store = (0, react.useCallback)((target, next) => {
2802
+ if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
2803
+ write(target, next);
2804
+ return;
2805
+ }
2806
+ const entry = target.snapshot.entries[0];
2807
+ if (entry === void 0) {
2808
+ setLocal(void 0);
2809
+ return;
2810
+ }
2811
+ const sent = generation.current;
2812
+ mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
2813
+ }, [
2814
+ defaultPinning,
2815
+ write,
2816
+ confirm
2817
+ ]);
2818
+ const flush = (0, react.useCallback)(() => {
2819
+ if (timer.current !== null) clearTimeout(timer.current);
2820
+ timer.current = null;
2821
+ const due = pending.current;
2822
+ pending.current = null;
2823
+ if (due !== null) write(due.target, due.value);
2824
+ }, [write]);
2825
+ const change = (0, react.useCallback)((next, debounced, touch) => {
2826
+ latest.current = next;
2827
+ generation.current += 1;
2828
+ setLocal({
2829
+ key,
2830
+ value: next
2831
+ });
2832
+ const target = settingsRef.current;
2833
+ if (target === void 0) return;
2834
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2835
+ const edits = held.current !== null && held.current.key === key ? held.current : {
2836
+ key,
2837
+ resetAll: false,
2838
+ resetSizes: false,
2839
+ sizes: new Map()
2840
+ };
2841
+ held.current = edits;
2842
+ touch(edits);
2843
+ return;
2844
+ }
2845
+ pending.current = {
2846
+ target,
2847
+ value: next
2848
+ };
2849
+ if (!debounced) {
2850
+ flush();
2851
+ return;
2852
+ }
2853
+ if (timer.current !== null) clearTimeout(timer.current);
2854
+ timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
2855
+ }, [key, flush]);
2856
+ (0, react.useEffect)(() => flush, [key]);
2857
+ (0, react.useEffect)(() => {
2858
+ const edits = held.current;
2859
+ if (edits === null) return;
2860
+ if (edits.key !== key) {
2861
+ held.current = null;
2862
+ return;
2863
+ }
2864
+ const target = settingsRef.current;
2865
+ if (!settled || target === void 0) return;
2866
+ held.current = null;
2867
+ const base = edits.resetAll ? fallback : stored ?? fallback;
2868
+ const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
2869
+ for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
2870
+ else sizes[column] = size;
2871
+ const next = {
2872
+ sizes,
2873
+ order: edits.order ?? base.order,
2874
+ pinning: edits.pinning ?? base.pinning
2875
+ };
2876
+ latest.current = next;
2877
+ generation.current += 1;
2878
+ setLocal({
2879
+ key,
2880
+ value: next
2881
+ });
2882
+ store(target, next);
2883
+ }, [settled, key]);
2884
+ const resetToken = resetLayout?.token ?? 0;
2885
+ const seenToken = (0, react.useRef)(resetToken);
2886
+ (0, react.useEffect)(() => {
2887
+ if (resetToken === seenToken.current) return;
2888
+ seenToken.current = resetToken;
2889
+ const all = resetLayout?.mode === "all";
2890
+ const next = all ? fallback : {
2891
+ ...latest.current,
2892
+ sizes: {}
2893
+ };
2894
+ if (timer.current !== null) clearTimeout(timer.current);
2895
+ timer.current = null;
2896
+ pending.current = null;
2897
+ latest.current = next;
2898
+ generation.current += 1;
2899
+ setLocal({
2900
+ key,
2901
+ value: next
2902
+ });
2903
+ const target = settingsRef.current;
2904
+ if (target === void 0) return;
2905
+ if (target.snapshot.pending || held.current !== null && held.current.key === key) {
2906
+ const prior = held.current !== null && held.current.key === key ? held.current : void 0;
2907
+ held.current = all ? {
2908
+ key,
2909
+ resetAll: true,
2910
+ resetSizes: false,
2911
+ sizes: new Map()
2912
+ } : {
2913
+ key,
2914
+ resetAll: prior?.resetAll ?? false,
2915
+ resetSizes: true,
2916
+ sizes: new Map(),
2917
+ ...prior?.order === void 0 ? {} : { order: prior.order },
2918
+ ...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
2919
+ };
2920
+ return;
2921
+ }
2922
+ store(target, next);
2923
+ }, [resetToken]);
2924
+ const setColumnSizing = (0, react.useCallback)((updater) => {
2925
+ const before = sizingRef.current;
2926
+ const sizes = resolve(updater, before);
2927
+ sizingRef.current = sizes;
2928
+ change({
2929
+ ...latest.current,
2930
+ sizes
2931
+ }, true, (edits) => {
2932
+ for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
2933
+ });
2934
+ }, [change]);
2935
+ const setColumnOrder = (0, react.useCallback)((updater) => {
2936
+ const order = resolve(updater, latest.current.order);
2937
+ change({
2938
+ ...latest.current,
2939
+ order
2940
+ }, false, (edits) => {
2941
+ edits.order = order;
2942
+ });
2943
+ }, [change]);
2944
+ const setColumnPinning = (0, react.useCallback)((updater) => {
2945
+ const resolved = resolve(updater, latest.current.pinning);
2946
+ const pinning = {
2947
+ left: resolved.left ?? [],
2948
+ right: resolved.right ?? []
2949
+ };
2950
+ change({
2951
+ ...latest.current,
2952
+ pinning
2953
+ }, false, (edits) => {
2954
+ edits.pinning = pinning;
2955
+ });
2956
+ }, [change]);
2957
+ return {
2958
+ columnSizing: sizing,
2959
+ setColumnSizing,
2960
+ columnOrder: layout.order,
2961
+ setColumnOrder,
2962
+ columnPinning: layout.pinning,
2963
+ setColumnPinning
2964
+ };
2965
+ }
2966
+ //#endregion
3042
2967
  //#region src/list-kit/cursor-load-more.tsx
3043
2968
  function CursorLoadMore({ hasMore, loading, onMore }) {
3044
2969
  const t = (0, _burojs_react.useT)();
@@ -3080,6 +3005,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
3080
3005
  }
3081
3006
  const SELECT_COL_ID = "__select__";
3082
3007
  const ACTIONS_COL_ID = "__actions__";
3008
+ const DEFAULT_PINNING = Object.freeze({
3009
+ left: Object.freeze([SELECT_COL_ID, "id"]),
3010
+ right: Object.freeze([ACTIONS_COL_ID])
3011
+ });
3083
3012
  const ROW_ACTION_CONTROL_PX = 28;
3084
3013
  const ROW_ACTION_GAP_PX = 4;
3085
3014
  const ACTIONS_CELL_PADDING_PX = 16;
@@ -3091,29 +3020,30 @@ function actionsColumnSize(items) {
3091
3020
  const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
3092
3021
  return Math.max(ACTIONS_COL_MIN_PX, strip);
3093
3022
  }
3094
- const COLUMN_SIZE_BY_TYPE = {
3095
- boolean: 96,
3096
- number: 112
3097
- };
3098
- const COLUMN_SIZE_BY_FORMAT = {
3099
- date: 132,
3100
- "date-time": 132
3023
+ const ALIGN_CLASS = {
3024
+ start: "text-start",
3025
+ center: "text-center",
3026
+ end: "text-end"
3101
3027
  };
3102
- function columnSize(field) {
3103
- if (field?.format !== void 0 && COLUMN_SIZE_BY_FORMAT[field.format] !== void 0) return COLUMN_SIZE_BY_FORMAT[field.format];
3104
- if (field?.type !== void 0 && COLUMN_SIZE_BY_TYPE[field.type] !== void 0) return COLUMN_SIZE_BY_TYPE[field.type];
3105
- if (field?.kind === "date" || field?.kind === "dateTime") return COLUMN_SIZE_BY_FORMAT[field.kind === "dateTime" ? "date-time" : "date"];
3106
- return field?.kind === void 0 ? void 0 : COLUMN_SIZE_BY_TYPE[field.kind];
3107
- }
3108
- function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
3028
+ const STANDALONE_COLUMN_UI = Object.freeze({
3029
+ default: 160,
3030
+ min: 90,
3031
+ align: "start",
3032
+ resize: true,
3033
+ fill: false,
3034
+ display: Object.freeze({ params: Object.freeze({}) })
3035
+ });
3036
+ function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
3109
3037
  10,
3110
3038
  25,
3111
3039
  50,
3112
3040
  100
3113
- ], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }) {
3041
+ ], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, sortable, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, columnUi, layoutId, tableRef }) {
3042
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
3114
3043
  const app = (0, _burojs_react.useApp)();
3115
3044
  const tid = (0, _burojs_react.useTestIds)();
3116
3045
  const t = (0, _burojs_react.useOptionalT)();
3046
+ const chromeT = (0, _burojs_react.useChromeT)();
3117
3047
  const locale = (0, _burojs_react.useLocale)();
3118
3048
  const nf = (0, react.useMemo)(() => new Intl.NumberFormat(locale), [locale]);
3119
3049
  const i18nCaps = (0, _burojs_react.useI18nCaps)(resource);
@@ -3135,23 +3065,6 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3135
3065
  (0, react.useEffect)(() => {
3136
3066
  emittedRef.current = null;
3137
3067
  }, [controlledSelection]);
3138
- const layoutKey = `buro:table-layout:${resource}`;
3139
- const [columnSizing, setColumnSizing] = usePersistedState(`${layoutKey}:sizing`, {}, 200);
3140
- const [columnOrder, setColumnOrder] = usePersistedState(`${layoutKey}:order`, []);
3141
- const DEFAULT_PINNING = {
3142
- left: [SELECT_COL_ID, "id"],
3143
- right: [ACTIONS_COL_ID]
3144
- };
3145
- const [columnPinning, setColumnPinning] = usePersistedState(`${layoutKey}:pinning`, DEFAULT_PINNING);
3146
- const resetToken = resetLayout?.token ?? 0;
3147
- (0, react.useEffect)(() => {
3148
- if (!resetToken) return;
3149
- setColumnSizing({});
3150
- if (resetLayout?.mode === "all") {
3151
- setColumnOrder([]);
3152
- setColumnPinning(DEFAULT_PINNING);
3153
- }
3154
- }, [resetToken]);
3155
3068
  (0, react.useEffect)(() => {
3156
3069
  if (controlled) return;
3157
3070
  setOwnSelection({});
@@ -3161,7 +3074,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3161
3074
  const accessPending = listAccess.status === "pending" || pathAccess.pending;
3162
3075
  const declaredColumnKeys = (0, react.useMemo)(() => {
3163
3076
  if (!def) return [];
3164
- return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
3077
+ return [...columnsOverride ?? def.views?.list?.columns?.map(_burojs_core.columnName) ?? Object.keys(def.fields)];
3165
3078
  }, [def, columnsOverride]);
3166
3079
  const visibleColumnKeys = (0, react.useMemo)(() => accessPending ? [] : declaredColumnKeys.filter((col) => listAccess.columnVisible(col) && (columnTarget(app.registry, resource, col) === void 0 || listAccess.columnVisible(col.split(".")[0]) && pathAccess.visible(col))), [
3167
3080
  accessPending,
@@ -3211,10 +3124,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3211
3124
  const resourceLabel = (0, _burojs_core.resolveResourceLabel)(i18nCaps, resource, "plural", def);
3212
3125
  const recordSingular = (0, _burojs_core.resolveResourceLabel)(i18nCaps, resource, "single", def);
3213
3126
  const ariaLabel = resourceLabel;
3214
- const actionsHeader = t("actions.column", { defaultValue: "Actions" });
3127
+ const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
3215
3128
  const viewRow = (0, _burojs_react.useViewRow)(resource);
3216
3129
  const volatileRef = (0, react.useRef)({
3217
3130
  onSort,
3131
+ sortable,
3218
3132
  onHideColumn,
3219
3133
  requiredColumns,
3220
3134
  renderCell,
@@ -3226,6 +3140,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3226
3140
  });
3227
3141
  volatileRef.current = {
3228
3142
  onSort,
3143
+ sortable,
3229
3144
  onHideColumn,
3230
3145
  requiredColumns,
3231
3146
  renderCell,
@@ -3249,6 +3164,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3249
3164
  }, [rowLinkKey]);
3250
3165
  const stableRowClassName = (0, react.useCallback)((row) => volatileRef.current.rowHref?.(volatileRef.current.viewRow(row.original)) || (volatileRef.current.rowQuick?.(row.original) ?? null) !== null ? "cursor-pointer" : void 0, [rowLinkKey]);
3251
3166
  const [listRoot, narrow] = useContainerNarrow();
3167
+ const columnUiOf = (0, react.useMemo)(() => {
3168
+ const entries = new Map();
3169
+ for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
3170
+ const name = (0, _burojs_core.columnName)(entry);
3171
+ if (!entries.has(name)) entries.set(name, entry);
3172
+ }
3173
+ return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : (0, _burojs_core.resolveColumnUi)(def, entries.get(col) ?? col, app.registry, _burojs_react.columnTypeWidth));
3174
+ }, [
3175
+ app,
3176
+ def,
3177
+ columnUi
3178
+ ]);
3179
+ const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
3180
+ resource,
3181
+ layoutId,
3182
+ rule: (0, react.useMemo)(() => {
3183
+ const known = new Set([
3184
+ ...declaredColumnKeys,
3185
+ ...(def?.views?.list?.columns ?? []).map(_burojs_core.columnName),
3186
+ ...(def?.views?.list?.optionalColumns ?? []).map(_burojs_core.columnName),
3187
+ ...Object.keys(def?.fields ?? {})
3188
+ ]);
3189
+ return (col) => {
3190
+ if (!known.has(col)) return void 0;
3191
+ const ui = columnUiOf(col);
3192
+ return {
3193
+ min: ui.min,
3194
+ max: ui.max,
3195
+ resize: ui.resize
3196
+ };
3197
+ };
3198
+ }, [
3199
+ declaredColumnKeys,
3200
+ def,
3201
+ columnUiOf
3202
+ ]),
3203
+ defaultPinning: DEFAULT_PINNING,
3204
+ resetLayout
3205
+ });
3252
3206
  const tableColumns = (0, react.useMemo)(() => {
3253
3207
  const cols = [];
3254
3208
  if (selectable) cols.push({
@@ -3271,19 +3225,32 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3271
3225
  });
3272
3226
  for (const col of visibleColumnKeys) {
3273
3227
  const target = columnTarget(app.registry, resource, col);
3274
- const field = target === void 0 ? def?.fields?.[col] : target.field;
3228
+ const field = target !== void 0 ? target.field : def === void 0 ? void 0 : (0, _burojs_core.declaredField)(def, col);
3275
3229
  const declaredLabel = (0, _burojs_core.declaredText)("field", field, "label");
3276
3230
  const headerLabel = target === void 0 ? (0, _burojs_core.resolveFieldLabel)(volatileRef.current.i18nCaps, col, declaredLabel) : (0, _burojs_core.resolveFieldLabel)(capsOf(volatileRef.current.i18nCaps, target.resource), target.name, declaredLabel);
3277
- const kindSize = columnSize(field);
3278
- const declared = target === void 0 && def !== void 0 ? (0, _burojs_core.declaredField)(def, col) : void 0;
3279
- const linkColumn = declared !== null && typeof declared === "object" && (0, _burojs_core.isLinkField)(declared);
3231
+ const ui = columnUiOf(col);
3232
+ const frameField = {
3233
+ ...field ?? { type: "unknown" },
3234
+ name: target === void 0 ? col : target.name
3235
+ };
3236
+ const cellEntry = { display: ui.display };
3237
+ const wraps = ui.display.overflow === "wrap";
3238
+ const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && (0, _burojs_core.isColumnSortable)(def, col, app, app.registry));
3280
3239
  cols.push({
3281
3240
  id: col,
3282
3241
  ...columnAccessor(col),
3283
- ...kindSize !== void 0 ? { size: kindSize } : {},
3284
- enableSorting: !linkColumn && Boolean(volatileRef.current.onSort),
3242
+ size: ui.default,
3243
+ minSize: ui.min,
3244
+ maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
3245
+ enableResizing: ui.resize,
3246
+ enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
3285
3247
  enableHiding: true,
3286
- meta: { testid: col },
3248
+ meta: {
3249
+ testid: col,
3250
+ className: ALIGN_CLASS[ui.align],
3251
+ ...wraps ? { wrap: true } : {},
3252
+ ...ui.fill ? { fill: true } : {}
3253
+ },
3287
3254
  header: ({ column, table }) => {
3288
3255
  const v = volatileRef.current;
3289
3256
  const pinSide = column.getIsPinned();
@@ -3314,9 +3281,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3314
3281
  };
3315
3282
  return (0, react_jsx_runtime.jsx)(ColumnHeaderMenu, {
3316
3283
  label: headerLabel,
3317
- sortable: !linkColumn && Boolean(v.onSort),
3284
+ sortable: columnSortable && Boolean(v.onSort),
3318
3285
  sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
3319
- onSort: !linkColumn && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3286
+ onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
3320
3287
  pinned: column.getIsPinned(),
3321
3288
  onPin: (side) => column.pin(side),
3322
3289
  canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
@@ -3332,38 +3299,47 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3332
3299
  const record = volatileRef.current.viewRow(raw);
3333
3300
  const value = record[col];
3334
3301
  const customCell = volatileRef.current.renderCell?.(col, value, record);
3335
- const cellInner = customCell !== void 0 ? customCell : (0, react_jsx_runtime.jsx)(ResolvedFieldDisplay, {
3302
+ const cellInner = customCell !== void 0 ? customCell : (0, react_jsx_runtime.jsx)(require_filter_builder.ResolvedFieldDisplay, {
3336
3303
  resource: target === void 0 ? resourceRef : target.ref,
3337
3304
  name: target === void 0 ? col : target.name,
3338
3305
  field: field ?? { type: "unknown" },
3339
3306
  value,
3340
3307
  record,
3308
+ uiSurface: "column",
3309
+ columnEntry: cellEntry,
3341
3310
  fallback: (0, react_jsx_runtime.jsx)("span", { children: String(value ?? "") })
3342
3311
  });
3312
+ const framed = (node) => customCell !== void 0 ? node : (0, react_jsx_runtime.jsx)(FieldValueFrame, {
3313
+ field: frameField,
3314
+ value,
3315
+ surface: "column",
3316
+ entry: cellEntry,
3317
+ children: node
3318
+ });
3343
3319
  const href = volatileRef.current.rowHref?.(record);
3344
3320
  const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
3345
3321
  const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
3346
- if (quick !== null) return href ? (0, react_jsx_runtime.jsx)(require_search_access.ClaimedQuickLink, {
3322
+ if (quick !== null) return framed(href ? (0, react_jsx_runtime.jsx)(require_relation_create.ClaimedQuickLink, {
3347
3323
  href,
3348
- className: require_search_access.relationLinkClassName,
3324
+ className: require_relation_create.relationLinkClassName,
3349
3325
  onOpen: quick,
3350
3326
  children: cellInner
3351
3327
  }) : (0, react_jsx_runtime.jsx)("button", {
3352
3328
  type: "button",
3353
3329
  [_burojs_react.QUICK_ANCHOR_ATTRIBUTE]: "",
3354
- className: `${require_search_access.relationLinkClassName} text-left`,
3330
+ className: `${require_relation_create.relationLinkClassName} text-left`,
3355
3331
  onClick: (e) => {
3356
3332
  e.stopPropagation();
3357
3333
  quick();
3358
3334
  },
3359
3335
  children: cellInner
3360
- });
3361
- return isFirstContentCell && href ? (0, react_jsx_runtime.jsx)(RouterLink, {
3336
+ }));
3337
+ return framed(isFirstContentCell && href ? (0, react_jsx_runtime.jsx)(RouterLink, {
3362
3338
  href,
3363
- className: require_search_access.relationLinkClassName,
3339
+ className: require_relation_create.relationLinkClassName,
3364
3340
  onClick: (e) => e.stopPropagation(),
3365
3341
  children: cellInner
3366
- }) : cellInner;
3342
+ }) : cellInner);
3367
3343
  }
3368
3344
  });
3369
3345
  }
@@ -3372,7 +3348,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3372
3348
  id: ACTIONS_COL_ID,
3373
3349
  enableSorting: false,
3374
3350
  enableResizing: false,
3375
- size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
3351
+ size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
3376
3352
  enableHiding: false,
3377
3353
  header: () => (0, react_jsx_runtime.jsx)("span", {
3378
3354
  className: "sr-only",
@@ -3383,7 +3359,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3383
3359
  noRowNav: true,
3384
3360
  noClip: true
3385
3361
  },
3386
- cell: ({ row }) => (0, react_jsx_runtime.jsx)(ActionOverflow, {
3362
+ cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : (0, react_jsx_runtime.jsx)(ActionOverflow, {
3387
3363
  resource,
3388
3364
  record: row.original,
3389
3365
  surface: "row"
@@ -3403,7 +3379,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3403
3379
  actionsHeader,
3404
3380
  i18nCaps,
3405
3381
  rowLinkKey,
3406
- narrow
3382
+ narrow,
3383
+ columnUiOf
3407
3384
  ]);
3408
3385
  const handleSelectionChange = (updater) => {
3409
3386
  if (controlled) {
@@ -3506,13 +3483,28 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3506
3483
  const isWrongPage = page > 1;
3507
3484
  const EmptyIcon = isFiltered ? _tabler_icons_react.IconFilterX : _tabler_icons_react.IconInbox;
3508
3485
  const items = resourceLabel.toLowerCase();
3509
- const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", { params: { items } }) : t("list.emptyTitle", { params: { items } });
3510
- const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", { params: { items } }) : createHref ? t("list.emptyBody", { params: { item: recordSingular.toLowerCase() } }) : t("list.emptyBodyReadOnly", { params: { items } });
3511
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
3512
- className: require_use_theme.cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
3513
- "data-testid": tid.listEmpty(resource),
3514
- children: [
3515
- (0, react_jsx_runtime.jsx)("div", {
3486
+ const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
3487
+ resource,
3488
+ params: { items }
3489
+ }) : t("list.emptyTitle", {
3490
+ resource,
3491
+ params: { items }
3492
+ });
3493
+ const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
3494
+ resource,
3495
+ params: { items }
3496
+ }) : createHref ? t("list.emptyBody", {
3497
+ resource,
3498
+ params: { item: recordSingular.toLowerCase() }
3499
+ }) : t("list.emptyBodyReadOnly", {
3500
+ resource,
3501
+ params: { items }
3502
+ });
3503
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
3504
+ className: require_use_theme.cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
3505
+ "data-testid": tid.listEmpty(resource),
3506
+ children: [
3507
+ (0, react_jsx_runtime.jsx)("div", {
3516
3508
  className: "rounded-full bg-muted p-3 text-muted-foreground",
3517
3509
  children: (0, react_jsx_runtime.jsx)(EmptyIcon, {
3518
3510
  className: "size-6",
@@ -3532,7 +3524,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3532
3524
  (0, react_jsx_runtime.jsxs)("div", {
3533
3525
  className: "flex justify-center gap-2 pt-2",
3534
3526
  children: [
3535
- isFiltered && onClearFilter && (0, react_jsx_runtime.jsx)(require_button.Button, {
3527
+ onClearFilter && (0, react_jsx_runtime.jsx)(require_button.Button, {
3536
3528
  type: "button",
3537
3529
  variant: "outline",
3538
3530
  size: "sm",
@@ -3720,43 +3712,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
3720
3712
  })
3721
3713
  });
3722
3714
  }
3723
- function usePersistedState(key, initial, debounceMs = 0) {
3724
- const [state, setState] = (0, react.useState)(() => {
3725
- if (typeof window === "undefined") return initial;
3726
- try {
3727
- const raw = window.localStorage.getItem(key);
3728
- return raw ? JSON.parse(raw) : initial;
3729
- } catch {
3730
- return initial;
3731
- }
3715
+ //#endregion
3716
+ //#region src/blocks/related-list-unbound.tsx
3717
+ function UnboundRelatedList(props) {
3718
+ const t = (0, _burojs_react.useOptionalT)();
3719
+ return (0, react_jsx_runtime.jsxs)("div", {
3720
+ className: "flex flex-col gap-2",
3721
+ "data-testid": `list-unbound:${props.resource}`,
3722
+ children: [props.title === void 0 ? null : (0, react_jsx_runtime.jsx)("h3", {
3723
+ className: "text-sm font-semibold",
3724
+ children: props.title
3725
+ }), (0, react_jsx_runtime.jsx)("p", {
3726
+ className: "text-sm text-muted-foreground",
3727
+ children: props.empty ?? t("table.empty", { defaultValue: "No records" })
3728
+ })]
3732
3729
  });
3733
- (0, react.useEffect)(() => {
3734
- try {
3735
- if (window.localStorage.getItem(key) == null) window.localStorage.setItem(key, JSON.stringify(initial));
3736
- } catch {}
3737
- }, [key]);
3738
- const writeTimer = (0, react.useRef)(null);
3739
- const write = (value) => {
3740
- const doWrite = () => {
3741
- try {
3742
- window.localStorage.setItem(key, JSON.stringify(value));
3743
- } catch {}
3744
- };
3745
- if (debounceMs <= 0) {
3746
- doWrite();
3747
- return;
3748
- }
3749
- if (writeTimer.current) clearTimeout(writeTimer.current);
3750
- writeTimer.current = setTimeout(doWrite, debounceMs);
3751
- };
3752
- const set = (updater) => {
3753
- setState((prev) => {
3754
- const next = typeof updater === "function" ? updater(prev) : updater;
3755
- write(next);
3756
- return next;
3757
- });
3758
- };
3759
- return [state, set];
3730
+ }
3731
+ function placementScope(scope, owner) {
3732
+ return scope ?? (owner === void 0 ? "session" : "none");
3733
+ }
3734
+ //#endregion
3735
+ //#region src/list-kit/pieces/bulk-reach.tsx
3736
+ function useBulkFlowReachable(resource) {
3737
+ const app = (0, _burojs_react.useApp)();
3738
+ const control = (0, _burojs_react.useFlowRowResolver)(resource);
3739
+ return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => (0, _burojs_core_internal.isFlowAction)(action) && control(action.name, void 0).state !== "absent");
3740
+ }
3741
+ function ExportReach(props) {
3742
+ const { available } = (0, _burojs_react.useResourceExport)(props.resource);
3743
+ const capability = (0, _burojs_react.useCapability)("run", {
3744
+ resource: props.resource,
3745
+ target: {
3746
+ kind: "action",
3747
+ name: "export"
3748
+ },
3749
+ mode: "list"
3750
+ });
3751
+ return props.children(available && capability.allowed);
3752
+ }
3753
+ function BulkExportReach(props) {
3754
+ return (0, _burojs_react.useOptionalDataRuntime)() !== null ? (0, react_jsx_runtime.jsx)(ExportReach, { ...props }) : props.children(false);
3760
3755
  }
3761
3756
  //#endregion
3762
3757
  //#region src/list-kit/pieces/piece.tsx
@@ -3824,15 +3819,47 @@ function ForceTableView(props) {
3824
3819
  }
3825
3820
  //#endregion
3826
3821
  //#region src/list-kit/pieces/list-body.tsx
3822
+ function LinkDetachRowButton(props) {
3823
+ const { resource, linkEditing } = useListSurface();
3824
+ const t = (0, _burojs_react.useOptionalT)();
3825
+ const [busy, setBusy] = (0, react.useState)(false);
3826
+ const detach = linkEditing?.detach;
3827
+ if (detach === void 0) return null;
3828
+ const label = t("field.link.detach", { defaultValue: "Detach" });
3829
+ return (0, react_jsx_runtime.jsx)(require_button.Button, {
3830
+ type: "button",
3831
+ size: "icon",
3832
+ variant: "ghost",
3833
+ className: "size-7",
3834
+ "aria-label": label,
3835
+ title: label,
3836
+ disabled: busy,
3837
+ "data-testid": `list-link-detach:${resource}:${props.row.id}`,
3838
+ onClick: (event) => {
3839
+ event.stopPropagation();
3840
+ setBusy(true);
3841
+ detach.onDetach([props.row]).finally(() => setBusy(false));
3842
+ },
3843
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconUnlink, {
3844
+ className: "size-4",
3845
+ "aria-hidden": "true"
3846
+ })
3847
+ });
3848
+ }
3849
+ const linkDetachRow = (row) => (0, react_jsx_runtime.jsx)(LinkDetachRowButton, { row });
3827
3850
  function ListBodyView() {
3828
3851
  usePieceMount("body");
3829
3852
  const list = (0, _burojs_react.useResourceListContext)();
3830
- const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly } = useListSurface();
3853
+ const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
3854
+ const detachable = readOnly && linkEditing?.detach !== void 0;
3855
+ const flowReachable = useBulkFlowReachable(resource);
3856
+ const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
3831
3857
  const item = (0, react.useContext)(ListItemContext);
3832
3858
  const rowsView = (0, react.useMemo)(() => rowsViewFor?.(item), [rowsViewFor, item]);
3833
3859
  const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
3834
3860
  const rows = (0, _burojs_react.policyRowsOf)(list, list.rows);
3835
- return (0, react_jsx_runtime.jsx)(ResourceList, {
3861
+ if (query.unbound) return (0, react_jsx_runtime.jsx)(UnboundRelatedList, { resource });
3862
+ const view = (exportReachable) => (0, react_jsx_runtime.jsx)(ResourceList, {
3836
3863
  resource,
3837
3864
  data: rows,
3838
3865
  total: pagination.total,
@@ -3849,29 +3876,50 @@ function ListBodyView() {
3849
3876
  onRequestCount: count.available ? count.request : void 0,
3850
3877
  isCountingRows: count.isLoading,
3851
3878
  columns: columns.visible,
3879
+ columnUi: columns.ui,
3852
3880
  isLoading: query.isLoading,
3853
3881
  error: query.error,
3854
3882
  onRetry: query.refetch,
3855
3883
  createHref: route && createAvailable && createPage ? routing.createHref : void 0,
3856
3884
  ...rowProps,
3857
3885
  ...rowsView === void 0 ? {} : { rowsView },
3858
- selectable: tableView && !readOnly,
3859
- rowActions: !readOnly,
3886
+ selectable: selectable(exportReachable),
3887
+ rowActions: detachable ? linkDetachRow : !readOnly,
3860
3888
  selectedIds: selection.ids,
3861
3889
  onSelectionChange: selection.set,
3862
3890
  isFiltered: filters.isFiltered,
3863
- onClearFilter: filters.clearAll,
3891
+ onClearFilter: filters.canReset ? filters.reset : void 0,
3864
3892
  emptyState,
3865
3893
  sort: sorting.value ?? [],
3866
3894
  requiredColumns: columns.required,
3867
3895
  resetLayout: columns.resetLayout,
3896
+ layoutId,
3868
3897
  onHideColumn: columns.hide,
3869
3898
  onSort: sorting.toggle,
3899
+ sortable: sorting.sortable,
3870
3900
  renderCell
3871
3901
  });
3902
+ return (0, react_jsx_runtime.jsx)(BulkExportReach, {
3903
+ resource,
3904
+ children: view
3905
+ });
3872
3906
  }
3873
3907
  const ListBody = listPiece("ListBody", ListBodyView);
3874
3908
  //#endregion
3909
+ //#region src/resource/form-context.tsx
3910
+ const FormControlContext = (0, react.createContext)(null);
3911
+ const FormControlProvider = FormControlContext.Provider;
3912
+ function useFormControlOf(controller) {
3913
+ const latest = (0, react.useRef)(controller);
3914
+ latest.current = controller;
3915
+ return (0, react.useMemo)(() => ({ setValues: (patch) => {
3916
+ for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
3917
+ } }), []);
3918
+ }
3919
+ function useFormControl() {
3920
+ return (0, react.useContext)(FormControlContext);
3921
+ }
3922
+ //#endregion
3875
3923
  //#region src/record/route-screen-context.ts
3876
3924
  const RouteScreenClaimContext = (0, react.createContext)(false);
3877
3925
  const RouteScreenContext = (0, react.createContext)(false);
@@ -3995,7 +4043,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
3995
4043
  region,
3996
4044
  form,
3997
4045
  labelledBy: title === void 0 ? void 0 : titleId,
3998
- children: renderChildren(node.children)
4046
+ children: renderChildren(node.children, node)
3999
4047
  }) : null,
4000
4048
  ...title === void 0 ? {} : { title },
4001
4049
  ...description === void 0 ? {} : { description },
@@ -4030,10 +4078,12 @@ function TabsHost({ node, renderChildren }) {
4030
4078
  const state = (0, _burojs_react.useScreenState)();
4031
4079
  const text = useNodeText();
4032
4080
  const panels = node.children.filter((child) => child.state !== "absent");
4033
- const open = panels.find((child) => child.open === true) ?? panels[0];
4081
+ const declared = compiled.nodes[node.index].options.default;
4082
+ const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
4083
+ const open = panels.find((child) => child.open === true) ?? effectiveDefault;
4034
4084
  const urlSelect = useTabsUrlSelect();
4035
4085
  const select = (panel, reveal) => {
4036
- if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === panels[0], reveal);
4086
+ if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
4037
4087
  else state?.openPanel(node.id, panelValue(panel));
4038
4088
  };
4039
4089
  const onSelect = (key) => {
@@ -4045,7 +4095,7 @@ function TabsHost({ node, renderChildren }) {
4045
4095
  key: panelValue(panel),
4046
4096
  title: titleOf(panel),
4047
4097
  errorCount: panel.key === void 0 ? 0 : controller.errorsIn(panel.key),
4048
- panel: panels.length === 1 || panel.open === true ? (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: renderChildren(panel.children) }) : null
4098
+ panel: panels.length === 1 || panel.open === true ? (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: renderChildren(panel.children, panel) }) : null
4049
4099
  }));
4050
4100
  const { view: View, id } = useNodeView(node, "tabs");
4051
4101
  const t = (0, _burojs_react.useFrameT)(id);
@@ -4156,7 +4206,7 @@ function WizardHost({ node, renderChildren }) {
4156
4206
  region: "step-panel",
4157
4207
  form,
4158
4208
  labelledBy: titleId,
4159
- children: renderChildren(panel.children)
4209
+ children: renderChildren(panel.children, panel)
4160
4210
  }),
4161
4211
  ...description === void 0 ? {} : { description },
4162
4212
  titleId,
@@ -4202,18 +4252,23 @@ function WizardHost({ node, renderChildren }) {
4202
4252
  //#region src/screen/nodes.tsx
4203
4253
  function FailedNode({ node }) {
4204
4254
  const { controller } = useScreenTree();
4205
- const t = (0, _burojs_react.useOptionalT)();
4206
- return (0, react_jsx_runtime.jsxs)("div", {
4207
- ...nodeAttributes(node),
4208
- role: "alert",
4209
- className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
4210
- children: [(0, react_jsx_runtime.jsx)("span", { children: t("screen.failed", { defaultValue: "This section could not be loaded." }) }), (0, react_jsx_runtime.jsx)(require_button.Button, {
4211
- type: "button",
4212
- variant: "outline",
4213
- size: "sm",
4214
- onClick: () => controller.retry(node.failedOn),
4215
- children: t("actions.retry", { defaultValue: "Retry" })
4216
- })]
4255
+ let error;
4256
+ for (const name of node.failedOn) {
4257
+ const source = controller.sources[name];
4258
+ if (source?.status === "failed") {
4259
+ error = source.error;
4260
+ break;
4261
+ }
4262
+ }
4263
+ return (0, react_jsx_runtime.jsx)(require_relation_create.LoadError, {
4264
+ surface: "tab",
4265
+ error,
4266
+ retry: () => controller.retry(node.failedOn),
4267
+ fallback: {
4268
+ key: "screen.failed",
4269
+ default: "This section could not be loaded."
4270
+ },
4271
+ attributes: nodeAttributes(node)
4217
4272
  });
4218
4273
  }
4219
4274
  function failedOnlyOnMeta(node) {
@@ -4226,8 +4281,17 @@ function drawn(node, hiddenFields) {
4226
4281
  if (node.state === "absent") return false;
4227
4282
  return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
4228
4283
  }
4229
- function SectionChildren({ nodes }) {
4230
- const { hiddenFields } = useScreenTree();
4284
+ function SectionChildren({ nodes, owner }) {
4285
+ const { compiled, hiddenFields } = useScreenTree();
4286
+ const text = useNodeText();
4287
+ if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
4288
+ const empty = text(compiled.nodes[owner.index]?.options.empty);
4289
+ return empty === void 0 || empty === "" ? null : (0, react_jsx_runtime.jsx)("p", {
4290
+ "data-buro-region": "empty",
4291
+ className: "text-sm text-muted-foreground",
4292
+ children: empty
4293
+ });
4294
+ }
4231
4295
  const out = [];
4232
4296
  let run = [];
4233
4297
  const flush = () => {
@@ -4249,8 +4313,11 @@ function SectionChildren({ nodes }) {
4249
4313
  flush();
4250
4314
  return out;
4251
4315
  }
4252
- function renderSection(nodes) {
4253
- return (0, react_jsx_runtime.jsx)(SectionChildren, { nodes });
4316
+ function renderSection(nodes, owner) {
4317
+ return (0, react_jsx_runtime.jsx)(SectionChildren, {
4318
+ nodes,
4319
+ owner
4320
+ });
4254
4321
  }
4255
4322
  function Layout({ node }) {
4256
4323
  return (0, react_jsx_runtime.jsx)("div", {
@@ -4372,17 +4439,14 @@ function pageHost(page) {
4372
4439
  }
4373
4440
  //#endregion
4374
4441
  //#region src/forms/declarative-form.tsx
4375
- function translateOrFallback(t, key, fallback) {
4376
- const translated = t(key);
4377
- return translated === key ? fallback : translated;
4378
- }
4379
- function issueText(issue, t, i18nPrefix) {
4442
+ function issueText(issue, caps, i18nPrefix) {
4380
4443
  return (0, _burojs_react.resolveValidationMessage)({
4381
4444
  code: issue.code ?? "",
4382
4445
  message: issue.message,
4383
- ...issue.params === void 0 ? {} : { params: issue.params }
4446
+ ...issue.check === void 0 ? {} : { check: issue.check }
4384
4447
  }, {
4385
- t,
4448
+ has: caps.has,
4449
+ t: caps.t,
4386
4450
  ...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
4387
4451
  });
4388
4452
  }
@@ -4449,7 +4513,9 @@ function DeclarativeFormBody(props) {
4449
4513
  ...props.surface === void 0 ? {} : { surface: props.surface },
4450
4514
  ...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
4451
4515
  });
4452
- const t = (0, _burojs_react.useOptionalT)();
4516
+ const formControl = useFormControlOf(form);
4517
+ const chromeT = (0, _burojs_react.useChromeT)();
4518
+ const caps = (0, _burojs_react.useI18nCaps)();
4453
4519
  const disabled = props.disabled === true;
4454
4520
  const uid = (0, react.useId)();
4455
4521
  const leafIds = (0, react.useMemo)(() => ({
@@ -4459,8 +4525,8 @@ function DeclarativeFormBody(props) {
4459
4525
  const formErrors = form.errors.filter((issue) => issue.field === "_form");
4460
4526
  const retryableChecks = [];
4461
4527
  for (const issue of form.checkOutcome?.blockers ?? []) {
4462
- const checkId = issue.params?.checkId;
4463
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
4528
+ const checkId = issue.check?.id;
4529
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
4464
4530
  if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
4465
4531
  }
4466
4532
  const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
@@ -4473,7 +4539,7 @@ function DeclarativeFormBody(props) {
4473
4539
  formErrors.length > 0 && (0, react_jsx_runtime.jsx)("div", {
4474
4540
  role: "alert",
4475
4541
  className: "mb-4 flex flex-col gap-1 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
4476
- children: formErrors.map((issue, index) => (0, react_jsx_runtime.jsx)("p", { children: issueText(issue, t, prefix) }, index))
4542
+ children: formErrors.map((issue, index) => (0, react_jsx_runtime.jsx)("p", { children: issueText(issue, caps, prefix) }, index))
4477
4543
  }),
4478
4544
  retryableChecks.length > 0 && (0, react_jsx_runtime.jsx)("div", {
4479
4545
  className: "mb-4 flex flex-wrap gap-2",
@@ -4483,25 +4549,28 @@ function DeclarativeFormBody(props) {
4483
4549
  size: "sm",
4484
4550
  "data-testid": `form-check-retry:${checkId}`,
4485
4551
  onClick: () => void form.revalidate(),
4486
- children: translateOrFallback(t, "actions.retry", "Retry")
4552
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4487
4553
  }, checkId))
4488
4554
  }),
4489
4555
  formWarnings.length > 0 && (0, react_jsx_runtime.jsx)("div", {
4490
4556
  role: "status",
4491
4557
  "aria-live": "polite",
4492
4558
  className: "mb-4 flex flex-col gap-1 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning-emphasis",
4493
- children: formWarnings.map((issue, index) => (0, react_jsx_runtime.jsx)("p", { children: issueText(issue, t, prefix) }, index))
4559
+ children: formWarnings.map((issue, index) => (0, react_jsx_runtime.jsx)("p", { children: issueText(issue, caps, prefix) }, index))
4494
4560
  }),
4495
- (0, react_jsx_runtime.jsx)(ScreenTree, {
4496
- region: "form-body",
4497
- resource: prefix,
4498
- mode: "create",
4499
- compiled: form.screen,
4500
- controller: form,
4501
- leafIds,
4502
- ...props.renderField === void 0 ? {} : { renderField: props.renderField },
4503
- disabled,
4504
- ...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
4561
+ (0, react_jsx_runtime.jsx)(FormControlProvider, {
4562
+ value: formControl,
4563
+ children: (0, react_jsx_runtime.jsx)(ScreenTree, {
4564
+ region: "form-body",
4565
+ resource: prefix,
4566
+ mode: "create",
4567
+ compiled: form.screen,
4568
+ controller: form,
4569
+ leafIds,
4570
+ ...props.renderField === void 0 ? {} : { renderField: props.renderField },
4571
+ disabled,
4572
+ ...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
4573
+ })
4505
4574
  }),
4506
4575
  props.slots?.footer === void 0 ? (0, react_jsx_runtime.jsx)("div", {
4507
4576
  className: "mt-4 flex justify-end",
@@ -4509,7 +4578,7 @@ function DeclarativeFormBody(props) {
4509
4578
  type: "submit",
4510
4579
  disabled: !form.canSubmit || disabled,
4511
4580
  onMouseDown: (event) => event.preventDefault(),
4512
- children: props.submitLabel ?? translateOrFallback(t, "actions.save", "Save")
4581
+ children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
4513
4582
  })
4514
4583
  }) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
4515
4584
  ]
@@ -4594,7 +4663,8 @@ function ExportOptionsDirty({ dirty }) {
4594
4663
  return null;
4595
4664
  }
4596
4665
  function getNoRunState() {}
4597
- function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount, scope }) {
4666
+ function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
4667
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
4598
4668
  const t = (0, _burojs_react.useOptionalT)();
4599
4669
  const dialogTestId = (0, _burojs_react.useTestIds)().exportDialog(resource);
4600
4670
  const { formats, start, exporter, available, error, loading } = (0, _burojs_react.useResourceExport)(resource, {
@@ -4716,14 +4786,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4716
4786
  })] }), (0, react_jsx_runtime.jsx)(ExportProgressView, {
4717
4787
  state: runState,
4718
4788
  dialogTestId,
4719
- t
4789
+ t,
4790
+ caps
4720
4791
  })] }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)(require_dialog.DialogHeader, { children: [(0, react_jsx_runtime.jsx)(require_dialog.DialogTitle, { children: titleText }), (0, react_jsx_runtime.jsx)(require_dialog.DialogDescription, {
4721
4792
  className: "sr-only",
4722
4793
  children: titleText
4723
4794
  })] }), !available ? (0, react_jsx_runtime.jsx)("p", {
4724
4795
  role: "alert",
4725
4796
  "data-testid": `${dialogTestId}:unavailable`,
4726
- children: error?.message ?? t("export.dialog.unavailable")
4797
+ children: error === void 0 || error === null ? t("export.dialog.unavailable") : (0, _burojs_react.resolveErrorText)(error, caps)
4727
4798
  }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4728
4799
  loading ? (0, react_jsx_runtime.jsx)("p", {
4729
4800
  role: "status",
@@ -4802,7 +4873,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
4802
4873
  })
4803
4874
  });
4804
4875
  }
4805
- function ExportProgressView({ state, dialogTestId, t }) {
4876
+ function ExportProgressView({ state, dialogTestId, t, caps }) {
4806
4877
  if (state === void 0) return null;
4807
4878
  if (state.status === "running") {
4808
4879
  const total = state.progress?.total;
@@ -4823,7 +4894,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4823
4894
  if (state.status === "failed") return (0, react_jsx_runtime.jsx)("p", {
4824
4895
  role: "alert",
4825
4896
  "data-testid": `${dialogTestId}:error`,
4826
- children: `${t("export.dialog.failed")}: ${state.error.message}`
4897
+ children: `${t("export.dialog.failed")}: ${(0, _burojs_react.resolveErrorText)(state.error, caps)}`
4827
4898
  });
4828
4899
  if (state.status === "canceled") return (0, react_jsx_runtime.jsx)("p", {
4829
4900
  "data-testid": `${dialogTestId}:canceled`,
@@ -4836,6 +4907,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
4836
4907
  function BulkSummary(props) {
4837
4908
  const loaded = require_check_confirm_dialog.useLoaded(require_check_confirm_dialog.loadBulkSummary);
4838
4909
  const t = (0, _burojs_react.useOptionalT)();
4910
+ const chromeT = (0, _burojs_react.useChromeT)();
4839
4911
  const failed = (retry) => (0, react_jsx_runtime.jsxs)("div", {
4840
4912
  role: "alert",
4841
4913
  className: "flex items-center gap-2 text-sm",
@@ -4844,7 +4916,7 @@ function BulkSummary(props) {
4844
4916
  size: "sm",
4845
4917
  variant: "outline",
4846
4918
  onClick: retry,
4847
- children: require_check_confirm_dialog.text(t, "actions.retry", "Retry")
4919
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
4848
4920
  })]
4849
4921
  });
4850
4922
  if (loaded.error !== void 0) return failed(loaded.retry);
@@ -4862,13 +4934,14 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4862
4934
  const control = (0, _burojs_react.useFlowRowResolver)(resource)(actionName, void 0);
4863
4935
  const chrome = require_check_confirm_dialog.useFlowChrome(resource, actionName);
4864
4936
  const def = app.registry.get(resource);
4865
- const each = (0, react.useMemo)(() => {
4937
+ const compiled = (0, react.useMemo)(() => {
4866
4938
  try {
4867
- return def === void 0 ? false : (0, _burojs_core.compileFlow)(def, actionName).each;
4939
+ return def === void 0 ? void 0 : (0, _burojs_core.compileFlow)(def, actionName);
4868
4940
  } catch {
4869
- return false;
4941
+ return;
4870
4942
  }
4871
4943
  }, [def, actionName]);
4944
+ const each = compiled?.each ?? false;
4872
4945
  const [running, setRunning] = (0, react.useState)(false);
4873
4946
  const [summary, setSummary] = (0, react.useState)(null);
4874
4947
  const testid = tid.bulkAction(resource, actionName);
@@ -4876,7 +4949,10 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4876
4949
  const variant = chrome.destructive ? "destructive" : "outline";
4877
4950
  const overLimit = each && ids.length > _burojs_core.BULK_EACH_LIMIT;
4878
4951
  if (overLimit || control.state === "disabled") return (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DisabledAction, {
4879
- reason: overLimit ? require_check_confirm_dialog.text(t, "flow.bulkLimit", `Select at most ${_burojs_core.BULK_EACH_LIMIT}.`, { count: _burojs_core.BULK_EACH_LIMIT }) : control.reason,
4952
+ reason: overLimit ? t("flow.bulkLimit", {
4953
+ defaultValue: `Select at most ${_burojs_core.BULK_EACH_LIMIT}.`,
4954
+ count: _burojs_core.BULK_EACH_LIMIT
4955
+ }) : control.reason,
4880
4956
  label: chrome.label,
4881
4957
  icon: chrome.icon,
4882
4958
  testid,
@@ -4938,14 +5014,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
4938
5014
  actionName,
4939
5015
  label: chrome.labelText,
4940
5016
  total: summary.total,
4941
- progress: summary.progress
5017
+ progress: summary.progress,
5018
+ texts: compiled?.flow.texts
4942
5019
  }),
4943
5020
  (0, react_jsx_runtime.jsx)(require_dialog.DialogFooter, { children: (0, react_jsx_runtime.jsx)(require_button.Button, {
4944
5021
  type: "button",
4945
5022
  variant: "outline",
4946
5023
  "data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
4947
5024
  onClick: () => setSummary(null),
4948
- children: require_check_confirm_dialog.text(t, "flow.close", "Close")
5025
+ children: t("flow.close", { defaultValue: "Close" })
4949
5026
  }) })
4950
5027
  ]
4951
5028
  })
@@ -4961,10 +5038,12 @@ function orderBulkActions(actions) {
4961
5038
  seq: index
4962
5039
  })).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
4963
5040
  }
4964
- function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope }) {
5041
+ function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
5042
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
4965
5043
  const app = (0, _burojs_react.useApp)();
4966
5044
  const tid = (0, _burojs_react.useTestIds)();
4967
5045
  const t = (0, _burojs_react.useOptionalT)();
5046
+ const chromeT = (0, _burojs_react.useChromeT)();
4968
5047
  const def = app.registry.get(resource);
4969
5048
  const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
4970
5049
  const ids = selectedIds ?? records.map((record) => record.id);
@@ -4997,7 +5076,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
4997
5076
  className: "mx-0.5 h-6 w-px bg-border",
4998
5077
  "aria-hidden": "true"
4999
5078
  }),
5000
- bulkActions.map((action) => (0, react_jsx_runtime.jsx)(BulkFlowButton, {
5079
+ actions !== void 0 ? actions : null,
5080
+ actions === void 0 && bulkActions.map((action) => (0, react_jsx_runtime.jsx)(BulkFlowButton, {
5001
5081
  resource,
5002
5082
  actionName: action.name,
5003
5083
  ids,
@@ -5006,7 +5086,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5006
5086
  else if (next.length === 0) handleClear();
5007
5087
  }
5008
5088
  }, action.name)),
5009
- (0, react_jsx_runtime.jsx)(BulkExportButton, {
5089
+ actions === void 0 && (0, react_jsx_runtime.jsx)(BulkExportButton, {
5010
5090
  resource,
5011
5091
  ids,
5012
5092
  visibleColumns: visibleColumns ?? [],
@@ -5023,7 +5103,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
5023
5103
  "aria-hidden": "true"
5024
5104
  }), (0, react_jsx_runtime.jsx)("span", {
5025
5105
  className: "hidden sm:inline",
5026
- children: t("actions.clearSelection", { defaultValue: "Clear" })
5106
+ children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
5027
5107
  })]
5028
5108
  })
5029
5109
  ]
@@ -5083,12 +5163,36 @@ function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
5083
5163
  }
5084
5164
  //#endregion
5085
5165
  //#region src/list-kit/pieces/list-bulk-bar.tsx
5166
+ function LinkDetachBulkButton(props) {
5167
+ const { selection } = (0, _burojs_react.useResourceListContext)();
5168
+ const { resource } = useListSurface();
5169
+ const t = (0, _burojs_react.useOptionalT)();
5170
+ const [busy, setBusy] = (0, react.useState)(false);
5171
+ return (0, react_jsx_runtime.jsxs)(require_button.Button, {
5172
+ type: "button",
5173
+ size: "sm",
5174
+ variant: "ghost",
5175
+ className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
5176
+ disabled: busy,
5177
+ "data-testid": `list-link-detach-bulk:${resource}`,
5178
+ onClick: () => {
5179
+ setBusy(true);
5180
+ props.onDetach(selection.records).finally(() => setBusy(false));
5181
+ },
5182
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconUnlink, {
5183
+ className: "size-4",
5184
+ "aria-hidden": "true"
5185
+ }), t("field.link.detach", { defaultValue: "Detach" })]
5186
+ });
5187
+ }
5086
5188
  function ListBulkBarView() {
5087
5189
  usePieceMount("bulk-bar");
5088
5190
  const { selection, columns, scope } = (0, _burojs_react.useResourceListContext)();
5089
- const { resource, tableView, readOnly } = useListSurface();
5090
- if (!tableView || readOnly || selection.ids.length === 0) return null;
5191
+ const { resource, tableView, readOnly, linkEditing } = useListSurface();
5192
+ const detach = readOnly ? linkEditing?.detach : void 0;
5193
+ if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
5091
5194
  return (0, react_jsx_runtime.jsx)(BulkActionToolbar, {
5195
+ ...detach === void 0 ? {} : { actions: (0, react_jsx_runtime.jsx)(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
5092
5196
  resource,
5093
5197
  selectedRecords: selection.records,
5094
5198
  selectedIds: selection.ids,
@@ -5177,7 +5281,8 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
5177
5281
  }
5178
5282
  //#endregion
5179
5283
  //#region src/list-kit/columns-toggle.tsx
5180
- function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5284
+ function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
5285
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
5181
5286
  const t = (0, _burojs_react.useT)();
5182
5287
  const columnsCaps = (0, _burojs_react.useI18nCaps)(resource);
5183
5288
  const app = (0, _burojs_react.useApp)();
@@ -5284,12 +5389,16 @@ function ListColumnsView() {
5284
5389
  const ListColumns = listPiece("ListColumns", ListColumnsView);
5285
5390
  //#endregion
5286
5391
  //#region src/resource/create-button.tsx
5287
- function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
5392
+ function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
5393
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
5288
5394
  const t = (0, _burojs_react.useOptionalT)();
5289
5395
  const Link = require_toaster.useLinkComponent();
5290
5396
  const availability = (0, _burojs_react.useAvailability)(resource, "create");
5291
5397
  const gate = (0, _burojs_react.useEntryGate)();
5292
- const text = label ?? t("actions.create", { defaultValue: "Create" });
5398
+ const text = label ?? t("actions.create", {
5399
+ resource,
5400
+ defaultValue: "Create"
5401
+ });
5293
5402
  if (onOpen !== void 0) {
5294
5403
  const answer = gate(resource, "create");
5295
5404
  if (answer.state === "disabled") return (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.DisabledAction, {
@@ -5360,7 +5469,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
5360
5469
  //#endregion
5361
5470
  //#region src/export/export-button.tsx
5362
5471
  const warnedProviderQueryResources = new Set();
5363
- function ExportButton({ resource, className }) {
5472
+ function ExportButton({ resource: resourceInput, className }) {
5473
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
5364
5474
  if (!((0, _burojs_react.useOptionalDataRuntime)() !== null)) return null;
5365
5475
  return (0, react_jsx_runtime.jsx)(ExportButtonActive, {
5366
5476
  resource,
@@ -5381,36 +5491,19 @@ function ExportButtonActive({ resource, className }) {
5381
5491
  const tid = (0, _burojs_react.useTestIds)();
5382
5492
  const t = (0, _burojs_react.useOptionalT)();
5383
5493
  const [open, setOpen] = react.useState(false);
5384
- const searchContext = react.useMemo(() => ({
5385
- resource,
5386
- def: list.def
5387
- }), [resource, list.def]);
5494
+ const sent = list.query.sent;
5388
5495
  const target = react.useMemo(() => {
5389
- const filter = (0, _burojs_core.composeListFilter)({
5390
- fixed: list.filters.fixed,
5391
- pinned: list.filters.value,
5392
- group: list.filters.group,
5393
- search: list.filters.search,
5394
- searchDecl: list.filters.searchEnabled ? list.filters.searchDecl ?? list.def?.views?.list?.search : void 0,
5395
- searchContext
5396
- });
5397
- const sort = list.sorting.value;
5496
+ if (sent === null) return null;
5497
+ const { filter, sort } = sent;
5398
5498
  return {
5399
5499
  kind: "filter",
5400
5500
  ...filter === void 0 ? {} : { filter },
5401
5501
  ...sort.length > 0 ? { sort } : {}
5402
5502
  };
5403
- }, [
5404
- list.filters.fixed,
5405
- list.filters.value,
5406
- list.filters.group,
5407
- list.filters.search,
5408
- list.filters.searchDecl,
5409
- list.filters.searchEnabled,
5410
- list.def,
5411
- searchContext,
5412
- list.sorting.value
5413
- ]);
5503
+ }, [sent]);
5504
+ const lastTarget = react.useRef(target);
5505
+ if (target !== null) lastTarget.current = target;
5506
+ const dialogTarget = target ?? lastTarget.current;
5414
5507
  react.useEffect(() => {
5415
5508
  if (!available || !capability.allowed) return;
5416
5509
  if (list.providerQuery.value === void 0) return;
@@ -5424,7 +5517,9 @@ function ExportButtonActive({ resource, className }) {
5424
5517
  resource
5425
5518
  ]);
5426
5519
  if (!available || !capability.allowed || list.isForbidden) return null;
5427
- return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(require_button.Button, {
5520
+ if (target === null && !open) return null;
5521
+ if (dialogTarget === null) return null;
5522
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [target === null ? null : (0, react_jsx_runtime.jsx)(require_button.Button, {
5428
5523
  type: "button",
5429
5524
  variant: "outline",
5430
5525
  size: "icon",
@@ -5443,7 +5538,7 @@ function ExportButtonActive({ resource, className }) {
5443
5538
  open,
5444
5539
  onOpenChange: setOpen,
5445
5540
  resource,
5446
- target,
5541
+ target: dialogTarget,
5447
5542
  visibleColumns: list.columns.visible,
5448
5543
  estimatedCount: list.pagination.total,
5449
5544
  scope: list.scope
@@ -5461,8 +5556,9 @@ function ListExportView() {
5461
5556
  const ListExport = listPiece("ListExport", ListExportView);
5462
5557
  //#endregion
5463
5558
  //#region src/list-kit/list-search-input.tsx
5464
- function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5465
- const t = (0, _burojs_react.useT)();
5559
+ function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
5560
+ const resource = (0, _burojs_core.routeResourceKey)(resourceInput);
5561
+ const caps = (0, _burojs_react.useI18nCaps)(resource);
5466
5562
  const [text, setText] = (0, react.useState)(value);
5467
5563
  const lastPushedRef = (0, react.useRef)(value);
5468
5564
  const timerRef = (0, react.useRef)(null);
@@ -5477,7 +5573,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5477
5573
  }, []);
5478
5574
  return (0, react_jsx_runtime.jsx)(require_input.Input, {
5479
5575
  type: "search",
5480
- placeholder: placeholder ?? t("toolbar.searchBy", { params: { name: label } }),
5576
+ placeholder: placeholder ?? (0, _burojs_core.resolveContentSlot)(caps, ["list.search"]) ?? "Search",
5481
5577
  value: text,
5482
5578
  "data-testid": `list-search:${resource}`,
5483
5579
  className: "h-8 w-full min-[420px]:w-72",
@@ -5495,24 +5591,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
5495
5591
  //#endregion
5496
5592
  //#region src/list-kit/filter-builder/filter-region.tsx
5497
5593
  function FilterRegion({ search } = {}) {
5498
- const { resource, def, filters, pagination, access } = (0, _burojs_react.useResourceListContext)();
5499
- const t = (0, _burojs_react.useT)();
5500
- const listCaps = (0, _burojs_react.useI18nCaps)(resource);
5594
+ const { resource, def, filters, pagination } = (0, _burojs_react.useResourceListContext)();
5595
+ const chromeT = (0, _burojs_react.useChromeT)();
5501
5596
  const testIdBase = `list-filter:${resource}`;
5597
+ const caps = (0, _burojs_react.useI18nCaps)(resource);
5598
+ const placeholder = search === void 0 ? (0, _burojs_core.resolveContentSlot)(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
5502
5599
  const searchDecl = search === void 0 ? def?.views?.list?.search : search ?? void 0;
5503
- const searchField = searchDecl !== void 0 && typeof searchDecl !== "function" ? (() => {
5504
- return searchDecl.fields.map((entry) => typeof entry === "string" ? entry : entry.field).find((name) => access.fieldVisible(name));
5505
- })() : void 0;
5506
- const searchGenericLabel = (0, _burojs_core.resolveContentSlot)(listCaps, ["list.search"]) ?? "Search";
5507
- const searchLabel = searchField ? (0, _burojs_core.resolveFieldLabel)(listCaps, searchField, (0, _burojs_core.declaredText)("field", def?.fields[searchField], "label")) : searchGenericLabel;
5508
- const searchPlaceholder = searchField ? t("toolbar.searchBy", { params: { name: searchLabel } }) : searchGenericLabel;
5509
5600
  return (0, react_jsx_runtime.jsxs)("div", {
5510
5601
  className: "flex flex-wrap items-center gap-2",
5511
5602
  children: [
5512
5603
  searchDecl !== void 0 && filters.searchEnabled && (0, react_jsx_runtime.jsx)(ListSearchInput, {
5513
5604
  resource,
5514
- label: searchLabel,
5515
- placeholder: searchPlaceholder,
5605
+ ...placeholder === void 0 ? {} : { placeholder },
5516
5606
  value: filters.search,
5517
5607
  onCommit: (v) => {
5518
5608
  pagination.setPage(1);
@@ -5524,19 +5614,21 @@ function FilterRegion({ search } = {}) {
5524
5614
  setGroup: filters.setGroup,
5525
5615
  available: filters.available,
5526
5616
  fixed: filters.fixed,
5617
+ presets: filters.presets,
5618
+ setPreset: filters.setPreset,
5527
5619
  pinnedFields: filters.pinnedFields,
5528
5620
  pinnedValue: filters.value,
5529
5621
  setPinnedValue: filters.set,
5530
5622
  setPage: pagination.setPage,
5531
5623
  testIdBase
5532
5624
  }),
5533
- filters.isFiltered && (0, react_jsx_runtime.jsx)(require_button.Button, {
5625
+ filters.canReset && (0, react_jsx_runtime.jsx)(require_button.Button, {
5534
5626
  type: "button",
5535
5627
  variant: "ghost",
5536
5628
  size: "sm",
5537
5629
  "data-testid": `${testIdBase}:clear-all`,
5538
- onClick: filters.clearAll,
5539
- children: t("filters.clearAll")
5630
+ onClick: filters.reset,
5631
+ children: chromeT("filters.clearAll")
5540
5632
  })
5541
5633
  ]
5542
5634
  });
@@ -5666,6 +5758,7 @@ function ViewItem({ view, caps, resource, onClick, testid }) {
5666
5758
  });
5667
5759
  }
5668
5760
  function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
5761
+ const chromeT = (0, _burojs_react.useChromeT)();
5669
5762
  const canRename = entry.permissions.update && onRename !== void 0;
5670
5763
  const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
5671
5764
  const canDelete = entry.permissions.remove && onDelete !== void 0;
@@ -5711,8 +5804,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
5711
5804
  canDelete && (0, react_jsx_runtime.jsx)("button", {
5712
5805
  type: "button",
5713
5806
  onClick: onDelete,
5714
- "aria-label": tr("actions.delete", "Delete"),
5715
- title: tr("actions.delete", "Delete"),
5807
+ "aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
5808
+ title: chromeT("actions.delete", { defaultValue: "Delete" }),
5716
5809
  "data-testid": deleteTestId,
5717
5810
  className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
5718
5811
  children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconTrash, {
@@ -5752,7 +5845,8 @@ function groupByScope(entries) {
5752
5845
  }
5753
5846
  return order.map((id) => byId.get(id));
5754
5847
  }
5755
- function SavedViewsDropdown({ resource, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
5848
+ function SavedViewsDropdown({ resource: resourceInput, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
5849
+ const resource = resourceInput === void 0 ? void 0 : (0, _burojs_core.routeResourceKey)(resourceInput);
5756
5850
  const t = (0, _burojs_react.useOptionalT)();
5757
5851
  const caps = (0, _burojs_react.useI18nCaps)(resource);
5758
5852
  const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
@@ -5966,9 +6060,10 @@ function useOptionalFrameChrome() {
5966
6060
  //#endregion
5967
6061
  //#region src/record/frame-pieces.tsx
5968
6062
  function useFrameId() {
5969
- const { recordId, record } = (0, _burojs_react.useRecordFrame)();
6063
+ const { resource, recordId, record } = (0, _burojs_react.useRecordFrame)();
6064
+ const def = (0, _burojs_react.useApp)().registry.get(resource);
5970
6065
  if (recordId !== void 0) return recordId;
5971
- const id = record?.id;
6066
+ const id = (0, _burojs_react.recordKeyOf)(def, record);
5972
6067
  return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
5973
6068
  }
5974
6069
  function RecordTitleView() {
@@ -5979,7 +6074,7 @@ function RecordTitleView() {
5979
6074
  const t = (0, _burojs_react.useOptionalT)();
5980
6075
  const title = (0, _burojs_react.useRecordTitle)(resource, mode === "create" ? void 0 : id, record);
5981
6076
  if (useFrameChrome().hostTitle === true) return null;
5982
- const text = mode === "create" ? t("pageHeader.createTitle", { params: { label: (0, _burojs_core.resolveResourceLabel)(caps, resource, "single", app.registry.get(resource)) } }) : title;
6077
+ const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: (0, _burojs_core.resolveResourceLabel)(caps, resource, "single", app.registry.get(resource)) }) : title;
5983
6078
  if (text === "") return null;
5984
6079
  return (0, react_jsx_runtime.jsx)("h2", {
5985
6080
  className: "min-w-0 break-words text-xl font-semibold tracking-tight",
@@ -6003,6 +6098,7 @@ function RecordActionsView() {
6003
6098
  resource,
6004
6099
  record: row,
6005
6100
  surface: mode,
6101
+ sources: controller.sources,
6006
6102
  ...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
6007
6103
  }), mode === "show" ? extraActions : null]
6008
6104
  });
@@ -6032,8 +6128,15 @@ function FormActionsView() {
6032
6128
  const app = (0, _burojs_react.useApp)();
6033
6129
  const navigate = (0, _burojs_react.useRouteNavigate)();
6034
6130
  const t = (0, _burojs_react.useOptionalT)();
6131
+ const chromeT = (0, _burojs_react.useChromeT)();
6132
+ const caps = (0, _burojs_react.useI18nCaps)();
6133
+ const labelCaps = (0, _burojs_react.useI18nCaps)(resource);
6035
6134
  const tid = (0, _burojs_react.useTestIds)();
6036
6135
  const id = useFrameId();
6136
+ const failedIssues = chrome.serverIssues;
6137
+ const livePlan = controller.plan;
6138
+ const alertIssues = (0, react.useMemo)(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : (0, _burojs_core.routeServerIssues)({ issues: failedIssues }, (segment) => segment, (field) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active")).form, [failedIssues, livePlan]);
6139
+ const fieldDefs = app.registry.get(resource)?.fields;
6037
6140
  if (mode === "show") return null;
6038
6141
  const busy = controller.isSubmitting;
6039
6142
  const cancel = () => {
@@ -6057,21 +6160,43 @@ function FormActionsView() {
6057
6160
  const text = (issue) => (0, _burojs_react.resolveValidationMessage)({
6058
6161
  code: issue.code ?? "",
6059
6162
  message: issue.message,
6060
- ...issue.params === void 0 ? {} : { params: issue.params }
6061
- }, { t });
6163
+ ...issue.check === void 0 ? {} : { check: issue.check }
6164
+ }, {
6165
+ has: caps.has,
6166
+ t: caps.t
6167
+ });
6062
6168
  const retryable = [];
6063
6169
  for (const issue of controller.checkOutcome?.blockers ?? []) {
6064
- const checkId = issue.params?.checkId;
6065
- if (issue.params?.cause === void 0 || typeof checkId !== "string") continue;
6170
+ const checkId = issue.check?.id;
6171
+ if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
6066
6172
  if (!retryable.includes(checkId)) retryable.push(checkId);
6067
6173
  }
6068
- const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t);
6174
+ const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
6175
+ const serverIssues = bySeverity(alertIssues).map((issue) => {
6176
+ const head = issue.path[0];
6177
+ const name = typeof head === "string" ? head : void 0;
6178
+ const declared = name === void 0 ? void 0 : fieldDefs?.[name];
6179
+ const message = (0, _burojs_react.resolveValidationMessage)({
6180
+ code: issue.code,
6181
+ message: issue.message,
6182
+ ...issue.check === void 0 ? {} : { check: issue.check }
6183
+ }, {
6184
+ has: caps.has,
6185
+ t: caps.t,
6186
+ ...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
6187
+ });
6188
+ return {
6189
+ label: declared === void 0 || name === void 0 ? void 0 : (0, _burojs_core.resolveFieldLabel)(labelCaps, name, (0, _burojs_core.declaredText)("field", declared, "label")),
6190
+ message,
6191
+ severity: issue.severity
6192
+ };
6193
+ });
6069
6194
  const steps = controller.steps;
6070
6195
  const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
6071
6196
  return (0, react_jsx_runtime.jsxs)("div", {
6072
6197
  className: "flex flex-col gap-3",
6073
6198
  children: [
6074
- failure === void 0 && formErrors.length === 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
6199
+ failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
6075
6200
  role: "alert",
6076
6201
  className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
6077
6202
  children: [
@@ -6081,10 +6206,22 @@ function FormActionsView() {
6081
6206
  }),
6082
6207
  (0, react_jsx_runtime.jsxs)("div", {
6083
6208
  className: "leading-snug",
6084
- children: [failure === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
6085
- className: "font-medium",
6086
- children: failure
6087
- }), formErrors.map((issue) => (0, react_jsx_runtime.jsx)("p", { children: text(issue) }, issueKey(issue)))]
6209
+ children: [
6210
+ failure === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
6211
+ className: "font-medium",
6212
+ children: failure
6213
+ }),
6214
+ serverIssues.map((issue, index) => (0, react_jsx_runtime.jsxs)("p", {
6215
+ "data-testid": `form-server-issue:${resource}:${mode}:${index}`,
6216
+ "data-buro-severity": issue.severity,
6217
+ className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
6218
+ children: [issue.label === void 0 ? null : (0, react_jsx_runtime.jsxs)("span", {
6219
+ className: "font-medium",
6220
+ children: [issue.label, ": "]
6221
+ }), issue.message]
6222
+ }, index)),
6223
+ formErrors.map((issue) => (0, react_jsx_runtime.jsx)("p", { children: text(issue) }, issueKey(issue)))
6224
+ ]
6088
6225
  }),
6089
6226
  failure !== void 0 && steps !== null ? (0, react_jsx_runtime.jsx)(require_button.Button, {
6090
6227
  type: "button",
@@ -6093,7 +6230,7 @@ function FormActionsView() {
6093
6230
  className: "ml-auto",
6094
6231
  onMouseDown: (event) => event.preventDefault(),
6095
6232
  onClick: () => void chrome.submit(),
6096
- children: t("actions.retry", { defaultValue: "Retry" })
6233
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6097
6234
  }) : null
6098
6235
  ]
6099
6236
  }),
@@ -6105,7 +6242,7 @@ function FormActionsView() {
6105
6242
  size: "sm",
6106
6243
  "data-testid": `form-check-retry:${checkId}`,
6107
6244
  onClick: () => void controller.revalidate(),
6108
- children: t("actions.retry", { defaultValue: "Retry" })
6245
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
6109
6246
  }, checkId))
6110
6247
  }),
6111
6248
  formWarnings.length === 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
@@ -6131,7 +6268,7 @@ function FormActionsView() {
6131
6268
  children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
6132
6269
  className: "h-3.5 w-3.5 animate-spin",
6133
6270
  "aria-hidden": "true"
6134
- }), (0, react_jsx_runtime.jsx)("span", { children: t("actions.validating", { defaultValue: "Validating…" }) })]
6271
+ }), (0, react_jsx_runtime.jsx)("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
6135
6272
  }) : null,
6136
6273
  (0, react_jsx_runtime.jsx)(require_button.Button, {
6137
6274
  type: "button",
@@ -6139,7 +6276,7 @@ function FormActionsView() {
6139
6276
  onClick: cancel,
6140
6277
  onMouseDown: (event) => event.preventDefault(),
6141
6278
  "data-testid": tid.formCancel(resource, mode),
6142
- children: t("actions.cancel", { defaultValue: "Cancel" })
6279
+ children: chromeT("actions.cancel", { defaultValue: "Cancel" })
6143
6280
  }),
6144
6281
  midSteps ? null : (0, react_jsx_runtime.jsxs)(require_button.Button, {
6145
6282
  type: "submit",
@@ -6156,37 +6293,71 @@ function FormActionsView() {
6156
6293
  children: [busy ? (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
6157
6294
  className: "mr-2 h-4 w-4 animate-spin",
6158
6295
  "aria-hidden": "true"
6159
- }) : null, busy ? mode === "edit" ? t("actions.saving", { defaultValue: "Saving…" }) : t("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? t("actions.save", { defaultValue: "Save" }) : t("actions.create", { defaultValue: "Create" }))]
6296
+ }) : null, busy ? mode === "edit" ? chromeT("actions.saving", { defaultValue: "Saving…" }) : chromeT("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? chromeT("actions.save", { defaultValue: "Save" }) : chromeT("actions.create", { defaultValue: "Create" }))]
6160
6297
  })
6161
6298
  ]
6162
6299
  })
6163
6300
  ]
6164
6301
  });
6165
6302
  }
6303
+ const NO_ALERT_ISSUES = Object.freeze([]);
6304
+ const SEVERITY_RANK = {
6305
+ error: 0,
6306
+ warning: 1,
6307
+ info: 2
6308
+ };
6309
+ function bySeverity(issues) {
6310
+ return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
6311
+ }
6166
6312
  function firstPreferred(issues) {
6167
- const first = issues.find((issue) => issue.params?.checkId === void 0) ?? issues[0];
6313
+ const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
6168
6314
  return first === void 0 ? [] : [first];
6169
6315
  }
6170
6316
  function issueKey(issue) {
6171
6317
  return JSON.stringify([
6172
6318
  issue.code ?? "",
6173
6319
  issue.message.key,
6174
- issue.params ?? null
6320
+ issue.check?.id ?? null
6175
6321
  ]);
6176
6322
  }
6177
- function failureText(error, t) {
6178
- const friendly = require_toaster.toFriendlyError(safeMessage(error), void 0, require_toaster.friendlyStringsFrom(t));
6179
- return friendly.replaced ? friendly.title : t("errors.failed", { defaultValue: "Failed" });
6323
+ function failureText(error, settled, t, caps) {
6324
+ const texts = require_toaster.technicalTexts(error).filter((text) => text.length <= 1024);
6325
+ const strings = require_toaster.friendlyStringsFrom(t);
6326
+ const friendly = texts.map((text) => require_toaster.toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? require_toaster.toFriendlyError(texts[0], void 0, strings);
6327
+ const applied = settled ?? ownDataProperty(error, "applied");
6328
+ if (applied === "partial" || applied === "unknown") {
6329
+ const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
6330
+ const sentence = caps.has(key) ? caps.t(key) : applied === "partial" ? "Part of this may have been saved. Check the record before trying again." : "It may have been saved. Check the record before trying again.";
6331
+ const lead = texts.some(require_toaster.isNetworkFailure) ? strings.offline : "";
6332
+ return lead === "" ? sentence : `${lead} ${sentence}`;
6333
+ }
6334
+ const code = ownDataProperty(error, "code");
6335
+ const kind = ownDataProperty(error, "kind");
6336
+ const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(_burojs_react.RECORD_KIND_KEYS, kind);
6337
+ const message = ownDataProperty(error, "message");
6338
+ if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? (0, _burojs_react.specificErrorText)({
6339
+ code,
6340
+ ...isMessageReference(message) ? { message } : {}
6341
+ }, caps, { record: true }) : void 0) ?? friendly.title;
6342
+ if (typeof code !== "string") return strings.title;
6343
+ const reference = {
6344
+ code,
6345
+ ...isMessageReference(message) ? { message } : {}
6346
+ };
6347
+ return (0, _burojs_react.specificErrorText)(reference, caps, { record: true }) ?? (typeof kind === "string" ? (0, _burojs_react.kindErrorText)(kind, caps, { record: true }) : void 0) ?? strings.title;
6180
6348
  }
6181
- function safeMessage(error) {
6349
+ function ownDataProperty(error, name) {
6182
6350
  if (error === null || typeof error !== "object") return void 0;
6183
6351
  try {
6184
- const descriptor = Object.getOwnPropertyDescriptor(error, "message");
6185
- return descriptor !== void 0 && "value" in descriptor && typeof descriptor.value === "string" && descriptor.value.length <= 1024 ? descriptor.value : void 0;
6352
+ const descriptor = Object.getOwnPropertyDescriptor(error, name);
6353
+ return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
6186
6354
  } catch {
6187
6355
  return;
6188
6356
  }
6189
6357
  }
6358
+ function isMessageReference(value) {
6359
+ return value !== null && typeof value === "object" && typeof value.key === "string";
6360
+ }
6190
6361
  function inlineFlowNames(screen, plan) {
6191
6362
  if (screen === null) return [];
6192
6363
  const names = [];
@@ -6459,31 +6630,35 @@ function PlannedFrame(props) {
6459
6630
  })
6460
6631
  }, name);
6461
6632
  const { PlacementScope } = page;
6633
+ const claimedRoute = (0, react.useContext)(_burojs_react.RouteSourceContext);
6462
6634
  const itemNode = page.plan.slots.item?.[0];
6463
6635
  const item = (0, react.useMemo)(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
6464
6636
  return (0, react_jsx_runtime.jsx)(_burojs_react.RouteSourceContext.Provider, {
6465
6637
  value: null,
6466
- children: (0, react_jsx_runtime.jsx)(PlacementScope, { children: (0, react_jsx_runtime.jsx)(ScreenTreeScope, {
6467
- resource,
6468
- mode: "show",
6469
- compiled,
6470
- page,
6471
- children: (0, react_jsx_runtime.jsx)(ListItemContext.Provider, {
6472
- value: item,
6473
- children: (0, react_jsx_runtime.jsx)(FrameView, {
6474
- resource,
6475
- compiled,
6476
- slots
6638
+ children: (0, react_jsx_runtime.jsx)(_burojs_react.ActionRouteContext.Provider, {
6639
+ value: claimedRoute,
6640
+ children: (0, react_jsx_runtime.jsx)(PlacementScope, { children: (0, react_jsx_runtime.jsx)(ScreenTreeScope, {
6641
+ resource,
6642
+ mode: "show",
6643
+ compiled,
6644
+ page,
6645
+ children: (0, react_jsx_runtime.jsx)(ListItemContext.Provider, {
6646
+ value: item,
6647
+ children: (0, react_jsx_runtime.jsx)(FrameView, {
6648
+ resource,
6649
+ compiled,
6650
+ slots
6651
+ })
6477
6652
  })
6478
- })
6479
- }) })
6653
+ }) })
6654
+ })
6480
6655
  });
6481
6656
  }
6482
6657
  function ListFrameHost({ resource }) {
6483
6658
  const compiled = useCompiledListScreen(resource);
6484
6659
  const hasDataRuntime = (0, _burojs_react.useOptionalDataRuntime)() !== null;
6485
6660
  const { def } = (0, _burojs_react.useResourceListContext)();
6486
- const rowsView = (def?.views?.list)?.view ?? "table";
6661
+ const rowsView = def?.views?.list?.initial?.view ?? "table";
6487
6662
  if (compiled === null) return (0, react_jsx_runtime.jsx)(Arrangement, {});
6488
6663
  if (compiled.nodes.length === 0 && rowsView === "table") {
6489
6664
  const empty = {};
@@ -6502,8 +6677,12 @@ function ListFrameHost({ resource }) {
6502
6677
  }
6503
6678
  //#endregion
6504
6679
  //#region src/list-kit/resource-list-surface.tsx
6505
- const RelatedCards = (0, react.lazy)(async () => ({ default: (await Promise.resolve().then(() => require("./related-list-cards-CFq3meUn.cjs"))).RelatedCards }));
6506
- const RelatedTimeline = (0, react.lazy)(async () => ({ default: (await Promise.resolve().then(() => require("./related-list-timeline-CCzl71aA.cjs"))).RelatedTimeline }));
6680
+ function declaresDisplay(field) {
6681
+ return field !== void 0 && (0, _burojs_core.resolveSurfaceUi)(field, "column").use !== void 0;
6682
+ }
6683
+ const NO_INITIAL_FILTERS = Object.freeze({});
6684
+ const RelatedCards = (0, react.lazy)(async () => ({ default: (await Promise.resolve().then(() => require("./related-list-cards-CelMxl4q.cjs"))).RelatedCards }));
6685
+ const RelatedTimeline = (0, react.lazy)(async () => ({ default: (await Promise.resolve().then(() => require("./related-list-timeline-DsLdi8sv.cjs"))).RelatedTimeline }));
6507
6686
  function discriminatorOptionLabel(field, value) {
6508
6687
  const text = (0, _burojs_core.fieldOptionText)(field, value);
6509
6688
  if (text !== void 0) return text;
@@ -6511,10 +6690,17 @@ function discriminatorOptionLabel(field, value) {
6511
6690
  const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
6512
6691
  return typeof label === "string" ? label : value;
6513
6692
  }
6514
- function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize, search: searchField, columns: columnNames, open, title, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, exportable, meta, renderCell, emptyState }) {
6693
+ function declaredInitialSort(def) {
6694
+ return def?.views?.list?.initial?.sort;
6695
+ }
6696
+ function declaredRouteView(def) {
6697
+ return def?.views?.list?.initial?.view;
6698
+ }
6699
+ function ResourceListSurface({ resource, variant, state, fixedFilters, initial, search: searchField, columns: columnEntries, open, title, empty, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, linkEditing, exportable, layoutId, meta, renderCell, emptyState }) {
6515
6700
  const route = variant === "route";
6516
6701
  const t = (0, _burojs_react.useT)();
6517
6702
  const optionalT = (0, _burojs_react.useOptionalT)();
6703
+ const chromeT = (0, _burojs_react.useChromeT)();
6518
6704
  const tid = (0, _burojs_react.useTestIds)();
6519
6705
  const listCaps = (0, _burojs_react.useI18nCaps)(resource);
6520
6706
  const RouteFiltersSlot = require_check_confirm_dialog.useListSlot("filters", resource);
@@ -6529,17 +6715,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6529
6715
  console.error(`[burojs] list.body-removed: list.body.${require_check_confirm_dialog.canonicalResourceName(resource)} is no longer rendered; a whole-body override is a list frame (views.list.frame).`);
6530
6716
  }, [removedBody, resource]);
6531
6717
  const searchDecl = (0, react.useMemo)(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
6532
- const list = (0, _burojs_react.useResourceList)(resource, route ? { meta } : {
6718
+ const appRegistry = (0, _burojs_react.useApp)().registry;
6719
+ const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
6720
+ const timeline = effectiveView === "timeline";
6721
+ const initialKey = JSON.stringify(initial ?? null);
6722
+ const placementInitial = (0, react.useMemo)(() => route ? void 0 : {
6723
+ ...initial,
6724
+ filters: initial?.filters ?? NO_INITIAL_FILTERS
6725
+ }, [route, initialKey]);
6726
+ const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
6727
+ const columnNames = (0, react.useMemo)(() => columnEntries?.map(_burojs_core.columnName), [columnEntries]);
6728
+ const list = (0, _burojs_react.useResourceList)(resource, route ? {
6729
+ meta,
6730
+ ...timeline ? { timeline } : {}
6731
+ } : {
6732
+ ...timeline ? { timeline } : {},
6533
6733
  ...state === void 0 ? {} : { state },
6534
6734
  ...fixedFilters === void 0 ? {} : { fixedFilters },
6535
- ...pageSize === void 0 ? {} : { pageSize },
6735
+ ...placementInitial === void 0 ? {} : { initial: placementInitial },
6536
6736
  ...searchDecl === void 0 ? {} : { search: searchDecl },
6537
- ...columnNames === void 0 ? {} : { columns: columnNames },
6737
+ ...columnEntries === void 0 ? {} : { columns: columnEntries },
6538
6738
  ...scope === void 0 ? {} : { scope },
6539
6739
  ...countPolicy === void 0 ? {} : { countPolicy }
6540
6740
  });
6541
6741
  const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
6542
6742
  (0, _burojs_react.policyRowsOf)(list, list.rows);
6743
+ const selectionRef = (0, react.useRef)(selection);
6744
+ selectionRef.current = selection;
6745
+ const onDetach = linkEditing?.detach?.onDetach;
6746
+ const surfaceLinkEditing = (0, react.useMemo)(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
6747
+ const left = await onDetach(detached);
6748
+ if (left.length > 0) {
6749
+ const gone = new Set(left);
6750
+ const current = selectionRef.current;
6751
+ if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
6752
+ }
6753
+ return left;
6754
+ } } }, [onDetach]);
6755
+ const onAttach = linkEditing?.attach?.onAttach;
6756
+ const attachBusy = linkEditing?.attach?.busy === true;
6543
6757
  const hasDataRuntime = (0, _burojs_react.useOptionalDataRuntime)() !== null;
6544
6758
  const scopeLabel = (scope) => scope.id === _burojs_react.LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
6545
6759
  const createAvailable = (0, _burojs_react.useAvailability)(resource, "create").state === "available";
@@ -6587,7 +6801,6 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6587
6801
  resource,
6588
6802
  routing.rowHref
6589
6803
  ]);
6590
- const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
6591
6804
  const tableView = effectiveView === "table";
6592
6805
  const rowsViewFor = (0, react.useMemo)(() => tableView ? void 0 : (planned) => ({
6593
6806
  skeleton: effectiveView === "cards" ? (0, react_jsx_runtime.jsx)(RelatedCardsSkeleton, {}) : (0, react_jsx_runtime.jsx)(RelatedTimelineSkeleton, {}),
@@ -6599,11 +6812,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6599
6812
  item: route ? planned : item,
6600
6813
  columns: route ? columns.visible : columnNames,
6601
6814
  rowQuick,
6602
- rowHref
6815
+ rowHref,
6816
+ ...empty === void 0 ? {} : { empty }
6603
6817
  };
6604
6818
  return (0, react_jsx_runtime.jsx)(react.Suspense, {
6605
6819
  fallback: effectiveView === "cards" ? (0, react_jsx_runtime.jsx)(RelatedCardsSkeleton, {}) : (0, react_jsx_runtime.jsx)(RelatedTimelineSkeleton, {}),
6606
- children: effectiveView === "cards" ? (0, react_jsx_runtime.jsx)(RelatedCards, { ...common }) : (0, react_jsx_runtime.jsx)(RelatedTimeline, { ...common })
6820
+ children: effectiveView === "cards" ? (0, react_jsx_runtime.jsx)(RelatedCards, { ...common }) : (0, react_jsx_runtime.jsx)(RelatedTimeline, {
6821
+ ...common,
6822
+ axis: timelineAxis
6823
+ })
6607
6824
  });
6608
6825
  }
6609
6826
  }), [
@@ -6616,7 +6833,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6616
6833
  columns.visible,
6617
6834
  columnNames,
6618
6835
  rowQuick,
6619
- rowHref
6836
+ rowHref,
6837
+ empty,
6838
+ timelineAxis
6620
6839
  ]);
6621
6840
  const pageTitle = (0, _burojs_core.resolveResourceLabel)(listCaps, resource, "plural", def);
6622
6841
  const [saveOpen, setSaveOpen] = (0, react.useState)(false);
@@ -6698,6 +6917,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6698
6917
  sonner.toast.error(t("toolbar.viewsForkFailed"));
6699
6918
  }
6700
6919
  };
6920
+ const columnNamesDisplay = (col, field) => {
6921
+ const resolved = list.columns.ui[col];
6922
+ return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
6923
+ };
6701
6924
  const renderCellLadder = (col, value, row) => {
6702
6925
  if (def?.types && col === def.types.by) {
6703
6926
  const raw = typeof value === "string" ? value : "";
@@ -6716,13 +6939,14 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6716
6939
  children: label
6717
6940
  });
6718
6941
  }
6942
+ const custom = (renderCell ?? CellSlot)?.(col, value, row);
6943
+ if (custom !== void 0) return custom;
6719
6944
  const fieldDef = def?.fields[col];
6720
- const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
6721
- if (enumName && typeof value === "string") return (0, react_jsx_runtime.jsx)(EnumBadge, {
6945
+ const enumName = (0, _burojs_core_internal.enumBindingKey)(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
6946
+ if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return (0, react_jsx_runtime.jsx)(EnumBadge, {
6722
6947
  enumName,
6723
6948
  value
6724
6949
  });
6725
- return (renderCell ?? CellSlot)?.(col, value, row);
6726
6950
  };
6727
6951
  const surface = {
6728
6952
  resource,
@@ -6750,10 +6974,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6750
6974
  }
6751
6975
  },
6752
6976
  rowsViewFor,
6753
- emptyState,
6977
+ emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? (0, react_jsx_runtime.jsx)("p", {
6978
+ "data-buro-region": "empty",
6979
+ className: "text-sm text-muted-foreground",
6980
+ children: empty
6981
+ }) : emptyState,
6754
6982
  renderCell: renderCellLadder,
6755
6983
  readOnly: readOnly === true,
6756
- exportable: exportable !== false
6984
+ exportable: exportable !== false,
6985
+ ...layoutId === void 0 ? {} : { layoutId },
6986
+ ...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
6757
6987
  };
6758
6988
  return (0, react_jsx_runtime.jsx)(_burojs_react.ResourceListProvider, {
6759
6989
  value: list,
@@ -6766,22 +6996,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6766
6996
  resource,
6767
6997
  mode: "list",
6768
6998
  title: pageTitle
6769
- }) : title === void 0 && onCreate === void 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
6999
+ }) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : (0, react_jsx_runtime.jsxs)("div", {
6770
7000
  className: "flex items-center justify-between gap-2",
6771
- children: [title === void 0 ? (0, react_jsx_runtime.jsx)("span", {}) : (0, react_jsx_runtime.jsx)("h3", {
6772
- className: "text-sm font-semibold",
6773
- children: title
6774
- }), onCreate === void 0 ? null : (0, react_jsx_runtime.jsxs)(require_button.Button, {
6775
- type: "button",
6776
- size: "sm",
6777
- variant: "outline",
6778
- "data-testid": `list-create:${resource}`,
6779
- onClick: onCreate,
6780
- children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconPlus, {
6781
- className: "mr-1 h-4 w-4",
6782
- "aria-hidden": "true"
6783
- }), optionalT("actions.create", { defaultValue: "Create" })]
6784
- })]
7001
+ children: [
7002
+ title === void 0 ? (0, react_jsx_runtime.jsx)("span", {}) : (0, react_jsx_runtime.jsx)("h3", {
7003
+ className: "text-sm font-semibold",
7004
+ children: title
7005
+ }),
7006
+ onAttach === void 0 ? null : (0, react_jsx_runtime.jsxs)(require_button.Button, {
7007
+ type: "button",
7008
+ size: "sm",
7009
+ variant: "outline",
7010
+ "data-testid": `list-link-attach:${resource}`,
7011
+ onClick: onAttach,
7012
+ disabled: attachBusy,
7013
+ "aria-busy": attachBusy,
7014
+ children: [attachBusy ? (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
7015
+ className: "mr-1 h-4 w-4 animate-spin",
7016
+ "aria-hidden": "true"
7017
+ }) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLink, {
7018
+ className: "mr-1 h-4 w-4",
7019
+ "aria-hidden": "true"
7020
+ }), optionalT("field.link.attach", { defaultValue: "Attach" })]
7021
+ }),
7022
+ onCreate === void 0 ? null : (0, react_jsx_runtime.jsxs)(require_button.Button, {
7023
+ type: "button",
7024
+ size: "sm",
7025
+ variant: "outline",
7026
+ "data-testid": `list-create:${resource}`,
7027
+ onClick: onCreate,
7028
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconPlus, {
7029
+ className: "mr-1 h-4 w-4",
7030
+ "aria-hidden": "true"
7031
+ }), optionalT("actions.create", {
7032
+ resource,
7033
+ defaultValue: "Create"
7034
+ })]
7035
+ })
7036
+ ]
6785
7037
  }),
6786
7038
  hasDataRuntime && (0, react_jsx_runtime.jsx)(ExportRunWatcherMount, {}),
6787
7039
  droppedPredicates > 0 && (0, react_jsx_runtime.jsx)("p", {
@@ -6830,13 +7082,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6830
7082
  variant: "outline",
6831
7083
  onClick: () => setSaveOpen(false),
6832
7084
  "data-testid": `list-saved-view-save-cancel:${resource}`,
6833
- children: t("actions.cancel")
7085
+ children: chromeT("actions.cancel")
6834
7086
  }), (0, react_jsx_runtime.jsx)(require_button.Button, {
6835
7087
  type: "button",
6836
7088
  onClick: handleSaveConfirm,
6837
7089
  disabled: saveLabel.trim().length === 0,
6838
7090
  "data-testid": `list-saved-view-save-confirm:${resource}`,
6839
- children: t("actions.save")
7091
+ children: chromeT("actions.save")
6840
7092
  })] })
6841
7093
  ]
6842
7094
  })
@@ -6873,7 +7125,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6873
7125
  variant: "outline",
6874
7126
  onClick: () => handleDeleteUserView(entry.id),
6875
7127
  "data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
6876
- children: t("actions.delete")
7128
+ children: chromeT("actions.delete")
6877
7129
  }) : (0, react_jsx_runtime.jsx)("span", {
6878
7130
  className: "shrink-0 text-xs text-muted-foreground",
6879
7131
  children: t("toolbar.viewsReadOnly")
@@ -6885,7 +7137,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6885
7137
  variant: "outline",
6886
7138
  onClick: () => setManageOpen(false),
6887
7139
  "data-testid": `list-saved-view-manage-close:${resource}`,
6888
- children: t("actions.close")
7140
+ children: chromeT("actions.close")
6889
7141
  }) })
6890
7142
  ]
6891
7143
  })
@@ -6918,13 +7170,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6918
7170
  setRenameTarget(null);
6919
7171
  },
6920
7172
  "data-testid": `list-saved-view-rename-cancel:${resource}`,
6921
- children: t("actions.cancel")
7173
+ children: chromeT("actions.cancel")
6922
7174
  }), (0, react_jsx_runtime.jsx)(require_button.Button, {
6923
7175
  type: "button",
6924
7176
  onClick: handleRenameConfirm,
6925
7177
  disabled: renameLabel.trim().length === 0,
6926
7178
  "data-testid": `list-saved-view-rename-confirm:${resource}`,
6927
- children: t("actions.save")
7179
+ children: chromeT("actions.save")
6928
7180
  })] })
6929
7181
  ]
6930
7182
  })
@@ -6959,7 +7211,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
6959
7211
  setForkTarget(null);
6960
7212
  },
6961
7213
  "data-testid": `list-saved-view-fork-cancel:${resource}`,
6962
- children: t("actions.cancel")
7214
+ children: chromeT("actions.cancel")
6963
7215
  }), (0, react_jsx_runtime.jsx)(require_button.Button, {
6964
7216
  type: "button",
6965
7217
  onClick: handleForkConfirm,
@@ -6982,6 +7234,582 @@ function ExportRunWatcherMount() {
6982
7234
  return null;
6983
7235
  }
6984
7236
  //#endregion
7237
+ //#region src/fields/use-relation-picker.tsx
7238
+ const noop = () => void 0;
7239
+ function safeKey(input) {
7240
+ if (input === void 0 || input === null) return void 0;
7241
+ try {
7242
+ return (0, _burojs_core.resourceKey)(input);
7243
+ } catch {
7244
+ return;
7245
+ }
7246
+ }
7247
+ function sessionIdentity(runtime) {
7248
+ const state = runtime.dataSession.getState();
7249
+ const snapshot = state.status === "ready" ? state.snapshot : state.target;
7250
+ const selection = snapshot.scope?.selection;
7251
+ const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
7252
+ let scopeKey;
7253
+ try {
7254
+ scopeKey = JSON.stringify(partition) ?? "";
7255
+ } catch {
7256
+ scopeKey = String(snapshot.scopeKey);
7257
+ }
7258
+ return {
7259
+ sessionKey: snapshot.sessionKey,
7260
+ scopeKey
7261
+ };
7262
+ }
7263
+ function PickerHost(props) {
7264
+ const { register } = props;
7265
+ react.useEffect(() => register(), [register]);
7266
+ return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
7267
+ }
7268
+ function useRelationPicker(options) {
7269
+ const app = (0, _burojs_react.useApp)();
7270
+ const runtime = (0, _burojs_react.useDataRuntime)();
7271
+ const frame = (0, _burojs_react.useOptionalRecordFrame)();
7272
+ const frames = (0, _burojs_react.useRecordScopeFrames)();
7273
+ const multiple = options.multiple === true;
7274
+ const fieldName = options.field;
7275
+ let to;
7276
+ let declaredBy;
7277
+ let declaredCandidates;
7278
+ if (fieldName !== void 0) {
7279
+ const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
7280
+ to = safeKey(meta?.to);
7281
+ declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
7282
+ declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
7283
+ } else to = safeKey(options.to);
7284
+ const target = to ?? "";
7285
+ const by = options.valueField ?? declaredBy;
7286
+ const scope = options.scope ?? "context";
7287
+ const selection = require_lazy_relation_picker_dialog.useCandidateSelection({
7288
+ to,
7289
+ candidates: options.candidates ?? declaredCandidates,
7290
+ sources: frame?.controller.sources,
7291
+ resource: frame?.resource ?? "",
7292
+ fieldName: fieldName ?? target,
7293
+ fixedFilters: options.fixedFilters
7294
+ });
7295
+ const held = selection.gate !== void 0;
7296
+ const pendingRef = react.useRef(null);
7297
+ const [active, setActive] = react.useState(null);
7298
+ const activeRef = react.useRef(null);
7299
+ activeRef.current = active;
7300
+ const rowsRef = react.useRef(new Map());
7301
+ const createdRowRef = react.useRef(void 0);
7302
+ const alive = react.useRef(true);
7303
+ const mounts = react.useRef(0);
7304
+ const sequence = react.useRef(0);
7305
+ const latest = react.useRef({
7306
+ multiple,
7307
+ select: options.select,
7308
+ idField: "id",
7309
+ valueField: "id",
7310
+ available: false,
7311
+ frames
7312
+ });
7313
+ const project = react.useCallback((row) => {
7314
+ const select = latest.current.select;
7315
+ if (select === void 0) return { ...row };
7316
+ const out = {};
7317
+ const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
7318
+ if (key !== void 0) out.id = key;
7319
+ const { valueField } = latest.current;
7320
+ if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
7321
+ for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
7322
+ return out;
7323
+ }, []);
7324
+ const settle = react.useCallback((result) => {
7325
+ const pending = pendingRef.current;
7326
+ if (pending === null) return;
7327
+ pendingRef.current = null;
7328
+ pending.detach();
7329
+ if (alive.current) setActive(null);
7330
+ pending.resolve(result);
7331
+ }, []);
7332
+ const toggle = react.useCallback((key, row) => {
7333
+ setActive((current) => {
7334
+ if (current === null) return current;
7335
+ const has = current.selected.some((k) => String(k) === String(key));
7336
+ if (has) rowsRef.current.delete(String(key));
7337
+ else if (row !== void 0) rowsRef.current.set(String(key), row);
7338
+ const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
7339
+ const next = {
7340
+ ...current,
7341
+ selected
7342
+ };
7343
+ activeRef.current = next;
7344
+ return next;
7345
+ });
7346
+ }, []);
7347
+ const onValue = react.useCallback((value) => {
7348
+ const row = createdRowRef.current;
7349
+ createdRowRef.current = void 0;
7350
+ if (latest.current.multiple) {
7351
+ const current = activeRef.current;
7352
+ if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
7353
+ return;
7354
+ }
7355
+ settle({
7356
+ kind: "selected",
7357
+ keys: [value],
7358
+ rows: row === void 0 ? [] : [project(row)]
7359
+ });
7360
+ }, [
7361
+ project,
7362
+ settle,
7363
+ toggle
7364
+ ]);
7365
+ const wiring = require_relation_select.useRelationSelect({
7366
+ to: target,
7367
+ by,
7368
+ fixedFilters: selection.fixedFilters,
7369
+ scope,
7370
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7371
+ held,
7372
+ onValue
7373
+ });
7374
+ latest.current = {
7375
+ multiple,
7376
+ select: options.select,
7377
+ idField: wiring.idField,
7378
+ valueField: wiring.valueField,
7379
+ available: to !== void 0 && wiring.targetDef !== void 0 && !held,
7380
+ frames
7381
+ };
7382
+ const { create: shared } = wiring;
7383
+ const create = react.useMemo(() => ({
7384
+ ...shared,
7385
+ start: (openCreate) => shared.start(() => {
7386
+ const opened = openCreate();
7387
+ opened.then((row) => {
7388
+ createdRowRef.current = row === null ? void 0 : { ...row };
7389
+ }, noop);
7390
+ return opened;
7391
+ })
7392
+ }), [shared]);
7393
+ react.useEffect(() => runtime.dataSession.subscribe(() => {
7394
+ const pending = pendingRef.current;
7395
+ if (pending === null) return;
7396
+ const now = sessionIdentity(runtime);
7397
+ if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
7398
+ kind: "cancelled",
7399
+ reason: "session-changed"
7400
+ });
7401
+ }), [runtime, settle]);
7402
+ react.useEffect(() => {
7403
+ alive.current = true;
7404
+ return () => {
7405
+ alive.current = false;
7406
+ settle({
7407
+ kind: "cancelled",
7408
+ reason: "unmounted"
7409
+ });
7410
+ };
7411
+ }, [settle]);
7412
+ react.useEffect(() => {
7413
+ if (held) settle({
7414
+ kind: "cancelled",
7415
+ reason: "unavailable"
7416
+ });
7417
+ }, [held, settle]);
7418
+ const register = react.useCallback(() => {
7419
+ mounts.current += 1;
7420
+ return () => {
7421
+ mounts.current -= 1;
7422
+ if (mounts.current === 0) settle({
7423
+ kind: "cancelled",
7424
+ reason: "unmounted"
7425
+ });
7426
+ };
7427
+ }, [settle]);
7428
+ const open = react.useCallback((opts = {}) => new Promise((resolve) => {
7429
+ settle({
7430
+ kind: "cancelled",
7431
+ reason: "superseded"
7432
+ });
7433
+ if (mounts.current === 0) {
7434
+ if (process.env.NODE_ENV !== "production") console.error("[burojs] useRelationPicker: open() was called but its element is not rendered — render `handle.element` once in your tree.");
7435
+ resolve({
7436
+ kind: "cancelled",
7437
+ reason: "not-mounted"
7438
+ });
7439
+ return;
7440
+ }
7441
+ const { signal } = opts;
7442
+ if (signal?.aborted === true) {
7443
+ resolve({
7444
+ kind: "cancelled",
7445
+ reason: "aborted"
7446
+ });
7447
+ return;
7448
+ }
7449
+ if (!latest.current.available) {
7450
+ resolve({
7451
+ kind: "cancelled",
7452
+ reason: "unavailable"
7453
+ });
7454
+ return;
7455
+ }
7456
+ sequence.current += 1;
7457
+ const id = sequence.current;
7458
+ const onAbort = () => {
7459
+ if (pendingRef.current?.id === id) settle({
7460
+ kind: "cancelled",
7461
+ reason: "aborted"
7462
+ });
7463
+ };
7464
+ signal?.addEventListener("abort", onAbort, { once: true });
7465
+ pendingRef.current = {
7466
+ id,
7467
+ ...sessionIdentity(runtime),
7468
+ resolve,
7469
+ detach: () => signal?.removeEventListener("abort", onAbort)
7470
+ };
7471
+ rowsRef.current = new Map();
7472
+ createdRowRef.current = void 0;
7473
+ const next = {
7474
+ id,
7475
+ frames: latest.current.frames,
7476
+ selected: opts.selected ?? []
7477
+ };
7478
+ activeRef.current = next;
7479
+ setActive(next);
7480
+ }), [runtime, settle]);
7481
+ const onSelectRow = react.useCallback((row) => {
7482
+ const field = wiring.valueField;
7483
+ const value = field === "id" ? row.id : row[field];
7484
+ if (typeof value !== "string" && typeof value !== "number") return;
7485
+ settle({
7486
+ kind: "selected",
7487
+ keys: [value],
7488
+ rows: [project(row)]
7489
+ });
7490
+ }, [
7491
+ wiring.valueField,
7492
+ settle,
7493
+ project
7494
+ ]);
7495
+ const onDone = react.useCallback(() => {
7496
+ const keys = activeRef.current?.selected ?? [];
7497
+ const rows = keys.flatMap((key) => {
7498
+ const row = rowsRef.current.get(String(key));
7499
+ return row === void 0 ? [] : [project(row)];
7500
+ });
7501
+ settle({
7502
+ kind: "selected",
7503
+ keys,
7504
+ rows
7505
+ });
7506
+ }, [settle, project]);
7507
+ const onOpenChange = react.useCallback((next) => {
7508
+ if (!next) settle({
7509
+ kind: "cancelled",
7510
+ reason: "dismissed"
7511
+ });
7512
+ }, [settle]);
7513
+ return {
7514
+ open,
7515
+ element: (0, react_jsx_runtime.jsx)(PickerHost, {
7516
+ register,
7517
+ children: active === null || held ? null : (0, react_jsx_runtime.jsxs)(_burojs_react.RecordScopeChain, {
7518
+ frames: active.frames,
7519
+ children: [(0, react_jsx_runtime.jsx)(require_lazy_relation_picker_dialog.LazyRelationPickerDialog, {
7520
+ to: target,
7521
+ open: true,
7522
+ onOpenChange,
7523
+ onSelect: noop,
7524
+ onSelectRow,
7525
+ valueField: wiring.valueField,
7526
+ ...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
7527
+ scope,
7528
+ ...fieldName === void 0 ? {} : { scopeField: fieldName },
7529
+ ...options.create === void 0 ? {} : {
7530
+ create,
7531
+ ...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
7532
+ },
7533
+ fieldName: fieldName ?? target,
7534
+ ...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
7535
+ ...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
7536
+ ...multiple ? {
7537
+ multiple: true,
7538
+ selected: active.selected,
7539
+ onToggle: (value, row) => toggle(value, row),
7540
+ onDone
7541
+ } : {}
7542
+ }), wiring.reader]
7543
+ }, active.id)
7544
+ })
7545
+ };
7546
+ }
7547
+ //#endregion
7548
+ //#region src/blocks/link-write-summary.tsx
7549
+ function LinkWriteSummary(props) {
7550
+ const { report, titles, onClose } = props;
7551
+ const testid = props.testid ?? "link-write-summary";
7552
+ const t = (0, _burojs_react.useOptionalT)();
7553
+ const caps = (0, _burojs_react.useI18nCaps)();
7554
+ const chromeT = (0, _burojs_react.useChromeT)();
7555
+ const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
7556
+ return (0, react_jsx_runtime.jsx)(require_dialog.Dialog, {
7557
+ open: true,
7558
+ onOpenChange: (open) => {
7559
+ if (!open) onClose();
7560
+ },
7561
+ children: (0, react_jsx_runtime.jsxs)(require_dialog.DialogContent, {
7562
+ "data-testid": testid,
7563
+ className: "flex max-h-[85vh] flex-col overflow-hidden",
7564
+ children: [
7565
+ (0, react_jsx_runtime.jsxs)(require_dialog.DialogHeader, { children: [(0, react_jsx_runtime.jsx)(require_dialog.DialogTitle, { children: heading }), (0, react_jsx_runtime.jsx)(require_dialog.DialogDescription, {
7566
+ className: "sr-only",
7567
+ children: heading
7568
+ })] }),
7569
+ (0, react_jsx_runtime.jsx)("ul", {
7570
+ className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
7571
+ children: report.failed.map(({ id, error }) => (0, react_jsx_runtime.jsxs)("li", {
7572
+ "data-testid": `${testid}:item:${id}`,
7573
+ className: "text-sm",
7574
+ children: [(0, react_jsx_runtime.jsx)("span", {
7575
+ className: "font-medium",
7576
+ children: titles[id] ?? id
7577
+ }), (0, react_jsx_runtime.jsx)("span", {
7578
+ className: "text-destructive",
7579
+ children: `: ${(0, _burojs_react.resolveErrorText)(error, caps)}`
7580
+ })]
7581
+ }, id))
7582
+ }),
7583
+ (0, react_jsx_runtime.jsx)(require_dialog.DialogFooter, { children: (0, react_jsx_runtime.jsx)(require_button.Button, {
7584
+ type: "button",
7585
+ onClick: onClose,
7586
+ "data-testid": `${testid}:close`,
7587
+ children: chromeT("actions.close", { defaultValue: "Close" })
7588
+ }) })
7589
+ ]
7590
+ })
7591
+ });
7592
+ }
7593
+ //#endregion
7594
+ //#region src/blocks/link-attach.tsx
7595
+ const NO_ISSUES = Object.freeze([]);
7596
+ function useLinkPanelWrites(options) {
7597
+ const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
7598
+ const app = (0, _burojs_react.useApp)();
7599
+ const runtime = (0, _burojs_react.useDataRuntime)();
7600
+ const t = (0, _burojs_react.useOptionalT)();
7601
+ const rights = (0, _burojs_react.useLinkRights)(owner, link, {
7602
+ surface: "panel",
7603
+ record,
7604
+ ...placement === void 0 ? {} : { placement }
7605
+ });
7606
+ const writer = (0, _burojs_react.useLinkWriter)(owner, link);
7607
+ const linkedKeys = (0, _burojs_react.useLinkedKeys)(owner, link);
7608
+ const reverse = (0, react.useMemo)(() => (0, _burojs_core.reverseLinkOf)((name) => app.registry.get(name)?.fields, owner, link), [
7609
+ app.registry,
7610
+ owner,
7611
+ link
7612
+ ]);
7613
+ let liveHides = false;
7614
+ if (reverse !== void 0) try {
7615
+ const { providerId } = runtime.app.resolveDataBinding(target);
7616
+ liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && (0, _burojs_react.linkCapabilitiesOf)(runtime, providerId)?.links?.filter.none !== false;
7617
+ } catch {
7618
+ liveHides = false;
7619
+ }
7620
+ const liveHidesRef = (0, react.useRef)(liveHides);
7621
+ liveHidesRef.current = liveHides;
7622
+ const [frozenHides, setFrozenHides] = (0, react.useState)(null);
7623
+ const hides = frozenHides ?? liveHides;
7624
+ const ownerKeyId = JSON.stringify(ownerKey ?? null);
7625
+ const hidden = (0, react.useMemo)(() => hides && reverse !== void 0 ? [{
7626
+ field: reverse,
7627
+ op: "hasNone",
7628
+ value: [ownerKey]
7629
+ }] : void 0, [
7630
+ hides,
7631
+ reverse,
7632
+ ownerKeyId
7633
+ ]);
7634
+ const disabledKeys = (0, react.useCallback)((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
7635
+ const disabledLabel = (0, react.useCallback)((count) => t("field.link.alreadyLinked", {
7636
+ count,
7637
+ defaultValue: `${count} already linked`
7638
+ }), [t]);
7639
+ const picker = useRelationPicker({
7640
+ to: target,
7641
+ multiple: true,
7642
+ valueField,
7643
+ scope,
7644
+ ...hidden === void 0 ? {
7645
+ disabledKeys,
7646
+ disabledLabel
7647
+ } : { fixedFilters: hidden }
7648
+ });
7649
+ const openPicker = picker.open;
7650
+ const [summary, setSummary] = (0, react.useState)(null);
7651
+ const finish = (0, react.useCallback)((report, titles, done) => {
7652
+ if (report.outcome !== "success") {
7653
+ setSummary({
7654
+ report,
7655
+ titles
7656
+ });
7657
+ return;
7658
+ }
7659
+ const count = report.succeeded.length;
7660
+ if (count === 0) return;
7661
+ sonner.toast.success(t(`field.link.${done}`, {
7662
+ count,
7663
+ defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
7664
+ }));
7665
+ }, [t]);
7666
+ const attaching = (0, react.useRef)(false);
7667
+ const [attachWriting, setAttachWriting] = (0, react.useState)(false);
7668
+ const onAttach = (0, react.useCallback)(() => {
7669
+ if (attaching.current) return;
7670
+ attaching.current = true;
7671
+ (async () => {
7672
+ try {
7673
+ setFrozenHides(liveHidesRef.current);
7674
+ let result;
7675
+ try {
7676
+ result = await openPicker();
7677
+ } finally {
7678
+ setFrozenHides(null);
7679
+ }
7680
+ if (result.kind !== "selected" || result.keys.length === 0) return;
7681
+ const titles = {};
7682
+ for (const row of result.rows) {
7683
+ const key = valueField === "id" ? row.id : row[valueField];
7684
+ const title = row[displayField];
7685
+ if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
7686
+ }
7687
+ setAttachWriting(true);
7688
+ try {
7689
+ finish(await writer.attach(result.keys, record), titles, "attached");
7690
+ } finally {
7691
+ setAttachWriting(false);
7692
+ }
7693
+ } finally {
7694
+ attaching.current = false;
7695
+ }
7696
+ })();
7697
+ }, [
7698
+ displayField,
7699
+ finish,
7700
+ openPicker,
7701
+ record,
7702
+ valueField,
7703
+ writer
7704
+ ]);
7705
+ const pending = (0, react.useRef)(null);
7706
+ const [confirming, setConfirming] = (0, react.useState)(null);
7707
+ const detaching = (0, react.useRef)(false);
7708
+ const [busy, setBusy] = (0, react.useState)(false);
7709
+ const onDetach = (0, react.useCallback)((rows) => new Promise((resolve) => {
7710
+ pending.current?.resolve([]);
7711
+ pending.current = {
7712
+ rows,
7713
+ resolve
7714
+ };
7715
+ setConfirming(rows.length);
7716
+ }), []);
7717
+ (0, react.useEffect)(() => () => {
7718
+ pending.current?.resolve([]);
7719
+ pending.current = null;
7720
+ }, []);
7721
+ const cancel = (0, react.useCallback)(() => {
7722
+ if (detaching.current) return;
7723
+ pending.current?.resolve([]);
7724
+ pending.current = null;
7725
+ setConfirming(null);
7726
+ }, []);
7727
+ const proceed = (0, react.useCallback)(() => {
7728
+ const current = pending.current;
7729
+ if (current === null || detaching.current) return;
7730
+ detaching.current = true;
7731
+ setBusy(true);
7732
+ (async () => {
7733
+ try {
7734
+ const titles = {};
7735
+ for (const row of current.rows) {
7736
+ const id = row[junctionIdField];
7737
+ if (id === void 0 || id === null) continue;
7738
+ const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
7739
+ if (title !== void 0 && title !== null) titles[String(id)] = String(title);
7740
+ }
7741
+ const report = await writer.detach(current.rows, record);
7742
+ pending.current = null;
7743
+ setConfirming(null);
7744
+ finish(report, titles, "detached");
7745
+ const left = new Set(report.succeeded);
7746
+ current.resolve(current.rows.filter((row) => {
7747
+ const id = row[junctionIdField];
7748
+ return id !== void 0 && id !== null && left.has(String(id));
7749
+ }).map((row) => String(row.id)));
7750
+ } finally {
7751
+ detaching.current = false;
7752
+ setBusy(false);
7753
+ }
7754
+ })();
7755
+ }, [
7756
+ displayField,
7757
+ finish,
7758
+ junctionIdField,
7759
+ record,
7760
+ targetKey,
7761
+ writer
7762
+ ]);
7763
+ const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
7764
+ const count = confirming ?? 0;
7765
+ const gate = {
7766
+ resource: junction,
7767
+ open: confirming !== null,
7768
+ busy,
7769
+ header: t("field.link.confirmDetach", {
7770
+ count,
7771
+ defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
7772
+ }),
7773
+ blockers: NO_ISSUES,
7774
+ confirmable: NO_ISSUES,
7775
+ proceed,
7776
+ cancel
7777
+ };
7778
+ const attachAllowed = rights.attach.allowed;
7779
+ const detachAllowed = rights.detach.allowed;
7780
+ return {
7781
+ linkEditing: (0, react.useMemo)(() => !attachAllowed && !detachAllowed ? void 0 : {
7782
+ ...attachAllowed ? { attach: {
7783
+ onAttach,
7784
+ busy: attachWriting
7785
+ } } : {},
7786
+ ...detachAllowed ? { detach: { onDetach } } : {}
7787
+ }, [
7788
+ attachAllowed,
7789
+ attachWriting,
7790
+ detachAllowed,
7791
+ onAttach,
7792
+ onDetach
7793
+ ]),
7794
+ element: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7795
+ picker.element,
7796
+ (0, react_jsx_runtime.jsx)(require_check_confirm_dialog.CheckConfirmDialog, {
7797
+ state: gate,
7798
+ testid: `link-detach-confirm:${junction}`,
7799
+ title: detachLabel,
7800
+ proceedLabel: detachLabel,
7801
+ tone: "destructive"
7802
+ }),
7803
+ summary === null ? null : (0, react_jsx_runtime.jsx)(LinkWriteSummary, {
7804
+ report: summary.report,
7805
+ titles: summary.titles,
7806
+ onClose: () => setSummary(null),
7807
+ testid: `link-write-summary:${junction}`
7808
+ })
7809
+ ] })
7810
+ };
7811
+ }
7812
+ //#endregion
6985
7813
  //#region src/quick/peek-filter.ts
6986
7814
  function peekFieldDefinition(def, name) {
6987
7815
  return (0, _burojs_core.declaredField)(def, name);
@@ -7005,32 +7833,12 @@ function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
7005
7833
  return peekRow(def, row, idField, capabilityFor, fieldVisible);
7006
7834
  }
7007
7835
  //#endregion
7008
- //#region src/blocks/related-list-unbound.tsx
7009
- function UnboundRelatedList(props) {
7010
- const t = (0, _burojs_react.useOptionalT)();
7011
- return (0, react_jsx_runtime.jsxs)("div", {
7012
- className: "flex flex-col gap-2",
7013
- "data-testid": `list-unbound:${props.resource}`,
7014
- children: [props.title === void 0 ? null : (0, react_jsx_runtime.jsx)("h3", {
7015
- className: "text-sm font-semibold",
7016
- children: props.title
7017
- }), (0, react_jsx_runtime.jsx)("p", {
7018
- className: "text-sm text-muted-foreground",
7019
- children: t("table.empty", { defaultValue: "No records" })
7020
- })]
7021
- });
7022
- }
7023
- function placementScope(scope, owner) {
7024
- return scope ?? (owner === void 0 ? "session" : "none");
7025
- }
7026
- //#endregion
7027
7836
  //#region src/blocks/link-panel.tsx
7028
7837
  const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
7029
7838
  const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
7030
- function qualify(ref, owner) {
7839
+ function qualify(ref) {
7031
7840
  try {
7032
- const namespace = (0, _burojs_core.normalizeEntityRef)(owner).namespace;
7033
- return (0, _burojs_core.resourceKey)((0, _burojs_core.normalizeEntityRef)(isResourceHandle(ref) ? ref.ref : ref, { defaultNamespace: namespace }));
7841
+ return (0, _burojs_core.resourceKey)((0, _burojs_core.normalizeEntityRef)(isResourceHandle(ref) ? ref.ref : ref));
7034
7842
  } catch {
7035
7843
  return;
7036
7844
  }
@@ -7049,7 +7857,7 @@ function linkField(registry, owner, link) {
7049
7857
  function linkTarget(registry, owner, link) {
7050
7858
  const found = linkField(registry, owner, link);
7051
7859
  if (found === void 0 || owner === void 0) return void 0;
7052
- return qualify(found.field.meta?.to, owner);
7860
+ return qualify(found.field.meta?.to);
7053
7861
  }
7054
7862
  function idFieldOf(def) {
7055
7863
  const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
@@ -7065,37 +7873,47 @@ function targetColumns(registry, target, def) {
7065
7873
  const source = def;
7066
7874
  const views = isRecord(def.views) ? def.views : void 0;
7067
7875
  const list = views !== void 0 && isRecord(views.list) ? views.list : void 0;
7068
- return (list !== void 0 && Array.isArray(list.columns) ? list.columns.filter((name) => typeof name === "string") : (0, _burojs_core.dtoFields)(source, "list")).filter((name) => {
7876
+ return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
7877
+ const name = isRecord(entry) ? entry.field : entry;
7878
+ return typeof name === "string" ? [name] : [];
7879
+ }) : (0, _burojs_core.dtoFields)(source, "list")).filter((name) => {
7069
7880
  if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && (0, _burojs_react.resolveColumnField)(registry, target, name) !== void 0;
7070
7881
  const field = peekFieldDefinition(source, name);
7071
7882
  return isRecord(field) && !(0, _burojs_core.isRelationField)(field) && (0, _burojs_core.fieldSensitivity)(field) === void 0;
7072
7883
  });
7073
7884
  }
7074
7885
  function LinkPanel(props) {
7075
- const { link, owner, own } = props;
7886
+ const { link, owner, own, empty } = props;
7076
7887
  const app = (0, _burojs_react.useApp)();
7077
7888
  const frame = usePlacementFrame();
7078
7889
  const routeScreen = useIsRouteScreen();
7079
7890
  const record = (0, _burojs_react.useOptionalRecordFrame)()?.record;
7891
+ const runtime = (0, _burojs_react.useOptionalDataRuntime)();
7080
7892
  const storage = (0, react.useMemo)(() => {
7081
7893
  const found = linkField(app.registry, owner, link);
7082
7894
  if (found === void 0) return void 0;
7083
7895
  const { declaration } = found;
7084
- const junction = qualify(declaration.junction, owner);
7085
- const target = qualify(found.field.meta?.to, owner);
7896
+ const junction = qualify(declaration.junction);
7897
+ const target = qualify(found.field.meta?.to);
7086
7898
  if (junction === void 0 || target === void 0) return void 0;
7087
7899
  const junctionDef = app.registry.get(junction);
7088
7900
  if (junctionDef === void 0) return void 0;
7089
7901
  const junctionFields = junctionDef.fields;
7902
+ const targetDef = app.registry.get(target);
7903
+ const targetBy = byOf(junctionFields[declaration.targetKey]);
7904
+ const declaredScope = found.field.meta?.scope;
7090
7905
  return {
7091
7906
  junction,
7092
7907
  target,
7093
7908
  sourceKey: declaration.sourceKey,
7094
7909
  targetKey: declaration.targetKey,
7095
7910
  ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
7096
- targetKeyedBy: byOf(junctionFields[declaration.targetKey]) !== void 0,
7911
+ targetKeyedBy: targetBy !== void 0,
7912
+ targetValueField: targetBy ?? idFieldOf(targetDef),
7913
+ junctionIdField: idFieldOf(junctionDef),
7097
7914
  where: declaration.where,
7098
- targetDef: app.registry.get(target)
7915
+ targetDef,
7916
+ pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
7099
7917
  };
7100
7918
  }, [
7101
7919
  app.registry,
@@ -7107,10 +7925,11 @@ function LinkPanel(props) {
7107
7925
  const fixedFilters = (0, react.useMemo)(() => {
7108
7926
  if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
7109
7927
  const { sourceKey, targetKey } = storage;
7928
+ const placed = (0, _burojs_core_internal.whereToFilter)(own.where ?? [], (field) => `${targetKey}.${field}`);
7110
7929
  return [
7111
7930
  (0, _burojs_core.whereEntryToFilter)(sourceKey, "eq", ownerKey),
7112
7931
  ...storage.where.map((entry) => (0, _burojs_core.whereEntryToFilter)(entry.field, entry.op, entry.value)),
7113
- ...(own.where ?? []).map((entry) => (0, _burojs_core.whereEntryToFilter)(`${targetKey}.${entry.field}`, entry.op, entry.value))
7932
+ ...placed === void 0 ? [] : [placed]
7114
7933
  ];
7115
7934
  }, [
7116
7935
  storage,
@@ -7120,7 +7939,12 @@ function LinkPanel(props) {
7120
7939
  const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
7121
7940
  const columns = (0, react.useMemo)(() => {
7122
7941
  if (storage === void 0) return void 0;
7123
- return [...(own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef)).map((name) => `${storage.targetKey}.${name}`), ...own.linkColumns ?? []];
7942
+ const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
7943
+ const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
7944
+ ...entry,
7945
+ field: `${storage.targetKey}.${entry.field}`
7946
+ };
7947
+ return [...shown.map(across), ...own.linkColumns ?? []];
7124
7948
  }, [
7125
7949
  storage,
7126
7950
  columnsKey,
@@ -7130,30 +7954,75 @@ function LinkPanel(props) {
7130
7954
  resource: storage.target,
7131
7955
  field: storage.targetKey
7132
7956
  }, [storage]);
7133
- const unbound = fixedFilters === void 0 || own.where?.some((entry) => entry.value === null || entry.value === void 0) === true;
7957
+ const unbound = fixedFilters === void 0 || own.where !== void 0 && (0, _burojs_core_internal.hasNullRef)(own.where);
7134
7958
  if (storage === void 0 || unbound) return (0, react_jsx_runtime.jsx)(UnboundRelatedList, {
7135
7959
  resource: storage?.target ?? "",
7136
- title: own.title
7960
+ title: own.title,
7961
+ ...empty === void 0 ? {} : { empty }
7137
7962
  });
7138
7963
  const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
7139
- const count = own.count === false ? "off" : own.count;
7140
- return (0, react_jsx_runtime.jsx)(ResourceListSurface, {
7964
+ let providerCounts;
7965
+ if (runtime !== null) try {
7966
+ providerCounts = (0, _burojs_react.linkCapabilitiesOf)(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
7967
+ } catch {
7968
+ providerCounts = void 0;
7969
+ }
7970
+ const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
7971
+ const layoutId = frame === null ? void 0 : placementLayoutId({
7972
+ owner,
7973
+ key: frame.key
7974
+ });
7975
+ const surface = {
7141
7976
  resource: storage.junction,
7142
7977
  variant: "placement",
7143
7978
  state,
7144
7979
  fixedFilters,
7145
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
7980
+ ...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
7146
7981
  ...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
7147
7982
  ...columns === void 0 ? {} : { columns },
7148
7983
  open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
7149
7984
  ...rowTarget === void 0 ? {} : { rowTarget },
7150
7985
  ...frame === null ? {} : { placementKey: frame.key },
7986
+ ...layoutId === void 0 ? {} : { layoutId },
7151
7987
  ...own.title === void 0 ? {} : { title: own.title },
7988
+ ...empty === void 0 ? {} : { empty },
7152
7989
  scope: placementScope(own.scope, owner),
7153
7990
  ...count === void 0 ? {} : { count },
7154
7991
  readOnly: true,
7155
7992
  exportable: false
7993
+ };
7994
+ if (runtime === null || record === void 0) return (0, react_jsx_runtime.jsx)(ResourceListSurface, { ...surface });
7995
+ return (0, react_jsx_runtime.jsx)(WritableLinkPanel, {
7996
+ surface,
7997
+ owner,
7998
+ link,
7999
+ storage,
8000
+ record,
8001
+ ownerKey,
8002
+ placement: own.linkActions
8003
+ });
8004
+ }
8005
+ function WritableLinkPanel(props) {
8006
+ const { surface, owner, link, storage, record, ownerKey, placement } = props;
8007
+ const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
8008
+ const { linkEditing, element } = useLinkPanelWrites({
8009
+ owner,
8010
+ junction: storage.junction,
8011
+ target: storage.target,
8012
+ link,
8013
+ targetKey: storage.targetKey,
8014
+ valueField: storage.targetValueField,
8015
+ junctionIdField: storage.junctionIdField,
8016
+ displayField,
8017
+ scope: storage.pickerScope ?? "context",
8018
+ record,
8019
+ ownerKey,
8020
+ placement
7156
8021
  });
8022
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(ResourceListSurface, {
8023
+ ...surface,
8024
+ ...linkEditing === void 0 ? {} : { linkEditing }
8025
+ }), element] });
7157
8026
  }
7158
8027
  //#endregion
7159
8028
  //#region src/blocks/route-list-context.ts
@@ -7163,7 +8032,7 @@ function useRouteList() {
7163
8032
  }
7164
8033
  //#endregion
7165
8034
  //#region src/blocks/related-list-view.tsx
7166
- function RelatedListView({ props }) {
8035
+ function RelatedListView({ props, empty }) {
7167
8036
  const own = props;
7168
8037
  const route = useRouteList();
7169
8038
  const frame = usePlacementFrame();
@@ -7171,7 +8040,7 @@ function RelatedListView({ props }) {
7171
8040
  const linked = own.link !== void 0;
7172
8041
  const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : (0, _burojs_core.resourceKey)(own.resource);
7173
8042
  const whereKey = JSON.stringify(own.where ?? []);
7174
- const fixedFilters = (0, react.useMemo)(() => own.where === void 0 ? void 0 : own.where.map(({ field, op, value }) => (0, _burojs_core.whereEntryToFilter)(field, op, value)), [whereKey]);
8043
+ const fixedFilters = (0, react.useMemo)(() => own.where === void 0 || own.where.length === 0 ? void 0 : [(0, _burojs_core_internal.whereToFilter)(own.where)], [whereKey]);
7175
8044
  const columnsKey = JSON.stringify(own.columns ?? null);
7176
8045
  const columns = (0, react.useMemo)(() => own.columns, [columnsKey]);
7177
8046
  const routeScreen = useIsRouteScreen();
@@ -7183,20 +8052,23 @@ function RelatedListView({ props }) {
7183
8052
  const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
7184
8053
  const granted = ownerDef === void 0 ? "unknown" : (0, _burojs_core.grantedPlacementKeys)(ownerDef);
7185
8054
  const trusted = frame !== null && !frame.nested && granted !== "unknown" && !granted.has(frame.key);
7186
- const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
8055
+ const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && own.create !== false && (own.create === void 0 || (0, _burojs_core_internal.actionWhenHolds)(own.create, frame?.sources ?? {})) && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
7187
8056
  useDeepLinkOpen(frame?.key ?? null, resource, route === null && !linked && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
7188
- const unbound = own.where?.some((entry) => entry.value === null || entry.value === void 0) === true;
8057
+ const unbound = own.where !== void 0 && (0, _burojs_core_internal.hasNullRef)(own.where);
7189
8058
  if (linked) return frame?.owner === void 0 ? (0, react_jsx_runtime.jsx)(UnboundRelatedList, {
7190
8059
  resource,
7191
- title: own.title
8060
+ title: own.title,
8061
+ ...empty === void 0 ? {} : { empty }
7192
8062
  }) : (0, react_jsx_runtime.jsx)(LinkPanel, {
7193
8063
  link: own.link,
7194
8064
  owner: frame.owner,
7195
- own
8065
+ own,
8066
+ ...empty === void 0 ? {} : { empty }
7196
8067
  });
7197
8068
  if (route !== null) return (0, react_jsx_runtime.jsx)(ResourceListSurface, {
7198
8069
  resource,
7199
8070
  variant: "route",
8071
+ layoutId: "route",
7200
8072
  ...route.meta === void 0 ? {} : { meta: route.meta },
7201
8073
  ...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
7202
8074
  ...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
@@ -7209,24 +8081,28 @@ function RelatedListView({ props }) {
7209
8081
  });
7210
8082
  if (unbound) return (0, react_jsx_runtime.jsx)(UnboundRelatedList, {
7211
8083
  resource,
7212
- title: own.title
8084
+ title: own.title,
8085
+ ...empty === void 0 ? {} : { empty }
7213
8086
  });
7214
8087
  const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
7215
8088
  const scope = placementScope(own.scope, frame?.owner);
7216
8089
  const count = own.count === false ? "off" : own.count;
8090
+ const layoutId = placementLayoutId(frame);
7217
8091
  return (0, react_jsx_runtime.jsx)(ResourceListSurface, {
7218
8092
  resource,
7219
8093
  variant: "placement",
7220
8094
  state,
7221
8095
  ...fixedFilters === void 0 ? {} : { fixedFilters },
7222
- ...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
8096
+ ...own.initial === void 0 ? {} : { initial: own.initial },
7223
8097
  ...own.search === void 0 ? {} : { search: own.search },
7224
8098
  ...columns === void 0 ? {} : { columns },
7225
8099
  open: own.open ?? "quick",
7226
8100
  ...own.view === void 0 ? {} : { view: own.view },
7227
8101
  ...isItemHandle(own.item) ? { item: own.item } : {},
7228
8102
  ...frame === null ? {} : { placementKey: frame.key },
8103
+ ...layoutId === void 0 ? {} : { layoutId },
7229
8104
  ...own.title === void 0 ? {} : { title: own.title },
8105
+ ...empty === void 0 ? {} : { empty },
7230
8106
  scope,
7231
8107
  ...count === void 0 ? {} : { count },
7232
8108
  ...onCreate === void 0 ? {} : { onCreate }
@@ -7261,7 +8137,7 @@ function Missing({ text }) {
7261
8137
  children: text ?? t("table.empty", { defaultValue: "No records" })
7262
8138
  });
7263
8139
  }
7264
- function RelatedRecordView({ props }) {
8140
+ function RelatedRecordView({ props, empty }) {
7265
8141
  const own = props;
7266
8142
  const app = (0, _burojs_react.useApp)();
7267
8143
  const resource = (0, _burojs_core.resourceKey)(own.resource);
@@ -7295,15 +8171,15 @@ function RelatedRecordView({ props }) {
7295
8171
  capabilityFor,
7296
8172
  access
7297
8173
  ]);
7298
- if (id === null) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing });
8174
+ if (id === null) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing ?? empty });
7299
8175
  if (result === void 0) {
7300
- if (query.error != null) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing });
8176
+ if (query.error != null) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing ?? empty });
7301
8177
  return (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, {
7302
8178
  "data-buro-skeleton": "",
7303
8179
  className: "h-9 w-full"
7304
8180
  });
7305
8181
  }
7306
- if (dtoRow === void 0) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing });
8182
+ if (dtoRow === void 0) return (0, react_jsx_runtime.jsx)(Missing, { text: own.missing ?? empty });
7307
8183
  if (projected.status === "pending" || projected.value === null) return (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, {
7308
8184
  "data-buro-skeleton": "",
7309
8185
  className: "h-9 w-full"
@@ -7336,13 +8212,21 @@ function RelatedRecordView({ props }) {
7336
8212
  children: (0, _burojs_core.resolveFieldLabel)(caps, name, (0, _burojs_core.declaredText)("field", field, "label"))
7337
8213
  }), (0, react_jsx_runtime.jsx)("dd", {
7338
8214
  className: "min-w-0 break-words text-sm",
7339
- children: (0, react_jsx_runtime.jsx)(ResolvedFieldDisplay, {
7340
- resource: own.resource,
7341
- name,
7342
- field,
8215
+ children: (0, react_jsx_runtime.jsx)(FieldValueFrame, {
8216
+ field: {
8217
+ ...field ?? {},
8218
+ name
8219
+ },
7343
8220
  value,
7344
- record: row,
7345
- fallback: toDisplayText(value).text
8221
+ surface: "display",
8222
+ children: (0, react_jsx_runtime.jsx)(require_filter_builder.ResolvedFieldDisplay, {
8223
+ resource: own.resource,
8224
+ name,
8225
+ field,
8226
+ value,
8227
+ record: row,
8228
+ fallback: toDisplayText(value).text
8229
+ })
7346
8230
  })
7347
8231
  })]
7348
8232
  }, name);
@@ -7413,28 +8297,39 @@ function Chrome({ node, size, state, children }) {
7413
8297
  });
7414
8298
  }
7415
8299
  function isAuthError(error) {
7416
- return (0, _burojs_core.isDataError)(error) && error.kind === "auth";
8300
+ return (0, _burojs_core.normalizeDataError)(error).kind === "auth";
7417
8301
  }
7418
- function FailedPlacement({ node, size, message, retry }) {
8302
+ const PLACE_NODE_MESSAGES = {
8303
+ failed: "This section could not be loaded.",
8304
+ sessionExpired: "Your session has expired. Sign in again to continue.",
8305
+ notImplemented: "This section is not available."
8306
+ };
8307
+ function FailedPlacement({ node, size, message, retry, error }) {
7419
8308
  const t = (0, _burojs_react.useOptionalT)();
8309
+ const chromeT = (0, _burojs_react.useChromeT)();
8310
+ const fallback = {
8311
+ key: `block.${message}`,
8312
+ default: PLACE_NODE_MESSAGES[message]
8313
+ };
7420
8314
  return (0, react_jsx_runtime.jsx)(Chrome, {
7421
8315
  node,
7422
8316
  size,
7423
8317
  state: "failed",
7424
- children: (0, react_jsx_runtime.jsxs)("div", {
8318
+ children: message === "notImplemented" ? (0, react_jsx_runtime.jsxs)("div", {
7425
8319
  role: "alert",
7426
8320
  className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
7427
- children: [(0, react_jsx_runtime.jsx)("span", { children: t(`block.${message}`, { defaultValue: {
7428
- failed: "This section could not be loaded.",
7429
- sessionExpired: "Your session has expired. Sign in again to continue.",
7430
- notImplemented: "This section is not available."
7431
- }[message] }) }), retry === void 0 ? null : (0, react_jsx_runtime.jsx)(require_button.Button, {
8321
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : (0, react_jsx_runtime.jsx)(require_button.Button, {
7432
8322
  type: "button",
7433
8323
  variant: "outline",
7434
8324
  size: "sm",
7435
8325
  onClick: retry,
7436
- children: t("actions.retry", { defaultValue: "Retry" })
8326
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
7437
8327
  })]
8328
+ }) : (0, react_jsx_runtime.jsx)(require_relation_create.LoadError, {
8329
+ surface: "block",
8330
+ error,
8331
+ ...retry === void 0 ? {} : { retry },
8332
+ fallback
7438
8333
  })
7439
8334
  });
7440
8335
  }
@@ -7514,7 +8409,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7514
8409
  nested,
7515
8410
  ...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
7516
8411
  closure: compiled.sourceClosure,
7517
- ...owner === void 0 ? {} : { owner }
8412
+ ...owner === void 0 ? {} : { owner },
8413
+ sources: controller.sources
7518
8414
  }), [
7519
8415
  node.key,
7520
8416
  node.id,
@@ -7522,7 +8418,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
7522
8418
  nested,
7523
8419
  declaredWhere,
7524
8420
  compiled.sourceClosure,
7525
- owner
8421
+ owner,
8422
+ controller.sources
7526
8423
  ]);
7527
8424
  return (0, react_jsx_runtime.jsx)(PlacementBoundary, {
7528
8425
  node,
@@ -7580,6 +8477,7 @@ function PlaceNode({ node }) {
7580
8477
  node,
7581
8478
  size,
7582
8479
  message: isAuthError(placement.error) ? "sessionExpired" : "failed",
8480
+ error: placement.error,
7583
8481
  ...placement.retry === void 0 ? {} : { retry: placement.retry }
7584
8482
  });
7585
8483
  if (implementation === void 0) return (0, react_jsx_runtime.jsx)(FailedPlacement, {
@@ -7715,6 +8613,12 @@ Object.defineProperty(exports, "FieldDisplay", {
7715
8613
  return FieldDisplay;
7716
8614
  }
7717
8615
  });
8616
+ Object.defineProperty(exports, "FieldValueFrame", {
8617
+ enumerable: true,
8618
+ get: function() {
8619
+ return FieldValueFrame;
8620
+ }
8621
+ });
7718
8622
  Object.defineProperty(exports, "FilterRegion", {
7719
8623
  enumerable: true,
7720
8624
  get: function() {
@@ -7727,6 +8631,12 @@ Object.defineProperty(exports, "FormActions", {
7727
8631
  return FormActions;
7728
8632
  }
7729
8633
  });
8634
+ Object.defineProperty(exports, "FormControlProvider", {
8635
+ enumerable: true,
8636
+ get: function() {
8637
+ return FormControlProvider;
8638
+ }
8639
+ });
7730
8640
  Object.defineProperty(exports, "FrameChromeContext", {
7731
8641
  enumerable: true,
7732
8642
  get: function() {
@@ -7847,12 +8757,6 @@ Object.defineProperty(exports, "RelatedItem", {
7847
8757
  return RelatedItem;
7848
8758
  }
7849
8759
  });
7850
- Object.defineProperty(exports, "ResolvedFieldDisplay", {
7851
- enumerable: true,
7852
- get: function() {
7853
- return ResolvedFieldDisplay;
7854
- }
7855
- });
7856
8760
  Object.defineProperty(exports, "ResourceList", {
7857
8761
  enumerable: true,
7858
8762
  get: function() {
@@ -7973,12 +8877,6 @@ Object.defineProperty(exports, "listTarget", {
7973
8877
  return listTarget;
7974
8878
  }
7975
8879
  });
7976
- Object.defineProperty(exports, "lowerLegacyField", {
7977
- enumerable: true,
7978
- get: function() {
7979
- return lowerLegacyField;
7980
- }
7981
- });
7982
8880
  Object.defineProperty(exports, "peekFieldDefinition", {
7983
8881
  enumerable: true,
7984
8882
  get: function() {
@@ -8021,6 +8919,12 @@ Object.defineProperty(exports, "resolveFrameView", {
8021
8919
  return resolveFrameView;
8022
8920
  }
8023
8921
  });
8922
+ Object.defineProperty(exports, "resolveResourceTitle", {
8923
+ enumerable: true,
8924
+ get: function() {
8925
+ return resolveResourceTitle;
8926
+ }
8927
+ });
8024
8928
  Object.defineProperty(exports, "resolveUpdater", {
8025
8929
  enumerable: true,
8026
8930
  get: function() {
@@ -8087,6 +8991,18 @@ Object.defineProperty(exports, "useExportRunWatcher", {
8087
8991
  return useExportRunWatcher;
8088
8992
  }
8089
8993
  });
8994
+ Object.defineProperty(exports, "useFormControl", {
8995
+ enumerable: true,
8996
+ get: function() {
8997
+ return useFormControl;
8998
+ }
8999
+ });
9000
+ Object.defineProperty(exports, "useFormControlOf", {
9001
+ enumerable: true,
9002
+ get: function() {
9003
+ return useFormControlOf;
9004
+ }
9005
+ });
8090
9006
  Object.defineProperty(exports, "useFrameChrome", {
8091
9007
  enumerable: true,
8092
9008
  get: function() {
@@ -8111,6 +9027,12 @@ Object.defineProperty(exports, "usePaletteState", {
8111
9027
  return usePaletteState;
8112
9028
  }
8113
9029
  });
9030
+ Object.defineProperty(exports, "useRelationPicker", {
9031
+ enumerable: true,
9032
+ get: function() {
9033
+ return useRelationPicker;
9034
+ }
9035
+ });
8114
9036
  Object.defineProperty(exports, "useShell", {
8115
9037
  enumerable: true,
8116
9038
  get: function() {
@@ -8136,4 +9058,4 @@ Object.defineProperty(exports, "viewOf", {
8136
9058
  }
8137
9059
  });
8138
9060
 
8139
- //# sourceMappingURL=place-node-C5SmIAYU.cjs.map
9061
+ //# sourceMappingURL=place-node-CNb3w-c7.cjs.map