@burojs/ui-shadcn 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +22 -5
  2. package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
  3. package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
  4. package/dist/bulk-summary-GxMDP4bw.js +108 -0
  5. package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
  6. package/dist/button-BxnnE28H.cjs +55 -0
  7. package/dist/button-BxnnE28H.cjs.map +1 -0
  8. package/dist/button-MWVeZaqj.js +44 -0
  9. package/dist/button-MWVeZaqj.js.map +1 -0
  10. package/dist/candidates-gate-Bb70LPAy.js +135 -0
  11. package/dist/candidates-gate-Bb70LPAy.js.map +1 -0
  12. package/dist/candidates-gate-D33jXLHO.cjs +153 -0
  13. package/dist/candidates-gate-D33jXLHO.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-B116Bmqi.js +31 -0
  15. package/dist/candidates-relation-picker-B116Bmqi.js.map +1 -0
  16. package/dist/candidates-relation-picker-Cy0rTxvI.cjs +31 -0
  17. package/dist/candidates-relation-picker-Cy0rTxvI.cjs.map +1 -0
  18. package/dist/check-confirm-dialog--gPFnEeg.cjs +1547 -0
  19. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-bRT8lt0d.js +1245 -0
  21. package/dist/check-confirm-dialog-bRT8lt0d.js.map +1 -0
  22. package/dist/dialog-B_rt9S1c.js +63 -0
  23. package/dist/dialog-B_rt9S1c.js.map +1 -0
  24. package/dist/dialog-DRNUq9EA.cjs +112 -0
  25. package/dist/dialog-DRNUq9EA.cjs.map +1 -0
  26. package/dist/filter-builder-Bhn-dhZV.js +1621 -0
  27. package/dist/filter-builder-Bhn-dhZV.js.map +1 -0
  28. package/dist/filter-builder-nXqtM_h4.cjs +1789 -0
  29. package/dist/filter-builder-nXqtM_h4.cjs.map +1 -0
  30. package/dist/flow-runtime-D3avVH0I.cjs +255 -0
  31. package/dist/flow-runtime-D3avVH0I.cjs.map +1 -0
  32. package/dist/flow-runtime-s6lkRPKF.js +252 -0
  33. package/dist/flow-runtime-s6lkRPKF.js.map +1 -0
  34. package/dist/flow-screen-CCvgDI9G.js +704 -0
  35. package/dist/flow-screen-CCvgDI9G.js.map +1 -0
  36. package/dist/flow-screen-Ci_Sik4Y.cjs +708 -0
  37. package/dist/flow-screen-Ci_Sik4Y.cjs.map +1 -0
  38. package/dist/index.cjs +3420 -11986
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1843 -2015
  41. package/dist/index.d.ts +1849 -2021
  42. package/dist/index.js +3090 -11675
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B6FI0ctQ.js +112 -0
  45. package/dist/inline-flow-host-B6FI0ctQ.js.map +1 -0
  46. package/dist/inline-flow-host-DV1et3c6.cjs +112 -0
  47. package/dist/inline-flow-host-DV1et3c6.cjs.map +1 -0
  48. package/dist/input-BI0czGyI.cjs +23 -0
  49. package/dist/input-BI0czGyI.cjs.map +1 -0
  50. package/dist/input-CUPStzZO.js +17 -0
  51. package/dist/input-CUPStzZO.js.map +1 -0
  52. package/dist/label-66W1qbke.js +29 -0
  53. package/dist/label-66W1qbke.js.map +1 -0
  54. package/dist/label-w6JYf2cF.cjs +36 -0
  55. package/dist/label-w6JYf2cF.cjs.map +1 -0
  56. package/dist/markdown/index.cjs +1 -1
  57. package/dist/markdown/index.d.cts +2 -3
  58. package/dist/markdown/index.d.ts +2 -3
  59. package/dist/markdown/index.js +1 -1
  60. package/dist/page-flow-route-hdnULWqd.cjs +277 -0
  61. package/dist/page-flow-route-hdnULWqd.cjs.map +1 -0
  62. package/dist/page-flow-route-i3ZfJ1sd.js +277 -0
  63. package/dist/page-flow-route-i3ZfJ1sd.js.map +1 -0
  64. package/dist/place-node-C5SmIAYU.cjs +8139 -0
  65. package/dist/place-node-C5SmIAYU.cjs.map +1 -0
  66. package/dist/place-node-NraNYKPx.js +7597 -0
  67. package/dist/place-node-NraNYKPx.js.map +1 -0
  68. package/dist/related-list-cards-BWZ9GfOG.js +42 -0
  69. package/dist/related-list-cards-BWZ9GfOG.js.map +1 -0
  70. package/dist/related-list-cards-CFq3meUn.cjs +42 -0
  71. package/dist/related-list-cards-CFq3meUn.cjs.map +1 -0
  72. package/dist/related-list-timeline-CCzl71aA.cjs +142 -0
  73. package/dist/related-list-timeline-CCzl71aA.cjs.map +1 -0
  74. package/dist/related-list-timeline-yrPsjA3F.js +142 -0
  75. package/dist/related-list-timeline-yrPsjA3F.js.map +1 -0
  76. package/dist/related-rows-ea43qhhy.cjs +208 -0
  77. package/dist/related-rows-ea43qhhy.cjs.map +1 -0
  78. package/dist/related-rows-o0_t-Yce.js +185 -0
  79. package/dist/related-rows-o0_t-Yce.js.map +1 -0
  80. package/dist/relation-create-IA40t1hi.cjs +364 -0
  81. package/dist/relation-create-IA40t1hi.cjs.map +1 -0
  82. package/dist/relation-create-h6F9B4C4.js +339 -0
  83. package/dist/relation-create-h6F9B4C4.js.map +1 -0
  84. package/dist/relation-many-picker-C8zFEjwd.cjs +467 -0
  85. package/dist/relation-many-picker-C8zFEjwd.cjs.map +1 -0
  86. package/dist/relation-many-picker-bvkjkv-k.js +466 -0
  87. package/dist/relation-many-picker-bvkjkv-k.js.map +1 -0
  88. package/dist/relation-picker-Z0ZJJ5uS.js +493 -0
  89. package/dist/relation-picker-Z0ZJJ5uS.js.map +1 -0
  90. package/dist/relation-picker-dialog-0IzkjmiA.cjs +304 -0
  91. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +1 -0
  92. package/dist/relation-picker-dialog-B6nIiJBf.js +303 -0
  93. package/dist/relation-picker-dialog-B6nIiJBf.js.map +1 -0
  94. package/dist/relation-picker-gag0qSR5.cjs +516 -0
  95. package/dist/relation-picker-gag0qSR5.cjs.map +1 -0
  96. package/dist/resource-page-router-3RUf9d1j.js +1588 -0
  97. package/dist/resource-page-router-3RUf9d1j.js.map +1 -0
  98. package/dist/resource-page-router-PXfMW9nh.cjs +1785 -0
  99. package/dist/resource-page-router-PXfMW9nh.cjs.map +1 -0
  100. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  101. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  102. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  103. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  104. package/dist/search-access-CPPmfuvy.js +654 -0
  105. package/dist/search-access-CPPmfuvy.js.map +1 -0
  106. package/dist/search-access-Dugvttlg.cjs +791 -0
  107. package/dist/search-access-Dugvttlg.cjs.map +1 -0
  108. package/dist/shell-BPlk8hEa.js +205 -0
  109. package/dist/shell-BPlk8hEa.js.map +1 -0
  110. package/dist/shell-DxhlsPwm.cjs +271 -0
  111. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  112. package/dist/skeleton-U2noefj5.js +13 -0
  113. package/dist/skeleton-U2noefj5.js.map +1 -0
  114. package/dist/skeleton-_1gP7wan.cjs +18 -0
  115. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  116. package/dist/styles.css +241 -9
  117. package/dist/toaster-C02XbB9r.js +135 -0
  118. package/dist/toaster-C02XbB9r.js.map +1 -0
  119. package/dist/toaster-Cr51X7bz.cjs +195 -0
  120. package/dist/toaster-Cr51X7bz.cjs.map +1 -0
  121. package/dist/use-dialog-filter-state-C--oMAZG.js +87 -0
  122. package/dist/use-dialog-filter-state-C--oMAZG.js.map +1 -0
  123. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs +92 -0
  124. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +1 -0
  125. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  126. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  127. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  128. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  129. package/package.json +16 -14
  130. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  131. package/dist/use-theme-IHmtrhEt.js.map +0 -1
