@burojs/ui-shadcn 0.5.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 (172) 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-CS_JZ4Fd.js +31 -0
  7. package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
  8. package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
  9. package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
  10. package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
  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-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +951 -605
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +618 -332
  33. package/dist/index.d.ts +618 -332
  34. package/dist/index.js +863 -522
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-B7U1OJUe.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-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/label-66W1qbke.js.map +1 -1
  41. package/dist/label-w6JYf2cF.cjs.map +1 -1
  42. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
  43. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  44. package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
  45. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  46. package/dist/markdown/index.d.cts +2 -3
  47. package/dist/markdown/index.d.ts +2 -3
  48. package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
  49. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  50. package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  51. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  52. package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
  53. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  54. package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
  55. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  56. package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
  57. package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  58. package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  59. package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  60. package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
  61. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  62. package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  63. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  64. package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
  65. package/dist/related-rows--uP2iKQC.js.map +1 -0
  66. package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
  67. package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
  68. package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
  69. package/dist/relation-create-C43cdPQt.js.map +1 -0
  70. package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
  71. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  72. package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
  73. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  74. package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
  75. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  76. package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
  77. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  78. package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
  79. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  80. package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
  81. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  82. package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
  83. package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
  84. package/dist/relation-select-B8Fq4o-t.js +48 -0
  85. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  86. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  87. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  88. package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
  89. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  90. package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
  91. package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
  92. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  93. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  94. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  95. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  96. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  97. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  98. package/dist/success-notices-LDEgEkc4.js +41 -0
  99. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  100. package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
  101. package/dist/toaster-BbByOytP.cjs.map +1 -0
  102. package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
  103. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  104. package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  105. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  106. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  107. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  108. package/dist/use-theme-DFlpXils.cjs.map +1 -1
  109. package/dist/use-theme-dnE3Od3I.js.map +1 -1
  110. package/package.json +11 -11
  111. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  112. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  113. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  114. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  115. package/dist/candidates-gate-CX2PBu6l.js +0 -63
  116. package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
  117. package/dist/candidates-gate-DiFI52r7.cjs +0 -70
  118. package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
  119. package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
  120. package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
  121. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
  122. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
  123. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
  124. package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
  125. package/dist/dialog-B_rt9S1c.js.map +0 -1
  126. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  127. package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
  128. package/dist/filter-builder-DOnaXNfP.js.map +0 -1
  129. package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
  130. package/dist/flow-runtime-D8h99FSg.js.map +0 -1
  131. package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
  132. package/dist/flow-screen-CmbARSqg.js.map +0 -1
  133. package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
  134. package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
  135. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
  136. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
  137. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
  138. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
  139. package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
  140. package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
  141. package/dist/peek-filter-CnqHwTMU.cjs +0 -51
  142. package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
  143. package/dist/peek-filter-ErLfO153.js +0 -28
  144. package/dist/peek-filter-ErLfO153.js.map +0 -1
  145. package/dist/place-node--GdsP688.js.map +0 -1
  146. package/dist/place-node-SIp4XYQH.cjs.map +0 -1
  147. package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
  148. package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
  149. package/dist/related-rows-B4levHcR.js.map +0 -1
  150. package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
  151. package/dist/relation-create-BOaUe12G.cjs +0 -362
  152. package/dist/relation-create-BOaUe12G.cjs.map +0 -1
  153. package/dist/relation-create-Bg-eEJLs.js +0 -337
  154. package/dist/relation-create-Bg-eEJLs.js.map +0 -1
  155. package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
  156. package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
  157. package/dist/relation-picker-BAY77ivO.js.map +0 -1
  158. package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
  159. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
  160. package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
  161. package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
  162. package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
  163. package/dist/search-access-B7G5oR8n.cjs.map +0 -1
  164. package/dist/search-access-CURsWn-U.js.map +0 -1
  165. package/dist/shell-BPlk8hEa.js +0 -205
  166. package/dist/shell-BPlk8hEa.js.map +0 -1
  167. package/dist/shell-DxhlsPwm.cjs +0 -271
  168. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  169. package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
  170. package/dist/toaster-CfCRLgR-.js.map +0 -1
  171. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
  172. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
@@ -1,16 +1,532 @@
1
1
  import { l as cn } from "./use-theme-dnE3Od3I.js";
2
- import { f as Popover, g as PopoverTrigger, h as PopoverContent, o as loadLanguage } from "./toaster-CfCRLgR-.js";
2
+ import { m as PopoverTrigger, p as PopoverContent, u as Popover } from "./toaster-Ct_YHGs6.js";
3
3
  import { t as Button } from "./button-MWVeZaqj.js";
4
4
  import { t as Input } from "./input-CUPStzZO.js";
5
- import { S as CommandList, b as CommandInput, f as useRowTitle, g as Command, n as narrowSearchDecl, v as CommandEmpty, x as CommandItem, y as CommandGroup } from "./search-access-CURsWn-U.js";
6
- import { EMPTY_FILTER_GROUP, convertValueForOp, isCompletePredicate, isFilterGroup, opArity, resolveEnumLabel } from "@burojs/core";
7
- import { useApp, useEnumValues, useI18nCaps, useList, useListAccess, useOptionalT, useRelatedRecord } from "@burojs/react";
8
- import { IconCheck, IconChevronDown, IconChevronUp, IconFilter, IconFolderPlus, IconLoader2, IconPlus, IconX } from "@tabler/icons-react";
5
+ import { C as CommandEmpty, D as CommandList, E as CommandItem, T as CommandInput, m as useRowTitle, o as narrowSearchDecl, w as CommandGroup, x as Command } from "./relation-create-C43cdPQt.js";
6
+ import { t as Skeleton } from "./skeleton-U2noefj5.js";
7
+ import { EMPTY_FILTER_GROUP, canonicalResourceKey, convertValueForOp, declaredText, enumValueText, formatEntityRef, isCompletePredicate, isFilterGroup, opArity, resolveEnumLabel, resolveSurfaceUi } from "@burojs/core";
8
+ import { DEFAULT_LANGUAGE_STORAGE_KEY, scopeValueOf, useApp, useChromeT, useEnumValues, useFieldDisplayText, useFieldImplementation, useFieldMeta, useFieldMetaSources, useI18nCaps, useImplementationTexts, useList, useListAccess, useLocale, useOptionalLocale, useOptionalT, useRelatedRecord, useResolvedFieldOptions } from "@burojs/react";
9
+ import { IconCheck, IconChevronDown, IconChevronUp, IconCirclePlus, IconFilter, IconFolderPlus, IconLoader2, IconPlus, IconX } from "@tabler/icons-react";
9
10
  import * as React from "react";