@@ -0,0 +1,1789 @@
1
+ const require_use_theme = require("./use-theme-DFlpXils.cjs");
2
+ const require_toaster = require("./toaster-Cr51X7bz.cjs");
3
+ const require_button = require("./button-BxnnE28H.cjs");
4
+ const require_input = require("./input-BI0czGyI.cjs");
5
+ const require_search_access = require("./search-access-Dugvttlg.cjs");
6
+ let _burojs_core = require("@burojs/core");
7
+ let _burojs_react = require("@burojs/react");
8
+ let _tabler_icons_react = require("@tabler/icons-react");
9
+ let react = require("react");
10
+ react = require_use_theme.__toESM(react, 1);
11
+ let react_jsx_runtime = require("react/jsx-runtime");
12
+ let class_variance_authority = require("class-variance-authority");
13
+ let _radix_ui_react_select = require("@radix-ui/react-select");
14
+ _radix_ui_react_select = require_use_theme.__toESM(_radix_ui_react_select, 1);
15
+ //#region src/layout/language-storage.ts
16
+ function loadLocale(storageKey = _burojs_react.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 = _burojs_react.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/primitives/badge.tsx
32
+ const badgeVariants = (0, class_variance_authority.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", {
33
+ variants: { variant: {
34
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
35
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
36
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
37
+ outline: "text-foreground",
38
+ success: "border-success/20 bg-success/15 text-success-emphasis hover:bg-success/20",
39
+ warning: "border-warning/20 bg-warning/15 text-warning-emphasis hover:bg-warning/20",
40
+ info: "border-info/20 bg-info/15 text-info-emphasis hover:bg-info/20"
41
+ } },
42
+ defaultVariants: { variant: "default" }
43
+ });
44
+ function Badge({ className, variant, children, ...props }) {
45
+ return (0, react_jsx_runtime.jsx)("div", {
46
+ className: require_use_theme.cn(badgeVariants({ variant }), className),
47
+ ...props,
48
+ children: (0, react_jsx_runtime.jsx)("span", {
49
+ className: "min-w-0 truncate",
50
+ children
51
+ })
52
+ });
53
+ }
54
+ //#endregion
55
+ //#region src/primitives/table.tsx
56
+ const Table = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("div", {
57
+ className: "relative w-full overflow-auto",
58
+ children: (0, react_jsx_runtime.jsx)("table", {
59
+ ref,
60
+ className: require_use_theme.cn("w-full caption-bottom text-sm", className),
61
+ ...props
62
+ })
63
+ }));
64
+ Table.displayName = "Table";
65
+ const TableHeader = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("thead", {
66
+ ref,
67
+ className: require_use_theme.cn("[&_tr]:border-b", className),
68
+ ...props
69
+ }));
70
+ TableHeader.displayName = "TableHeader";
71
+ const TableBody = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("tbody", {
72
+ ref,
73
+ className: require_use_theme.cn("[&_tr:last-child]:border-0", className),
74
+ ...props
75
+ }));
76
+ TableBody.displayName = "TableBody";
77
+ const TableRow = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("tr", {
78
+ ref,
79
+ className: require_use_theme.cn("group/row border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-primary/10 data-[state=selected]:hover:bg-primary/15", className),
80
+ ...props
81
+ }));
82
+ TableRow.displayName = "TableRow";
83
+ const TableHead = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("th", {
84
+ ref,
85
+ className: require_use_theme.cn("h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", className),
86
+ ...props
87
+ }));
88
+ TableHead.displayName = "TableHead";
89
+ const TableCell = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("td", {
90
+ ref,
91
+ className: require_use_theme.cn("p-2 align-middle [&:has([role=checkbox])]:pr-0", className),
92
+ ...props
93
+ }));
94
+ TableCell.displayName = "TableCell";
95
+ const TableCaption = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)("caption", {
96
+ ref,
97
+ className: require_use_theme.cn("mt-4 text-sm text-muted-foreground", className),
98
+ ...props
99
+ }));
100
+ TableCaption.displayName = "TableCaption";
101
+ //#endregion
102
+ //#region src/primitives/select.tsx
103
+ const Select = _radix_ui_react_select.Root;
104
+ const SelectGroup = _radix_ui_react_select.Group;
105
+ const SelectValue = _radix_ui_react_select.Value;
106
+ const SelectTrigger = react.forwardRef(({ className, children, ...props }, ref) => (0, react_jsx_runtime.jsxs)(_radix_ui_react_select.Trigger, {
107
+ ref,
108
+ className: require_use_theme.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),
109
+ ...props,
110
+ children: [children, (0, react_jsx_runtime.jsx)(_radix_ui_react_select.Icon, {
111
+ asChild: true,
112
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconChevronDown, { className: "h-4 w-4 opacity-50" })
113
+ })]
114
+ }));
115
+ SelectTrigger.displayName = _radix_ui_react_select.Trigger.displayName;
116
+ const SelectScrollUpButton = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_select.ScrollUpButton, {
117
+ ref,
118
+ className: require_use_theme.cn("flex cursor-default items-center justify-center py-1", className),
119
+ ...props,
120
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconChevronUp, { className: "h-4 w-4" })
121
+ }));
122
+ SelectScrollUpButton.displayName = _radix_ui_react_select.ScrollUpButton.displayName;
123
+ const SelectScrollDownButton = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_select.ScrollDownButton, {
124
+ ref,
125
+ className: require_use_theme.cn("flex cursor-default items-center justify-center py-1", className),
126
+ ...props,
127
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconChevronDown, { className: "h-4 w-4" })
128
+ }));
129
+ SelectScrollDownButton.displayName = _radix_ui_react_select.ScrollDownButton.displayName;
130
+ const SelectContent = react.forwardRef(({ className, children, position = "popper", ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_select.Portal, { children: (0, react_jsx_runtime.jsxs)(_radix_ui_react_select.Content, {
131
+ ref,
132
+ className: require_use_theme.cn("relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md duration-[var(--duration-overlay)] ease-[var(--ease-overlay)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className),
133
+ position,
134
+ ...props,
135
+ children: [
136
+ (0, react_jsx_runtime.jsx)(SelectScrollUpButton, {}),
137
+ (0, react_jsx_runtime.jsx)(_radix_ui_react_select.Viewport, {
138
+ className: require_use_theme.cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),
139
+ children
140
+ }),
141
+ (0, react_jsx_runtime.jsx)(SelectScrollDownButton, {})
142
+ ]
143
+ }) }));
144
+ SelectContent.displayName = _radix_ui_react_select.Content.displayName;
145
+ const SelectLabel = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_select.Label, {
146
+ ref,
147
+ className: require_use_theme.cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
148
+ ...props
149
+ }));
150
+ SelectLabel.displayName = _radix_ui_react_select.Label.displayName;
151
+ const SelectItem = react.forwardRef(({ className, children, ...props }, ref) => (0, react_jsx_runtime.jsxs)(_radix_ui_react_select.Item, {
152
+ ref,
153
+ className: require_use_theme.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),
154
+ ...props,
155
+ children: [(0, react_jsx_runtime.jsx)("span", {
156
+ className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center",
157
+ children: (0, react_jsx_runtime.jsx)(_radix_ui_react_select.ItemIndicator, { children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, { className: "h-4 w-4" }) })
158
+ }), (0, react_jsx_runtime.jsx)(_radix_ui_react_select.ItemText, { children })]
159
+ }));
160
+ SelectItem.displayName = _radix_ui_react_select.Item.displayName;
161
+ const SelectSeparator = react.forwardRef(({ className, ...props }, ref) => (0, react_jsx_runtime.jsx)(_radix_ui_react_select.Separator, {
162
+ ref,
163
+ className: require_use_theme.cn("-mx-1 my-1 h-px bg-muted", className),
164
+ ...props
165
+ }));
166
+ SelectSeparator.displayName = _radix_ui_react_select.Separator.displayName;
167
+ //#endregion
168
+ //#region src/fields/format.ts
169
+ function localeTag(locale) {
170
+ return locale || loadLocale() || "en-US";
171
+ }
172
+ const dateCache = new Map();
173
+ const dateTimeCache = new Map();
174
+ const numberCache = new Map();
175
+ function cached(cache, key, make) {
176
+ const hit = cache.get(key);
177
+ if (hit) return hit;
178
+ let made;
179
+ try {
180
+ made = make(key);
181
+ } catch {
182
+ made = make("en-US");
183
+ }
184
+ cache.set(key, made);
185
+ return made;
186
+ }
187
+ function formatDate(d, locale) {
188
+ const tag = localeTag(locale);
189
+ return cached(dateCache, tag, (t) => new Intl.DateTimeFormat(t, {
190
+ year: "numeric",
191
+ month: "short",
192
+ day: "numeric"
193
+ })).format(d);
194
+ }
195
+ function formatDateTime(d, locale) {
196
+ const tag = localeTag(locale);
197
+ return cached(dateTimeCache, tag, (t) => new Intl.DateTimeFormat(t, {
198
+ year: "numeric",
199
+ month: "short",
200
+ day: "numeric",
201
+ hour: "2-digit",
202
+ minute: "2-digit"
203
+ })).format(d);
204
+ }
205
+ function formatNumber(n, locale) {
206
+ const tag = localeTag(locale);
207
+ return cached(numberCache, tag, (t) => new Intl.NumberFormat(t)).format(n);
208
+ }
209
+ //#endregion
210
+ //#region src/list-kit/filter-builder/describe-row.ts
211
+ const MAX_DESCRIBE_DEPTH = 2;
212
+ function describeValue(value, t, spec, formatValue) {
213
+ if (value === void 0) return null;
214
+ if (Array.isArray(value)) return t("filters.selectedCount", { count: value.length }, `${value.length} selected`);
215
+ const optionLabel = spec?.options?.find((o) => o.value === String(value))?.label;
216
+ if (optionLabel !== void 0) return optionLabel;
217
+ if (spec && formatValue) {
218
+ const formatted = formatValue(spec.kind, value);
219
+ if (formatted !== void 0) return formatted;
220
+ }
221
+ return String(value);
222
+ }
223
+ function describeAt(row, available, t, depth, formatValue) {
224
+ if ((0, _burojs_core.isFilterGroup)(row)) {
225
+ if (depth > MAX_DESCRIBE_DEPTH) return "…";
226
+ const joinLabel = t(`filters.${row.join}`, void 0, row.join);
227
+ return `(${row.rows.map((child) => describeAt(child, available, t, depth + 1, formatValue)).join(` ${joinLabel} `)})`;
228
+ }
229
+ const spec = available.find((s) => s.field === row.field);
230
+ const label = spec?.label ?? row.field;
231
+ const opLabel = t(`filters.operators.${row.op}`, void 0, row.op);
232
+ const valueText = (0, _burojs_core.opArity)(row.op) === "none" ? null : describeValue(row.value, t, spec, formatValue);
233
+ return valueText === null ? `${label} ${opLabel}` : `${label} ${opLabel} ${valueText}`;
234
+ }
235
+ function describeRow(row, available, t, formatValue) {
236
+ return describeAt(row, available, t, 1, formatValue);
237
+ }
238
+ function relationLabelKey(to, by, value) {
239
+ return `${by === void 0 ? to : `${to}#${by}`}::${value}`;
240
+ }
241
+ function collectFormatRefs(rows, available) {
242
+ const enumNames = new Set();
243
+ const relationRefs = new Map();
244
+ const walk = (nodes) => {
245
+ for (const row of nodes) {
246
+ if ((0, _burojs_core.isFilterGroup)(row)) {
247
+ walk(row.rows);
248
+ continue;
249
+ }
250
+ if (row.value === void 0 || Array.isArray(row.value)) continue;
251
+ const spec = available.find((s) => s.field === row.field);
252
+ if (!spec) continue;
253
+ if (spec.kind.kind === "enum") enumNames.add(spec.kind.enumName);
254
+ else if (spec.kind.kind === "relation") {
255
+ const id = String(row.value);
256
+ const { to, by } = spec.kind;
257
+ relationRefs.set(relationLabelKey(to, by, id), by === void 0 ? {
258
+ to,
259
+ id
260
+ } : {
261
+ to,
262
+ id,
263
+ by
264
+ });
265
+ }
266
+ }
267
+ };
268
+ walk(rows);
269
+ return {
270
+ enumNames: Array.from(enumNames),
271
+ relationRefs: Array.from(relationRefs.values())
272
+ };
273
+ }
274
+ //#endregion
275
+ //#region src/list-kit/filter-builder/describe-value-resolvers.tsx
276
+ function EnumLabelResolver(props) {
277
+ const { enumName, onResolve } = props;
278
+ const { values } = (0, _burojs_react.useEnumValues)(enumName);
279
+ const caps = (0, _burojs_react.useI18nCaps)();
280
+ const def = (0, _burojs_react.useApp)().registry.enums.get(enumName);
281
+ (0, react.useEffect)(() => {
282
+ if (!values) return;
283
+ const labels = {};
284
+ for (const entry of values) labels[entry.value] = (0, _burojs_core.resolveEnumLabel)(caps, enumName, entry.value, (0, _burojs_core.enumValueText)(def, entry.value) ?? entry.label);
285
+ onResolve(enumName, labels);
286
+ }, [
287
+ values,
288
+ enumName,
289
+ caps,
290
+ def,
291
+ onResolve
292
+ ]);
293
+ return null;
294
+ }
295
+ function RelationLabelResolver(props) {
296
+ const { to, id, by, onResolve } = props;
297
+ const targetDef = (0, _burojs_react.useApp)().registry.get(to);
298
+ const { record } = (0, _burojs_react.useRelatedRecord)(to, id, by === void 0 ? void 0 : { by });
299
+ const rowTitle = require_search_access.useRowTitle(to, targetDef?.displayField);
300
+ (0, react.useEffect)(() => {
301
+ if (!record) return;
302
+ onResolve(relationLabelKey(to, by, id), rowTitle(record));
303
+ }, [
304
+ record,
305
+ to,
306
+ by,
307
+ id,
308
+ rowTitle,
309
+ onResolve
310
+ ]);
311
+ return null;
312
+ }
313
+ function FormatResolvers(props) {
314
+ const { refs, onEnumResolved, onRelationResolved } = props;
315
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [refs.enumNames.map((name) => (0, react_jsx_runtime.jsx)(EnumLabelResolver, {
316
+ enumName: name,
317
+ onResolve: onEnumResolved
318
+ }, `enum:${name}`)), refs.relationRefs.map(({ to, id, by }) => (0, react_jsx_runtime.jsx)(RelationLabelResolver, {
319
+ to,
320
+ id,
321
+ ...by === void 0 ? {} : { by },
322
+ onResolve: onRelationResolved
323
+ }, `relation:${relationLabelKey(to, by, id)}`))] });
324
+ }
325
+ function useFormatValueMaps() {
326
+ const [enumLabels, setEnumLabels] = (0, react.useState)({});
327
+ const [relationLabels, setRelationLabels] = (0, react.useState)({});
328
+ return {
329
+ enumLabels,
330
+ relationLabels,
331
+ onEnumResolved: (0, react.useCallback)((enumName, labels) => {
332
+ setEnumLabels((prev) => prev[enumName] === labels ? prev : {
333
+ ...prev,
334
+ [enumName]: labels
335
+ });
336
+ }, []),
337
+ onRelationResolved: (0, react.useCallback)((key, label) => {
338
+ setRelationLabels((prev) => prev[key] === label ? prev : {
339
+ ...prev,
340
+ [key]: label
341
+ });
342
+ }, [])
343
+ };
344
+ }
345
+ //#endregion
346
+ //#region src/list-kit/filter-builder/pinned-ownership.ts
347
+ function isFilterPredicateExpr(expr) {
348
+ return "field" in expr;
349
+ }
350
+ function findOwnedIndex(value, spec) {
351
+ const op = spec.operators[0] ?? "eq";
352
+ return value.findIndex((expr) => isFilterPredicateExpr(expr) && expr.field === spec.field && expr.op === op);
353
+ }
354
+ function claimedPinnedIndices(value, pinnedFields) {
355
+ const claimed = new Set();
356
+ for (const spec of pinnedFields) {
357
+ const op = spec.operators[0] ?? "eq";
358
+ const index = value.findIndex((expr, i) => !claimed.has(i) && isFilterPredicateExpr(expr) && expr.field === spec.field && expr.op === op);
359
+ if (index >= 0) claimed.add(index);
360
+ }
361
+ return claimed;
362
+ }
363
+ //#endregion
364
+ //#region src/list-kit/filter-builder/filter-badges.tsx
365
+ function badgeKey(row, index) {
366
+ if ((0, _burojs_core.isFilterGroup)(row)) return `${index}:group:${row.join}`;
367
+ return `${index}:${row.field}:${row.op}`;
368
+ }
369
+ function FilterBadges(props) {
370
+ const { group, setGroup, available, fixed, testIdBase, onOpenAt, onRemoved, pinnedFields = [], pinnedValue = [], setPinnedValue } = props;
371
+ const t = (0, _burojs_react.useOptionalT)();
372
+ const tt = (key, params, fallback) => t(key, {
373
+ params,
374
+ ...typeof params?.count === "number" ? { count: params.count } : {},
375
+ defaultValue: fallback
376
+ });
377
+ const removeLabel = t("filters.removeRow", { defaultValue: "Remove filter" });
378
+ const { enumLabels, relationLabels, onEnumResolved, onRelationResolved } = useFormatValueMaps();
379
+ const formatValue = (kind, value) => {
380
+ if (kind.kind === "enum") return enumLabels[kind.enumName]?.[String(value)];
381
+ if (kind.kind === "relation") return relationLabels[relationLabelKey(kind.to, kind.by, String(value))];
382
+ if (kind.kind === "date") {
383
+ const d = new Date(String(value));
384
+ if (Number.isNaN(d.getTime())) return void 0;
385
+ return kind.time ? formatDateTime(d) : formatDate(d);
386
+ }
387
+ };
388
+ const orphanPinned = (0, react.useMemo)(() => {
389
+ const claimed = claimedPinnedIndices(pinnedValue, pinnedFields);
390
+ return pinnedValue.map((expr, index) => ({
391
+ expr,
392
+ index
393
+ })).filter(({ expr, index }) => !claimed.has(index) && isFilterPredicateExpr(expr));
394
+ }, [pinnedValue, pinnedFields]);
395
+ const refs = (0, react.useMemo)(() => collectFormatRefs([...group.rows, ...orphanPinned.map(({ expr }) => expr)], available), [
396
+ group,
397
+ orphanPinned,
398
+ available
399
+ ]);
400
+ const handleRemove = (index) => {
401
+ setGroup({
402
+ ...group,
403
+ rows: group.rows.filter((_, i) => i !== index)
404
+ });
405
+ onRemoved?.(index);
406
+ };
407
+ const handleRemovePinned = (index) => {
408
+ setPinnedValue?.(pinnedValue.filter((_, i) => i !== index));
409
+ };
410
+ const labelledFixed = fixed.filter((entry) => entry.label !== void 0);
411
+ return (0, react_jsx_runtime.jsxs)("div", {
412
+ className: "flex flex-wrap items-center gap-1",
413
+ children: [
414
+ (0, react_jsx_runtime.jsx)(FormatResolvers, {
415
+ refs,
416
+ onEnumResolved,
417
+ onRelationResolved
418
+ }),
419
+ group.rows.map((row, index) => (0, react_jsx_runtime.jsxs)(Badge, {
420
+ variant: "secondary",
421
+ className: "gap-1 py-1 pr-1",
422
+ children: [(0, react_jsx_runtime.jsx)("button", {
423
+ type: "button",
424
+ "data-testid": `${testIdBase}:badge:${index}`,
425
+ onClick: () => onOpenAt([index]),
426
+ className: "max-w-full cursor-pointer truncate bg-transparent p-0 text-left",
427
+ children: describeRow(row, available, tt, formatValue)
428
+ }), (0, react_jsx_runtime.jsx)("button", {
429
+ type: "button",
430
+ "data-testid": `${testIdBase}:badge:${index}:remove`,
431
+ "aria-label": removeLabel,
432
+ onClick: () => handleRemove(index),
433
+ className: "rounded-sm hover:bg-accent",
434
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
435
+ className: "h-3 w-3",
436
+ "aria-hidden": "true"
437
+ })
438
+ })]
439
+ }, badgeKey(row, index))),
440
+ labelledFixed.map((entry, index) => (0, react_jsx_runtime.jsx)(Badge, {
441
+ variant: "outline",
442
+ "data-testid": `${testIdBase}:badge:fixed:${index}`,
443
+ children: entry.label
444
+ }, `fixed-${index}`)),
445
+ orphanPinned.map(({ expr, index }) => (0, react_jsx_runtime.jsxs)(Badge, {
446
+ variant: "secondary",
447
+ className: "gap-1 py-1",
448
+ children: [(0, react_jsx_runtime.jsx)("span", {
449
+ "data-testid": `${testIdBase}:badge:pinned:${index}`,
450
+ children: describeRow(expr, available, tt, formatValue)
451
+ }), (0, react_jsx_runtime.jsx)("button", {
452
+ type: "button",
453
+ "data-testid": `${testIdBase}:badge:pinned:${index}:remove`,
454
+ "aria-label": removeLabel,
455
+ onClick: () => handleRemovePinned(index),
456
+ className: "rounded-sm hover:bg-accent",
457
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
458
+ className: "h-3 w-3",
459
+ "aria-hidden": "true"
460
+ })
461
+ })]
462
+ }, `pinned-${index}`))
463
+ ]
464
+ });
465
+ }
466
+ let counter = 0;
467
+ function nextId() {
468
+ counter += 1;
469
+ return `r${counter}`;
470
+ }
471
+ function isDraftGroup(node) {
472
+ return Object.hasOwn(node, "rows");
473
+ }
474
+ function toDraft(group) {
475
+ return {
476
+ rowId: nextId(),
477
+ join: group.join,
478
+ rows: group.rows.map(toDraftNode)
479
+ };
480
+ }
481
+ function toDraftNode(row) {
482
+ if ((0, _burojs_core.isFilterGroup)(row)) return toDraft(row);
483
+ const predicate = row;
484
+ return {
485
+ rowId: nextId(),
486
+ field: predicate.field,
487
+ op: predicate.op,
488
+ ...Object.hasOwn(predicate, "value") ? { value: predicate.value } : {}
489
+ };
490
+ }
491
+ function fromDraft(draft) {
492
+ return {
493
+ join: draft.join,
494
+ rows: draft.rows.map((node) => isDraftGroup(node) ? fromDraft(node) : {
495
+ field: node.field,
496
+ op: node.op,
497
+ ...Object.hasOwn(node, "value") ? { value: node.value } : {}
498
+ })
499
+ };
500
+ }
501
+ function pruneIncompleteRows(group) {
502
+ const rows = [];
503
+ for (const row of group.rows) if ((0, _burojs_core.isFilterGroup)(row)) {
504
+ const pruned = pruneIncompleteRows(row);
505
+ if (pruned.rows.length > 0) rows.push(pruned);
506
+ } else if ((0, _burojs_core.isCompletePredicate)(row)) rows.push(row);
507
+ return {
508
+ join: group.join,
509
+ rows
510
+ };
511
+ }
512
+ function mapGroupAt(draft, path, replace) {
513
+ if (path.length === 0) return replace(draft);
514
+ const [head, ...rest] = path;
515
+ const child = draft.rows[head];
516
+ if (child === void 0 || !isDraftGroup(child)) return draft;
517
+ const next = mapGroupAt(child, rest, replace);
518
+ if (next === child) return draft;
519
+ const rows = draft.rows.slice();
520
+ rows[head] = next;
521
+ return {
522
+ ...draft,
523
+ rows
524
+ };
525
+ }
526
+ function depthOf(path) {
527
+ return path.length + 1;
528
+ }
529
+ function defaultValueForOp(op) {
530
+ const arity = (0, _burojs_core.opArity)(op);
531
+ if (arity === "none") return void 0;
532
+ if (arity === "multi") return [];
533
+ return "";
534
+ }
535
+ function addRow(draft, path, available) {
536
+ const spec = available[0];
537
+ if (!spec) return draft;
538
+ const op = spec.operators[0] ?? "eq";
539
+ const value = defaultValueForOp(op);
540
+ const row = {
541
+ rowId: nextId(),
542
+ field: spec.field,
543
+ op,
544
+ ...value === void 0 ? {} : { value }
545
+ };
546
+ return mapGroupAt(draft, path, (group) => ({
547
+ ...group,
548
+ rows: [...group.rows, row]
549
+ }));
550
+ }
551
+ function addGroup(draft, path) {
552
+ if (depthOf(path) >= 3) return draft;
553
+ const group = {
554
+ rowId: nextId(),
555
+ join: "and",
556
+ rows: []
557
+ };
558
+ return mapGroupAt(draft, path, (parent) => ({
559
+ ...parent,
560
+ rows: [...parent.rows, group]
561
+ }));
562
+ }
563
+ function removeAt(draft, path) {
564
+ if (path.length === 0) return draft;
565
+ const parentPath = path.slice(0, -1);
566
+ const index = path[path.length - 1];
567
+ return mapGroupAt(draft, parentPath, (group) => {
568
+ if (group.rows[index] === void 0) return group;
569
+ const rows = group.rows.slice();
570
+ rows.splice(index, 1);
571
+ return {
572
+ ...group,
573
+ rows
574
+ };
575
+ });
576
+ }
577
+ function setJoin(draft, path, join) {
578
+ return mapGroupAt(draft, path, (group) => group.join === join ? group : {
579
+ ...group,
580
+ join
581
+ });
582
+ }
583
+ function updateRow(draft, path, patch) {
584
+ if (path.length === 0) return draft;
585
+ const parentPath = path.slice(0, -1);
586
+ const index = path[path.length - 1];
587
+ return mapGroupAt(draft, parentPath, (group) => {
588
+ const current = group.rows[index];
589
+ if (current === void 0 || isDraftGroup(current)) return group;
590
+ const fieldChanged = patch.field !== void 0 && patch.field !== current.field;
591
+ const op = patch.op ?? current.op;
592
+ let value;
593
+ if (fieldChanged) value = defaultValueForOp(op);
594
+ else if (patch.op !== void 0 && patch.op !== current.op) value = (0, _burojs_core.convertValueForOp)(current.value, current.op, patch.op);
595
+ else value = Object.hasOwn(patch, "value") ? patch.value : current.value;
596
+ const next = {
597
+ rowId: current.rowId,
598
+ field: patch.field ?? current.field,
599
+ op,
600
+ ...value === void 0 ? {} : { value }
601
+ };
602
+ const rows = group.rows.slice();
603
+ rows[index] = next;
604
+ return {
605
+ ...group,
606
+ rows
607
+ };
608
+ });
609
+ }
610
+ const EMPTY_DRAFT = () => toDraft(_burojs_core.EMPTY_FILTER_GROUP);
611
+ //#endregion
612
+ //#region src/list-kit/filter-builder/values/use-selectable-value-control.ts
613
+ function useSelectableValueControl(options) {
614
+ const { value, arity, onChange, emitSingle = (token) => token } = options;
615
+ const [open, setOpen] = (0, react.useState)(false);
616
+ const selected = arity === "multi" ? new Set(Array.isArray(value) ? value.map(String) : []) : new Set(value === void 0 || value === null ? [] : [String(value)]);
617
+ const select = (token) => {
618
+ if (arity === "single") {
619
+ onChange(emitSingle(token));
620
+ setOpen(false);
621
+ return;
622
+ }
623
+ const next = new Set(selected);
624
+ if (next.has(token)) next.delete(token);
625
+ else next.add(token);
626
+ onChange(next.size === 0 ? void 0 : Array.from(next));
627
+ };
628
+ const triggerLabel = (labelOf, placeholder) => {
629
+ const picked = Array.from(selected);
630
+ return picked.length === 0 ? placeholder : picked.map(labelOf).join(", ");
631
+ };
632
+ const clear = () => {
633
+ onChange(void 0);
634
+ setOpen(false);
635
+ };
636
+ return {
637
+ open,
638
+ setOpen,
639
+ selected,
640
+ select,
641
+ triggerLabel,
642
+ clear
643
+ };
644
+ }
645
+ //#endregion
646
+ //#region src/list-kit/filter-builder/values/boolean-value-control.tsx
647
+ const OPTIONS = ["true", "false"];
648
+ function BooleanValueControl(props) {
649
+ const { value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
650
+ const t = (0, _burojs_react.useOptionalT)();
651
+ const ariaLabel = fieldLabel ? t("filters.valueFor", {
652
+ defaultValue: `Value for ${fieldLabel}`,
653
+ params: { field: fieldLabel }
654
+ }) : t("filters.value", { defaultValue: "Value" });
655
+ const yesLabel = t("boolean.yes", { defaultValue: "Yes" });
656
+ const noLabel = t("boolean.no", { defaultValue: "No" });
657
+ const labelOf = (token) => token === "true" ? yesLabel : noLabel;
658
+ const clearLabel = t("filters.clear", { defaultValue: "Clear" });
659
+ const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
660
+ value,
661
+ arity,
662
+ onChange,
663
+ emitSingle: (token) => token === "true"
664
+ });
665
+ return (0, react_jsx_runtime.jsxs)(require_toaster.Popover, {
666
+ open,
667
+ onOpenChange: setOpen,
668
+ children: [(0, react_jsx_runtime.jsx)(require_toaster.PopoverTrigger, {
669
+ asChild: true,
670
+ children: (0, react_jsx_runtime.jsx)(require_button.Button, {
671
+ type: "button",
672
+ variant: "outline",
673
+ size: "sm",
674
+ autoFocus,
675
+ "data-testid": testId,
676
+ "aria-label": ariaLabel,
677
+ className: require_use_theme.cn("h-8 justify-between", fill && "w-full"),
678
+ children: triggerLabel(labelOf, ariaLabel)
679
+ })
680
+ }), (0, react_jsx_runtime.jsx)(require_toaster.PopoverContent, {
681
+ className: "w-[140px] p-0",
682
+ align: "start",
683
+ children: (0, react_jsx_runtime.jsx)(require_search_access.Command, { children: (0, react_jsx_runtime.jsx)(require_search_access.CommandList, { children: (0, react_jsx_runtime.jsxs)(require_search_access.CommandGroup, { children: [arity === "single" && selected.size > 0 && (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
684
+ "data-testid": `${testId}:clear`,
685
+ value: "__clear__",
686
+ onSelect: () => clear(),
687
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
688
+ className: "mr-2 h-4 w-4",
689
+ "aria-hidden": "true"
690
+ }), clearLabel]
691
+ }), OPTIONS.map((token) => {
692
+ const isSelected = selected.has(token);
693
+ return (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
694
+ value: labelOf(token),
695
+ "data-testid": `${testId}:option:${token}`,
696
+ onSelect: () => select(token),
697
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, {
698
+ className: require_use_theme.cn("mr-2 h-4 w-4", isSelected ? "opacity-100" : "opacity-0"),
699
+ "aria-hidden": "true"
700
+ }), labelOf(token)]
701
+ }, token);
702
+ })] }) }) })
703
+ })]
704
+ });
705
+ }
706
+ //#endregion
707
+ //#region src/list-kit/filter-builder/values/select-list-value-control.tsx
708
+ function SelectListValueControl(props) {
709
+ const { options, value, onChange, testId, arity, autoFocus, fieldLabel, isLoading = false } = props;
710
+ const t = (0, _burojs_react.useOptionalT)();
711
+ const ariaLabel = fieldLabel ? t("filters.valueFor", {
712
+ defaultValue: `Value for ${fieldLabel}`,
713
+ params: { field: fieldLabel }
714
+ }) : t("filters.value", { defaultValue: "Value" });
715
+ const loadingLabel = t("table.loading", { defaultValue: "Loading…" });
716
+ const clearLabel = t("filters.clear", { defaultValue: "Clear" });
717
+ const labelFor = props.labelFor ?? ((raw) => options.find((o) => o.value === raw)?.label ?? raw);
718
+ const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
719
+ value,
720
+ arity,
721
+ onChange
722
+ });
723
+ return (0, react_jsx_runtime.jsxs)(require_toaster.Popover, {
724
+ open,
725
+ onOpenChange: setOpen,
726
+ children: [(0, react_jsx_runtime.jsx)(require_toaster.PopoverTrigger, {
727
+ asChild: true,
728
+ children: (0, react_jsx_runtime.jsx)(require_button.Button, {
729
+ type: "button",
730
+ variant: "outline",
731
+ size: "sm",
732
+ autoFocus,
733
+ "data-testid": testId,
734
+ "aria-label": ariaLabel,
735
+ className: require_use_theme.cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[200px]"),
736
+ children: (0, react_jsx_runtime.jsx)("span", {
737
+ className: "truncate",
738
+ children: isLoading ? loadingLabel : triggerLabel(labelFor, ariaLabel)
739
+ })
740
+ })
741
+ }), (0, react_jsx_runtime.jsx)(require_toaster.PopoverContent, {
742
+ className: "w-[220px] p-0",
743
+ align: "start",
744
+ children: (0, react_jsx_runtime.jsxs)(require_search_access.Command, { children: [(0, react_jsx_runtime.jsx)(require_search_access.CommandInput, {
745
+ placeholder: ariaLabel,
746
+ "data-testid": `${testId}:search`
747
+ }), (0, react_jsx_runtime.jsxs)(require_search_access.CommandList, { children: [
748
+ isLoading && (0, react_jsx_runtime.jsxs)("div", {
749
+ className: "flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground",
750
+ "data-testid": `${testId}:loading`,
751
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
752
+ className: "h-4 w-4 animate-spin",
753
+ "aria-hidden": "true"
754
+ }), loadingLabel]
755
+ }),
756
+ !isLoading && (0, react_jsx_runtime.jsx)(require_search_access.CommandEmpty, { children: t("relation.noResults", { defaultValue: "No results." }) }),
757
+ !isLoading && (0, react_jsx_runtime.jsxs)(require_search_access.CommandGroup, { children: [arity === "single" && selected.size > 0 && (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
758
+ "data-testid": `${testId}:clear`,
759
+ value: "__clear__",
760
+ onSelect: () => clear(),
761
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
762
+ className: "mr-2 h-4 w-4",
763
+ "aria-hidden": "true"
764
+ }), clearLabel]
765
+ }), options.map((entry) => {
766
+ const isSelected = selected.has(entry.value);
767
+ const label = labelFor(entry.value);
768
+ return (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
769
+ value: label,
770
+ "data-testid": `${testId}:option:${entry.value}`,
771
+ onSelect: () => select(entry.value),
772
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, {
773
+ className: require_use_theme.cn("mr-2 h-4 w-4", isSelected ? "opacity-100" : "opacity-0"),
774
+ "aria-hidden": "true"
775
+ }), label]
776
+ }, entry.value);
777
+ })] })
778
+ ] })] })
779
+ })]
780
+ });
781
+ }
782
+ //#endregion
783
+ //#region src/list-kit/filter-builder/values/enum-value-control.tsx
784
+ function EnumValueControl(props) {
785
+ const { enumName, value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
786
+ const caps = (0, _burojs_react.useI18nCaps)();
787
+ const def = (0, _burojs_react.useApp)().registry.enums.get(enumName);
788
+ const { values, isLoading } = (0, _burojs_react.useEnumValues)(enumName);
789
+ const labelFor = (raw) => (0, _burojs_core.resolveEnumLabel)(caps, enumName, raw, (0, _burojs_core.enumValueText)(def, raw) ?? values?.find((v) => v.value === raw)?.label);
790
+ const options = (values ?? []).map((entry) => ({
791
+ value: entry.value,
792
+ label: entry.value
793
+ }));
794
+ return (0, react_jsx_runtime.jsx)(SelectListValueControl, {
795
+ value,
796
+ onChange,
797
+ testId,
798
+ arity,
799
+ autoFocus,
800
+ fieldLabel,
801
+ options,
802
+ labelFor,
803
+ isLoading,
804
+ fill
805
+ });
806
+ }
807
+ //#endregion
808
+ //#region src/list-kit/use-related-labels.ts
809
+ function useRelatedLabels(to, ids, by) {
810
+ const targetDef = (0, _burojs_react.useApp)().registry.get(to);
811
+ const idField = (targetDef?.data)?.idField;
812
+ const rowTitle = require_search_access.useRowTitle(to, targetDef?.displayField);
813
+ const field = by ?? (typeof idField === "string" && idField.length > 0 ? idField : "id");
814
+ const query = (0, _burojs_react.useList)(to, {
815
+ pagination: {
816
+ kind: "offset",
817
+ page: 1,
818
+ pageSize: Math.min(Math.max(1, ids.length), 100)
819
+ },
820
+ ...ids.length ? { filter: {
821
+ field,
822
+ op: "in",
823
+ value: ids
824
+ } } : {},
825
+ scope: "none",
826
+ enabled: ids.length > 0
827
+ });
828
+ return (0, react.useMemo)(() => {
829
+ const out = {};
830
+ for (const row of query.data?.data ?? []) {
831
+ const key = row[field];
832
+ if (key !== null && key !== void 0) out[String(key)] = rowTitle(row);
833
+ }
834
+ return out;
835
+ }, [
836
+ query.data,
837
+ rowTitle,
838
+ field
839
+ ]);
840
+ }
841
+ //#endregion
842
+ //#region src/list-kit/filter-builder/values/relation-value-control.tsx
843
+ function RelationValueControl(props) {
844
+ const { to, by, value, onChange, testId, arity, autoFocus, fieldLabel } = props;
845
+ const app = (0, _burojs_react.useApp)();
846
+ const t = (0, _burojs_react.useOptionalT)();
847
+ const targetDef = app.registry.get(to);
848
+ const displayField = targetDef?.displayField ?? "name";
849
+ const [search, setSearch] = (0, react.useState)("");
850
+ const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
851
+ value,
852
+ arity,
853
+ onChange
854
+ });
855
+ const rowTitle = require_search_access.useRowTitle(to, targetDef?.displayField);
856
+ const access = (0, _burojs_react.useListAccess)(to);
857
+ const searchable = (0, react.useMemo)(() => access.status === "ready" && require_search_access.narrowSearchDecl({ fields: [displayField] }, (name) => access.fieldVisible(name)) !== void 0, [access, displayField]);
858
+ const listQuery = (0, _burojs_react.useList)(to, {
859
+ pagination: {
860
+ kind: "offset",
861
+ page: 1,
862
+ pageSize: 10
863
+ },
864
+ ...searchable && search.trim() ? { filter: {
865
+ field: displayField,
866
+ op: "contains",
867
+ value: search.trim()
868
+ } } : {},
869
+ scope: "session",
870
+ enabled: open && access.status === "ready"
871
+ });
872
+ const rows = listQuery.data?.data ?? [];
873
+ const fallbackLabels = useRelatedLabels(to, Array.from(selected), by);
874
+ const idField = (targetDef?.data)?.idField;
875
+ const valueField = by ?? (typeof idField === "string" && idField.length > 0 ? idField : "id");
876
+ const keyOf = (row) => {
877
+ const key = row[valueField];
878
+ return key === null || key === void 0 || key === "" ? void 0 : String(key);
879
+ };
880
+ const labelFor = (id) => {
881
+ const fromRows = rows.find((r) => keyOf(r) === id);
882
+ if (fromRows) return rowTitle(fromRows);
883
+ return fallbackLabels[id] ?? id;
884
+ };
885
+ const ariaLabel = fieldLabel ? t("filters.valueFor", {
886
+ defaultValue: `Value for ${fieldLabel}`,
887
+ params: { field: fieldLabel }
888
+ }) : t("filters.value", { defaultValue: "Value" });
889
+ const clearLabel = t("filters.clear", { defaultValue: "Clear" });
890
+ return (0, react_jsx_runtime.jsxs)(require_toaster.Popover, {
891
+ open,
892
+ onOpenChange: setOpen,
893
+ children: [(0, react_jsx_runtime.jsx)(require_toaster.PopoverTrigger, {
894
+ asChild: true,
895
+ children: (0, react_jsx_runtime.jsx)(require_button.Button, {
896
+ type: "button",
897
+ variant: "outline",
898
+ size: "sm",
899
+ autoFocus,
900
+ "data-testid": testId,
901
+ "aria-label": ariaLabel,
902
+ className: require_use_theme.cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[220px]"),
903
+ children: (0, react_jsx_runtime.jsx)("span", {
904
+ className: "truncate",
905
+ children: triggerLabel(labelFor, ariaLabel)
906
+ })
907
+ })
908
+ }), (0, react_jsx_runtime.jsx)(require_toaster.PopoverContent, {
909
+ className: "w-[240px] p-0",
910
+ align: "start",
911
+ children: (0, react_jsx_runtime.jsxs)(require_search_access.Command, {
912
+ shouldFilter: false,
913
+ children: [searchable && (0, react_jsx_runtime.jsx)(require_search_access.CommandInput, {
914
+ value: search,
915
+ onValueChange: setSearch,
916
+ placeholder: ariaLabel,
917
+ "data-testid": `${testId}:search`
918
+ }), (0, react_jsx_runtime.jsxs)(require_search_access.CommandList, { children: [
919
+ listQuery.isLoading && (0, react_jsx_runtime.jsxs)("div", {
920
+ className: "flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground",
921
+ "data-testid": `${testId}:loading`,
922
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
923
+ className: "h-4 w-4 animate-spin",
924
+ "aria-hidden": "true"
925
+ }), t("table.loading", { defaultValue: "Loading…" })]
926
+ }),
927
+ !listQuery.isLoading && rows.length === 0 && (0, react_jsx_runtime.jsx)(require_search_access.CommandEmpty, { children: t("relation.noResults", { defaultValue: "No results." }) }),
928
+ !listQuery.isLoading && arity === "single" && selected.size > 0 && (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
929
+ "data-testid": `${testId}:clear`,
930
+ value: "__clear__",
931
+ onSelect: () => clear(),
932
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
933
+ className: "mr-2 h-4 w-4",
934
+ "aria-hidden": "true"
935
+ }), clearLabel]
936
+ }),
937
+ !listQuery.isLoading && rows.map((row) => {
938
+ const id = keyOf(row);
939
+ if (id === void 0) return null;
940
+ const label = rowTitle(row);
941
+ const isSelected = selected.has(id);
942
+ return (0, react_jsx_runtime.jsxs)(require_search_access.CommandItem, {
943
+ value: `${label} ${id}`,
944
+ "data-testid": `${testId}:option:${id}`,
945
+ onSelect: () => select(id),
946
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconCheck, {
947
+ className: require_use_theme.cn("mr-2 h-4 w-4", isSelected ? "opacity-100" : "opacity-0"),
948
+ "aria-hidden": "true"
949
+ }), (0, react_jsx_runtime.jsx)("span", {
950
+ className: "truncate",
951
+ children: label
952
+ })]
953
+ }, id);
954
+ })
955
+ ] })]
956
+ })
957
+ })]
958
+ });
959
+ }
960
+ //#endregion
961
+ //#region src/list-kit/filter-builder/values/scalar-value-input.tsx
962
+ function isDateTime(kind) {
963
+ return kind.kind === "date" && kind.time === true;
964
+ }
965
+ function inputTypeFor(kind) {
966
+ switch (kind.kind) {
967
+ case "number": return "number";
968
+ case "date": return isDateTime(kind) ? "datetime-local" : "date";
969
+ default: return "text";
970
+ }
971
+ }
972
+ function toText(kind, value) {
973
+ if (value === void 0 || value === null || value === "") return "";
974
+ if (!isDateTime(kind)) return String(value);
975
+ const d = new Date(String(value));
976
+ if (Number.isNaN(d.getTime())) return "";
977
+ const pad = (n) => String(n).padStart(2, "0");
978
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
979
+ }
980
+ function parseOne(kind, raw) {
981
+ if (raw === "") return void 0;
982
+ if (kind.kind === "number") {
983
+ const n = Number(raw);
984
+ return Number.isFinite(n) ? n : void 0;
985
+ }
986
+ if (isDateTime(kind)) {
987
+ const d = new Date(raw);
988
+ return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
989
+ }
990
+ return raw;
991
+ }
992
+ function ScalarValueInput(props) {
993
+ const t = (0, _burojs_react.useOptionalT)();
994
+ const inputType = inputTypeFor(props.kind);
995
+ const ariaLabel = props.fieldLabel ? t("filters.valueFor", {
996
+ defaultValue: `Value for ${props.fieldLabel}`,
997
+ params: { field: props.fieldLabel }
998
+ }) : t("filters.value", { defaultValue: "Value" });
999
+ if (props.arity === "multi") return (0, react_jsx_runtime.jsx)(ScalarMultiInput, {
1000
+ ...props,
1001
+ inputType,
1002
+ ariaLabel
1003
+ });
1004
+ return (0, react_jsx_runtime.jsx)(ScalarSingleInput, {
1005
+ ...props,
1006
+ inputType,
1007
+ ariaLabel
1008
+ });
1009
+ }
1010
+ function ScalarSingleInput(props) {
1011
+ const { value, onChange, kind, testId, autoFocus, inputType, ariaLabel, debounceMs = 300 } = props;
1012
+ const storedText = toText(kind, value);
1013
+ const [text, setText] = (0, react.useState)(storedText);
1014
+ const lastPushedRef = (0, react.useRef)(storedText);
1015
+ const timerRef = (0, react.useRef)(null);
1016
+ const pendingRawRef = (0, react.useRef)(null);
1017
+ const commitRef = (0, react.useRef)(() => {});
1018
+ (0, react.useEffect)(() => {
1019
+ commitRef.current = (raw) => {
1020
+ const parsed = parseOne(kind, raw);
1021
+ lastPushedRef.current = toText(kind, parsed);
1022
+ pendingRawRef.current = null;
1023
+ onChange(parsed);
1024
+ };
1025
+ });
1026
+ (0, react.useEffect)(() => {
1027
+ if (storedText !== lastPushedRef.current) {
1028
+ setText(storedText);
1029
+ lastPushedRef.current = storedText;
1030
+ if (timerRef.current) {
1031
+ clearTimeout(timerRef.current);
1032
+ timerRef.current = null;
1033
+ }
1034
+ pendingRawRef.current = null;
1035
+ }
1036
+ }, [storedText]);
1037
+ (0, react.useEffect)(() => () => {
1038
+ if (timerRef.current) clearTimeout(timerRef.current);
1039
+ if (pendingRawRef.current !== null) commitRef.current(pendingRawRef.current);
1040
+ }, []);
1041
+ const handleChange = (raw) => {
1042
+ setText(raw);
1043
+ if (timerRef.current) {
1044
+ clearTimeout(timerRef.current);
1045
+ timerRef.current = null;
1046
+ }
1047
+ if (debounceMs <= 0) {
1048
+ pendingRawRef.current = null;
1049
+ commitRef.current(raw);
1050
+ return;
1051
+ }
1052
+ pendingRawRef.current = raw;
1053
+ timerRef.current = setTimeout(() => commitRef.current(raw), debounceMs);
1054
+ };
1055
+ return (0, react_jsx_runtime.jsx)(require_input.Input, {
1056
+ type: inputType,
1057
+ value: text,
1058
+ autoFocus,
1059
+ "aria-label": ariaLabel,
1060
+ placeholder: props.placeholder,
1061
+ "data-testid": testId,
1062
+ onChange: (event) => handleChange(event.target.value),
1063
+ className: require_use_theme.cn("h-8", props.fill ? "w-full" : "w-[150px]")
1064
+ });
1065
+ }
1066
+ function ScalarMultiInput(props) {
1067
+ const { value, onChange, kind, testId, autoFocus, inputType, ariaLabel } = props;
1068
+ const chips = Array.isArray(value) ? value.map((v) => String(v)) : [];
1069
+ const [draft, setDraft] = (0, react.useState)("");
1070
+ const addChip = () => {
1071
+ const parsed = parseOne(kind, draft.trim());
1072
+ if (parsed === void 0) return;
1073
+ const text = String(parsed);
1074
+ setDraft("");
1075
+ if (chips.includes(text)) return;
1076
+ onChange([...chips, text]);
1077
+ };
1078
+ const removeChip = (chip) => {
1079
+ const next = chips.filter((c) => c !== chip);
1080
+ onChange(next.length === 0 ? void 0 : next);
1081
+ };
1082
+ return (0, react_jsx_runtime.jsxs)("div", {
1083
+ className: require_use_theme.cn("flex flex-wrap items-center gap-1", props.fill && "w-full"),
1084
+ children: [chips.map((chip) => (0, react_jsx_runtime.jsxs)(Badge, {
1085
+ variant: "secondary",
1086
+ "data-testid": `${testId}:chip:${chip}`,
1087
+ className: "gap-1 rounded-sm px-1 font-normal",
1088
+ children: [chip, (0, react_jsx_runtime.jsx)("button", {
1089
+ type: "button",
1090
+ "data-testid": `${testId}:chip:${chip}:remove`,
1091
+ "aria-label": `Remove ${chip}`,
1092
+ onClick: () => removeChip(chip),
1093
+ className: "rounded-sm hover:bg-accent",
1094
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
1095
+ className: "h-3 w-3",
1096
+ "aria-hidden": "true"
1097
+ })
1098
+ })]
1099
+ }, chip)), (0, react_jsx_runtime.jsx)(require_input.Input, {
1100
+ type: inputType,
1101
+ value: draft,
1102
+ autoFocus,
1103
+ "aria-label": ariaLabel,
1104
+ placeholder: props.placeholder,
1105
+ "data-testid": testId,
1106
+ onChange: (event) => setDraft(event.target.value),
1107
+ onKeyDown: (event) => {
1108
+ if (event.key !== "Enter") return;
1109
+ event.preventDefault();
1110
+ addChip();
1111
+ },
1112
+ className: require_use_theme.cn("h-8", props.fill ? "min-w-[8rem] flex-1" : "w-[120px]")
1113
+ })]
1114
+ });
1115
+ }
1116
+ //#endregion
1117
+ //#region src/list-kit/filter-builder/values/filter-value-control.tsx
1118
+ function FilterValueControl(props) {
1119
+ const arity = (0, _burojs_core.opArity)(props.op);
1120
+ if (arity === "none") return null;
1121
+ if (props.options && props.options.length > 0) return (0, react_jsx_runtime.jsx)(SelectListValueControl, {
1122
+ value: props.value,
1123
+ onChange: props.onChange,
1124
+ testId: props.testId,
1125
+ arity,
1126
+ autoFocus: props.autoFocus,
1127
+ fieldLabel: props.fieldLabel,
1128
+ options: props.options,
1129
+ fill: props.fill
1130
+ });
1131
+ switch (props.kind.kind) {
1132
+ case "enum": return (0, react_jsx_runtime.jsx)(EnumValueControl, {
1133
+ ...props,
1134
+ enumName: props.kind.enumName,
1135
+ arity
1136
+ });
1137
+ case "enum-values": return (0, react_jsx_runtime.jsx)(SelectListValueControl, {
1138
+ value: props.value,
1139
+ onChange: props.onChange,
1140
+ testId: props.testId,
1141
+ arity,
1142
+ autoFocus: props.autoFocus,
1143
+ fieldLabel: props.fieldLabel,
1144
+ options: props.kind.values.map((entry) => ({
1145
+ value: entry,
1146
+ label: entry
1147
+ }))
1148
+ });
1149
+ case "relation": return (0, react_jsx_runtime.jsx)(RelationValueControl, {
1150
+ ...props,
1151
+ to: props.kind.to,
1152
+ ...props.kind.by === void 0 ? {} : { by: props.kind.by },
1153
+ arity
1154
+ });
1155
+ case "boolean": return (0, react_jsx_runtime.jsx)(BooleanValueControl, {
1156
+ ...props,
1157
+ arity
1158
+ });
1159
+ default: return (0, react_jsx_runtime.jsx)(ScalarValueInput, {
1160
+ ...props,
1161
+ arity
1162
+ });
1163
+ }
1164
+ }
1165
+ //#endregion
1166
+ //#region src/list-kit/filter-builder/filter-row.tsx
1167
+ const ROW_CLASS = "flex min-w-0 items-center gap-2";
1168
+ const FIELD_CLASS = "h-8 min-w-0 shrink basis-40";
1169
+ const OPERATOR_CLASS = "h-8 min-w-0 shrink basis-[9.5rem]";
1170
+ function FilterRow(props) {
1171
+ const { row, path, available, onPatch, onRemove, testIdBase, debounceMs } = props;
1172
+ const t = (0, _burojs_react.useOptionalT)();
1173
+ const base = `${testIdBase}:row:${path.join("-")}`;
1174
+ const spec = available.find((s) => s.field === row.field);
1175
+ const removeLabel = t("filters.removeRow", { defaultValue: "Remove filter" });
1176
+ if (!spec) return (0, react_jsx_runtime.jsxs)("div", {
1177
+ className: ROW_CLASS,
1178
+ children: [
1179
+ (0, react_jsx_runtime.jsx)(require_button.Button, {
1180
+ type: "button",
1181
+ variant: "outline",
1182
+ size: "sm",
1183
+ disabled: true,
1184
+ "data-testid": `${base}:field`,
1185
+ className: require_use_theme.cn(FIELD_CLASS, "justify-start truncate opacity-60"),
1186
+ children: row.field
1187
+ }),
1188
+ (0, react_jsx_runtime.jsx)("div", { className: "flex-1" }),
1189
+ (0, react_jsx_runtime.jsx)(require_button.Button, {
1190
+ type: "button",
1191
+ variant: "ghost",
1192
+ size: "icon",
1193
+ "aria-label": removeLabel,
1194
+ "data-testid": `${base}:remove`,
1195
+ onClick: () => onRemove(path),
1196
+ className: "shrink-0",
1197
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
1198
+ className: "h-4 w-4",
1199
+ "aria-hidden": "true"
1200
+ })
1201
+ })
1202
+ ]
1203
+ });
1204
+ const fieldTestId = `${base}:field`;
1205
+ const opTestId = `${base}:op`;
1206
+ const handleFieldChange = (field) => {
1207
+ const op = available.find((s) => s.field === field)?.operators[0] ?? row.op;
1208
+ onPatch(path, {
1209
+ field,
1210
+ op
1211
+ });
1212
+ };
1213
+ const handleOpChange = (op) => onPatch(path, { op });
1214
+ const handleValueChange = (value) => onPatch(path, { value });
1215
+ const fieldAriaLabel = t("filters.field", { defaultValue: "Field" });
1216
+ const operatorAriaLabel = t("filters.operator", { defaultValue: "Operator" });
1217
+ return (0, react_jsx_runtime.jsxs)("div", {
1218
+ className: ROW_CLASS,
1219
+ children: [
1220
+ (0, react_jsx_runtime.jsxs)(Select, {
1221
+ value: row.field,
1222
+ onValueChange: handleFieldChange,
1223
+ children: [(0, react_jsx_runtime.jsx)(SelectTrigger, {
1224
+ "data-testid": fieldTestId,
1225
+ "aria-label": fieldAriaLabel,
1226
+ className: FIELD_CLASS,
1227
+ children: (0, react_jsx_runtime.jsx)(SelectValue, {})
1228
+ }), (0, react_jsx_runtime.jsx)(SelectContent, { children: available.map((s) => (0, react_jsx_runtime.jsx)(SelectItem, {
1229
+ value: s.field,
1230
+ "data-testid": `${fieldTestId}:option:${s.field}`,
1231
+ children: s.label
1232
+ }, s.field)) })]
1233
+ }),
1234
+ (0, react_jsx_runtime.jsxs)(Select, {
1235
+ value: row.op,
1236
+ onValueChange: handleOpChange,
1237
+ children: [(0, react_jsx_runtime.jsx)(SelectTrigger, {
1238
+ "data-testid": opTestId,
1239
+ "aria-label": operatorAriaLabel,
1240
+ className: OPERATOR_CLASS,
1241
+ children: (0, react_jsx_runtime.jsx)(SelectValue, {})
1242
+ }), (0, react_jsx_runtime.jsx)(SelectContent, { children: spec.operators.map((op) => (0, react_jsx_runtime.jsx)(SelectItem, {
1243
+ value: op,
1244
+ "data-testid": `${opTestId}:option:${op}`,
1245
+ children: t(`filters.operators.${op}`, { defaultValue: op })
1246
+ }, op)) })]
1247
+ }),
1248
+ (0, react_jsx_runtime.jsx)("div", {
1249
+ className: "min-w-0 flex-1",
1250
+ children: (0, react_jsx_runtime.jsx)(FilterValueControl, {
1251
+ kind: spec.kind,
1252
+ op: row.op,
1253
+ value: row.value,
1254
+ onChange: handleValueChange,
1255
+ testId: `${base}:value`,
1256
+ debounceMs,
1257
+ fieldLabel: spec.label,
1258
+ options: spec.options,
1259
+ fill: true
1260
+ })
1261
+ }),
1262
+ (0, react_jsx_runtime.jsx)(require_button.Button, {
1263
+ type: "button",
1264
+ variant: "ghost",
1265
+ size: "icon",
1266
+ "aria-label": removeLabel,
1267
+ "data-testid": `${base}:remove`,
1268
+ onClick: () => onRemove(path),
1269
+ className: "shrink-0",
1270
+ children: (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
1271
+ className: "h-4 w-4",
1272
+ "aria-hidden": "true"
1273
+ })
1274
+ })
1275
+ ]
1276
+ });
1277
+ }
1278
+ //#endregion
1279
+ //#region src/list-kit/filter-builder/filter-group-editor.tsx
1280
+ const JOIN_VALUES = ["and", "or"];
1281
+ const GROUP_CLASS = "grid grid-cols-[auto_minmax(0,1fr)] items-start gap-2";
1282
+ const CONNECTOR_CLASS = "pt-1.5 text-right text-sm text-muted-foreground";
1283
+ function FilterGroupEditor(props) {
1284
+ const { group, onChange } = props;
1285
+ const addGroupAt = (targetPath) => onChange(addGroup(group, targetPath));
1286
+ return (0, react_jsx_runtime.jsx)(GroupEditorInner, {
1287
+ ...props,
1288
+ addGroupAt
1289
+ });
1290
+ }
1291
+ function GroupEditorInner(props) {
1292
+ const { group, path, available, onChange, testIdBase, onRemove, addGroupAt, debounceMs } = props;
1293
+ const t = (0, _burojs_react.useOptionalT)();
1294
+ const base = `${testIdBase}:group:${path.join("-")}`;
1295
+ const canNestGroup = depthOf(path) < 3;
1296
+ const joinLabel = (join) => t(`filters.${join}`, { defaultValue: join });
1297
+ const handlePatch = (rowPath, patch) => {
1298
+ const index = rowPath[rowPath.length - 1];
1299
+ if (index === void 0) return;
1300
+ onChange(updateRow(group, [index], patch));
1301
+ };
1302
+ const handleRemove = (nodePath) => {
1303
+ const index = nodePath[nodePath.length - 1];
1304
+ if (index === void 0) return;
1305
+ onChange(removeAt(group, [index]));
1306
+ };
1307
+ const handleChildGroupChange = (index, next) => {
1308
+ if (next === group.rows[index]) return;
1309
+ const rows = group.rows.slice();
1310
+ rows[index] = next;
1311
+ onChange({
1312
+ ...group,
1313
+ rows
1314
+ });
1315
+ };
1316
+ const handleAddRow = () => onChange(addRow(group, [], available));
1317
+ const handleAddGroup = () => addGroupAt(path);
1318
+ const handleSetJoin = (join) => onChange(setJoin(group, [], join));
1319
+ const otherJoin = group.join === "and" ? "or" : "and";
1320
+ return (0, react_jsx_runtime.jsxs)("div", {
1321
+ className: GROUP_CLASS,
1322
+ "data-testid": base,
1323
+ children: [
1324
+ group.rows.map((node, index) => {
1325
+ const childPath = [...path, index];
1326
+ return (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
1327
+ className: CONNECTOR_CLASS,
1328
+ children: [
1329
+ index === 0 && t("filters.where", { defaultValue: "Where" }),
1330
+ index === 1 && (0, react_jsx_runtime.jsx)("button", {
1331
+ type: "button",
1332
+ "data-testid": `${base}:join`,
1333
+ "aria-label": t("filters.toggleJoin", {
1334
+ defaultValue: `Join: ${joinLabel(group.join)} — activate to switch to ${joinLabel(otherJoin)}`,
1335
+ params: {
1336
+ current: joinLabel(group.join),
1337
+ next: joinLabel(otherJoin)
1338
+ }
1339
+ }),
1340
+ "aria-pressed": group.join === "or",
1341
+ onClick: () => handleSetJoin(otherJoin),
1342
+ className: "inline-grid justify-items-end rounded-xs font-medium text-foreground underline underline-offset-2 hover:no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
1343
+ children: JOIN_VALUES.map((j) => (0, react_jsx_runtime.jsx)("span", {
1344
+ className: require_use_theme.cn("col-start-1 row-start-1", j !== group.join && "invisible"),
1345
+ children: joinLabel(j)
1346
+ }, j))
1347
+ }),
1348
+ index >= 2 && joinLabel(group.join)
1349
+ ]
1350
+ }), isDraftGroup(node) ? (0, react_jsx_runtime.jsx)("div", {
1351
+ className: "min-w-0 rounded-md border bg-muted/30 p-2",
1352
+ children: (0, react_jsx_runtime.jsx)(GroupEditorInner, {
1353
+ group: node,
1354
+ path: childPath,
1355
+ available,
1356
+ onChange: (next) => handleChildGroupChange(index, next),
1357
+ testIdBase,
1358
+ onRemove: handleRemove,
1359
+ addGroupAt,
1360
+ debounceMs
1361
+ })
1362
+ }) : (0, react_jsx_runtime.jsx)(FilterRow, {
1363
+ row: node,
1364
+ path: childPath,
1365
+ available,
1366
+ onPatch: handlePatch,
1367
+ onRemove: handleRemove,
1368
+ testIdBase,
1369
+ debounceMs
1370
+ })] }, node.rowId);
1371
+ }),
1372
+ (0, react_jsx_runtime.jsx)("div", { "aria-hidden": "true" }),
1373
+ (0, react_jsx_runtime.jsxs)("div", {
1374
+ className: "flex items-center gap-2",
1375
+ children: [
1376
+ (0, react_jsx_runtime.jsxs)(require_button.Button, {
1377
+ type: "button",
1378
+ variant: "ghost",
1379
+ size: "sm",
1380
+ "data-testid": `${base}:add-row`,
1381
+ onClick: handleAddRow,
1382
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconPlus, {
1383
+ className: "h-4 w-4",
1384
+ "aria-hidden": "true"
1385
+ }), t("filters.addRow", { defaultValue: "Add filter" })]
1386
+ }),
1387
+ canNestGroup && (0, react_jsx_runtime.jsxs)(require_button.Button, {
1388
+ type: "button",
1389
+ variant: "ghost",
1390
+ size: "sm",
1391
+ "data-testid": `${base}:add-group`,
1392
+ onClick: handleAddGroup,
1393
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconFolderPlus, {
1394
+ className: "h-4 w-4",
1395
+ "aria-hidden": "true"
1396
+ }), t("filters.addGroup", { defaultValue: "Add group" })]
1397
+ }),
1398
+ onRemove && (0, react_jsx_runtime.jsxs)(require_button.Button, {
1399
+ type: "button",
1400
+ variant: "ghost",
1401
+ size: "sm",
1402
+ "data-testid": `${base}:remove`,
1403
+ onClick: () => onRemove(path),
1404
+ children: [(0, react_jsx_runtime.jsx)(_tabler_icons_react.IconX, {
1405
+ className: "h-4 w-4",
1406
+ "aria-hidden": "true"
1407
+ }), t("filters.removeGroup", { defaultValue: "Remove group" })]
1408
+ })
1409
+ ]
1410
+ })
1411
+ ]
1412
+ });
1413
+ }
1414
+ //#endregion
1415
+ //#region src/list-kit/filter-builder/filter-panel.tsx
1416
+ function countGroupRows(group) {
1417
+ let total = 0;
1418
+ for (const row of group.rows) total += "rows" in row ? countGroupRows(row) : 1;
1419
+ return total;
1420
+ }
1421
+ function FilterPanel(props) {
1422
+ const { group, setGroup, available, setPage, testIdBase, open, onOpenChange, focusPath, externalRemoval } = props;
1423
+ const t = (0, _burojs_react.useOptionalT)();
1424
+ const [draft, setDraft] = (0, react.useState)(() => toDraft(group));
1425
+ const wasOpenRef = (0, react.useRef)(open);
1426
+ const topLevelIdsRef = (0, react.useRef)([]);
1427
+ const panelRef = (0, react.useRef)(null);
1428
+ (0, react.useEffect)(() => {
1429
+ if (open && !wasOpenRef.current) {
1430
+ const seeded = toDraft(group);
1431
+ setDraft(seeded);
1432
+ topLevelIdsRef.current = seeded.rows.map((node) => node.rowId);
1433
+ if (focusPath && focusPath.length > 0) {
1434
+ const targetTestId = `${testIdBase}:row:${focusPath.join("-")}:field`;
1435
+ requestAnimationFrame(() => {
1436
+ panelRef.current?.querySelector(`[data-testid="${targetTestId}"]`)?.focus();
1437
+ });
1438
+ }
1439
+ }
1440
+ wasOpenRef.current = open;
1441
+ }, [open]);
1442
+ (0, react.useEffect)(() => {
1443
+ if (!externalRemoval) return;
1444
+ const { index } = externalRemoval;
1445
+ const rowId = topLevelIdsRef.current[index];
1446
+ topLevelIdsRef.current = topLevelIdsRef.current.filter((_, i) => i !== index);
1447
+ if (rowId === void 0) return;
1448
+ setDraft((current) => {
1449
+ const foundIndex = current.rows.findIndex((node) => node.rowId === rowId);
1450
+ return foundIndex < 0 ? current : removeAt(current, [foundIndex]);
1451
+ });
1452
+ }, [externalRemoval]);
1453
+ const handleApply = () => {
1454
+ setGroup(pruneIncompleteRows(fromDraft(draft)));
1455
+ setPage(1);
1456
+ onOpenChange(false);
1457
+ };
1458
+ const handleReset = () => setDraft(EMPTY_DRAFT());
1459
+ const appliedCount = countGroupRows(group);
1460
+ return (0, react_jsx_runtime.jsxs)(require_toaster.Popover, {
1461
+ open,
1462
+ onOpenChange,
1463
+ children: [(0, react_jsx_runtime.jsx)(require_toaster.PopoverTrigger, {
1464
+ asChild: true,
1465
+ children: (0, react_jsx_runtime.jsxs)(require_button.Button, {
1466
+ type: "button",
1467
+ variant: "outline",
1468
+ size: "sm",
1469
+ "data-testid": `${testIdBase}:button`,
1470
+ children: [
1471
+ (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconFilter, {
1472
+ className: "h-4 w-4",
1473
+ "aria-hidden": "true"
1474
+ }),
1475
+ t("filters.title", { defaultValue: "Filters" }),
1476
+ appliedCount > 0 && (0, react_jsx_runtime.jsx)("span", {
1477
+ "data-testid": `${testIdBase}:count`,
1478
+ className: "ml-1 rounded-full bg-primary px-1.5 text-xs text-primary-foreground",
1479
+ children: appliedCount
1480
+ })
1481
+ ]
1482
+ })
1483
+ }), (0, react_jsx_runtime.jsxs)(require_toaster.PopoverContent, {
1484
+ ref: panelRef,
1485
+ "data-testid": `${testIdBase}:panel`,
1486
+ align: "start",
1487
+ className: "w-[min(46rem,calc(100vw-2rem))]",
1488
+ children: [(0, react_jsx_runtime.jsx)(FilterGroupEditor, {
1489
+ group: draft,
1490
+ path: [],
1491
+ available,
1492
+ onChange: setDraft,
1493
+ testIdBase,
1494
+ debounceMs: 0
1495
+ }), (0, react_jsx_runtime.jsxs)("div", {
1496
+ className: "mt-3 flex justify-end gap-2 border-t pt-3",
1497
+ children: [(0, react_jsx_runtime.jsx)(require_button.Button, {
1498
+ type: "button",
1499
+ variant: "ghost",
1500
+ size: "sm",
1501
+ "data-testid": `${testIdBase}:reset`,
1502
+ onClick: handleReset,
1503
+ children: t("filters.reset", { defaultValue: "Reset" })
1504
+ }), (0, react_jsx_runtime.jsx)(require_button.Button, {
1505
+ type: "button",
1506
+ size: "sm",
1507
+ "data-testid": `${testIdBase}:apply`,
1508
+ onClick: handleApply,
1509
+ children: t("filters.apply", { defaultValue: "Apply" })
1510
+ })]
1511
+ })]
1512
+ })]
1513
+ });
1514
+ }
1515
+ //#endregion
1516
+ //#region src/list-kit/filter-builder/pinned-filter-controls.tsx
1517
+ function isEmptyValue(value) {
1518
+ return value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
1519
+ }
1520
+ function PinnedFilterControls(props) {
1521
+ const { pinnedFields, value, set, testIdBase } = props;
1522
+ return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: pinnedFields.map((spec) => (0, react_jsx_runtime.jsx)(PinnedControl, {
1523
+ spec,
1524
+ value,
1525
+ set,
1526
+ testIdBase
1527
+ }, spec.field)) });
1528
+ }
1529
+ function PinnedControl(props) {
1530
+ const { spec, value, set, testIdBase } = props;
1531
+ const op = spec.operators[0] ?? "eq";
1532
+ const index = findOwnedIndex(value, spec);
1533
+ const owned = index >= 0 ? value[index] : void 0;
1534
+ const handleChange = (next) => {
1535
+ if (isEmptyValue(next)) {
1536
+ if (index < 0) return;
1537
+ set(value.filter((_, i) => i !== index));
1538
+ return;
1539
+ }
1540
+ const predicate = {
1541
+ field: spec.field,
1542
+ op,
1543
+ value: next
1544
+ };
1545
+ if (index < 0) set([...value, predicate]);
1546
+ else {
1547
+ const nextValue = value.slice();
1548
+ nextValue[index] = predicate;
1549
+ set(nextValue);
1550
+ }
1551
+ };
1552
+ return (0, react_jsx_runtime.jsx)(FilterValueControl, {
1553
+ kind: spec.kind,
1554
+ op,
1555
+ value: owned?.value,
1556
+ onChange: handleChange,
1557
+ testId: `${testIdBase}:pinned:${spec.field}`,
1558
+ fieldLabel: spec.label,
1559
+ placeholder: spec.label,
1560
+ options: spec.options
1561
+ });
1562
+ }
1563
+ //#endregion
1564
+ //#region src/list-kit/filter-builder/filter-builder.tsx
1565
+ const EMPTY_PINNED_FIELDS = Object.freeze([]);
1566
+ const EMPTY_PINNED_VALUE = Object.freeze([]);
1567
+ function FilterBuilder(props) {
1568
+ const { group, setGroup, available, fixed, pinnedFields = EMPTY_PINNED_FIELDS, pinnedValue = EMPTY_PINNED_VALUE, setPinnedValue, setPage, testIdBase } = props;
1569
+ const [open, setOpen] = (0, react.useState)(false);
1570
+ const [focusPath, setFocusPath] = (0, react.useState)(void 0);
1571
+ const [externalRemoval, setExternalRemoval] = (0, react.useState)(void 0);
1572
+ const handleOpenChange = (next) => {
1573
+ setOpen(next);
1574
+ if (!next) setFocusPath(void 0);
1575
+ };
1576
+ const setGroupResettingPage = (next) => {
1577
+ setPage(1);
1578
+ setGroup(next);
1579
+ };
1580
+ const setPinnedResettingPage = (next) => {
1581
+ setPage(1);
1582
+ setPinnedValue?.(next);
1583
+ };
1584
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1585
+ (0, react_jsx_runtime.jsx)(PinnedFilterControls, {
1586
+ pinnedFields,
1587
+ value: pinnedValue,
1588
+ set: setPinnedResettingPage,
1589
+ testIdBase
1590
+ }),
1591
+ available.length > 0 && (0, react_jsx_runtime.jsx)(FilterPanel, {
1592
+ group,
1593
+ setGroup,
1594
+ available,
1595
+ setPage,
1596
+ testIdBase,
1597
+ open,
1598
+ onOpenChange: handleOpenChange,
1599
+ focusPath,
1600
+ externalRemoval
1601
+ }),
1602
+ (0, react_jsx_runtime.jsx)(FilterBadges, {
1603
+ group,
1604
+ setGroup: setGroupResettingPage,
1605
+ available,
1606
+ fixed,
1607
+ pinnedFields,
1608
+ pinnedValue,
1609
+ setPinnedValue: setPinnedResettingPage,
1610
+ testIdBase,
1611
+ onOpenAt: (path) => {
1612
+ setFocusPath(path);
1613
+ setOpen(true);
1614
+ },
1615
+ onRemoved: (index) => setExternalRemoval({ index })
1616
+ })
1617
+ ] });
1618
+ }
1619
+ //#endregion
1620
+ Object.defineProperty(exports, "Badge", {
1621
+ enumerable: true,
1622
+ get: function() {
1623
+ return Badge;
1624
+ }
1625
+ });
1626
+ Object.defineProperty(exports, "FilterBadges", {
1627
+ enumerable: true,
1628
+ get: function() {
1629
+ return FilterBadges;
1630
+ }
1631
+ });
1632
+ Object.defineProperty(exports, "FilterBuilder", {
1633
+ enumerable: true,
1634
+ get: function() {
1635
+ return FilterBuilder;
1636
+ }
1637
+ });
1638
+ Object.defineProperty(exports, "FilterPanel", {
1639
+ enumerable: true,
1640
+ get: function() {
1641
+ return FilterPanel;
1642
+ }
1643
+ });
1644
+ Object.defineProperty(exports, "FilterValueControl", {
1645
+ enumerable: true,
1646
+ get: function() {
1647
+ return FilterValueControl;
1648
+ }
1649
+ });
1650
+ Object.defineProperty(exports, "PinnedFilterControls", {
1651
+ enumerable: true,
1652
+ get: function() {
1653
+ return PinnedFilterControls;
1654
+ }
1655
+ });
1656
+ Object.defineProperty(exports, "Select", {
1657
+ enumerable: true,
1658
+ get: function() {
1659
+ return Select;
1660
+ }
1661
+ });
1662
+ Object.defineProperty(exports, "SelectContent", {
1663
+ enumerable: true,
1664
+ get: function() {
1665
+ return SelectContent;
1666
+ }
1667
+ });
1668
+ Object.defineProperty(exports, "SelectGroup", {
1669
+ enumerable: true,
1670
+ get: function() {
1671
+ return SelectGroup;
1672
+ }
1673
+ });
1674
+ Object.defineProperty(exports, "SelectItem", {
1675
+ enumerable: true,
1676
+ get: function() {
1677
+ return SelectItem;
1678
+ }
1679
+ });
1680
+ Object.defineProperty(exports, "SelectLabel", {
1681
+ enumerable: true,
1682
+ get: function() {
1683
+ return SelectLabel;
1684
+ }
1685
+ });
1686
+ Object.defineProperty(exports, "SelectScrollDownButton", {
1687
+ enumerable: true,
1688
+ get: function() {
1689
+ return SelectScrollDownButton;
1690
+ }
1691
+ });
1692
+ Object.defineProperty(exports, "SelectScrollUpButton", {
1693
+ enumerable: true,
1694
+ get: function() {
1695
+ return SelectScrollUpButton;
1696
+ }
1697
+ });
1698
+ Object.defineProperty(exports, "SelectSeparator", {
1699
+ enumerable: true,
1700
+ get: function() {
1701
+ return SelectSeparator;
1702
+ }
1703
+ });
1704
+ Object.defineProperty(exports, "SelectTrigger", {
1705
+ enumerable: true,
1706
+ get: function() {
1707
+ return SelectTrigger;
1708
+ }
1709
+ });
1710
+ Object.defineProperty(exports, "SelectValue", {
1711
+ enumerable: true,
1712
+ get: function() {
1713
+ return SelectValue;
1714
+ }
1715
+ });
1716
+ Object.defineProperty(exports, "Table", {
1717
+ enumerable: true,
1718
+ get: function() {
1719
+ return Table;
1720
+ }
1721
+ });
1722
+ Object.defineProperty(exports, "TableBody", {
1723
+ enumerable: true,
1724
+ get: function() {
1725
+ return TableBody;
1726
+ }
1727
+ });
1728
+ Object.defineProperty(exports, "TableCaption", {
1729
+ enumerable: true,
1730
+ get: function() {
1731
+ return TableCaption;
1732
+ }
1733
+ });
1734
+ Object.defineProperty(exports, "TableCell", {
1735
+ enumerable: true,
1736
+ get: function() {
1737
+ return TableCell;
1738
+ }
1739
+ });
1740
+ Object.defineProperty(exports, "TableHead", {
1741
+ enumerable: true,
1742
+ get: function() {
1743
+ return TableHead;
1744
+ }
1745
+ });
1746
+ Object.defineProperty(exports, "TableHeader", {
1747
+ enumerable: true,
1748
+ get: function() {
1749
+ return TableHeader;
1750
+ }
1751
+ });
1752
+ Object.defineProperty(exports, "TableRow", {
1753
+ enumerable: true,
1754
+ get: function() {
1755
+ return TableRow;
1756
+ }
1757
+ });
1758
+ Object.defineProperty(exports, "badgeVariants", {
1759
+ enumerable: true,
1760
+ get: function() {
1761
+ return badgeVariants;
1762
+ }
1763
+ });
1764
+ Object.defineProperty(exports, "formatDate", {
1765
+ enumerable: true,
1766
+ get: function() {
1767
+ return formatDate;
1768
+ }
1769
+ });
1770
+ Object.defineProperty(exports, "formatNumber", {
1771
+ enumerable: true,
1772
+ get: function() {
1773
+ return formatNumber;
1774
+ }
1775
+ });
1776
+ Object.defineProperty(exports, "loadLocale", {
1777
+ enumerable: true,
1778
+ get: function() {
1779
+ return loadLocale;
1780
+ }
1781
+ });
1782
+ Object.defineProperty(exports, "saveLocale", {
1783
+ enumerable: true,
1784
+ get: function() {
1785
+ return saveLocale;
1786
+ }
1787
+ });
1788
+
1789
+ //# sourceMappingURL=filter-builder-nXqtM_h4.cjs.map