10
- import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
11
+ import { Fragment, createElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
11
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
13
  import { cva } from "class-variance-authority";
13
14
  import * as SelectPrimitive from "@radix-ui/react-select";
15
+ //#region src/layout/language-storage.ts
16
+ function loadLocale(storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
17
+ if (typeof window === "undefined") return void 0;
18
+ try {
19
+ return window.localStorage.getItem(storageKey) ?? void 0;
20
+ } catch {
21
+ return;
22
+ }
23
+ }
24
+ function saveLocale(code, storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
25
+ if (typeof window === "undefined") return;
26
+ try {
27
+ window.localStorage.setItem(storageKey, code);
28
+ } catch {}
29
+ }
30
+ //#endregion
31
+ //#region src/fields/legacy-field.ts
32
+ const STRING_ITEM = Object.freeze({ type: "string" });
33
+ const LEGACY_KIND_DECLARATIONS = Object.freeze({
34
+ string: { type: "string" },
35
+ text: {
36
+ type: "string",
37
+ format: "text"
38
+ },
39
+ textarea: {
40
+ type: "string",
41
+ format: "textarea"
42
+ },
43
+ code: {
44
+ type: "string",
45
+ format: "code"
46
+ },
47
+ email: {
48
+ type: "string",
49
+ format: "email"
50
+ },
51
+ url: {
52
+ type: "string",
53
+ format: "url"
54
+ },
55
+ uuid: {
56
+ type: "string",
57
+ format: "uuid"
58
+ },
59
+ number: { type: "number" },
60
+ boolean: { type: "boolean" },
61
+ date: {
62
+ type: "string",
63
+ format: "date"
64
+ },
65
+ dateTime: {
66
+ type: "string",
67
+ format: "date-time"
68
+ },
69
+ enum: {
70
+ type: "string",
71
+ format: "enum"
72
+ },
73
+ json: {
74
+ type: "object",
75
+ format: "json"
76
+ },
77
+ richText: {
78
+ type: "string",
79
+ format: "rich-text"
80
+ },
81
+ file: {
82
+ type: "array",
83
+ format: "file",
84
+ items: STRING_ITEM
85
+ },
86
+ relation: { type: "relation" },
87
+ relationMany: {
88
+ type: "array",
89
+ format: "relation",
90
+ items: STRING_ITEM
91
+ }
92
+ });
93
+ const SEMANTIC_KEYS = Object.freeze([
94
+ "type",
95
+ "format",
96
+ "meta",
97
+ "required",
98
+ "nullable",
99
+ "empty",
100
+ "default",
101
+ "constraints",
102
+ "texts",
103
+ "options",
104
+ "items",
105
+ "properties",
106
+ "additionalProperties",
107
+ "variants"
108
+ ]);
109
+ function ownData(value, key) {
110
+ if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
111
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
112
+ if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
113
+ return {
114
+ present: true,
115
+ value: descriptor.value
116
+ };
117
+ }
118
+ function ownString(value, key) {
119
+ const candidate = ownData(value, key).value;
120
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
121
+ }
122
+ function ownSelection(value, key) {
123
+ const candidate = ownData(value, key).value;
124
+ if (typeof candidate === "string") return candidate.length > 0 ? candidate : void 0;
125
+ return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0;
126
+ }
127
+ function projectUiSelection(input) {
128
+ const selection = {};
129
+ const inputId = ownSelection(input, "input");
130
+ const displayId = ownSelection(input, "display");
131
+ if (inputId !== void 0) selection.input = inputId;
132
+ if (displayId !== void 0) selection.display = displayId;
133
+ return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
134
+ }
135
+ function projectFieldDeclaration(input) {
136
+ const type = ownString(input, "type");
137
+ if (type === void 0) return void 0;
138
+ const projected = { type };
139
+ for (const key of SEMANTIC_KEYS) {
140
+ if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
141
+ const candidate = ownData(input, key);
142
+ if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
143
+ }
144
+ const ui = projectUiSelection(ownData(input, "ui").value);
145
+ if (ui !== void 0) projected.ui = ui;
146
+ if (type === "array") {
147
+ const item = projectFieldDeclaration(ownData(input, "items").value);
148
+ if (item !== void 0) projected.items = item;
149
+ }
150
+ if (type === "object") {
151
+ const properties = ownData(input, "properties").value;
152
+ if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
153
+ const output = {};
154
+ for (const key of Object.keys(properties)) {
155
+ const child = projectFieldDeclaration(ownData(properties, key).value);
156
+ if (child !== void 0) output[key] = child;
157
+ }
158
+ projected.properties = Object.freeze(output);
159
+ }
160
+ }
161
+ if (type === "union") {
162
+ const variants = ownData(input, "variants").value;
163
+ if (Array.isArray(variants)) {
164
+ const output = [];
165
+ for (let index = 0; index < variants.length; index += 1) {
166
+ const child = projectFieldDeclaration(ownData(variants, index).value);
167
+ if (child !== void 0) output.push(child);
168
+ }
169
+ projected.variants = Object.freeze(output);
170
+ }
171
+ }
172
+ return Object.freeze(projected);
173
+ }
174
+ function projectUiHints(input) {
175
+ if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
176
+ const projected = {};
177
+ for (const key of Object.keys(input)) {
178
+ const candidate = ownData(input, key);
179
+ if (candidate.present) projected[key] = candidate.value;
180
+ }
181
+ return Object.freeze(projected);
182
+ }
183
+ function enumValueOptions(field) {
184
+ const own = ownData(field, "values").value;
185
+ const values = Array.isArray(own) ? own : ownData(ownData(field, "ui").value, "values").value;
186
+ if (!Array.isArray(values)) return void 0;
187
+ const options = values.filter((value) => typeof value === "string").map((value) => Object.freeze({ value }));
188
+ return options.length === 0 ? void 0 : Object.freeze(options);
189
+ }
190
+ function lowerLegacyField(field) {
191
+ const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
192
+ if (declaration !== void 0) return declaration;
193
+ const direct = projectFieldDeclaration(field);
194
+ const kind = ownString(field, "kind");
195
+ const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
196
+ if (direct !== void 0) {
197
+ const format = direct.format ?? mapping?.format ?? kind;
198
+ return format === void 0 ? direct : Object.freeze({
199
+ ...direct,
200
+ format
201
+ });
202
+ }
203
+ const ui = projectUiHints(ownData(field, "ui").value);
204
+ const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
205
+ const format = ownString(field, "format") ?? mapping?.format ?? kind;
206
+ const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
207
+ const meta = ownData(field, "meta");
208
+ const texts = ownData(field, "texts");
209
+ const options = kind === "enum" ? enumValueOptions(field) : void 0;
210
+ const required = ownData(field, "required");
211
+ const nullable = ownData(field, "nullable");
212
+ const opts = ownData(field, "opts").value;
213
+ const optionRequired = ownData(opts, "required");
214
+ const optionNullable = ownData(opts, "nullable");
215
+ const requiredValue = required.present ? required.value : optionRequired.value;
216
+ const nullableValue = nullable.present ? nullable.value : optionNullable.value;
217
+ return Object.freeze({
218
+ type,
219
+ ...format === void 0 ? {} : { format },
220
+ ...items === void 0 ? {} : { items },
221
+ ...meta.present && meta.value !== void 0 ? { meta: meta.value } : {},
222
+ ...options === void 0 ? {} : { options },
223
+ ...texts.present && texts.value !== void 0 ? { texts: texts.value } : {},
224
+ ...requiredValue === true ? { required: true } : {},
225
+ ...nullableValue === true ? { nullable: true } : {}
226
+ });
227
+ }
228
+ function lowerLegacyFieldSource(field) {
229
+ const declaration = lowerLegacyField(field);
230
+ const ui = projectUiHints(ownData(field, "ui").value);
231
+ if (ui === void 0) return declaration;
232
+ return Object.freeze({
233
+ ...declaration,
234
+ ui
235
+ });
236
+ }
237
+ function fieldDescription(field) {
238
+ const declaration = field?.declaration;
239
+ return declaredText("field", declaration, "description") ?? declaredText("field", field, "description");
240
+ }
241
+ //#endregion
242
+ //#region src/fields/resolved-field.tsx
243
+ const EMPTY = Object.freeze({});
244
+ const EMPTY_ISSUES = Object.freeze([]);
245
+ const EMPTY_OPTIONS = Object.freeze([]);
246
+ function canonical(field) {
247
+ return lowerLegacyField(field);
248
+ }
249
+ function useLoweredField(source) {
250
+ return useMemo(() => {
251
+ const sourceAtEdge = lowerLegacyFieldSource(source);
252
+ return {
253
+ sourceAtEdge,
254
+ field: canonical(sourceAtEdge),
255
+ ui: sourceAtEdge.ui ?? EMPTY
256
+ };
257
+ }, [source]);
258
+ }
259
+ const warnedObjectLabels = new Set();
260
+ function optionsFor(field, name) {
261
+ if (Array.isArray(field.options)) return field.options;
262
+ const candidate = field.meta?.options;
263
+ if (!Array.isArray(candidate)) return EMPTY_OPTIONS;
264
+ const output = [];
265
+ for (const item of candidate) {
266
+ if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
267
+ const raw = item;
268
+ if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
269
+ const { value, label, description, ...legacyMeta } = raw;
270
+ if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
271
+ warnedObjectLabels.add(`${name}:${value}`);
272
+ 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).`);
273
+ }
274
+ output.push(Object.freeze({
275
+ value,
276
+ label: typeof label === "string" ? label : String(value),
277
+ ...typeof description === "string" ? { description } : {},
278
+ ...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
279
+ }));
280
+ }
281
+ return Object.freeze(output);
282
+ }
283
+ function useContextOptions(field, name, meta, ui, resource) {
284
+ const legacy = field.meta?.options;
285
+ const options = useMemo(() => optionsFor(field, name), [
286
+ field.options,
287
+ legacy,
288
+ name
289
+ ]);
290
+ return useResolvedFieldOptions({
291
+ name,
292
+ meta,
293
+ ui,
294
+ texts: field.texts
295
+ }, options, resource.id);
296
+ }
297
+ function useContextTexts(field, name, resolved, resource) {
298
+ return useImplementationTexts({
299
+ fieldName: name,
300
+ fieldTexts: field.texts,
301
+ id: resolved.id,
302
+ implementation: resolved.implementation
303
+ }, resource.id);
304
+ }
305
+ function useFieldRuntimeContext(resource, record, values, metaSources) {
306
+ const locale = useLocale();
307
+ const caps = useI18nCaps(canonicalResourceKey(resource));
308
+ const metaSourcesOut = useFieldMetaSources({
309
+ record,
310
+ values,
311
+ extra: metaSources
312
+ });
313
+ const scopeState = metaSourcesOut.scope;
314
+ const scope = scopeState === void 0 ? void 0 : scopeValueOf(scopeState);
315
+ return useMemo(() => Object.freeze({
316
+ i18n: Object.freeze({
317
+ locale,
318
+ has: caps.has,
319
+ t: caps.t
320
+ }),
321
+ sources: Object.freeze({
322
+ record: record ?? EMPTY,
323
+ values: values ?? EMPTY,
324
+ scope: scope ?? EMPTY
325
+ }),
326
+ metaSources: metaSourcesOut
327
+ }), [
328
+ caps,
329
+ locale,
330
+ record,
331
+ scope,
332
+ values,
333
+ metaSourcesOut
334
+ ]);
335
+ }
336
+ function useFieldMetaDiagnostic(state, resource, name, field, surface, entry) {
337
+ const reported = useRef(void 0);
338
+ const reason = state.status === "failed" ? state.reason : void 0;
339
+ useEffect(() => {
340
+ if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
341
+ reported.current = reason;
342
+ const implementation = resolveSurfaceUi(field, surface, entry).use ?? field.format ?? field.type;
343
+ console.error(`[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`);
344
+ }, [
345
+ reason,
346
+ resource,
347
+ name,
348
+ field,
349
+ surface,
350
+ entry
351
+ ]);
352
+ }
353
+ function MetaPlaceholder({ state }) {
354
+ const t = useOptionalT();
355
+ if (state.status === "pending") return jsx(Skeleton, {
356
+ "data-buro-skeleton": "",
357
+ "data-buro-field-meta": "pending",
358
+ className: "h-9 w-full"
359
+ });
360
+ return jsx("div", {
361
+ role: "alert",
362
+ "data-buro-field-meta": "failed",
363
+ className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
364
+ children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
365
+ });
366
+ }
367
+ function useFieldImplementationDiagnostic(diagnostic) {
368
+ const reported = useRef(void 0);
369
+ useEffect(() => {
370
+ if (diagnostic === void 0 || reported.current === diagnostic || process.env.NODE_ENV === "production") return;
371
+ reported.current = diagnostic;
372
+ console.warn("[buro/ui-shadcn] missing-field-implementation; rendering fallback.", diagnostic);
373
+ }, [diagnostic]);
374
+ }
375
+ function ResolvedFieldDisplay({ resource, name, field: source, value, record, testId, fallback, presentation, metaSources, uiSurface = "display", columnEntry }) {
376
+ const { sourceAtEdge, field, ui } = useLoweredField(source);
377
+ const surfaceField = useMemo(() => sourceAtEdge.ui === void 0 ? field : Object.freeze({
378
+ ...field,
379
+ ui
380
+ }), [
381
+ sourceAtEdge,
382
+ field,
383
+ ui
384
+ ]);
385
+ const resolved = useFieldImplementation({
386
+ field: surfaceField,
387
+ surface: "display",
388
+ uiSurface,
389
+ ...columnEntry === void 0 ? {} : { uiEntry: columnEntry }
390
+ });
391
+ const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
392
+ const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources, resolved.params);
393
+ const textField = useMemo(() => Object.freeze({
394
+ ...surfaceField,
395
+ name
396
+ }), [surfaceField, name]);
397
+ const text = useFieldDisplayText(textField, value, {
398
+ surface: uiSurface,
399
+ ...columnEntry === void 0 ? {} : { entry: columnEntry },
400
+ resource: canonicalResourceKey(resource)
401
+ });
402
+ const options = useContextOptions(field, name, metaState.status === "ready" ? metaState.meta : void 0, ui, resource);
403
+ const texts = useContextTexts(field, name, resolved, resource);
404
+ useFieldMetaDiagnostic(metaState, resource, name, surfaceField, uiSurface, columnEntry);
405
+ useFieldImplementationDiagnostic(resolved.diagnostic);
406
+ if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
407
+ if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
408
+ const Component = resolved.Component;
409
+ const context = {
410
+ owner: resource,
411
+ resource,
412
+ field,
413
+ path: Object.freeze([name]),
414
+ surface: "display",
415
+ presentation: presentation ?? "default",
416
+ record,
417
+ meta: metaState.meta,
418
+ ui,
419
+ options,
420
+ texts,
421
+ ...text === void 0 ? {} : { text },
422
+ i18n: runtimeContext.i18n,
423
+ sources: runtimeContext.sources,
424
+ pending: false,
425
+ touched: false,
426
+ issues: EMPTY_ISSUES,
427
+ accessibility: EMPTY,
428
+ testIds: testId === void 0 ? EMPTY : Object.freeze({ display: testId })
429
+ };
430
+ if (resolved.multiple) return createElement(Component, {
431
+ ...context,
432
+ multiple: true,
433
+ value: Array.isArray(value) ? value : []
434
+ });
435
+ return createElement(Component, {
436
+ ...context,
437
+ multiple: false,
438
+ value
439
+ });
440
+ }
441
+ function ResolvedFieldInput({ resource, name, field: source, value, record, form, testId, inputId, onChange, onBlur, disabled, required, placeholder, descriptionId, labelId, errorId, errorMessage, pending = false, touched = errorMessage != null, issues: suppliedIssues, relationSeeds, fixedFilters, link, metaSources, fallback }) {
442
+ const { sourceAtEdge, field: base } = useLoweredField(source);
443
+ const field = useMemo(() => required === void 0 || required === base.required ? base : Object.freeze({
444
+ ...base,
445
+ required
446
+ }), [base, required]);
447
+ const ui = useMemo(() => Object.freeze({
448
+ ...sourceAtEdge.ui ?? EMPTY,
449
+ ...placeholder === void 0 ? {} : { placeholder }
450
+ }), [sourceAtEdge, placeholder]);
451
+ const inputField = useMemo(() => ({
452
+ ...field,
453
+ ui
454
+ }), [field, ui]);
455
+ const resolved = useFieldImplementation({
456
+ field: inputField,
457
+ surface: "input"
458
+ });
459
+ const runtimeContext = useFieldRuntimeContext(resource, record, form, metaSources);
460
+ const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources, resolved.params);
461
+ const options = useContextOptions(field, name, metaState.status === "ready" ? metaState.meta : void 0, ui, resource);
462
+ const texts = useContextTexts(field, name, resolved, resource);
463
+ useFieldMetaDiagnostic(metaState, resource, name, inputField, "input");
464
+ useFieldImplementationDiagnostic(resolved.diagnostic);
465
+ if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
466
+ if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
467
+ const Component = resolved.Component;
468
+ const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES : Object.freeze([Object.freeze({
469
+ code: "ui.field-error",
470
+ message: {
471
+ key: "ui.field-error",
472
+ default: errorMessage
473
+ },
474
+ path: Object.freeze([]),
475
+ source: "field",
476
+ severity: "error"
477
+ })]));
478
+ const context = {
479
+ owner: resource,
480
+ resource,
481
+ field,
482
+ path: Object.freeze([name]),
483
+ surface: "input",
484
+ presentation: "default",
485
+ record,
486
+ form,
487
+ meta: metaState.meta,
488
+ ui,
489
+ options,
490
+ texts,
491
+ i18n: runtimeContext.i18n,
492
+ sources: runtimeContext.sources,
493
+ pending,
494
+ touched,
495
+ issues,
496
+ accessibility: Object.freeze({
497
+ input: inputId ?? name,
498
+ label: labelId,
499
+ description: descriptionId,
500
+ error: errorId
501
+ }),
502
+ testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
503
+ };
504
+ if (resolved.multiple) return createElement(Component, {
505
+ ...context,
506
+ multiple: true,
507
+ value: Array.isArray(value) ? value : [],
508
+ onChange,
509
+ onBlur,
510
+ disabled,
511
+ relationSeeds,
512
+ fixedFilters,
513
+ link,
514
+ metaSources: runtimeContext.metaSources
515
+ });
516
+ return createElement(Component, {
517
+ ...context,
518
+ multiple: false,
519
+ value,
520
+ onChange,
521
+ onBlur,
522
+ disabled,
523
+ relationSeeds,
524
+ fixedFilters,
525
+ link,
526
+ metaSources: runtimeContext.metaSources
527
+ });
528
+ }
529
+ //#endregion
14
530
  //#region src/primitives/badge.tsx
15
531
  const badgeVariants = cva("inline-flex max-w-full items-center truncate rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2", {
16
532
  variants: { variant: {
@@ -88,11 +604,11 @@ const SelectGroup = SelectPrimitive.Group;
88
604
  const SelectValue = SelectPrimitive.Value;
89
605
  const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => jsxs(SelectPrimitive.Trigger, {
90
606
  ref,
91
- className: cn("flex h-9 w-full items-center justify-between rounded-md border border-input bg-background text-foreground px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled [&>span]:line-clamp-1", className),
607
+ className: cn("flex h-9 w-full items-center justify-between rounded-md border border-input bg-background text-foreground px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled [&>span]:block [&>span]:truncate [&>span]:min-w-0 [&>span]:max-h-[1lh] [&>span]:overflow-hidden [&>span]:text-left", className),
92
608
  ...props,
93
609
  children: [children, jsx(SelectPrimitive.Icon, {
94
610
  asChild: true,
95
- children: jsx(IconChevronDown, { className: "h-4 w-4 opacity-50" })
611
+ children: jsx(IconChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })
96
612
  })]
97
613
  }));
98
614
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
@@ -131,15 +647,24 @@ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => jsx(Selec
131
647
  ...props
132
648
  }));
133
649
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
134
- const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => jsxs(SelectPrimitive.Item, {
135
- ref,
136
- className: cn("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
137
- ...props,
138
- children: [jsx("span", {
139
- className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center",
140
- children: jsx(SelectPrimitive.ItemIndicator, { children: jsx(IconCheck, { className: "h-4 w-4" }) })
141
- }), jsx(SelectPrimitive.ItemText, { children })]
142
- }));
650
+ const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => {
651
+ const hasDescription = description !== void 0 && description !== null;
652
+ return jsxs(SelectPrimitive.Item, {
653
+ ref,
654
+ className: cn("relative flex w-full cursor-default select-none rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", hasDescription ? "items-start" : "items-center", className),
655
+ ...props,
656
+ children: [jsx("span", {
657
+ className: cn("absolute left-2 flex h-3.5 w-3.5 items-center justify-center", hasDescription && "top-[9px]"),
658
+ children: jsx(SelectPrimitive.ItemIndicator, { children: jsx(IconCheck, { className: "h-4 w-4" }) })
659
+ }), !hasDescription ? jsx(SelectPrimitive.ItemText, { children }) : jsxs("span", {
660
+ className: "flex min-w-0 flex-col gap-0.5",
661
+ children: [jsx(SelectPrimitive.ItemText, { children }), jsx("span", {
662
+ className: "text-xs leading-snug text-muted-foreground",
663
+ children: description
664
+ })]
665
+ })]
666
+ });
667
+ });
143
668
  SelectItem.displayName = SelectPrimitive.Item.displayName;
144
669
  const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(SelectPrimitive.Separator, {
145
670
  ref,
@@ -149,13 +674,8 @@ const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(S
149
674
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
150
675
  //#endregion
151
676
  //#region src/fields/format.ts
152
- const LOCALE_TAG = {
153
- ru: "ru-RU",
154
- en: "en-US"
155
- };
156
677
  function localeTag(locale) {
157
- if (locale) return LOCALE_TAG[locale] ?? locale;
158
- return LOCALE_TAG[loadLanguage()] ?? "en-US";
678
+ return locale || loadLocale() || "en-US";
159
679
  }
160
680
  const dateCache = new Map();
161
681
  const dateTimeCache = new Map();
@@ -197,9 +717,9 @@ function formatNumber(n, locale) {
197
717
  //#endregion
198
718
  //#region src/list-kit/filter-builder/describe-row.ts
199
719
  const MAX_DESCRIBE_DEPTH = 2;
200
- function describeValue(value, t, spec, formatValue) {
720
+ function describeValue(value, chromeT, spec, formatValue) {
201
721
  if (value === void 0) return null;
202
- if (Array.isArray(value)) return t("filters.selectedCount", { count: value.length }, `${value.length} selected`);
722
+ if (Array.isArray(value)) return chromeT("filters.selectedCount", { count: value.length }, `${value.length} selected`);
203
723
  const optionLabel = spec?.options?.find((o) => o.value === String(value))?.label;
204
724
  if (optionLabel !== void 0) return optionLabel;
205
725
  if (spec && formatValue) {
@@ -208,20 +728,20 @@ function describeValue(value, t, spec, formatValue) {
208
728
  }
209
729
  return String(value);
210
730
  }
211
- function describeAt(row, available, t, depth, formatValue) {
731
+ function describeAt(row, available, chromeT, depth, formatValue) {
212
732
  if (isFilterGroup(row)) {
213
733
  if (depth > MAX_DESCRIBE_DEPTH) return "…";
214
- const joinLabel = t(`filters.${row.join}`, void 0, row.join);
215
- return `(${row.rows.map((child) => describeAt(child, available, t, depth + 1, formatValue)).join(` ${joinLabel} `)})`;
734
+ const joinLabel = chromeT(`filters.${row.join}`, void 0, row.join);
735
+ return `(${row.rows.map((child) => describeAt(child, available, chromeT, depth + 1, formatValue)).join(` ${joinLabel} `)})`;
216
736
  }
217
737
  const spec = available.find((s) => s.field === row.field);
218
738
  const label = spec?.label ?? row.field;
219
- const opLabel = t(`filters.operators.${row.op}`, void 0, row.op);
220
- const valueText = opArity(row.op) === "none" ? null : describeValue(row.value, t, spec, formatValue);
739
+ const opLabel = chromeT(`filters.operators.${row.op}`, void 0, row.op);
740
+ const valueText = opArity(row.op) === "none" ? null : describeValue(row.value, chromeT, spec, formatValue);
221
741
  return valueText === null ? `${label} ${opLabel}` : `${label} ${opLabel} ${valueText}`;
222
742
  }
223
- function describeRow(row, available, t, formatValue) {
224
- return describeAt(row, available, t, 1, formatValue);
743
+ function describeRow(row, available, chromeT, formatValue) {
744
+ return describeAt(row, available, chromeT, 1, formatValue);
225
745
  }
226
746
  function relationLabelKey(to, by, value) {
227
747
  return `${by === void 0 ? to : `${to}#${by}`}::${value}`;
@@ -265,15 +785,17 @@ function EnumLabelResolver(props) {
265
785
  const { enumName, onResolve } = props;
266
786
  const { values } = useEnumValues(enumName);
267
787
  const caps = useI18nCaps();
788
+ const def = useApp().registry.enums.get(enumName);
268
789
  useEffect(() => {
269
790
  if (!values) return;
270
791
  const labels = {};
271
- for (const entry of values) labels[entry.value] = resolveEnumLabel(caps, enumName, entry.value, entry.label);
792
+ for (const entry of values) labels[entry.value] = resolveEnumLabel(caps, enumName, entry.value, enumValueText(def, entry.value) ?? entry.label);
272
793
  onResolve(enumName, labels);
273
794
  }, [
274
795
  values,
275
796
  enumName,
276
797
  caps,
798
+ def,
277
799
  onResolve
278
800
  ]);
279
801
  return null;
@@ -352,15 +874,18 @@ function badgeKey(row, index) {
352
874
  if (isFilterGroup(row)) return `${index}:group:${row.join}`;
353
875
  return `${index}:${row.field}:${row.op}`;
354
876
  }
877
+ const BADGE_ROW = "inline-flex max-w-full items-center gap-1 align-middle";
878
+ const REMOVE_BUTTON = "inline-flex size-4 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-foreground/10 hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring";
355
879
  function FilterBadges(props) {
356
880
  const { group, setGroup, available, fixed, testIdBase, onOpenAt, onRemoved, pinnedFields = [], pinnedValue = [], setPinnedValue } = props;
357
- const t = useOptionalT();
358
- const tt = (key, params, fallback) => t(key, {
881
+ const chromeT = useChromeT();
882
+ const locale = useOptionalLocale();
883
+ const tt = (key, params, fallback) => chromeT(key, {
359
884
  params,
360
885
  ...typeof params?.count === "number" ? { count: params.count } : {},
361
886
  defaultValue: fallback
362
887
  });
363
- const removeLabel = t("filters.removeRow", { defaultValue: "Remove filter" });
888
+ const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
364
889
  const { enumLabels, relationLabels, onEnumResolved, onRelationResolved } = useFormatValueMaps();
365
890
  const formatValue = (kind, value) => {
366
891
  if (kind.kind === "enum") return enumLabels[kind.enumName]?.[String(value)];
@@ -368,7 +893,7 @@ function FilterBadges(props) {
368
893
  if (kind.kind === "date") {
369
894
  const d = new Date(String(value));
370
895
  if (Number.isNaN(d.getTime())) return void 0;
371
- return kind.time ? formatDateTime(d) : formatDate(d);
896
+ return kind.time ? formatDateTime(d, locale) : formatDate(d, locale);
372
897
  }
373
898
  };
374
899
  const orphanPinned = useMemo(() => {
@@ -402,49 +927,56 @@ function FilterBadges(props) {
402
927
  onEnumResolved,
403
928
  onRelationResolved
404
929
  }),
405
- group.rows.map((row, index) => jsxs(Badge, {
930
+ group.rows.map((row, index) => jsx(Badge, {
406
931
  variant: "secondary",
407
- className: "gap-1 py-1 pr-1",
408
- children: [jsx("button", {
409
- type: "button",
410
- "data-testid": `${testIdBase}:badge:${index}`,
411
- onClick: () => onOpenAt([index]),
412
- className: "max-w-full cursor-pointer truncate bg-transparent p-0 text-left",
413
- children: describeRow(row, available, tt, formatValue)
414
- }), jsx("button", {
415
- type: "button",
416
- "data-testid": `${testIdBase}:badge:${index}:remove`,
417
- "aria-label": removeLabel,
418
- onClick: () => handleRemove(index),
419
- className: "rounded-sm hover:bg-accent",
420
- children: jsx(IconX, {
421
- className: "h-3 w-3",
422
- "aria-hidden": "true"
423
- })
424
- })]
932
+ className: "py-1 pr-1",
933
+ children: jsxs("span", {
934
+ className: BADGE_ROW,
935
+ children: [jsx("button", {
936
+ type: "button",
937
+ "data-testid": `${testIdBase}:badge:${index}`,
938
+ onClick: () => onOpenAt([index]),
939
+ className: "min-w-0 cursor-pointer truncate bg-transparent p-0 text-left",
940
+ children: describeRow(row, available, tt, formatValue)
941
+ }), jsx("button", {
942
+ type: "button",
943
+ "data-testid": `${testIdBase}:badge:${index}:remove`,
944
+ "aria-label": removeLabel,
945
+ onClick: () => handleRemove(index),
946
+ className: REMOVE_BUTTON,
947
+ children: jsx(IconX, {
948
+ className: "size-3",
949
+ "aria-hidden": "true"
950
+ })
951
+ })]
952
+ })
425
953
  }, badgeKey(row, index))),
426
954
  labelledFixed.map((entry, index) => jsx(Badge, {
427
955
  variant: "outline",
428
956
  "data-testid": `${testIdBase}:badge:fixed:${index}`,
429
957
  children: entry.label
430
958
  }, `fixed-${index}`)),
431
- orphanPinned.map(({ expr, index }) => jsxs(Badge, {
959
+ orphanPinned.map(({ expr, index }) => jsx(Badge, {
432
960
  variant: "secondary",
433
- className: "gap-1 py-1",
434
- children: [jsx("span", {
435
- "data-testid": `${testIdBase}:badge:pinned:${index}`,
436
- children: describeRow(expr, available, tt, formatValue)
437
- }), jsx("button", {
438
- type: "button",
439
- "data-testid": `${testIdBase}:badge:pinned:${index}:remove`,
440
- "aria-label": removeLabel,
441
- onClick: () => handleRemovePinned(index),
442
- className: "rounded-sm hover:bg-accent",
443
- children: jsx(IconX, {
444
- className: "h-3 w-3",
445
- "aria-hidden": "true"
446
- })
447
- })]
961
+ className: "py-1 pr-1",
962
+ children: jsxs("span", {
963
+ className: BADGE_ROW,
964
+ children: [jsx("span", {
965
+ "data-testid": `${testIdBase}:badge:pinned:${index}`,
966
+ className: "min-w-0 truncate",
967
+ children: describeRow(expr, available, tt, formatValue)
968
+ }), jsx("button", {
969
+ type: "button",
970
+ "data-testid": `${testIdBase}:badge:pinned:${index}:remove`,
971
+ "aria-label": removeLabel,
972
+ onClick: () => handleRemovePinned(index),
973
+ className: REMOVE_BUTTON,
974
+ children: jsx(IconX, {
975
+ className: "size-3",
976
+ "aria-hidden": "true"
977
+ })
978
+ })]
979
+ })
448
980
  }, `pinned-${index}`))
449
981
  ]
450
982
  });
@@ -634,14 +1166,15 @@ const OPTIONS = ["true", "false"];
634
1166
  function BooleanValueControl(props) {
635
1167
  const { value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
636
1168
  const t = useOptionalT();
637
- const ariaLabel = fieldLabel ? t("filters.valueFor", {
1169
+ const chromeT = useChromeT();
1170
+ const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
638
1171
  defaultValue: `Value for ${fieldLabel}`,
639
1172
  params: { field: fieldLabel }
640
- }) : t("filters.value", { defaultValue: "Value" });
1173
+ }) : chromeT("filters.value", { defaultValue: "Value" });
641
1174
  const yesLabel = t("boolean.yes", { defaultValue: "Yes" });
642
1175
  const noLabel = t("boolean.no", { defaultValue: "No" });
643
1176
  const labelOf = (token) => token === "true" ? yesLabel : noLabel;
644
- const clearLabel = t("filters.clear", { defaultValue: "Clear" });
1177
+ const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
645
1178
  const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
646
1179
  value,
647
1180
  arity,
@@ -661,7 +1194,7 @@ function BooleanValueControl(props) {
661
1194
  "data-testid": testId,
662
1195
  "aria-label": ariaLabel,
663
1196
  className: cn("h-8 justify-between", fill && "w-full"),
664
- children: triggerLabel(labelOf, ariaLabel)
1197
+ children: triggerLabel(labelOf, props.placeholder ?? ariaLabel)
665
1198
  })
666
1199
  }), jsx(PopoverContent, {
667
1200
  className: "w-[140px] p-0",
@@ -694,12 +1227,13 @@ function BooleanValueControl(props) {
694
1227
  function SelectListValueControl(props) {
695
1228
  const { options, value, onChange, testId, arity, autoFocus, fieldLabel, isLoading = false } = props;
696
1229
  const t = useOptionalT();
697
- const ariaLabel = fieldLabel ? t("filters.valueFor", {
1230
+ const chromeT = useChromeT();
1231
+ const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
698
1232
  defaultValue: `Value for ${fieldLabel}`,
699
1233
  params: { field: fieldLabel }
700
- }) : t("filters.value", { defaultValue: "Value" });
1234
+ }) : chromeT("filters.value", { defaultValue: "Value" });
701
1235
  const loadingLabel = t("table.loading", { defaultValue: "Loading…" });
702
- const clearLabel = t("filters.clear", { defaultValue: "Clear" });
1236
+ const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
703
1237
  const labelFor = props.labelFor ?? ((raw) => options.find((o) => o.value === raw)?.label ?? raw);
704
1238
  const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
705
1239
  value,
@@ -721,7 +1255,7 @@ function SelectListValueControl(props) {
721
1255
  className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[200px]"),
722
1256
  children: jsx("span", {
723
1257
  className: "truncate",
724
- children: isLoading ? loadingLabel : triggerLabel(labelFor, ariaLabel)
1258
+ children: isLoading ? loadingLabel : triggerLabel(labelFor, props.placeholder ?? ariaLabel)
725
1259
  })
726
1260
  })
727
1261
  }), jsx(PopoverContent, {
@@ -770,8 +1304,9 @@ function SelectListValueControl(props) {
770
1304
  function EnumValueControl(props) {
771
1305
  const { enumName, value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
772
1306
  const caps = useI18nCaps();
1307
+ const def = useApp().registry.enums.get(enumName);
773
1308
  const { values, isLoading } = useEnumValues(enumName);
774
- const labelFor = (raw) => resolveEnumLabel(caps, enumName, raw, values?.find((v) => v.value === raw)?.label);
1309
+ const labelFor = (raw) => resolveEnumLabel(caps, enumName, raw, enumValueText(def, raw) ?? values?.find((v) => v.value === raw)?.label);
775
1310
  const options = (values ?? []).map((entry) => ({
776
1311
  value: entry.value,
777
1312
  label: entry.value
@@ -783,6 +1318,7 @@ function EnumValueControl(props) {
783
1318
  arity,
784
1319
  autoFocus,
785
1320
  fieldLabel,
1321
+ placeholder: props.placeholder,
786
1322
  options,
787
1323
  labelFor,
788
1324
  isLoading,
@@ -807,6 +1343,7 @@ function useRelatedLabels(to, ids, by) {
807
1343
  op: "in",
808
1344
  value: ids
809
1345
  } } : {},
1346
+ scope: "none",
810
1347
  enabled: ids.length > 0
811
1348
  });
812
1349
  return useMemo(() => {
@@ -828,6 +1365,7 @@ function RelationValueControl(props) {
828
1365
  const { to, by, value, onChange, testId, arity, autoFocus, fieldLabel } = props;
829
1366
  const app = useApp();
830
1367
  const t = useOptionalT();
1368
+ const chromeT = useChromeT();
831
1369
  const targetDef = app.registry.get(to);
832
1370
  const displayField = targetDef?.displayField ?? "name";
833
1371
  const [search, setSearch] = useState("");
@@ -850,6 +1388,7 @@ function RelationValueControl(props) {
850
1388
  op: "contains",
851
1389
  value: search.trim()
852
1390
  } } : {},
1391
+ scope: "session",
853
1392
  enabled: open && access.status === "ready"
854
1393
  });
855
1394
  const rows = listQuery.data?.data ?? [];
@@ -865,11 +1404,11 @@ function RelationValueControl(props) {
865
1404
  if (fromRows) return rowTitle(fromRows);
866
1405
  return fallbackLabels[id] ?? id;
867
1406
  };
868
- const ariaLabel = fieldLabel ? t("filters.valueFor", {
1407
+ const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
869
1408
  defaultValue: `Value for ${fieldLabel}`,
870
1409
  params: { field: fieldLabel }
871
- }) : t("filters.value", { defaultValue: "Value" });
872
- const clearLabel = t("filters.clear", { defaultValue: "Clear" });
1410
+ }) : chromeT("filters.value", { defaultValue: "Value" });
1411
+ const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
873
1412
  return jsxs(Popover, {
874
1413
  open,
875
1414
  onOpenChange: setOpen,
@@ -885,7 +1424,7 @@ function RelationValueControl(props) {
885
1424
  className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[220px]"),
886
1425
  children: jsx("span", {
887
1426
  className: "truncate",
888
- children: triggerLabel(labelFor, ariaLabel)
1427
+ children: triggerLabel(labelFor, props.placeholder ?? ariaLabel)
889
1428
  })
890
1429
  })
891
1430
  }), jsx(PopoverContent, {
@@ -941,6 +1480,20 @@ function RelationValueControl(props) {
941
1480
  });
942
1481
  }
943
1482
  //#endregion
1483
+ //#region src/fields/local-date-time.ts
1484
+ function toLocalDateTimeText(value) {
1485
+ if (value === void 0 || value === null || value === "") return "";
1486
+ const d = value instanceof Date ? value : new Date(String(value));
1487
+ if (Number.isNaN(d.getTime())) return "";
1488
+ const pad = (n) => String(n).padStart(2, "0");
1489
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
1490
+ }
1491
+ function fromLocalDateTimeText(text) {
1492
+ if (text === "") return void 0;
1493
+ const d = new Date(text);
1494
+ return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
1495
+ }
1496
+ //#endregion
944
1497
  //#region src/list-kit/filter-builder/values/scalar-value-input.tsx
945
1498
  function isDateTime(kind) {
946
1499
  return kind.kind === "date" && kind.time === true;
@@ -953,12 +1506,9 @@ function inputTypeFor(kind) {
953
1506
  }
954
1507
  }
955
1508
  function toText(kind, value) {
1509
+ if (isDateTime(kind)) return toLocalDateTimeText(value);
956
1510
  if (value === void 0 || value === null || value === "") return "";
957
- if (!isDateTime(kind)) return String(value);
958
- const d = new Date(String(value));
959
- if (Number.isNaN(d.getTime())) return "";
960
- const pad = (n) => String(n).padStart(2, "0");
961
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
1511
+ return String(value);
962
1512
  }
963
1513
  function parseOne(kind, raw) {
964
1514
  if (raw === "") return void 0;
@@ -966,19 +1516,16 @@ function parseOne(kind, raw) {
966
1516
  const n = Number(raw);
967
1517
  return Number.isFinite(n) ? n : void 0;
968
1518
  }
969
- if (isDateTime(kind)) {
970
- const d = new Date(raw);
971
- return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
972
- }
1519
+ if (isDateTime(kind)) return fromLocalDateTimeText(raw);
973
1520
  return raw;
974
1521
  }
975
1522
  function ScalarValueInput(props) {
976
- const t = useOptionalT();
1523
+ const chromeT = useChromeT();
977
1524
  const inputType = inputTypeFor(props.kind);
978
- const ariaLabel = props.fieldLabel ? t("filters.valueFor", {
1525
+ const ariaLabel = props.fieldLabel ? chromeT("filters.valueFor", {
979
1526
  defaultValue: `Value for ${props.fieldLabel}`,
980
1527
  params: { field: props.fieldLabel }
981
- }) : t("filters.value", { defaultValue: "Value" });
1528
+ }) : chromeT("filters.value", { defaultValue: "Value" });
982
1529
  if (props.arity === "multi") return jsx(ScalarMultiInput, {
983
1530
  ...props,
984
1531
  inputType,
@@ -1108,6 +1655,7 @@ function FilterValueControl(props) {
1108
1655
  arity,
1109
1656
  autoFocus: props.autoFocus,
1110
1657
  fieldLabel: props.fieldLabel,
1658
+ placeholder: props.placeholder,
1111
1659
  options: props.options,
1112
1660
  fill: props.fill
1113
1661
  });
@@ -1124,6 +1672,7 @@ function FilterValueControl(props) {
1124
1672
  arity,
1125
1673
  autoFocus: props.autoFocus,
1126
1674
  fieldLabel: props.fieldLabel,
1675
+ placeholder: props.placeholder,
1127
1676
  options: props.kind.values.map((entry) => ({
1128
1677
  value: entry,
1129
1678
  label: entry
@@ -1152,10 +1701,10 @@ const FIELD_CLASS = "h-8 min-w-0 shrink basis-40";
1152
1701
  const OPERATOR_CLASS = "h-8 min-w-0 shrink basis-[9.5rem]";
1153
1702
  function FilterRow(props) {
1154
1703
  const { row, path, available, onPatch, onRemove, testIdBase, debounceMs } = props;
1155
- const t = useOptionalT();
1704
+ const chromeT = useChromeT();
1156
1705
  const base = `${testIdBase}:row:${path.join("-")}`;
1157
1706
  const spec = available.find((s) => s.field === row.field);
1158
- const removeLabel = t("filters.removeRow", { defaultValue: "Remove filter" });
1707
+ const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
1159
1708
  if (!spec) return jsxs("div", {
1160
1709
  className: ROW_CLASS,
1161
1710
  children: [
@@ -1195,8 +1744,8 @@ function FilterRow(props) {
1195
1744
  };
1196
1745
  const handleOpChange = (op) => onPatch(path, { op });
1197
1746
  const handleValueChange = (value) => onPatch(path, { value });
1198
- const fieldAriaLabel = t("filters.field", { defaultValue: "Field" });
1199
- const operatorAriaLabel = t("filters.operator", { defaultValue: "Operator" });
1747
+ const fieldAriaLabel = chromeT("filters.field", { defaultValue: "Field" });
1748
+ const operatorAriaLabel = chromeT("filters.operator", { defaultValue: "Operator" });
1200
1749
  return jsxs("div", {
1201
1750
  className: ROW_CLASS,
1202
1751
  children: [
@@ -1225,7 +1774,7 @@ function FilterRow(props) {
1225
1774
  }), jsx(SelectContent, { children: spec.operators.map((op) => jsx(SelectItem, {
1226
1775
  value: op,
1227
1776
  "data-testid": `${opTestId}:option:${op}`,
1228
- children: t(`filters.operators.${op}`, { defaultValue: op })
1777
+ children: chromeT(`filters.operators.${op}`, { defaultValue: op })
1229
1778
  }, op)) })]
1230
1779
  }),
1231
1780
  jsx("div", {
@@ -1273,10 +1822,10 @@ function FilterGroupEditor(props) {
1273
1822
  }
1274
1823
  function GroupEditorInner(props) {
1275
1824
  const { group, path, available, onChange, testIdBase, onRemove, addGroupAt, debounceMs } = props;
1276
- const t = useOptionalT();
1825
+ const chromeT = useChromeT();
1277
1826
  const base = `${testIdBase}:group:${path.join("-")}`;
1278
1827
  const canNestGroup = depthOf(path) < 3;
1279
- const joinLabel = (join) => t(`filters.${join}`, { defaultValue: join });
1828
+ const joinLabel = (join) => chromeT(`filters.${join}`, { defaultValue: join });
1280
1829
  const handlePatch = (rowPath, patch) => {
1281
1830
  const index = rowPath[rowPath.length - 1];
1282
1831
  if (index === void 0) return;
@@ -1309,11 +1858,11 @@ function GroupEditorInner(props) {
1309
1858
  return jsxs(Fragment, { children: [jsxs("div", {
1310
1859
  className: CONNECTOR_CLASS,
1311
1860
  children: [
1312
- index === 0 && t("filters.where", { defaultValue: "Where" }),
1861
+ index === 0 && chromeT("filters.where", { defaultValue: "Where" }),
1313
1862
  index === 1 && jsx("button", {
1314
1863
  type: "button",
1315
1864
  "data-testid": `${base}:join`,
1316
- "aria-label": t("filters.toggleJoin", {
1865
+ "aria-label": chromeT("filters.toggleJoin", {
1317
1866
  defaultValue: `Join: ${joinLabel(group.join)} — activate to switch to ${joinLabel(otherJoin)}`,
1318
1867
  params: {
1319
1868
  current: joinLabel(group.join),
@@ -1365,7 +1914,7 @@ function GroupEditorInner(props) {
1365
1914
  children: [jsx(IconPlus, {
1366
1915
  className: "h-4 w-4",
1367
1916
  "aria-hidden": "true"
1368
- }), t("filters.addRow", { defaultValue: "Add filter" })]
1917
+ }), chromeT("filters.addRow", { defaultValue: "Add filter" })]
1369
1918
  }),
1370
1919
  canNestGroup && jsxs(Button, {
1371
1920
  type: "button",
@@ -1376,7 +1925,7 @@ function GroupEditorInner(props) {
1376
1925
  children: [jsx(IconFolderPlus, {
1377
1926
  className: "h-4 w-4",
1378
1927
  "aria-hidden": "true"
1379
- }), t("filters.addGroup", { defaultValue: "Add group" })]
1928
+ }), chromeT("filters.addGroup", { defaultValue: "Add group" })]
1380
1929
  }),
1381
1930
  onRemove && jsxs(Button, {
1382
1931
  type: "button",
@@ -1387,7 +1936,7 @@ function GroupEditorInner(props) {
1387
1936
  children: [jsx(IconX, {
1388
1937
  className: "h-4 w-4",
1389
1938
  "aria-hidden": "true"
1390
- }), t("filters.removeGroup", { defaultValue: "Remove group" })]
1939
+ }), chromeT("filters.removeGroup", { defaultValue: "Remove group" })]
1391
1940
  })
1392
1941
  ]
1393
1942
  })
@@ -1403,7 +1952,7 @@ function countGroupRows(group) {
1403
1952
  }
1404
1953
  function FilterPanel(props) {
1405
1954
  const { group, setGroup, available, setPage, testIdBase, open, onOpenChange, focusPath, externalRemoval } = props;
1406
- const t = useOptionalT();
1955
+ const chromeT = useChromeT();
1407
1956
  const [draft, setDraft] = useState(() => toDraft(group));
1408
1957
  const wasOpenRef = useRef(open);
1409
1958
  const topLevelIdsRef = useRef([]);
@@ -1455,7 +2004,7 @@ function FilterPanel(props) {
1455
2004
  className: "h-4 w-4",
1456
2005
  "aria-hidden": "true"
1457
2006
  }),
1458
- t("filters.title", { defaultValue: "Filters" }),
2007
+ chromeT("filters.title", { defaultValue: "Filters" }),
1459
2008
  appliedCount > 0 && jsx("span", {
1460
2009
  "data-testid": `${testIdBase}:count`,
1461
2010
  className: "ml-1 rounded-full bg-primary px-1.5 text-xs text-primary-foreground",
@@ -1483,13 +2032,13 @@ function FilterPanel(props) {
1483
2032
  size: "sm",
1484
2033
  "data-testid": `${testIdBase}:reset`,
1485
2034
  onClick: handleReset,
1486
- children: t("filters.reset", { defaultValue: "Reset" })
2035
+ children: chromeT("filters.reset", { defaultValue: "Reset" })
1487
2036
  }), jsx(Button, {
1488
2037
  type: "button",
1489
2038
  size: "sm",
1490
2039
  "data-testid": `${testIdBase}:apply`,
1491
2040
  onClick: handleApply,
1492
- children: t("filters.apply", { defaultValue: "Apply" })
2041
+ children: chromeT("filters.apply", { defaultValue: "Apply" })
1493
2042
  })]
1494
2043
  })]
1495
2044
  })]
@@ -1544,11 +2093,204 @@ function PinnedControl(props) {
1544
2093
  });
1545
2094
  }
1546
2095
  //#endregion
2096
+ //#region src/data-table/data-table-faceted-filter.tsx
2097
+ function DataTableFacetedFilter({ column, getTestId, ...rest }) {
2098
+ const filterValue = column.getFilterValue();
2099
+ return jsx(FacetedFilterPicker, {
2100
+ ...rest,
2101
+ ...getTestId === void 0 ? {} : { getTestId: (role, value) => role === "hint" ? void 0 : getTestId(role, value) },
2102
+ selected: Array.isArray(filterValue) ? filterValue.map(String) : [],
2103
+ onChange: (next) => column.setFilterValue(next.length === 0 ? void 0 : next)
2104
+ });
2105
+ }
2106
+ function FacetedFilterPicker({ title, options = [], loadOptions, selectedLabelFor, searchPlaceholder, emptyText, getTestId, className, selected: selectedList, onChange, hint, emptyLabel, clearable = true }) {
2107
+ const t = useOptionalT();
2108
+ const chromeT = useChromeT();
2109
+ const selected = useMemo(() => new Set(selectedList), [selectedList]);
2110
+ const [search, setSearch] = useState("");
2111
+ const [asyncOptions, setAsyncOptions] = useState([]);
2112
+ const [loading, setLoading] = useState(false);
2113
+ const debounceRef = useRef(null);
2114
+ const loadOptionsRef = useRef(loadOptions);
2115
+ useEffect(() => {
2116
+ loadOptionsRef.current = loadOptions;
2117
+ });
2118
+ useEffect(() => {
2119
+ if (!loadOptionsRef.current) return;
2120
+ setLoading(true);
2121
+ if (debounceRef.current !== null) clearTimeout(debounceRef.current);
2122
+ let cancelled = false;
2123
+ debounceRef.current = setTimeout(() => {
2124
+ const loader = loadOptionsRef.current;
2125
+ if (!loader) return;
2126
+ loader(search).then((results) => {
2127
+ if (!cancelled) {
2128
+ setAsyncOptions(results);
2129
+ setLoading(false);
2130
+ }
2131
+ }).catch(() => {
2132
+ if (!cancelled) setLoading(false);
2133
+ });
2134
+ }, 250);
2135
+ return () => {
2136
+ cancelled = true;
2137
+ if (debounceRef.current !== null) clearTimeout(debounceRef.current);
2138
+ };
2139
+ }, [search]);
2140
+ const effectiveOptions = loadOptions ? asyncOptions : options;
2141
+ const labelFor = (value) => {
2142
+ const opt = effectiveOptions.find((o) => o.value === value);
2143
+ if (opt) return opt.label;
2144
+ return selectedLabelFor?.(value) ?? value;
2145
+ };
2146
+ const toggle = (value) => {
2147
+ const next = new Set(selected);
2148
+ if (next.has(value)) next.delete(value);
2149
+ else next.add(value);
2150
+ onChange(Array.from(next));
2151
+ };
2152
+ const selectedValues = Array.from(selected);
2153
+ return jsxs(Popover, { children: [jsx(PopoverTrigger, {
2154
+ asChild: true,
2155
+ children: jsxs(Button, {
2156
+ type: "button",
2157
+ variant: "outline",
2158
+ size: "sm",
2159
+ "data-testid": getTestId?.("trigger"),
2160
+ title: hint,
2161
+ className: cn("h-8 border-dashed", className),
2162
+ children: [
2163
+ jsx(IconCirclePlus, {
2164
+ className: "mr-2 h-4 w-4",
2165
+ "aria-hidden": "true"
2166
+ }),
2167
+ title,
2168
+ selected.size === 0 && emptyLabel !== void 0 && jsxs(Fragment$1, { children: [jsx("span", {
2169
+ className: "mx-2 h-4 w-px bg-border",
2170
+ "aria-hidden": "true"
2171
+ }), jsx(Badge, {
2172
+ variant: "secondary",
2173
+ className: "rounded-sm px-1 font-normal",
2174
+ children: emptyLabel
2175
+ })] }),
2176
+ selected.size > 0 && jsxs(Fragment$1, { children: [
2177
+ jsx("span", {
2178
+ className: "mx-2 h-4 w-px bg-border",
2179
+ "aria-hidden": "true"
2180
+ }),
2181
+ jsx(Badge, {
2182
+ variant: "secondary",
2183
+ className: "rounded-sm px-1 font-normal lg:hidden",
2184
+ children: selected.size
2185
+ }),
2186
+ jsx("div", {
2187
+ className: "hidden gap-1 lg:flex",
2188
+ children: selected.size > 2 ? jsx(Badge, {
2189
+ variant: "secondary",
2190
+ className: "rounded-sm px-1 font-normal",
2191
+ children: t("table.selectedCount", {
2192
+ count: selected.size,
2193
+ defaultValue: "{{count}} selected"
2194
+ })
2195
+ }) : selectedValues.map((value) => jsx(Badge, {
2196
+ variant: "secondary",
2197
+ className: "rounded-sm px-1 font-normal",
2198
+ children: labelFor(value)
2199
+ }, value))
2200
+ })
2201
+ ] })
2202
+ ]
2203
+ })
2204
+ }), jsx(PopoverContent, {
2205
+ className: "w-[220px] p-0",
2206
+ align: "start",
2207
+ children: jsxs(Command, {
2208
+ shouldFilter: !loadOptions,
2209
+ children: [
2210
+ hint !== void 0 && jsx("p", {
2211
+ "data-testid": getTestId?.("hint"),
2212
+ className: "border-b px-3 py-2 text-xs text-muted-foreground",
2213
+ children: hint
2214
+ }),
2215
+ jsx(CommandInput, {
2216
+ placeholder: searchPlaceholder ?? title,
2217
+ "data-testid": getTestId?.("search"),
2218
+ ...loadOptions ? {
2219
+ value: search,
2220
+ onValueChange: setSearch
2221
+ } : {}
2222
+ }),
2223
+ jsxs(CommandList, { children: [
2224
+ loading && jsx("div", {
2225
+ className: "py-6 text-center text-sm text-muted-foreground",
2226
+ children: t("list.loadingShort", { defaultValue: "Loading…" })
2227
+ }),
2228
+ jsx(CommandEmpty, { children: emptyText ?? t("relation.noResults", { defaultValue: "No results." }) }),
2229
+ jsx(CommandGroup, { children: effectiveOptions.map((opt) => {
2230
+ const isSelected = selected.has(opt.value);
2231
+ const Icon = opt.icon;
2232
+ return jsxs(CommandItem, {
2233
+ value: opt.label,
2234
+ "data-testid": getTestId?.("option", opt.value),
2235
+ role: "option",
2236
+ "aria-selected": isSelected,
2237
+ "aria-checked": isSelected,
2238
+ onSelect: () => toggle(opt.value),
2239
+ children: [
2240
+ jsx("span", {
2241
+ className: cn("mr-2 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border", isSelected ? "border-primary bg-primary text-primary-foreground" : "border-input opacity-50"),
2242
+ "aria-hidden": "true",
2243
+ children: isSelected && jsx(IconCheck, { className: "h-3.5 w-3.5" })
2244
+ }),
2245
+ Icon && jsx(Icon, {
2246
+ className: "mr-2 h-4 w-4 text-muted-foreground",
2247
+ "aria-hidden": "true"
2248
+ }),
2249
+ jsxs("span", {
2250
+ className: "flex min-w-0 flex-1 flex-col text-left",
2251
+ children: [jsx("span", { children: opt.label }), opt.summary !== void 0 && jsx("span", {
2252
+ className: "text-xs text-muted-foreground",
2253
+ children: opt.summary
2254
+ })]
2255
+ })
2256
+ ]
2257
+ }, opt.value);
2258
+ }) })
2259
+ ] }),
2260
+ clearable && selected.size > 0 && jsx("button", {
2261
+ type: "button",
2262
+ "data-testid": getTestId?.("clear"),
2263
+ onClick: () => onChange([]),
2264
+ className: "flex w-full items-center justify-center border-t px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
2265
+ children: chromeT("filters.clearAll", { defaultValue: "Clear filters" })
2266
+ })
2267
+ ]
2268
+ })
2269
+ })] });
2270
+ }
2271
+ //#endregion
2272
+ //#region src/list-kit/filter-builder/preset-filter-controls.tsx
2273
+ function PresetFilterControls(props) {
2274
+ const { presets, setPreset, testIdBase } = props;
2275
+ return jsx(Fragment$1, { children: presets.map((filter) => jsx(FacetedFilterPicker, {
2276
+ title: filter.label,
2277
+ ...filter.hint === void 0 ? {} : { hint: filter.hint },
2278
+ options: filter.options.map((option) => ({
2279
+ value: option.id,
2280
+ label: option.label,
2281
+ ...option.summary === void 0 ? {} : { summary: option.summary }
2282
+ })),
2283
+ selected: filter.selected,
2284
+ onChange: (next) => setPreset(filter.id, filter.options.map((option) => option.id).filter((id) => next.includes(id))),
2285
+ getTestId: (role, value) => `${testIdBase}:preset:${filter.id}:${role}${value === void 0 ? "" : `:${value}`}`
2286
+ }, filter.id)) });
2287
+ }
2288
+ //#endregion
1547
2289
  //#region src/list-kit/filter-builder/filter-builder.tsx
1548
2290
  const EMPTY_PINNED_FIELDS = Object.freeze([]);
1549
2291
  const EMPTY_PINNED_VALUE = Object.freeze([]);
1550
2292
  function FilterBuilder(props) {
1551
- const { group, setGroup, available, fixed, pinnedFields = EMPTY_PINNED_FIELDS, pinnedValue = EMPTY_PINNED_VALUE, setPinnedValue, setPage, testIdBase } = props;
2293
+ const { group, setGroup, available, fixed, pinnedFields = EMPTY_PINNED_FIELDS, pinnedValue = EMPTY_PINNED_VALUE, setPinnedValue, presets, setPreset, setPage, testIdBase } = props;
1552
2294
  const [open, setOpen] = useState(false);
1553
2295
  const [focusPath, setFocusPath] = useState(void 0);
1554
2296
  const [externalRemoval, setExternalRemoval] = useState(void 0);
@@ -1565,6 +2307,11 @@ function FilterBuilder(props) {
1565
2307
  setPinnedValue?.(next);
1566
2308
  };
1567
2309
  return jsxs(Fragment$1, { children: [
2310
+ presets !== void 0 && presets.length > 0 && setPreset !== void 0 && jsx(PresetFilterControls, {
2311
+ presets,
2312
+ setPreset,
2313
+ testIdBase
2314
+ }),
1568
2315
  jsx(PinnedFilterControls, {
1569
2316
  pinnedFields,
1570
2317
  value: pinnedValue,
@@ -1600,6 +2347,6 @@ function FilterBuilder(props) {
1600
2347
  ] });
1601
2348
  }
1602
2349
  //#endregion
1603
- export { TableHeader as C, badgeVariants as E, TableHead as S, Badge as T, SelectValue as _, FilterBadges as a, TableCaption as b, Select as c, SelectItem as d, SelectLabel as f, SelectTrigger as g, SelectSeparator as h, FilterValueControl as i, SelectContent as l, SelectScrollUpButton as m, PinnedFilterControls as n, formatDate as o, SelectScrollDownButton as p, FilterPanel as r, formatNumber as s, FilterBuilder as t, SelectGroup as u, Table as v, TableRow as w, TableCell as x, TableBody as y };
2350
+ export { TableRow as A, loadLocale as B, SelectValue as C, TableCell as D, TableCaption as E, ResolvedFieldInput as F, useFieldMetaDiagnostic as I, fieldDescription as L, badgeVariants as M, MetaPlaceholder as N, TableHead as O, ResolvedFieldDisplay as P, lowerLegacyField as R, SelectTrigger as S, TableBody as T, saveLocale as V, SelectItem as _, PinnedFilterControls as a, SelectScrollUpButton as b, fromLocalDateTimeText as c, formatDate as d, formatDateTime as f, SelectGroup as g, SelectContent as h, FacetedFilterPicker as i, Badge as j, TableHeader as k, toLocalDateTimeText as l, Select as m, PresetFilterControls as n, FilterPanel as o, formatNumber as p, DataTableFacetedFilter as r, FilterValueControl as s, FilterBuilder as t, FilterBadges as u, SelectLabel as v, Table as w, SelectSeparator as x, SelectScrollDownButton as y, DEFAULT_LANGUAGE_STORAGE_KEY as z };
1604
2351
 
1605
- //# sourceMappingURL=filter-builder-DOnaXNfP.js.map
2352
+ //# sourceMappingURL=filter-builder-DaWHUZuV.js.map