@bmsuisse/datagrid 0.37.1 → 0.39.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.
package/dist/index.js CHANGED
@@ -147,6 +147,72 @@ function evaluateFilter(row, filter) {
147
147
  return evaluateLeaf(row, filter);
148
148
  }
149
149
 
150
+ // src/filter/labels.tsx
151
+ import { createContext, useContext, useMemo } from "react";
152
+ import { jsx } from "react/jsx-runtime";
153
+ var defaultFilterLabels = {
154
+ filterAriaLabel: (header) => `Filter ${header}`,
155
+ searchPlaceholder: (header) => `Search ${header.toLowerCase()}...`,
156
+ selectAll: "Select all",
157
+ selectAllOfGroup: (group) => `Select all of ${group}`,
158
+ noMatches: "No matches.",
159
+ selectedCount: (count) => `${count} selected`,
160
+ stringPlaceholder: (header) => `Filter ${header.toLowerCase()}...`,
161
+ stringContains: "Contains",
162
+ stringIs: "Is",
163
+ stringStartsWith: "Starts with",
164
+ stringEndsWith: "Ends with",
165
+ operatorAriaLabel: (header) => `${header} filter operator`,
166
+ booleanAll: "All",
167
+ booleanYes: "Yes",
168
+ booleanNo: "No",
169
+ booleanAriaLabel: (header) => `${header} filter`,
170
+ minPlaceholder: "Min",
171
+ maxPlaceholder: "Max",
172
+ valuePlaceholder: "Value",
173
+ rangeSeparator: "to",
174
+ minimumAriaLabel: (header) => `${header} minimum`,
175
+ maximumAriaLabel: (header) => `${header} maximum`,
176
+ minimumSliderAriaLabel: (header) => `${header} minimum (slider)`,
177
+ maximumSliderAriaLabel: (header) => `${header} maximum (slider)`,
178
+ numberGreaterThan: "Greater than",
179
+ numberGreaterThanOrEqual: "Greater than or equal to",
180
+ numberLessThan: "Less than",
181
+ numberLessThanOrEqual: "Less than or equal to",
182
+ numberEquals: "Equals",
183
+ numberNotEquals: "Does not equal",
184
+ numberBetween: "Between",
185
+ clear: "Clear",
186
+ loading: "Loading\u2026",
187
+ loadFailed: "Couldn't load values.",
188
+ retry: "Retry",
189
+ dateToday: "Today",
190
+ dateLast7Days: "Last 7 days",
191
+ dateThisMonth: "This month",
192
+ dateCustom: "Custom"
193
+ };
194
+ function mergeFilterLabels(base, overrides) {
195
+ if (!overrides) return base;
196
+ const merged = { ...base };
197
+ for (const [key, value] of Object.entries(overrides)) {
198
+ if (value !== void 0) merged[key] = value;
199
+ }
200
+ return merged;
201
+ }
202
+ var FilterLabelsContext = createContext(defaultFilterLabels);
203
+ function FilterLabelsProvider({
204
+ labels,
205
+ children
206
+ }) {
207
+ const parent = useContext(FilterLabelsContext);
208
+ const value = useMemo(() => mergeFilterLabels(parent, labels), [parent, labels]);
209
+ return /* @__PURE__ */ jsx(FilterLabelsContext.Provider, { value, children });
210
+ }
211
+ function useFilterLabels(overrides) {
212
+ const fromContext = useContext(FilterLabelsContext);
213
+ return useMemo(() => mergeFilterLabels(fromContext, overrides), [fromContext, overrides]);
214
+ }
215
+
150
216
  // src/filter/BooleanFilter.tsx
151
217
  import { FunnelIcon } from "@heroicons/react/24/outline";
152
218
 
@@ -166,7 +232,7 @@ function stopRowClick(event) {
166
232
  }
167
233
 
168
234
  // src/components/ui/button.tsx
169
- import { jsx } from "react/jsx-runtime";
235
+ import { jsx as jsx2 } from "react/jsx-runtime";
170
236
  var buttonVariants = cva(
171
237
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
172
238
  {
@@ -193,7 +259,7 @@ var Button = forwardRef(
193
259
  ({ className, variant, size, asChild = false, ...props }, ref) => {
194
260
  const Comp = asChild ? Slot : "button";
195
261
  const variantClass = buttonVariants({ variant, size });
196
- return /* @__PURE__ */ jsx(Comp, { className: className ? cn(variantClass, className) : variantClass, ref, ...props });
262
+ return /* @__PURE__ */ jsx2(Comp, { className: className ? cn(variantClass, className) : variantClass, ref, ...props });
197
263
  }
198
264
  );
199
265
  Button.displayName = "Button";
@@ -201,10 +267,10 @@ Button.displayName = "Button";
201
267
  // src/components/ui/popover.tsx
202
268
  import * as PopoverPrimitive from "@radix-ui/react-popover";
203
269
  import { forwardRef as forwardRef2 } from "react";
204
- import { jsx as jsx2 } from "react/jsx-runtime";
270
+ import { jsx as jsx3 } from "react/jsx-runtime";
205
271
  var Popover = PopoverPrimitive.Root;
206
272
  var PopoverTrigger = PopoverPrimitive.Trigger;
207
- var PopoverContent = forwardRef2(({ className, align = "start", sideOffset = 4, collisionPadding = 8, ...props }, ref) => /* @__PURE__ */ jsx2(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx2(
273
+ var PopoverContent = forwardRef2(({ className, align = "start", sideOffset = 4, collisionPadding = 8, ...props }, ref) => /* @__PURE__ */ jsx3(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx3(
208
274
  PopoverPrimitive.Content,
209
275
  {
210
276
  ref,
@@ -222,9 +288,9 @@ PopoverContent.displayName = "PopoverContent";
222
288
 
223
289
  // src/components/ui/select.tsx
224
290
  import * as SelectPrimitive from "@radix-ui/react-select";
225
- import { Check, ChevronDown } from "lucide-react";
291
+ import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/outline";
226
292
  import { forwardRef as forwardRef3 } from "react";
227
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
293
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
228
294
  var Select = SelectPrimitive.Root;
229
295
  var SelectValue = SelectPrimitive.Value;
230
296
  var SelectTrigger = forwardRef3(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
@@ -238,12 +304,12 @@ var SelectTrigger = forwardRef3(({ className, children, ...props }, ref) => /* @
238
304
  ...props,
239
305
  children: [
240
306
  children,
241
- /* @__PURE__ */ jsx3(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx3(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
307
+ /* @__PURE__ */ jsx4(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "h-4 w-4 opacity-50" }) })
242
308
  ]
243
309
  }
244
310
  ));
245
311
  SelectTrigger.displayName = "SelectTrigger";
246
- var SelectContent = forwardRef3(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx3(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx3(
312
+ var SelectContent = forwardRef3(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx4(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx4(
247
313
  SelectPrimitive.Content,
248
314
  {
249
315
  ref,
@@ -254,7 +320,7 @@ var SelectContent = forwardRef3(({ className, children, position = "popper", ...
254
320
  className
255
321
  ),
256
322
  ...props,
257
- children: /* @__PURE__ */ jsx3(SelectPrimitive.Viewport, { className: "p-1", children })
323
+ children: /* @__PURE__ */ jsx4(SelectPrimitive.Viewport, { className: "p-1", children })
258
324
  }
259
325
  ) }));
260
326
  SelectContent.displayName = "SelectContent";
@@ -268,19 +334,19 @@ var SelectItem = forwardRef3(({ className, children, ...props }, ref) => /* @__P
268
334
  ),
269
335
  ...props,
270
336
  children: [
271
- /* @__PURE__ */ jsx3("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx3(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx3(Check, { className: "h-4 w-4" }) }) }),
272
- /* @__PURE__ */ jsx3(SelectPrimitive.ItemText, { children })
337
+ /* @__PURE__ */ jsx4("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx4(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx4(CheckIcon, { className: "h-4 w-4" }) }) }),
338
+ /* @__PURE__ */ jsx4(SelectPrimitive.ItemText, { children })
273
339
  ]
274
340
  }
275
341
  ));
276
342
  SelectItem.displayName = "SelectItem";
277
343
 
278
344
  // src/filter/BooleanFilter.tsx
279
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
345
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
280
346
  var BOOLEAN_CHOICES = [
281
- { value: "all", label: "All" },
282
- { value: "yes", label: "Yes" },
283
- { value: "no", label: "No" }
347
+ { value: "all", labelKey: "booleanAll" },
348
+ { value: "yes", labelKey: "booleanYes" },
349
+ { value: "no", labelKey: "booleanNo" }
284
350
  ];
285
351
  function choiceOf(value) {
286
352
  if (!value) return "all";
@@ -300,11 +366,13 @@ function BooleanFilter({
300
366
  column,
301
367
  value,
302
368
  onChange,
303
- bare = false
369
+ bare = false,
370
+ labels: labelOverrides
304
371
  }) {
372
+ const labels = useFilterLabels(labelOverrides);
305
373
  const choice = choiceOf(value);
306
374
  const isFiltered = choice !== "all";
307
- const summary = choice === "yes" ? "Yes" : choice === "no" ? "No" : "";
375
+ const summary = choice === "yes" ? labels.booleanYes : choice === "no" ? labels.booleanNo : "";
308
376
  function emit(next) {
309
377
  if (next === "all") {
310
378
  onChange(void 0);
@@ -321,36 +389,36 @@ function BooleanFilter({
321
389
  if (match) emit(match);
322
390
  },
323
391
  children: [
324
- /* @__PURE__ */ jsx4(SelectTrigger, { "aria-label": `${column.header} filter`, children: /* @__PURE__ */ jsx4(SelectValue, {}) }),
325
- /* @__PURE__ */ jsx4(SelectContent, { children: BOOLEAN_CHOICES.map((option) => /* @__PURE__ */ jsx4(SelectItem, { value: option.value, children: option.label }, option.value)) })
392
+ /* @__PURE__ */ jsx5(SelectTrigger, { "aria-label": labels.booleanAriaLabel(column.header), children: /* @__PURE__ */ jsx5(SelectValue, {}) }),
393
+ /* @__PURE__ */ jsx5(SelectContent, { children: BOOLEAN_CHOICES.map((option) => /* @__PURE__ */ jsx5(SelectItem, { value: option.value, children: labels[option.labelKey] }, option.value)) })
326
394
  ]
327
395
  }
328
396
  );
329
397
  if (bare) {
330
- return /* @__PURE__ */ jsx4("div", { "data-testid": `filter-${column.id}`, children: panel });
398
+ return /* @__PURE__ */ jsx5("div", { "data-testid": `filter-${column.id}`, children: panel });
331
399
  }
332
400
  return /* @__PURE__ */ jsxs2(Popover, { children: [
333
- /* @__PURE__ */ jsx4(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
401
+ /* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
334
402
  Button,
335
403
  {
336
404
  variant: "outline",
337
405
  size: "sm",
338
406
  className: cn("gap-1", isFiltered ? "max-w-[140px] px-2" : "w-8 justify-center px-0"),
339
- "aria-label": `Filter ${column.header}`,
407
+ "aria-label": labels.filterAriaLabel(column.header),
340
408
  "data-testid": `filter-${column.id}`,
341
409
  children: [
342
- /* @__PURE__ */ jsx4(
410
+ /* @__PURE__ */ jsx5(
343
411
  FunnelIcon,
344
412
  {
345
413
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
346
414
  "aria-hidden": true
347
415
  }
348
416
  ),
349
- isFiltered && /* @__PURE__ */ jsx4("span", { className: "truncate", children: summary })
417
+ isFiltered && /* @__PURE__ */ jsx5("span", { className: "truncate", children: summary })
350
418
  ]
351
419
  }
352
420
  ) }),
353
- /* @__PURE__ */ jsx4(PopoverContent, { className: "w-40 p-2", children: panel })
421
+ /* @__PURE__ */ jsx5(PopoverContent, { className: "w-40 p-2", children: panel })
354
422
  ] });
355
423
  }
356
424
 
@@ -359,13 +427,18 @@ import { CalendarDaysIcon } from "@heroicons/react/24/outline";
359
427
  import { endOfDay, endOfMonth, format, parseISO as parseISO2, startOfDay, startOfMonth, subDays } from "date-fns";
360
428
 
361
429
  // src/components/ui/calendar.tsx
362
- import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } from "lucide-react";
430
+ import {
431
+ ChevronDownIcon as ChevronDownIcon2,
432
+ ChevronLeftIcon,
433
+ ChevronRightIcon,
434
+ ChevronUpIcon
435
+ } from "@heroicons/react/24/outline";
363
436
  import { useEffect, useRef } from "react";
364
437
  import { DayPicker, getDefaultClassNames } from "react-day-picker";
365
- import { jsx as jsx5 } from "react/jsx-runtime";
438
+ import { jsx as jsx6 } from "react/jsx-runtime";
366
439
  function Calendar({ className, classNames, ...props }) {
367
440
  const defaultClassNames = getDefaultClassNames();
368
- return /* @__PURE__ */ jsx5(
441
+ return /* @__PURE__ */ jsx6(
369
442
  DayPicker,
370
443
  {
371
444
  className: cn("w-fit", className),
@@ -415,15 +488,15 @@ function Calendar({ className, classNames, ...props }) {
415
488
  },
416
489
  components: {
417
490
  Chevron: ({ className: chevronClassName, orientation }) => {
418
- const Icon2 = orientation === "up" ? ChevronUpIcon : orientation === "down" ? ChevronDownIcon : orientation === "right" ? ChevronRightIcon : ChevronLeftIcon;
419
- return /* @__PURE__ */ jsx5(Icon2, { className: cn("h-4 w-4", chevronClassName), "aria-hidden": true });
491
+ const Icon2 = orientation === "up" ? ChevronUpIcon : orientation === "down" ? ChevronDownIcon2 : orientation === "right" ? ChevronRightIcon : ChevronLeftIcon;
492
+ return /* @__PURE__ */ jsx6(Icon2, { className: cn("h-4 w-4", chevronClassName), "aria-hidden": true });
420
493
  },
421
494
  DayButton: ({ className: dayClassName, day: _day, modifiers, ...dayProps }) => {
422
495
  const ref = useRef(null);
423
496
  useEffect(() => {
424
497
  if (modifiers.focused) ref.current?.focus();
425
498
  }, [modifiers.focused]);
426
- return /* @__PURE__ */ jsx5(
499
+ return /* @__PURE__ */ jsx6(
427
500
  "button",
428
501
  {
429
502
  ref,
@@ -447,21 +520,21 @@ function Calendar({ className, classNames, ...props }) {
447
520
  }
448
521
 
449
522
  // src/filter/DateRangeFilter.tsx
450
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
523
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
451
524
  var PRESETS = [
452
525
  {
453
526
  key: "today",
454
- label: "Today",
527
+ labelKey: "dateToday",
455
528
  range: (today) => ({ from: startOfDay(today), to: endOfDay(today) })
456
529
  },
457
530
  {
458
531
  key: "last7",
459
- label: "Last 7 days",
532
+ labelKey: "dateLast7Days",
460
533
  range: (today) => ({ from: startOfDay(subDays(today, 6)), to: endOfDay(today) })
461
534
  },
462
535
  {
463
536
  key: "thisMonth",
464
- label: "This month",
537
+ labelKey: "dateThisMonth",
465
538
  range: (today) => ({ from: startOfMonth(today), to: endOfMonth(today) })
466
539
  }
467
540
  ];
@@ -486,8 +559,11 @@ function DateRangeFilter({
486
559
  value,
487
560
  onChange,
488
561
  today = /* @__PURE__ */ new Date(),
489
- bare = false
562
+ bare = false,
563
+ labels: labelOverrides
490
564
  }) {
565
+ const labels = useFilterLabels(labelOverrides);
566
+ const locale = labels.dateLocale ? { locale: labels.dateLocale } : void 0;
491
567
  const range = rangeOf(value);
492
568
  function emit(next) {
493
569
  if (!next?.from || !next.to) {
@@ -502,56 +578,56 @@ function DateRangeFilter({
502
578
  });
503
579
  }
504
580
  const isFiltered = range?.from !== void 0;
505
- const summary = range?.from ? range.to ? `${format(range.from, "PP")} - ${format(range.to, "PP")}` : format(range.from, "PP") : "";
581
+ const summary = range?.from ? range.to ? `${format(range.from, "PP", locale)} - ${format(range.to, "PP", locale)}` : format(range.from, "PP", locale) : "";
506
582
  const panel = /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
507
583
  /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap gap-2", children: [
508
- PRESETS.map((preset) => /* @__PURE__ */ jsx6(
584
+ PRESETS.map((preset) => /* @__PURE__ */ jsx7(
509
585
  Button,
510
586
  {
511
587
  type: "button",
512
588
  variant: "outline",
513
589
  size: "sm",
514
590
  onClick: () => emit(preset.range(today)),
515
- children: preset.label
591
+ children: labels[preset.labelKey]
516
592
  },
517
593
  preset.key
518
594
  )),
519
- /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", size: "sm", onClick: () => emit(void 0), children: "Custom" })
595
+ /* @__PURE__ */ jsx7(Button, { type: "button", variant: "outline", size: "sm", onClick: () => emit(void 0), children: labels.dateCustom })
520
596
  ] }),
521
- /* @__PURE__ */ jsx6(Calendar, { mode: "range", selected: range, onSelect: emit, numberOfMonths: 2 })
597
+ /* @__PURE__ */ jsx7(Calendar, { mode: "range", selected: range, onSelect: emit, numberOfMonths: 2, locale: labels.dateLocale })
522
598
  ] });
523
599
  if (bare) {
524
- return /* @__PURE__ */ jsx6("div", { "data-testid": `filter-${column.id}`, children: panel });
600
+ return /* @__PURE__ */ jsx7("div", { "data-testid": `filter-${column.id}`, children: panel });
525
601
  }
526
602
  return /* @__PURE__ */ jsxs3(Popover, { children: [
527
- /* @__PURE__ */ jsx6(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
603
+ /* @__PURE__ */ jsx7(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
528
604
  Button,
529
605
  {
530
606
  variant: "outline",
531
607
  size: "sm",
532
608
  className: cn("gap-1", isFiltered ? "max-w-[220px] px-2" : "w-8 justify-center px-0"),
533
- "aria-label": `Filter ${column.header}`,
609
+ "aria-label": labels.filterAriaLabel(column.header),
534
610
  "data-testid": `filter-${column.id}`,
535
611
  children: [
536
- /* @__PURE__ */ jsx6(CalendarDaysIcon, { className: "h-3.5 w-3.5 shrink-0 opacity-70", "aria-hidden": true }),
537
- isFiltered && /* @__PURE__ */ jsx6("span", { className: "truncate", children: summary })
612
+ /* @__PURE__ */ jsx7(CalendarDaysIcon, { className: "h-3.5 w-3.5 shrink-0 opacity-70", "aria-hidden": true }),
613
+ isFiltered && /* @__PURE__ */ jsx7("span", { className: "truncate", children: summary })
538
614
  ]
539
615
  }
540
616
  ) }),
541
- /* @__PURE__ */ jsx6(PopoverContent, { className: "w-auto", children: panel })
617
+ /* @__PURE__ */ jsx7(PopoverContent, { className: "w-auto max-w-[var(--radix-popover-content-available-width)]", children: panel })
542
618
  ] });
543
619
  }
544
620
 
545
621
  // src/filter/EnumFilter.tsx
546
622
  import { FunnelIcon as FunnelIcon2 } from "@heroicons/react/24/outline";
547
- import { useMemo, useState } from "react";
623
+ import { useMemo as useMemo2, useState } from "react";
548
624
 
549
625
  // src/components/ui/checkbox.tsx
550
626
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
551
- import { Check as Check2, Minus } from "lucide-react";
627
+ import { CheckIcon as CheckIcon2, MinusIcon } from "@heroicons/react/24/outline";
552
628
  import { forwardRef as forwardRef4 } from "react";
553
- import { jsx as jsx7 } from "react/jsx-runtime";
554
- var Checkbox = forwardRef4(({ className, ...props }, ref) => /* @__PURE__ */ jsx7(
629
+ import { jsx as jsx8 } from "react/jsx-runtime";
630
+ var Checkbox = forwardRef4(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
555
631
  CheckboxPrimitive.Root,
556
632
  {
557
633
  ref,
@@ -564,16 +640,16 @@ var Checkbox = forwardRef4(({ className, ...props }, ref) => /* @__PURE__ */ jsx
564
640
  className
565
641
  ),
566
642
  ...props,
567
- children: /* @__PURE__ */ jsx7(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: props.checked === "indeterminate" ? /* @__PURE__ */ jsx7(Minus, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx7(Check2, { className: "h-3 w-3" }) })
643
+ children: /* @__PURE__ */ jsx8(CheckboxPrimitive.Indicator, { className: "flex items-center justify-center text-current", children: props.checked === "indeterminate" ? /* @__PURE__ */ jsx8(MinusIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx8(CheckIcon2, { className: "h-3 w-3" }) })
568
644
  }
569
645
  ));
570
646
  Checkbox.displayName = "Checkbox";
571
647
 
572
648
  // src/components/ui/input.tsx
573
649
  import { forwardRef as forwardRef5 } from "react";
574
- import { jsx as jsx8 } from "react/jsx-runtime";
650
+ import { jsx as jsx9 } from "react/jsx-runtime";
575
651
  var Input = forwardRef5(({ className, type, ...props }, ref) => {
576
- return /* @__PURE__ */ jsx8(
652
+ return /* @__PURE__ */ jsx9(
577
653
  "input",
578
654
  {
579
655
  type,
@@ -589,7 +665,7 @@ var Input = forwardRef5(({ className, type, ...props }, ref) => {
589
665
  Input.displayName = "Input";
590
666
 
591
667
  // src/filter/EnumFilter.tsx
592
- import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
668
+ import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
593
669
  function selectedValuesOf(value) {
594
670
  if (!value || value.operator !== "in" || !Array.isArray(value.value)) return [];
595
671
  return value.value.filter((entry) => typeof entry === "string");
@@ -622,16 +698,18 @@ function EnumFilter({
622
698
  column,
623
699
  value,
624
700
  onChange,
625
- bare = false
701
+ bare = false,
702
+ labels: labelOverrides
626
703
  }) {
704
+ const labels = useFilterLabels(labelOverrides);
627
705
  const [search, setSearch] = useState("");
628
706
  const selected = selectedValuesOf(value);
629
- const visibleOptions = useMemo(() => {
707
+ const visibleOptions = useMemo2(() => {
630
708
  const term = search.trim().toLowerCase();
631
709
  if (!term) return column.options;
632
710
  return column.options.filter((option) => option.label.toLowerCase().includes(term));
633
711
  }, [column.options, search]);
634
- const renderChunks = useMemo(() => buildRenderChunks(visibleOptions), [visibleOptions]);
712
+ const renderChunks = useMemo2(() => buildRenderChunks(visibleOptions), [visibleOptions]);
635
713
  function emit(next) {
636
714
  onChange(next.length === 0 ? void 0 : { field: column.id, operator: "in", value: next });
637
715
  }
@@ -662,7 +740,7 @@ function EnumFilter({
662
740
  {
663
741
  className: "flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
664
742
  children: [
665
- /* @__PURE__ */ jsx9(
743
+ /* @__PURE__ */ jsx10(
666
744
  Checkbox,
667
745
  {
668
746
  checked: selected.includes(option.value),
@@ -676,29 +754,29 @@ function EnumFilter({
676
754
  );
677
755
  }
678
756
  const isFiltered = selected.length > 0;
679
- const summary = `${selected.length} selected`;
757
+ const summary = labels.selectedCount(selected.length);
680
758
  const panel = /* @__PURE__ */ jsxs4("div", { className: bare ? "flex w-full flex-col gap-2" : "flex w-64 flex-col gap-2 p-2", children: [
681
- /* @__PURE__ */ jsx9(
759
+ /* @__PURE__ */ jsx10(
682
760
  Input,
683
761
  {
684
- placeholder: `Search ${column.header.toLowerCase()}...`,
762
+ placeholder: labels.searchPlaceholder(column.header),
685
763
  value: search,
686
764
  onChange: (event) => setSearch(event.target.value)
687
765
  }
688
766
  ),
689
767
  /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 border-b px-1 pb-2 text-sm", children: [
690
- /* @__PURE__ */ jsx9(
768
+ /* @__PURE__ */ jsx10(
691
769
  Checkbox,
692
770
  {
693
771
  checked: someVisibleSelected ? "indeterminate" : allVisibleSelected,
694
772
  disabled: visibleValues.length === 0,
695
773
  onCheckedChange: toggleSelectAllVisible,
696
- "aria-label": "Select all"
774
+ "aria-label": labels.selectAll
697
775
  }
698
776
  ),
699
- "Select all"
777
+ labels.selectAll
700
778
  ] }),
701
- /* @__PURE__ */ jsx9("div", { className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: renderChunks.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "py-2 text-center text-sm text-muted-foreground", children: "No matches." }) : renderChunks.map((chunk) => {
779
+ /* @__PURE__ */ jsx10("div", { className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: renderChunks.length === 0 ? /* @__PURE__ */ jsx10("p", { className: "py-2 text-center text-sm text-muted-foreground", children: labels.noMatches }) : renderChunks.map((chunk) => {
702
780
  if (chunk.kind === "single") return renderOption(chunk.row.option);
703
781
  const headerState = groupCheckState(visibleOptions, selected, chunk.group);
704
782
  return (
@@ -713,12 +791,12 @@ function EnumFilter({
713
791
  "data-group-header": true,
714
792
  className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
715
793
  children: [
716
- /* @__PURE__ */ jsx9(
794
+ /* @__PURE__ */ jsx10(
717
795
  Checkbox,
718
796
  {
719
797
  checked: headerState === "indeterminate" ? "indeterminate" : headerState === "checked",
720
798
  onCheckedChange: () => toggleGroup(chunk.group),
721
- "aria-label": `Select all of ${chunk.group}`
799
+ "aria-label": labels.selectAllOfGroup(chunk.group)
722
800
  }
723
801
  ),
724
802
  chunk.group
@@ -731,30 +809,30 @@ function EnumFilter({
731
809
  }) })
732
810
  ] });
733
811
  if (bare) {
734
- return /* @__PURE__ */ jsx9("div", { "data-testid": `filter-${column.id}`, children: panel });
812
+ return /* @__PURE__ */ jsx10("div", { "data-testid": `filter-${column.id}`, children: panel });
735
813
  }
736
814
  return /* @__PURE__ */ jsxs4(Popover, { children: [
737
- /* @__PURE__ */ jsx9(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
815
+ /* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
738
816
  Button,
739
817
  {
740
818
  variant: "outline",
741
819
  size: "sm",
742
820
  className: cn("gap-1", isFiltered ? "max-w-[180px] px-2" : "w-8 justify-center px-0"),
743
- "aria-label": `Filter ${column.header}`,
821
+ "aria-label": labels.filterAriaLabel(column.header),
744
822
  "data-testid": `filter-${column.id}`,
745
823
  children: [
746
- /* @__PURE__ */ jsx9(
824
+ /* @__PURE__ */ jsx10(
747
825
  FunnelIcon2,
748
826
  {
749
827
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
750
828
  "aria-hidden": true
751
829
  }
752
830
  ),
753
- isFiltered && /* @__PURE__ */ jsx9("span", { className: "truncate", children: summary })
831
+ isFiltered && /* @__PURE__ */ jsx10("span", { className: "truncate", children: summary })
754
832
  ]
755
833
  }
756
834
  ) }),
757
- /* @__PURE__ */ jsx9(PopoverContent, { className: "w-64 p-2", children: panel })
835
+ /* @__PURE__ */ jsx10(PopoverContent, { className: "w-64 p-2", children: panel })
758
836
  ] });
759
837
  }
760
838
 
@@ -897,15 +975,15 @@ function descriptorFor(field, range) {
897
975
  }
898
976
 
899
977
  // src/filter/NumberComparisonFilter.tsx
900
- import { Fragment, jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
978
+ import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
901
979
  var NUMBER_OPERATORS = [
902
- { value: "gt", label: "Greater than" },
903
- { value: "gte", label: "Greater than or equal to" },
904
- { value: "lt", label: "Less than" },
905
- { value: "lte", label: "Less than or equal to" },
906
- { value: "eq", label: "Equals" },
907
- { value: "neq", label: "Does not equal" },
908
- { value: "between", label: "Between" }
980
+ { value: "gt", labelKey: "numberGreaterThan" },
981
+ { value: "gte", labelKey: "numberGreaterThanOrEqual" },
982
+ { value: "lt", labelKey: "numberLessThan" },
983
+ { value: "lte", labelKey: "numberLessThanOrEqual" },
984
+ { value: "eq", labelKey: "numberEquals" },
985
+ { value: "neq", labelKey: "numberNotEquals" },
986
+ { value: "between", labelKey: "numberBetween" }
909
987
  ];
910
988
  var OPERATOR_SYMBOL = {
911
989
  gt: ">",
@@ -939,8 +1017,10 @@ function NumberComparisonFilter({
939
1017
  column,
940
1018
  value,
941
1019
  onChange,
942
- bare = true
1020
+ bare = true,
1021
+ labels: labelOverrides
943
1022
  }) {
1023
+ const labels = useFilterLabels(labelOverrides);
944
1024
  const initial = stateFromValue(value);
945
1025
  const [operator, setOperator] = useState2(initial.operator);
946
1026
  const [text, setText] = useState2(initial.text);
@@ -994,20 +1074,20 @@ function NumberComparisonFilter({
994
1074
  emit(nextOperator, text, text2);
995
1075
  },
996
1076
  children: [
997
- /* @__PURE__ */ jsx10(SelectTrigger, { id: operatorId, "aria-label": `${column.header} filter operator`, children: /* @__PURE__ */ jsx10(SelectValue, {}) }),
998
- /* @__PURE__ */ jsx10(SelectContent, { children: NUMBER_OPERATORS.map((option) => /* @__PURE__ */ jsx10(SelectItem, { value: option.value, children: option.label }, option.value)) })
1077
+ /* @__PURE__ */ jsx11(SelectTrigger, { id: operatorId, "aria-label": labels.operatorAriaLabel(column.header), children: /* @__PURE__ */ jsx11(SelectValue, {}) }),
1078
+ /* @__PURE__ */ jsx11(SelectContent, { children: NUMBER_OPERATORS.map((option) => /* @__PURE__ */ jsx11(SelectItem, { value: option.value, children: labels[option.labelKey] }, option.value)) })
999
1079
  ]
1000
1080
  }
1001
1081
  ),
1002
1082
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1003
- /* @__PURE__ */ jsx10(
1083
+ /* @__PURE__ */ jsx11(
1004
1084
  Input,
1005
1085
  {
1006
1086
  id: valueId,
1007
1087
  type: "number",
1008
1088
  className: "min-w-0",
1009
- "aria-label": isBetween ? `${column.header} minimum` : column.header,
1010
- placeholder: isBetween ? "Min" : "Value",
1089
+ "aria-label": isBetween ? labels.minimumAriaLabel(column.header) : column.header,
1090
+ placeholder: isBetween ? labels.minPlaceholder : labels.valuePlaceholder,
1011
1091
  value: text,
1012
1092
  onChange: (event) => {
1013
1093
  setText(event.target.value);
@@ -1016,14 +1096,14 @@ function NumberComparisonFilter({
1016
1096
  }
1017
1097
  ),
1018
1098
  isBetween && /* @__PURE__ */ jsxs5(Fragment, { children: [
1019
- /* @__PURE__ */ jsx10("span", { className: "shrink-0 text-xs text-muted-foreground", children: "to" }),
1020
- /* @__PURE__ */ jsx10(
1099
+ /* @__PURE__ */ jsx11("span", { className: "shrink-0 text-xs text-muted-foreground", children: labels.rangeSeparator }),
1100
+ /* @__PURE__ */ jsx11(
1021
1101
  Input,
1022
1102
  {
1023
1103
  type: "number",
1024
1104
  className: "min-w-0",
1025
- "aria-label": `${column.header} maximum`,
1026
- placeholder: "Max",
1105
+ "aria-label": labels.maximumAriaLabel(column.header),
1106
+ placeholder: labels.maxPlaceholder,
1027
1107
  value: text2,
1028
1108
  onChange: (event) => {
1029
1109
  setText2(event.target.value);
@@ -1035,37 +1115,37 @@ function NumberComparisonFilter({
1035
1115
  ] })
1036
1116
  ] });
1037
1117
  if (bare) {
1038
- return /* @__PURE__ */ jsx10("div", { "data-testid": `filter-${column.id}`, children: panel });
1118
+ return /* @__PURE__ */ jsx11("div", { "data-testid": `filter-${column.id}`, children: panel });
1039
1119
  }
1040
1120
  return /* @__PURE__ */ jsxs5(Popover, { children: [
1041
- /* @__PURE__ */ jsx10(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
1121
+ /* @__PURE__ */ jsx11(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
1042
1122
  Button,
1043
1123
  {
1044
1124
  variant: "outline",
1045
1125
  size: "sm",
1046
1126
  className: cn("gap-1", isFiltered ? "max-w-[180px] px-2" : "w-8 justify-center px-0"),
1047
- "aria-label": `Filter ${column.header}`,
1127
+ "aria-label": labels.filterAriaLabel(column.header),
1048
1128
  "data-testid": `filter-${column.id}`,
1049
1129
  children: [
1050
- /* @__PURE__ */ jsx10(
1130
+ /* @__PURE__ */ jsx11(
1051
1131
  FunnelIcon3,
1052
1132
  {
1053
1133
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
1054
1134
  "aria-hidden": true
1055
1135
  }
1056
1136
  ),
1057
- isFiltered && /* @__PURE__ */ jsx10("span", { className: "truncate", children: summary })
1137
+ isFiltered && /* @__PURE__ */ jsx11("span", { className: "truncate", children: summary })
1058
1138
  ]
1059
1139
  }
1060
1140
  ) }),
1061
- /* @__PURE__ */ jsx10(PopoverContent, { className: "w-auto p-3", children: panel })
1141
+ /* @__PURE__ */ jsx11(PopoverContent, { className: "w-auto p-3", children: panel })
1062
1142
  ] });
1063
1143
  }
1064
1144
 
1065
1145
  // src/filter/NumberRangeFilter.tsx
1066
1146
  import { FunnelIcon as FunnelIcon4 } from "@heroicons/react/24/outline";
1067
1147
  import { useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState3 } from "react";
1068
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
1148
+ import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
1069
1149
  function parseNumber2(raw) {
1070
1150
  if (raw.trim() === "") return void 0;
1071
1151
  const parsed = Number(raw);
@@ -1075,8 +1155,10 @@ function NumberRangeFilter({
1075
1155
  column,
1076
1156
  value,
1077
1157
  onChange,
1078
- bare = false
1158
+ bare = false,
1159
+ labels: labelOverrides
1079
1160
  }) {
1161
+ const labels = useFilterLabels(labelOverrides);
1080
1162
  const initial = rangeOf2(value);
1081
1163
  const [minText, setMinText] = useState3(initial.min?.toString() ?? "");
1082
1164
  const [maxText, setMaxText] = useState3(initial.max?.toString() ?? "");
@@ -1102,13 +1184,13 @@ function NumberRangeFilter({
1102
1184
  const isFiltered = min !== void 0 || max !== void 0;
1103
1185
  const summary = min !== void 0 && max !== void 0 ? `${defaultFormat(column, min)} \u2013 ${defaultFormat(column, max)}` : min !== void 0 ? `\u2265 ${defaultFormat(column, min)}` : max !== void 0 ? `\u2264 ${defaultFormat(column, max)}` : "";
1104
1186
  const panel = /* @__PURE__ */ jsxs6("div", { className: "flex w-full max-w-[220px] items-center gap-2", children: [
1105
- /* @__PURE__ */ jsx11(
1187
+ /* @__PURE__ */ jsx12(
1106
1188
  Input,
1107
1189
  {
1108
1190
  id: minId,
1109
1191
  type: "number",
1110
- "aria-label": `${column.header} minimum`,
1111
- placeholder: "Min",
1192
+ "aria-label": labels.minimumAriaLabel(column.header),
1193
+ placeholder: labels.minPlaceholder,
1112
1194
  className: "min-w-0",
1113
1195
  value: minText,
1114
1196
  onChange: (event) => {
@@ -1117,14 +1199,14 @@ function NumberRangeFilter({
1117
1199
  }
1118
1200
  }
1119
1201
  ),
1120
- /* @__PURE__ */ jsx11("span", { className: "shrink-0 text-xs text-muted-foreground", children: "to" }),
1121
- /* @__PURE__ */ jsx11(
1202
+ /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-xs text-muted-foreground", children: labels.rangeSeparator }),
1203
+ /* @__PURE__ */ jsx12(
1122
1204
  Input,
1123
1205
  {
1124
1206
  id: maxId,
1125
1207
  type: "number",
1126
- "aria-label": `${column.header} maximum`,
1127
- placeholder: "Max",
1208
+ "aria-label": labels.maximumAriaLabel(column.header),
1209
+ placeholder: labels.maxPlaceholder,
1128
1210
  className: "min-w-0",
1129
1211
  value: maxText,
1130
1212
  onChange: (event) => {
@@ -1135,37 +1217,37 @@ function NumberRangeFilter({
1135
1217
  )
1136
1218
  ] });
1137
1219
  if (bare) {
1138
- return /* @__PURE__ */ jsx11("div", { "data-testid": `filter-${column.id}`, children: panel });
1220
+ return /* @__PURE__ */ jsx12("div", { "data-testid": `filter-${column.id}`, children: panel });
1139
1221
  }
1140
1222
  return /* @__PURE__ */ jsxs6(Popover, { children: [
1141
- /* @__PURE__ */ jsx11(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6(
1223
+ /* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6(
1142
1224
  Button,
1143
1225
  {
1144
1226
  variant: "outline",
1145
1227
  size: "sm",
1146
1228
  className: cn("gap-1", isFiltered ? "max-w-[180px] px-2" : "w-8 justify-center px-0"),
1147
- "aria-label": `Filter ${column.header}`,
1229
+ "aria-label": labels.filterAriaLabel(column.header),
1148
1230
  "data-testid": `filter-${column.id}`,
1149
1231
  children: [
1150
- /* @__PURE__ */ jsx11(
1232
+ /* @__PURE__ */ jsx12(
1151
1233
  FunnelIcon4,
1152
1234
  {
1153
1235
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
1154
1236
  "aria-hidden": true
1155
1237
  }
1156
1238
  ),
1157
- isFiltered && /* @__PURE__ */ jsx11("span", { className: "truncate", children: summary })
1239
+ isFiltered && /* @__PURE__ */ jsx12("span", { className: "truncate", children: summary })
1158
1240
  ]
1159
1241
  }
1160
1242
  ) }),
1161
- /* @__PURE__ */ jsx11(PopoverContent, { className: "w-auto p-3", children: panel })
1243
+ /* @__PURE__ */ jsx12(PopoverContent, { className: "w-auto p-3", children: panel })
1162
1244
  ] });
1163
1245
  }
1164
1246
 
1165
1247
  // src/filter/NumberHistogramFilter.tsx
1166
- import { FunnelIcon as FunnelIcon5 } from "@heroicons/react/24/outline";
1167
- import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "react";
1168
- import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
1248
+ import { ExclamationTriangleIcon, FunnelIcon as FunnelIcon5 } from "@heroicons/react/24/outline";
1249
+ import { useCallback, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
1250
+ import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
1169
1251
  var BAR_HEIGHT_PX = 56;
1170
1252
  var DEFAULT_BUCKETS = 24;
1171
1253
  function tLog(v) {
@@ -1182,35 +1264,42 @@ function NumberHistogramFilter({
1182
1264
  loadValues,
1183
1265
  format: format2 = (v) => v.toLocaleString(),
1184
1266
  buckets = DEFAULT_BUCKETS,
1185
- bare = true
1267
+ bare = true,
1268
+ labels: labelOverrides
1186
1269
  }) {
1270
+ const labels = useFilterLabels(labelOverrides);
1187
1271
  const [openState, setOpen] = useState4(false);
1188
1272
  const open = bare || openState;
1189
1273
  const trackRef = useRef4(null);
1190
1274
  const [minOnTop, setMinOnTop] = useState4(false);
1191
1275
  const [loadedValues, setLoadedValues] = useState4([]);
1192
1276
  const [loading, setLoading] = useState4(false);
1277
+ const [loadError, setLoadError] = useState4(void 0);
1278
+ const [retryKey, setRetryKey] = useState4(0);
1193
1279
  const { min, max } = rangeOf2(value);
1194
1280
  useEffect4(() => {
1195
1281
  if (!open || !loadValues) return;
1196
1282
  let cancelled = false;
1197
1283
  setLoading(true);
1284
+ setLoadError(void 0);
1198
1285
  loadValues().then((values) => {
1199
1286
  if (!cancelled) setLoadedValues(values);
1287
+ }).catch((err) => {
1288
+ if (!cancelled) setLoadError(err instanceof Error ? err.message : String(err));
1200
1289
  }).finally(() => {
1201
1290
  if (!cancelled) setLoading(false);
1202
1291
  });
1203
1292
  return () => {
1204
1293
  cancelled = true;
1205
1294
  };
1206
- }, [open, loadValues]);
1295
+ }, [open, loadValues, retryKey]);
1207
1296
  const sourceValues = allValues ?? loadedValues;
1208
- const nums = useMemo2(
1297
+ const nums = useMemo3(
1209
1298
  () => open ? sourceValues.filter((v) => v != null) : [],
1210
1299
  [sourceValues, open]
1211
1300
  );
1212
- const dataMin = useMemo2(() => nums.reduce((a, b) => Math.min(a, b), Infinity), [nums]);
1213
- const dataMax = useMemo2(() => nums.reduce((a, b) => Math.max(a, b), -Infinity), [nums]);
1301
+ const dataMin = useMemo3(() => nums.reduce((a, b) => Math.min(a, b), Infinity), [nums]);
1302
+ const dataMax = useMemo3(() => nums.reduce((a, b) => Math.max(a, b), -Infinity), [nums]);
1214
1303
  const activeMin = min ?? dataMin;
1215
1304
  const activeMax = max ?? dataMax;
1216
1305
  const [minText, setMinText] = useState4("");
@@ -1227,7 +1316,7 @@ function NumberHistogramFilter({
1227
1316
  (v) => logDataMax === logDataMin ? 0 : (tLog(v) - logDataMin) / (logDataMax - logDataMin) * 100,
1228
1317
  [logDataMin, logDataMax]
1229
1318
  );
1230
- const histBars = useMemo2(() => {
1319
+ const histBars = useMemo3(() => {
1231
1320
  if (!nums.length || dataMin === dataMax || !Number.isFinite(dataMin) || !Number.isFinite(dataMax)) return [];
1232
1321
  const step = (logDataMax - logDataMin) / buckets;
1233
1322
  const counts = Array(buckets).fill(0);
@@ -1266,8 +1355,22 @@ function NumberHistogramFilter({
1266
1355
  const isFiltered = value !== void 0;
1267
1356
  const summary = isFiltered ? `${format2(activeMin)} \u2013 ${format2(activeMax)}` : "";
1268
1357
  const panel = /* @__PURE__ */ jsxs7("div", { className: bare ? "flex w-full flex-col gap-3" : "flex w-80 flex-col gap-3", children: [
1269
- loading && /* @__PURE__ */ jsx12("p", { className: "text-xs text-muted-foreground", "data-testid": `filter-${column.id}-loading`, children: "Loading\u2026" }),
1270
- /* @__PURE__ */ jsx12("div", { className: "flex items-end gap-px", style: { height: BAR_HEIGHT_PX }, "data-testid": `filter-${column.id}-histogram`, children: histBars.map((bar, i) => /* @__PURE__ */ jsx12(
1358
+ loading && /* @__PURE__ */ jsx13("p", { className: "text-xs text-muted-foreground", "data-testid": `filter-${column.id}-loading`, children: labels.loading }),
1359
+ loadError !== void 0 && !loading && /* @__PURE__ */ jsxs7(
1360
+ "span",
1361
+ {
1362
+ role: "alert",
1363
+ title: loadError,
1364
+ className: "flex items-center gap-1.5 text-xs text-destructive",
1365
+ "data-testid": `filter-${column.id}-error`,
1366
+ children: [
1367
+ /* @__PURE__ */ jsx13(ExclamationTriangleIcon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }),
1368
+ labels.loadFailed,
1369
+ /* @__PURE__ */ jsx13("button", { type: "button", className: "underline underline-offset-2", onClick: () => setRetryKey((k) => k + 1), children: labels.retry })
1370
+ ]
1371
+ }
1372
+ ),
1373
+ /* @__PURE__ */ jsx13("div", { className: "flex items-end gap-px", style: { height: BAR_HEIGHT_PX }, "data-testid": `filter-${column.id}-histogram`, children: histBars.map((bar, i) => /* @__PURE__ */ jsx13(
1271
1374
  "div",
1272
1375
  {
1273
1376
  className: cn("flex-1 rounded-sm", bar.active ? "bg-primary/70" : "bg-muted-foreground/20"),
@@ -1276,11 +1379,11 @@ function NumberHistogramFilter({
1276
1379
  i
1277
1380
  )) }),
1278
1381
  /* @__PURE__ */ jsxs7("div", { ref: trackRef, className: "relative h-5", onMouseMove: handleTrackHover, children: [
1279
- /* @__PURE__ */ jsx12(
1382
+ /* @__PURE__ */ jsx13(
1280
1383
  "input",
1281
1384
  {
1282
1385
  type: "range",
1283
- "aria-label": `${column.header} minimum (slider)`,
1386
+ "aria-label": labels.minimumSliderAriaLabel(column.header),
1284
1387
  min: logDataMin,
1285
1388
  max: logDataMax,
1286
1389
  step: logStep,
@@ -1290,11 +1393,11 @@ function NumberHistogramFilter({
1290
1393
  style: { zIndex: minOnTop ? 5 : 3 }
1291
1394
  }
1292
1395
  ),
1293
- /* @__PURE__ */ jsx12(
1396
+ /* @__PURE__ */ jsx13(
1294
1397
  "input",
1295
1398
  {
1296
1399
  type: "range",
1297
- "aria-label": `${column.header} maximum (slider)`,
1400
+ "aria-label": labels.maximumSliderAriaLabel(column.header),
1298
1401
  min: logDataMin,
1299
1402
  max: logDataMax,
1300
1403
  step: logStep,
@@ -1304,14 +1407,14 @@ function NumberHistogramFilter({
1304
1407
  style: { zIndex: minOnTop ? 3 : 5 }
1305
1408
  }
1306
1409
  ),
1307
- /* @__PURE__ */ jsx12("div", { className: "pointer-events-none absolute top-1/2 h-1.5 w-full -translate-y-1/2 rounded-full bg-muted", children: /* @__PURE__ */ jsx12(
1410
+ /* @__PURE__ */ jsx13("div", { className: "pointer-events-none absolute top-1/2 h-1.5 w-full -translate-y-1/2 rounded-full bg-muted", children: /* @__PURE__ */ jsx13(
1308
1411
  "div",
1309
1412
  {
1310
1413
  className: "absolute h-full rounded-full bg-primary",
1311
1414
  style: { left: `${logPct(activeMin)}%`, right: `${100 - logPct(activeMax)}%` }
1312
1415
  }
1313
1416
  ) }),
1314
- [activeMin, activeMax].map((v, i) => /* @__PURE__ */ jsx12(
1417
+ [activeMin, activeMax].map((v, i) => /* @__PURE__ */ jsx13(
1315
1418
  "div",
1316
1419
  {
1317
1420
  className: "pointer-events-none absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary bg-background shadow",
@@ -1321,12 +1424,12 @@ function NumberHistogramFilter({
1321
1424
  ))
1322
1425
  ] }),
1323
1426
  /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
1324
- /* @__PURE__ */ jsx12(
1427
+ /* @__PURE__ */ jsx13(
1325
1428
  Input,
1326
1429
  {
1327
1430
  type: "text",
1328
1431
  inputMode: "numeric",
1329
- "aria-label": `${column.header} minimum`,
1432
+ "aria-label": labels.minimumAriaLabel(column.header),
1330
1433
  value: minText,
1331
1434
  onChange: (event) => {
1332
1435
  setMinText(event.target.value);
@@ -1335,13 +1438,13 @@ function NumberHistogramFilter({
1335
1438
  }
1336
1439
  }
1337
1440
  ),
1338
- /* @__PURE__ */ jsx12("span", { className: "shrink-0 text-xs text-muted-foreground", children: "to" }),
1339
- /* @__PURE__ */ jsx12(
1441
+ /* @__PURE__ */ jsx13("span", { className: "shrink-0 text-xs text-muted-foreground", children: labels.rangeSeparator }),
1442
+ /* @__PURE__ */ jsx13(
1340
1443
  Input,
1341
1444
  {
1342
1445
  type: "text",
1343
1446
  inputMode: "numeric",
1344
- "aria-label": `${column.header} maximum`,
1447
+ "aria-label": labels.maximumAriaLabel(column.header),
1345
1448
  value: maxText,
1346
1449
  onChange: (event) => {
1347
1450
  setMaxText(event.target.value);
@@ -1351,33 +1454,33 @@ function NumberHistogramFilter({
1351
1454
  }
1352
1455
  )
1353
1456
  ] }),
1354
- isFiltered && /* @__PURE__ */ jsx12(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onChange(void 0), children: "Clear" })
1457
+ isFiltered && /* @__PURE__ */ jsx13(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onChange(void 0), children: labels.clear })
1355
1458
  ] });
1356
1459
  if (bare) {
1357
- return /* @__PURE__ */ jsx12("div", { "data-testid": `filter-${column.id}`, children: panel });
1460
+ return /* @__PURE__ */ jsx13("div", { "data-testid": `filter-${column.id}`, children: panel });
1358
1461
  }
1359
1462
  return /* @__PURE__ */ jsxs7(Popover, { open, onOpenChange: setOpen, children: [
1360
- /* @__PURE__ */ jsx12(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs7(
1463
+ /* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs7(
1361
1464
  Button,
1362
1465
  {
1363
1466
  variant: "outline",
1364
1467
  size: "sm",
1365
1468
  className: cn("gap-1", isFiltered ? "max-w-[200px] px-2" : "w-8 justify-center px-0"),
1366
- "aria-label": `Filter ${column.header}`,
1469
+ "aria-label": labels.filterAriaLabel(column.header),
1367
1470
  "data-testid": `filter-${column.id}`,
1368
1471
  children: [
1369
- /* @__PURE__ */ jsx12(
1472
+ /* @__PURE__ */ jsx13(
1370
1473
  FunnelIcon5,
1371
1474
  {
1372
1475
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
1373
1476
  "aria-hidden": true
1374
1477
  }
1375
1478
  ),
1376
- isFiltered && /* @__PURE__ */ jsx12("span", { className: "truncate", children: summary })
1479
+ isFiltered && /* @__PURE__ */ jsx13("span", { className: "truncate", children: summary })
1377
1480
  ]
1378
1481
  }
1379
1482
  ) }),
1380
- /* @__PURE__ */ jsx12(PopoverContent, { className: "w-80", children: panel })
1483
+ /* @__PURE__ */ jsx13(PopoverContent, { className: "w-80", children: panel })
1381
1484
  ] });
1382
1485
  }
1383
1486
 
@@ -1429,20 +1532,22 @@ function facetedNumberValues(data, column, filter) {
1429
1532
  // src/filter/StringFilter.tsx
1430
1533
  import { FunnelIcon as FunnelIcon6 } from "@heroicons/react/24/outline";
1431
1534
  import { useEffect as useEffect5, useId as useId3, useState as useState5 } from "react";
1432
- import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1535
+ import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
1433
1536
  var STRING_OPERATORS = [
1434
- { value: "contains", label: "Contains" },
1435
- { value: "eq", label: "Is" },
1436
- { value: "startsWith", label: "Starts with" },
1437
- { value: "endsWith", label: "Ends with" }
1537
+ { value: "contains", labelKey: "stringContains" },
1538
+ { value: "eq", labelKey: "stringIs" },
1539
+ { value: "startsWith", labelKey: "stringStartsWith" },
1540
+ { value: "endsWith", labelKey: "stringEndsWith" }
1438
1541
  ];
1439
1542
  var DEFAULT_OPERATOR2 = "contains";
1440
1543
  function StringFilter({
1441
1544
  column,
1442
1545
  value,
1443
1546
  onChange,
1444
- bare = false
1547
+ bare = false,
1548
+ labels: labelOverrides
1445
1549
  }) {
1550
+ const labels = useFilterLabels(labelOverrides);
1446
1551
  const [operator, setOperator] = useState5(value?.operator ?? DEFAULT_OPERATOR2);
1447
1552
  const text = typeof value?.value === "string" ? value.value : "";
1448
1553
  const inputId = useId3();
@@ -1469,66 +1574,66 @@ function StringFilter({
1469
1574
  emit(nextOperator, text);
1470
1575
  },
1471
1576
  children: [
1472
- /* @__PURE__ */ jsx13(SelectTrigger, { "aria-label": `${column.header} filter operator`, children: /* @__PURE__ */ jsx13(SelectValue, {}) }),
1473
- /* @__PURE__ */ jsx13(SelectContent, { children: STRING_OPERATORS.map((option) => /* @__PURE__ */ jsx13(SelectItem, { value: option.value, children: option.label }, option.value)) })
1577
+ /* @__PURE__ */ jsx14(SelectTrigger, { "aria-label": labels.operatorAriaLabel(column.header), children: /* @__PURE__ */ jsx14(SelectValue, {}) }),
1578
+ /* @__PURE__ */ jsx14(SelectContent, { children: STRING_OPERATORS.map((option) => /* @__PURE__ */ jsx14(SelectItem, { value: option.value, children: labels[option.labelKey] }, option.value)) })
1474
1579
  ]
1475
1580
  }
1476
1581
  ),
1477
- /* @__PURE__ */ jsx13(
1582
+ /* @__PURE__ */ jsx14(
1478
1583
  Input,
1479
1584
  {
1480
1585
  id: inputId,
1481
1586
  value: text,
1482
- placeholder: `Filter ${column.header.toLowerCase()}...`,
1587
+ placeholder: labels.stringPlaceholder(column.header),
1483
1588
  onChange: (event) => emit(operator, event.target.value)
1484
1589
  }
1485
1590
  )
1486
1591
  ] });
1487
1592
  if (bare) {
1488
- return /* @__PURE__ */ jsx13("div", { "data-testid": `filter-${column.id}`, children: panel });
1593
+ return /* @__PURE__ */ jsx14("div", { "data-testid": `filter-${column.id}`, children: panel });
1489
1594
  }
1490
1595
  return /* @__PURE__ */ jsxs8(Popover, { children: [
1491
- /* @__PURE__ */ jsx13(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs8(
1596
+ /* @__PURE__ */ jsx14(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs8(
1492
1597
  Button,
1493
1598
  {
1494
1599
  variant: "outline",
1495
1600
  size: "sm",
1496
1601
  className: cn("gap-1", isFiltered ? "max-w-[160px] px-2" : "w-8 justify-center px-0"),
1497
- "aria-label": `Filter ${column.header}`,
1602
+ "aria-label": labels.filterAriaLabel(column.header),
1498
1603
  "data-testid": `filter-${column.id}`,
1499
1604
  children: [
1500
- /* @__PURE__ */ jsx13(
1605
+ /* @__PURE__ */ jsx14(
1501
1606
  FunnelIcon6,
1502
1607
  {
1503
1608
  className: cn("h-3.5 w-3.5 shrink-0", isFiltered ? "opacity-100 text-primary" : "opacity-40"),
1504
1609
  "aria-hidden": true
1505
1610
  }
1506
1611
  ),
1507
- isFiltered && /* @__PURE__ */ jsx13("span", { className: "truncate", children: text })
1612
+ isFiltered && /* @__PURE__ */ jsx14("span", { className: "truncate", children: text })
1508
1613
  ]
1509
1614
  }
1510
1615
  ) }),
1511
- /* @__PURE__ */ jsx13(PopoverContent, { className: "w-auto p-3", children: panel })
1616
+ /* @__PURE__ */ jsx14(PopoverContent, { className: "w-auto p-3", children: panel })
1512
1617
  ] });
1513
1618
  }
1514
1619
 
1515
1620
  // src/filter/registry.tsx
1516
- import { jsx as jsx14 } from "react/jsx-runtime";
1621
+ import { jsx as jsx15 } from "react/jsx-runtime";
1517
1622
  function renderDefaultFilterWidget(column, value, onChange) {
1518
1623
  const bare = column.filterDisplay !== "row";
1519
1624
  switch (column.type) {
1520
1625
  case "string":
1521
- return /* @__PURE__ */ jsx14(StringFilter, { column, value, onChange, bare });
1626
+ return /* @__PURE__ */ jsx15(StringFilter, { column, value, onChange, bare });
1522
1627
  case "enum":
1523
- return /* @__PURE__ */ jsx14(EnumFilter, { column, value, onChange, bare });
1628
+ return /* @__PURE__ */ jsx15(EnumFilter, { column, value, onChange, bare });
1524
1629
  case "boolean":
1525
- return /* @__PURE__ */ jsx14(BooleanFilter, { column, value, onChange, bare });
1630
+ return /* @__PURE__ */ jsx15(BooleanFilter, { column, value, onChange, bare });
1526
1631
  case "number":
1527
1632
  case "currency":
1528
- return /* @__PURE__ */ jsx14(NumberRangeFilter, { column, value, onChange, bare });
1633
+ return /* @__PURE__ */ jsx15(NumberRangeFilter, { column, value, onChange, bare });
1529
1634
  case "date":
1530
1635
  case "datetime":
1531
- return /* @__PURE__ */ jsx14(DateRangeFilter, { column, value, onChange, bare });
1636
+ return /* @__PURE__ */ jsx15(DateRangeFilter, { column, value, onChange, bare });
1532
1637
  }
1533
1638
  }
1534
1639
 
@@ -1538,13 +1643,13 @@ function editErrorId(rowId, columnId) {
1538
1643
  }
1539
1644
 
1540
1645
  // src/edit/EditFieldError.tsx
1541
- import { jsx as jsx15 } from "react/jsx-runtime";
1646
+ import { jsx as jsx16 } from "react/jsx-runtime";
1542
1647
  function EditFieldError({ id, message }) {
1543
- return /* @__PURE__ */ jsx15("span", { id, className: "mt-0.5 block text-xs text-destructive", children: message });
1648
+ return /* @__PURE__ */ jsx16("span", { id, className: "mt-0.5 block text-xs text-destructive", children: message });
1544
1649
  }
1545
1650
 
1546
1651
  // src/edit/BooleanEditor.tsx
1547
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
1652
+ import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
1548
1653
  function toBoolean(value) {
1549
1654
  if (typeof value === "boolean") return value;
1550
1655
  if (typeof value === "string") return value.trim().toLowerCase() === "true";
@@ -1565,7 +1670,7 @@ function BooleanEditor({
1565
1670
  // `mt-0.5` already provides the same spacing every other editor gets;
1566
1671
  // adding both would double it for this one editor only.
1567
1672
  /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center", children: [
1568
- /* @__PURE__ */ jsx16(
1673
+ /* @__PURE__ */ jsx17(
1569
1674
  Checkbox,
1570
1675
  {
1571
1676
  "aria-label": `Edit ${column.header}`,
@@ -1577,14 +1682,14 @@ function BooleanEditor({
1577
1682
  onCheckedChange: (next) => onChange(next === true)
1578
1683
  }
1579
1684
  ),
1580
- error && /* @__PURE__ */ jsx16(EditFieldError, { id: errorId, message: error })
1685
+ error && /* @__PURE__ */ jsx17(EditFieldError, { id: errorId, message: error })
1581
1686
  ] })
1582
1687
  );
1583
1688
  }
1584
1689
 
1585
1690
  // src/edit/DateEditor.tsx
1586
1691
  import { format as formatDate, parseISO as parseISO4 } from "date-fns";
1587
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1692
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1588
1693
  function inputValueOf(value, columnType) {
1589
1694
  const date = toDate(value);
1590
1695
  if (!date) return "";
@@ -1600,7 +1705,7 @@ function DateEditor({
1600
1705
  }) {
1601
1706
  const errorId = editErrorId(rowId, column.id);
1602
1707
  return /* @__PURE__ */ jsxs10("div", { children: [
1603
- /* @__PURE__ */ jsx17(
1708
+ /* @__PURE__ */ jsx18(
1604
1709
  Input,
1605
1710
  {
1606
1711
  type: column.type === "datetime" ? "datetime-local" : "date",
@@ -1615,12 +1720,12 @@ function DateEditor({
1615
1720
  onChange: (event) => onChange(event.target.value === "" ? null : parseISO4(event.target.value))
1616
1721
  }
1617
1722
  ),
1618
- error && /* @__PURE__ */ jsx17(EditFieldError, { id: errorId, message: error })
1723
+ error && /* @__PURE__ */ jsx18(EditFieldError, { id: errorId, message: error })
1619
1724
  ] });
1620
1725
  }
1621
1726
 
1622
1727
  // src/edit/EnumEditor.tsx
1623
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
1728
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
1624
1729
  function EnumEditor({
1625
1730
  column,
1626
1731
  rowId,
@@ -1641,7 +1746,7 @@ function EnumEditor({
1641
1746
  open,
1642
1747
  onOpenChange,
1643
1748
  children: [
1644
- /* @__PURE__ */ jsx18(
1749
+ /* @__PURE__ */ jsx19(
1645
1750
  SelectTrigger,
1646
1751
  {
1647
1752
  className: "h-8",
@@ -1650,14 +1755,14 @@ function EnumEditor({
1650
1755
  "aria-describedby": error ? errorId : void 0,
1651
1756
  "data-testid": `edit-${rowId}-${column.id}`,
1652
1757
  autoFocus,
1653
- children: /* @__PURE__ */ jsx18(SelectValue, {})
1758
+ children: /* @__PURE__ */ jsx19(SelectValue, {})
1654
1759
  }
1655
1760
  ),
1656
- /* @__PURE__ */ jsx18(SelectContent, { children: column.options.map((option) => /* @__PURE__ */ jsx18(SelectItem, { value: option.value, children: option.label }, option.value)) })
1761
+ /* @__PURE__ */ jsx19(SelectContent, { children: column.options.map((option) => /* @__PURE__ */ jsx19(SelectItem, { value: option.value, children: option.label }, option.value)) })
1657
1762
  ]
1658
1763
  }
1659
1764
  ),
1660
- error && /* @__PURE__ */ jsx18(EditFieldError, { id: errorId, message: error })
1765
+ error && /* @__PURE__ */ jsx19(EditFieldError, { id: errorId, message: error })
1661
1766
  ] });
1662
1767
  }
1663
1768
 
@@ -1666,9 +1771,9 @@ import { useLayoutEffect, useRef as useRef5 } from "react";
1666
1771
 
1667
1772
  // src/components/ui/textarea.tsx
1668
1773
  import { forwardRef as forwardRef6 } from "react";
1669
- import { jsx as jsx19 } from "react/jsx-runtime";
1774
+ import { jsx as jsx20 } from "react/jsx-runtime";
1670
1775
  var Textarea = forwardRef6(({ className, ...props }, ref) => {
1671
- return /* @__PURE__ */ jsx19(
1776
+ return /* @__PURE__ */ jsx20(
1672
1777
  "textarea",
1673
1778
  {
1674
1779
  className: cn(
@@ -1683,7 +1788,7 @@ var Textarea = forwardRef6(({ className, ...props }, ref) => {
1683
1788
  Textarea.displayName = "Textarea";
1684
1789
 
1685
1790
  // src/edit/MultilineStringEditor.tsx
1686
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
1791
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
1687
1792
  function MultilineStringEditor({
1688
1793
  column,
1689
1794
  rowId,
@@ -1707,7 +1812,7 @@ function MultilineStringEditor({
1707
1812
  }
1708
1813
  }, [text]);
1709
1814
  return /* @__PURE__ */ jsxs12("div", { children: [
1710
- /* @__PURE__ */ jsx20(
1815
+ /* @__PURE__ */ jsx21(
1711
1816
  Textarea,
1712
1817
  {
1713
1818
  ref: textareaRef,
@@ -1724,13 +1829,13 @@ function MultilineStringEditor({
1724
1829
  }
1725
1830
  }
1726
1831
  ),
1727
- error && /* @__PURE__ */ jsx20(EditFieldError, { id: errorId, message: error })
1832
+ error && /* @__PURE__ */ jsx21(EditFieldError, { id: errorId, message: error })
1728
1833
  ] });
1729
1834
  }
1730
1835
 
1731
1836
  // src/edit/NumberEditor.tsx
1732
1837
  import { useEffect as useEffect6, useRef as useRef6, useState as useState6 } from "react";
1733
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1838
+ import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1734
1839
  function textFromValue(value) {
1735
1840
  return value === null || value === void 0 ? "" : String(value);
1736
1841
  }
@@ -1751,7 +1856,7 @@ function NumberEditor({
1751
1856
  setText(textFromValue(value));
1752
1857
  }, [value]);
1753
1858
  return /* @__PURE__ */ jsxs13("div", { children: [
1754
- /* @__PURE__ */ jsx21(
1859
+ /* @__PURE__ */ jsx22(
1755
1860
  Input,
1756
1861
  {
1757
1862
  type: "number",
@@ -1770,12 +1875,12 @@ function NumberEditor({
1770
1875
  }
1771
1876
  }
1772
1877
  ),
1773
- error && /* @__PURE__ */ jsx21(EditFieldError, { id: errorId, message: error })
1878
+ error && /* @__PURE__ */ jsx22(EditFieldError, { id: errorId, message: error })
1774
1879
  ] });
1775
1880
  }
1776
1881
 
1777
1882
  // src/edit/StringEditor.tsx
1778
- import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1883
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1779
1884
  function StringEditor({
1780
1885
  column,
1781
1886
  rowId,
@@ -1786,7 +1891,7 @@ function StringEditor({
1786
1891
  }) {
1787
1892
  const errorId = editErrorId(rowId, column.id);
1788
1893
  return /* @__PURE__ */ jsxs14("div", { children: [
1789
- /* @__PURE__ */ jsx22(
1894
+ /* @__PURE__ */ jsx23(
1790
1895
  Input,
1791
1896
  {
1792
1897
  className: "h-8",
@@ -1799,26 +1904,26 @@ function StringEditor({
1799
1904
  onChange: (event) => onChange(event.target.value)
1800
1905
  }
1801
1906
  ),
1802
- error && /* @__PURE__ */ jsx22(EditFieldError, { id: errorId, message: error })
1907
+ error && /* @__PURE__ */ jsx23(EditFieldError, { id: errorId, message: error })
1803
1908
  ] });
1804
1909
  }
1805
1910
 
1806
1911
  // src/edit/registry.tsx
1807
- import { jsx as jsx23 } from "react/jsx-runtime";
1912
+ import { jsx as jsx24 } from "react/jsx-runtime";
1808
1913
  function renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus) {
1809
1914
  switch (column.type) {
1810
1915
  case "string":
1811
- return column.multiline ? /* @__PURE__ */ jsx23(MultilineStringEditor, { column, rowId, value, onChange, error, autoFocus }) : /* @__PURE__ */ jsx23(StringEditor, { column, rowId, value, onChange, error, autoFocus });
1916
+ return column.multiline ? /* @__PURE__ */ jsx24(MultilineStringEditor, { column, rowId, value, onChange, error, autoFocus }) : /* @__PURE__ */ jsx24(StringEditor, { column, rowId, value, onChange, error, autoFocus });
1812
1917
  case "enum":
1813
- return /* @__PURE__ */ jsx23(EnumEditor, { column, rowId, value, onChange, error, autoFocus });
1918
+ return /* @__PURE__ */ jsx24(EnumEditor, { column, rowId, value, onChange, error, autoFocus });
1814
1919
  case "boolean":
1815
- return /* @__PURE__ */ jsx23(BooleanEditor, { column, rowId, value, onChange, error, autoFocus });
1920
+ return /* @__PURE__ */ jsx24(BooleanEditor, { column, rowId, value, onChange, error, autoFocus });
1816
1921
  case "number":
1817
1922
  case "currency":
1818
- return /* @__PURE__ */ jsx23(NumberEditor, { column, rowId, value, onChange, error, autoFocus });
1923
+ return /* @__PURE__ */ jsx24(NumberEditor, { column, rowId, value, onChange, error, autoFocus });
1819
1924
  case "date":
1820
1925
  case "datetime":
1821
- return /* @__PURE__ */ jsx23(DateEditor, { column, rowId, value, onChange, error, autoFocus });
1926
+ return /* @__PURE__ */ jsx24(DateEditor, { column, rowId, value, onChange, error, autoFocus });
1822
1927
  }
1823
1928
  }
1824
1929
 
@@ -1931,7 +2036,7 @@ function useEditingState(editing, getRowId) {
1931
2036
  }
1932
2037
 
1933
2038
  // src/edit/renderEditableCell.tsx
1934
- import { jsx as jsx24 } from "react/jsx-runtime";
2039
+ import { jsx as jsx25 } from "react/jsx-runtime";
1935
2040
  function renderEditableCell(column, row, rawValue, ctx) {
1936
2041
  const staticContent = (value2) => column.cell ? column.cell(value2, row) : defaultFormat(column, value2);
1937
2042
  if (!ctx || !isEditable(column, row)) return staticContent(rawValue);
@@ -1943,7 +2048,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1943
2048
  const autoFocus = ctx.autoFocusTarget?.rowId === rowId && ctx.autoFocusTarget.columnId === column.id;
1944
2049
  const onChange = (next) => ctx.onEdit(column, row, next);
1945
2050
  const editor = column.renderEditCell ? column.renderEditCell(value, row, onChange, error, autoFocus) : renderDefaultEditWidget(column, rowId, value, onChange, error, autoFocus);
1946
- return /* @__PURE__ */ jsx24(
2051
+ return /* @__PURE__ */ jsx25(
1947
2052
  "div",
1948
2053
  {
1949
2054
  onKeyDown: (event) => {
@@ -1956,7 +2061,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1956
2061
  }
1957
2062
  );
1958
2063
  }
1959
- return /* @__PURE__ */ jsx24(
2064
+ return /* @__PURE__ */ jsx25(
1960
2065
  "span",
1961
2066
  {
1962
2067
  role: "button",
@@ -1979,7 +2084,7 @@ function renderEditableCell(column, row, rawValue, ctx) {
1979
2084
  }
1980
2085
 
1981
2086
  // src/edit/EditingBar.tsx
1982
- import { jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
2087
+ import { jsx as jsx26, jsxs as jsxs15 } from "react/jsx-runtime";
1983
2088
  function resolveEditLabel(label, changedRowCount, fallback) {
1984
2089
  if (label === void 0) return fallback(changedRowCount);
1985
2090
  return typeof label === "function" ? label(changedRowCount) : label;
@@ -2017,7 +2122,7 @@ function EditingBar({
2017
2122
  ] })
2018
2123
  ] }),
2019
2124
  /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
2020
- /* @__PURE__ */ jsx25(
2125
+ /* @__PURE__ */ jsx26(
2021
2126
  Button,
2022
2127
  {
2023
2128
  type: "button",
@@ -2029,7 +2134,7 @@ function EditingBar({
2029
2134
  children: resolveEditLabel(editing.discardLabel, pendingRowCount, () => "Discard")
2030
2135
  }
2031
2136
  ),
2032
- /* @__PURE__ */ jsx25(
2137
+ /* @__PURE__ */ jsx26(
2033
2138
  Button,
2034
2139
  {
2035
2140
  type: "button",
@@ -2131,15 +2236,15 @@ function extendRangeForFillToCellRange(base, target, rowIds, columnIds, rowIndex
2131
2236
  }
2132
2237
 
2133
2238
  // src/cell-editing/useCellSelection.ts
2134
- import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef8, useState as useState8 } from "react";
2239
+ import { useCallback as useCallback2, useMemo as useMemo4, useRef as useRef8, useState as useState8 } from "react";
2135
2240
  function useCellSelection({ rowIds, columnIds, onNavigateToRow }) {
2136
2241
  const [selection, setSelectionState] = useState8(void 0);
2137
2242
  const [isDragging, setIsDragging] = useState8(false);
2138
2243
  const [dragFocus, setDragFocus] = useState8(void 0);
2139
2244
  const draggingRef = useRef8(false);
2140
2245
  const dragFocusRef = useRef8(void 0);
2141
- const rowIndex = useMemo3(() => buildIndexMap(rowIds), [rowIds]);
2142
- const columnIndex = useMemo3(() => buildIndexMap(columnIds), [columnIds]);
2246
+ const rowIndex = useMemo4(() => buildIndexMap(rowIds), [rowIds]);
2247
+ const columnIndex = useMemo4(() => buildIndexMap(columnIds), [columnIds]);
2143
2248
  const startSelection = useCallback2((cell, options) => {
2144
2249
  setSelectionState((prev) => options?.extend && prev ? { anchor: prev.anchor, focus: cell } : { anchor: cell, focus: cell });
2145
2250
  draggingRef.current = true;
@@ -2193,7 +2298,7 @@ function useCellSelection({ rowIds, columnIds, onNavigateToRow }) {
2193
2298
  setDragFocus(void 0);
2194
2299
  setSelectionState(void 0);
2195
2300
  }, []);
2196
- const effectiveRange = useMemo3(() => {
2301
+ const effectiveRange = useMemo4(() => {
2197
2302
  if (!selection) return void 0;
2198
2303
  if (isDragging && dragFocus) return { anchor: selection.anchor, focus: dragFocus };
2199
2304
  return selection;
@@ -2229,7 +2334,7 @@ function useCellSelection({ rowIds, columnIds, onNavigateToRow }) {
2229
2334
  setSelectionState(finalRange);
2230
2335
  return { sourceRange, finalRange };
2231
2336
  }, [rowIds, columnIds, rowIndex, columnIndex]);
2232
- const fillPreviewRange = useMemo3(() => {
2337
+ const fillPreviewRange = useMemo4(() => {
2233
2338
  if (!isFillDragging || !selection || !fillTarget) return void 0;
2234
2339
  return extendRangeForFillToCellRange(selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex);
2235
2340
  }, [isFillDragging, selection, fillTarget, rowIds, columnIds, rowIndex, columnIndex]);
@@ -2367,7 +2472,7 @@ function useCellEditingState(cellEditing, getRowId) {
2367
2472
  }
2368
2473
 
2369
2474
  // src/cell-editing/renderCellModeCell.tsx
2370
- import { jsx as jsx26 } from "react/jsx-runtime";
2475
+ import { jsx as jsx27 } from "react/jsx-runtime";
2371
2476
  function isAtomicEditorType(type) {
2372
2477
  return type === "enum" || type === "boolean" || type === "date" || type === "datetime";
2373
2478
  }
@@ -2378,7 +2483,7 @@ function renderCellModeCell(column, row, rawValue, ctx) {
2378
2483
  const value = ctx.resolveValue(rowId, column.id, rawValue);
2379
2484
  const isThisCellEditing = ctx.editingCell?.rowId === rowId && ctx.editingCell.columnId === column.id && isEditable(column, row);
2380
2485
  if (!isThisCellEditing) {
2381
- return /* @__PURE__ */ jsx26("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: staticContent(value) });
2486
+ return /* @__PURE__ */ jsx27("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: staticContent(value) });
2382
2487
  }
2383
2488
  const isAtomic = isAtomicEditorType(column.type);
2384
2489
  const error = ctx.getError(rowId, column.id);
@@ -2418,12 +2523,12 @@ function renderCellModeCell(column, row, rawValue, ctx) {
2418
2523
  if (validate(currentValue)) ctx.onCancelEdit();
2419
2524
  else ctx.onCommitEdit(row, value);
2420
2525
  }
2421
- return /* @__PURE__ */ jsx26("div", { onKeyDown: handleKeyDown, onBlur: handleBlur, children: editor });
2526
+ return /* @__PURE__ */ jsx27("div", { onKeyDown: handleKeyDown, onBlur: handleBlur, children: editor });
2422
2527
  }
2423
2528
 
2424
2529
  // src/cell-editing/SelectionOverlay.tsx
2425
2530
  import { useLayoutEffect as useLayoutEffect2, useRef as useRef10, useState as useState10 } from "react";
2426
- import { Fragment as Fragment2, jsx as jsx27, jsxs as jsxs16 } from "react/jsx-runtime";
2531
+ import { Fragment as Fragment2, jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
2427
2532
  function cellSelector(rowId, columnId) {
2428
2533
  const escape = (value) => typeof CSS !== "undefined" && CSS.escape ? CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
2429
2534
  return `[data-cell-row="${escape(rowId)}"][data-cell-col="${escape(columnId)}"]`;
@@ -2478,7 +2583,7 @@ function SelectionOverlay({
2478
2583
  }, [containerRef.current]);
2479
2584
  if (!rect) return null;
2480
2585
  return /* @__PURE__ */ jsxs16(Fragment2, { children: [
2481
- /* @__PURE__ */ jsx27(
2586
+ /* @__PURE__ */ jsx28(
2482
2587
  "div",
2483
2588
  {
2484
2589
  "data-testid": "cell-selection-overlay",
@@ -2487,7 +2592,7 @@ function SelectionOverlay({
2487
2592
  style: { top: rect.top, left: rect.left, width: rect.width, height: rect.height }
2488
2593
  }
2489
2594
  ),
2490
- previewRect && /* @__PURE__ */ jsx27(
2595
+ previewRect && /* @__PURE__ */ jsx28(
2491
2596
  "div",
2492
2597
  {
2493
2598
  "data-testid": "cell-fill-preview-overlay",
@@ -2496,7 +2601,7 @@ function SelectionOverlay({
2496
2601
  style: { top: previewRect.top, left: previewRect.left, width: previewRect.width, height: previewRect.height }
2497
2602
  }
2498
2603
  ),
2499
- onFillHandleMouseDown && /* @__PURE__ */ jsx27(
2604
+ onFillHandleMouseDown && /* @__PURE__ */ jsx28(
2500
2605
  "div",
2501
2606
  {
2502
2607
  "data-testid": "cell-fill-handle",
@@ -2701,17 +2806,17 @@ function computeFillChanges(sourceRange, finalRange, rowIds, columnIds, columns,
2701
2806
  }
2702
2807
 
2703
2808
  // src/column-selector/ColumnSelector.tsx
2704
- import { Columns3, GripVertical } from "lucide-react";
2809
+ import { Bars2Icon, ViewColumnsIcon } from "@heroicons/react/24/outline";
2705
2810
  import { useEffect as useEffect8, useState as useState11 } from "react";
2706
2811
 
2707
2812
  // src/components/ui/dialog.tsx
2708
2813
  import * as DialogPrimitive from "@radix-ui/react-dialog";
2709
- import { X } from "lucide-react";
2814
+ import { XMarkIcon } from "@heroicons/react/24/outline";
2710
2815
  import { forwardRef as forwardRef7 } from "react";
2711
- import { jsx as jsx28, jsxs as jsxs17 } from "react/jsx-runtime";
2816
+ import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
2712
2817
  var Dialog = DialogPrimitive.Root;
2713
2818
  var DialogTrigger = DialogPrimitive.Trigger;
2714
- var DialogOverlay = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(
2819
+ var DialogOverlay = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx29(
2715
2820
  DialogPrimitive.Overlay,
2716
2821
  {
2717
2822
  ref,
@@ -2721,7 +2826,7 @@ var DialogOverlay = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ *
2721
2826
  ));
2722
2827
  DialogOverlay.displayName = "DialogOverlay";
2723
2828
  var DialogContent = forwardRef7(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs17(DialogPrimitive.Portal, { children: [
2724
- /* @__PURE__ */ jsx28(DialogOverlay, {}),
2829
+ /* @__PURE__ */ jsx29(DialogOverlay, {}),
2725
2830
  /* @__PURE__ */ jsxs17(
2726
2831
  DialogPrimitive.Content,
2727
2832
  {
@@ -2734,8 +2839,8 @@ var DialogContent = forwardRef7(({ className, children, ...props }, ref) => /* @
2734
2839
  children: [
2735
2840
  children,
2736
2841
  /* @__PURE__ */ jsxs17(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
2737
- /* @__PURE__ */ jsx28(X, { className: "h-4 w-4" }),
2738
- /* @__PURE__ */ jsx28("span", { className: "sr-only", children: "Close" })
2842
+ /* @__PURE__ */ jsx29(XMarkIcon, { className: "h-4 w-4" }),
2843
+ /* @__PURE__ */ jsx29("span", { className: "sr-only", children: "Close" })
2739
2844
  ] })
2740
2845
  ]
2741
2846
  }
@@ -2745,10 +2850,10 @@ DialogContent.displayName = "DialogContent";
2745
2850
  var DialogHeader = ({
2746
2851
  className,
2747
2852
  ...props
2748
- }) => /* @__PURE__ */ jsx28("div", { className: cn("flex flex-col gap-1.5", className), ...props });
2749
- var DialogTitle = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
2853
+ }) => /* @__PURE__ */ jsx29("div", { className: cn("flex flex-col gap-1.5", className), ...props });
2854
+ var DialogTitle = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx29(DialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
2750
2855
  DialogTitle.displayName = "DialogTitle";
2751
- var DialogDescription = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx28(
2856
+ var DialogDescription = forwardRef7(({ className, ...props }, ref) => /* @__PURE__ */ jsx29(
2752
2857
  DialogPrimitive.Description,
2753
2858
  {
2754
2859
  ref,
@@ -2857,7 +2962,7 @@ function canHideGroup(columns, visibility, groupColumnIds) {
2857
2962
  }
2858
2963
 
2859
2964
  // src/column-selector/ColumnSelector.tsx
2860
- import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
2965
+ import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
2861
2966
  function ColumnSelector({
2862
2967
  columns,
2863
2968
  visibility,
@@ -2903,11 +3008,11 @@ function ColumnSelector({
2903
3008
  if (persistKey) writePersistedColumnOrder(persistKey, next);
2904
3009
  }
2905
3010
  return /* @__PURE__ */ jsxs18(Dialog, { open, onOpenChange, children: [
2906
- /* @__PURE__ */ jsx29(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx29(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx29(Columns3, { className: "h-4 w-4", "aria-hidden": true }) }) }),
3011
+ /* @__PURE__ */ jsx30(DialogTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx30(Button, { variant: "outline", size: "icon", "aria-label": "Choose columns", children: /* @__PURE__ */ jsx30(ViewColumnsIcon, { className: "h-4 w-4", "aria-hidden": true }) }) }),
2907
3012
  /* @__PURE__ */ jsxs18(DialogContent, { className: "w-fit max-w-[calc(100vw-2rem)] sm:max-w-2xl", children: [
2908
- /* @__PURE__ */ jsx29(DialogHeader, { children: /* @__PURE__ */ jsx29(DialogTitle, { children: "Columns" }) }),
2909
- /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs18("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
2910
- group && /* @__PURE__ */ jsx29("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
3013
+ /* @__PURE__ */ jsx30(DialogHeader, { children: /* @__PURE__ */ jsx30(DialogTitle, { children: "Columns" }) }),
3014
+ /* @__PURE__ */ jsx30("div", { className: "flex flex-wrap gap-x-8 gap-y-5", children: groups.map(({ group, columns: groupCols }) => /* @__PURE__ */ jsxs18("div", { className: "flex min-w-32 flex-col gap-1.5", children: [
3015
+ group && /* @__PURE__ */ jsx30("span", { className: "border-b pb-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: group }),
2911
3016
  groupCols.map((column) => {
2912
3017
  const visible = isColumnVisible(visibility, column.id);
2913
3018
  return /* @__PURE__ */ jsxs18(
@@ -2937,14 +3042,14 @@ function ColumnSelector({
2937
3042
  overId === column.id && draggedId !== null && draggedId !== column.id && "outline -outline-offset-2 outline-2 outline-primary"
2938
3043
  ),
2939
3044
  children: [
2940
- canReorder && /* @__PURE__ */ jsx29(
2941
- GripVertical,
3045
+ canReorder && /* @__PURE__ */ jsx30(
3046
+ Bars2Icon,
2942
3047
  {
2943
- className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/50",
3048
+ className: "h-3.5 w-3.5 shrink-0 rotate-90 text-muted-foreground/50",
2944
3049
  "aria-hidden": true
2945
3050
  }
2946
3051
  ),
2947
- /* @__PURE__ */ jsx29(
3052
+ /* @__PURE__ */ jsx30(
2948
3053
  Checkbox,
2949
3054
  {
2950
3055
  checked: visible,
@@ -2975,16 +3080,16 @@ function resolveMenuItems(items, ctx) {
2975
3080
  }
2976
3081
 
2977
3082
  // src/menu/ActionsMenu.tsx
2978
- import { MoreVertical } from "lucide-react";
2979
- import { useMemo as useMemo4 } from "react";
3083
+ import { EllipsisVerticalIcon } from "@heroicons/react/24/outline";
3084
+ import { useMemo as useMemo5 } from "react";
2980
3085
 
2981
3086
  // src/components/ui/dropdown-menu.tsx
2982
3087
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2983
3088
  import { forwardRef as forwardRef8 } from "react";
2984
- import { jsx as jsx30 } from "react/jsx-runtime";
3089
+ import { jsx as jsx31 } from "react/jsx-runtime";
2985
3090
  var DropdownMenu = DropdownMenuPrimitive.Root;
2986
3091
  var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
2987
- var DropdownMenuContent = forwardRef8(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx30(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx30(
3092
+ var DropdownMenuContent = forwardRef8(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx31(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx31(
2988
3093
  DropdownMenuPrimitive.Content,
2989
3094
  {
2990
3095
  ref,
@@ -2997,7 +3102,7 @@ var DropdownMenuContent = forwardRef8(({ className, sideOffset = 4, ...props },
2997
3102
  }
2998
3103
  ) }));
2999
3104
  DropdownMenuContent.displayName = "DropdownMenuContent";
3000
- var DropdownMenuItem = forwardRef8(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx30(
3105
+ var DropdownMenuItem = forwardRef8(({ className, danger, ...props }, ref) => /* @__PURE__ */ jsx31(
3001
3106
  DropdownMenuPrimitive.Item,
3002
3107
  {
3003
3108
  ref,
@@ -3010,7 +3115,7 @@ var DropdownMenuItem = forwardRef8(({ className, danger, ...props }, ref) => /*
3010
3115
  }
3011
3116
  ));
3012
3117
  DropdownMenuItem.displayName = "DropdownMenuItem";
3013
- var DropdownMenuSeparator = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx30(
3118
+ var DropdownMenuSeparator = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx31(
3014
3119
  DropdownMenuPrimitive.Separator,
3015
3120
  {
3016
3121
  ref,
@@ -3019,7 +3124,7 @@ var DropdownMenuSeparator = forwardRef8(({ className, ...props }, ref) => /* @__
3019
3124
  }
3020
3125
  ));
3021
3126
  DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
3022
- var DropdownMenuLabel = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx30(
3127
+ var DropdownMenuLabel = forwardRef8(({ className, ...props }, ref) => /* @__PURE__ */ jsx31(
3023
3128
  DropdownMenuPrimitive.Label,
3024
3129
  {
3025
3130
  ref,
@@ -3030,16 +3135,16 @@ var DropdownMenuLabel = forwardRef8(({ className, ...props }, ref) => /* @__PURE
3030
3135
  DropdownMenuLabel.displayName = "DropdownMenuLabel";
3031
3136
 
3032
3137
  // src/menu/ActionsMenu.tsx
3033
- import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
3138
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
3034
3139
  function ActionsMenu({ items, ctx, triggerLabel }) {
3035
- const resolved = useMemo4(
3140
+ const resolved = useMemo5(
3036
3141
  () => resolveMenuItems(items, ctx),
3037
3142
  [items, ctx.row, ctx.selectedRows]
3038
3143
  );
3039
3144
  if (resolved.length === 0) return null;
3040
3145
  return /* @__PURE__ */ jsxs19(DropdownMenu, { children: [
3041
- /* @__PURE__ */ jsx31(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx31(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx31(MoreVertical, { className: "h-4 w-4", "aria-hidden": true }) }) }),
3042
- /* @__PURE__ */ jsx31(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx31(
3146
+ /* @__PURE__ */ jsx32(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx32(Button, { variant: "ghost", size: "icon", "aria-label": triggerLabel, onClick: stopRowClick, children: /* @__PURE__ */ jsx32(EllipsisVerticalIcon, { className: "h-4 w-4", "aria-hidden": true }) }) }),
3147
+ /* @__PURE__ */ jsx32(DropdownMenuContent, { align: "end", children: resolved.map((item) => /* @__PURE__ */ jsx32(
3043
3148
  DropdownMenuItem,
3044
3149
  {
3045
3150
  danger: item.danger,
@@ -3053,18 +3158,123 @@ function ActionsMenu({ items, ctx, triggerLabel }) {
3053
3158
  }
3054
3159
 
3055
3160
  // src/grid/DataGrid.tsx
3056
- import { BarsArrowDownIcon, BarsArrowUpIcon, FunnelIcon as FunnelIcon7 } from "@heroicons/react/24/outline";
3161
+ import {
3162
+ BarsArrowDownIcon,
3163
+ BarsArrowUpIcon,
3164
+ ChevronRightIcon as ChevronRightIcon2,
3165
+ FunnelIcon as FunnelIcon7
3166
+ } from "@heroicons/react/24/outline";
3057
3167
  import { columnResizingFeature, columnSizingFeature, flexRender, tableFeatures, useTable } from "@tanstack/react-table";
3058
3168
  import { useVirtualizer } from "@tanstack/react-virtual";
3059
- import { ChevronRight, Loader2 } from "lucide-react";
3060
- import { Fragment as Fragment3, useEffect as useEffect12, useMemo as useMemo8, useRef as useRef13, useState as useState16 } from "react";
3169
+
3170
+ // src/components/status.tsx
3171
+ import { ArrowPathIcon, ExclamationTriangleIcon as ExclamationTriangleIcon2 } from "@heroicons/react/24/outline";
3172
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
3173
+ function toErrorInfo(error) {
3174
+ if (error == null || error === false || error === "") return void 0;
3175
+ if (typeof error === "string") return { message: error };
3176
+ if (error instanceof Error) return { message: error.message || void 0, details: error.stack };
3177
+ if (typeof error === "object") {
3178
+ const { title, message, details } = error;
3179
+ if (title !== void 0 || message !== void 0 || details !== void 0) return { title, message, details };
3180
+ }
3181
+ try {
3182
+ return { message: typeof error === "object" ? "Unknown error" : String(error) };
3183
+ } catch {
3184
+ return { message: "Unknown error" };
3185
+ }
3186
+ }
3187
+ var defaultGridStatusLabels = {
3188
+ loading: "Loading...",
3189
+ noResults: "No results.",
3190
+ errorTitle: "Couldn't load data",
3191
+ retry: "Retry",
3192
+ showDetails: "Show details",
3193
+ nodeLoadFailed: "Failed to load."
3194
+ };
3195
+ function resolveStatusLabels(overrides) {
3196
+ return overrides ? { ...defaultGridStatusLabels, ...overrides } : defaultGridStatusLabels;
3197
+ }
3198
+ function LoadingRows({ label, rows = 4 }) {
3199
+ return /* @__PURE__ */ jsxs20("div", { role: "status", "aria-busy": "true", "data-testid": "grid-loading-rows", className: "flex flex-col gap-2 p-1", children: [
3200
+ /* @__PURE__ */ jsx33("span", { className: "sr-only", children: label }),
3201
+ Array.from({ length: rows }, (_, i) => /* @__PURE__ */ jsx33("div", { className: "h-4 animate-pulse rounded-md bg-muted", style: { width: i === rows - 1 ? "60%" : "100%" } }, i))
3202
+ ] });
3203
+ }
3204
+ function RefreshBar(props) {
3205
+ return /* @__PURE__ */ jsx33(
3206
+ "div",
3207
+ {
3208
+ role: "progressbar",
3209
+ "aria-label": "Refreshing",
3210
+ className: "pointer-events-none absolute inset-x-0 top-0 z-30 h-0.5 overflow-hidden bg-muted",
3211
+ ...props,
3212
+ children: /* @__PURE__ */ jsx33("div", { className: "h-full w-1/3 animate-pulse bg-primary motion-reduce:animate-none" })
3213
+ }
3214
+ );
3215
+ }
3216
+ function GridErrorState({
3217
+ error,
3218
+ onRetry,
3219
+ labels
3220
+ }) {
3221
+ const info = toErrorInfo(error);
3222
+ if (!info) return null;
3223
+ return /* @__PURE__ */ jsxs20(
3224
+ "div",
3225
+ {
3226
+ role: "alert",
3227
+ "data-testid": "grid-error-state",
3228
+ className: "flex min-h-24 w-full flex-col items-center justify-center gap-1 p-4 text-center text-sm",
3229
+ children: [
3230
+ /* @__PURE__ */ jsx33(ExclamationTriangleIcon2, { className: "h-8 w-8 text-destructive", "aria-hidden": "true" }),
3231
+ /* @__PURE__ */ jsx33("p", { className: "font-semibold", children: info.title ?? labels.errorTitle }),
3232
+ info.message ? /* @__PURE__ */ jsx33("p", { className: "text-muted-foreground", children: info.message }) : null,
3233
+ info.details ? /* @__PURE__ */ jsxs20("details", { className: "w-full max-w-lg text-left text-xs", children: [
3234
+ /* @__PURE__ */ jsx33("summary", { className: "cursor-pointer select-none underline underline-offset-2", children: labels.showDetails }),
3235
+ /* @__PURE__ */ jsx33("pre", { className: "mt-1 max-h-40 overflow-auto whitespace-pre-wrap break-words rounded bg-muted p-2 text-muted-foreground", children: info.details })
3236
+ ] }) : null,
3237
+ onRetry ? /* @__PURE__ */ jsxs20(Button, { type: "button", variant: "outline", size: "sm", className: "mt-2", onClick: onRetry, children: [
3238
+ /* @__PURE__ */ jsx33(ArrowPathIcon, { className: "h-4 w-4", "aria-hidden": "true" }),
3239
+ labels.retry
3240
+ ] }) : null
3241
+ ]
3242
+ }
3243
+ );
3244
+ }
3245
+ function GridInlineError({
3246
+ error,
3247
+ onRetry,
3248
+ labels,
3249
+ className
3250
+ }) {
3251
+ const info = toErrorInfo(error);
3252
+ if (!info) return null;
3253
+ return /* @__PURE__ */ jsxs20(
3254
+ "span",
3255
+ {
3256
+ role: "alert",
3257
+ "data-testid": "grid-inline-error",
3258
+ title: info.details,
3259
+ className: cn("inline-flex min-w-0 items-center gap-1.5 text-xs text-red-700 dark:text-red-300", className),
3260
+ children: [
3261
+ /* @__PURE__ */ jsx33(ExclamationTriangleIcon2, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }),
3262
+ /* @__PURE__ */ jsx33("span", { className: "truncate", children: info.message ?? info.title ?? labels.errorTitle }),
3263
+ onRetry ? /* @__PURE__ */ jsx33("button", { type: "button", className: "shrink-0 underline underline-offset-2", onClick: onRetry, children: labels.retry }) : null
3264
+ ]
3265
+ }
3266
+ );
3267
+ }
3268
+
3269
+ // src/grid/DataGrid.tsx
3270
+ import { Fragment as Fragment3, useEffect as useEffect12, useMemo as useMemo9, useRef as useRef13, useState as useState16 } from "react";
3061
3271
 
3062
3272
  // src/cell-editing/renderAlwaysEditCell.tsx
3063
3273
  import {
3064
3274
  useEffect as useEffect9,
3065
3275
  useState as useState12
3066
3276
  } from "react";
3067
- import { jsx as jsx32 } from "react/jsx-runtime";
3277
+ import { jsx as jsx34 } from "react/jsx-runtime";
3068
3278
  function AlwaysEditCell(props) {
3069
3279
  const { column, row, rawValue, ctx, atomicGesture } = props;
3070
3280
  const rowId = ctx.getRowId(row);
@@ -3077,7 +3287,7 @@ function AlwaysEditCell(props) {
3077
3287
  if (shouldOpenEnumNow) setEnumOpen(true);
3078
3288
  }, [shouldOpenEnumNow]);
3079
3289
  if (!isEditable(column, row)) {
3080
- return /* @__PURE__ */ jsx32("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: column.cell ? column.cell(resolvedValue, row) : defaultFormat(column, resolvedValue) });
3290
+ return /* @__PURE__ */ jsx34("span", { "data-testid": `cell-${rowId}-${column.id}`, className: "block w-full", children: column.cell ? column.cell(resolvedValue, row) : defaultFormat(column, resolvedValue) });
3081
3291
  }
3082
3292
  const isAtomic = isAtomicEditorType(column.type);
3083
3293
  const error = ctx.getError(rowId, column.id);
@@ -3144,7 +3354,7 @@ function AlwaysEditCell(props) {
3144
3354
  handleModifierCapture(event);
3145
3355
  handleEnumOpenCapture(event);
3146
3356
  }
3147
- return /* @__PURE__ */ jsx32(
3357
+ return /* @__PURE__ */ jsx34(
3148
3358
  "fieldset",
3149
3359
  {
3150
3360
  disabled: ctx.disabled,
@@ -3153,7 +3363,7 @@ function AlwaysEditCell(props) {
3153
3363
  onPointerDownCapture: handlePointerDownCapture,
3154
3364
  onClickCapture: handleClickCapture,
3155
3365
  className: "contents",
3156
- children: column.type === "enum" && !column.renderEditCell ? /* @__PURE__ */ jsx32(
3366
+ children: column.type === "enum" && !column.renderEditCell ? /* @__PURE__ */ jsx34(
3157
3367
  EnumEditor,
3158
3368
  {
3159
3369
  column,
@@ -3214,9 +3424,9 @@ function useStickyGroupHeaderTop(enabled) {
3214
3424
  }
3215
3425
 
3216
3426
  // src/hooks/useVisibleColumns.ts
3217
- import { useMemo as useMemo5 } from "react";
3427
+ import { useMemo as useMemo6 } from "react";
3218
3428
  function useVisibleColumns(columns, columnVisibility) {
3219
- return useMemo5(
3429
+ return useMemo6(
3220
3430
  () => columnVisibility ? columns.filter((column) => isColumnVisible(columnVisibility, column.id)) : columns,
3221
3431
  [columns, columnVisibility]
3222
3432
  );
@@ -3263,10 +3473,10 @@ function processClientData(data, state) {
3263
3473
  }
3264
3474
 
3265
3475
  // src/grid/useGridState.ts
3266
- import { useCallback as useCallback4, useEffect as useEffect11, useMemo as useMemo7, useState as useState15 } from "react";
3476
+ import { useCallback as useCallback4, useEffect as useEffect11, useMemo as useMemo8, useState as useState15 } from "react";
3267
3477
 
3268
3478
  // src/hooks/useDebouncedCallback.ts
3269
- import { useEffect as useEffect10, useMemo as useMemo6, useRef as useRef12 } from "react";
3479
+ import { useEffect as useEffect10, useMemo as useMemo7, useRef as useRef12 } from "react";
3270
3480
  function useDebouncedCallback(callback, delayMs) {
3271
3481
  const callbackRef = useRef12(callback);
3272
3482
  callbackRef.current = callback;
@@ -3276,7 +3486,7 @@ function useDebouncedCallback(callback, delayMs) {
3276
3486
  clearTimeout(timeoutRef.current);
3277
3487
  };
3278
3488
  }, []);
3279
- return useMemo6(() => {
3489
+ return useMemo7(() => {
3280
3490
  return {
3281
3491
  run: (...args) => {
3282
3492
  clearTimeout(timeoutRef.current);
@@ -3329,7 +3539,7 @@ function useGridState(dataSource, initialState, externalState, showPagination =
3329
3539
  cancelDebouncedNotify();
3330
3540
  setState(externalState);
3331
3541
  }, [externalState, cancelDebouncedNotify]);
3332
- const filtersByColumn = useMemo7(() => {
3542
+ const filtersByColumn = useMemo8(() => {
3333
3543
  const map = /* @__PURE__ */ new Map();
3334
3544
  for (const child of state.filter?.filters ?? []) {
3335
3545
  if ("operator" in child) map.set(fieldKey(child.field), child);
@@ -3393,7 +3603,7 @@ function useGridState(dataSource, initialState, externalState, showPagination =
3393
3603
  }
3394
3604
 
3395
3605
  // src/grid/DataGrid.tsx
3396
- import { Fragment as Fragment4, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
3606
+ import { Fragment as Fragment4, jsx as jsx35, jsxs as jsxs21 } from "react/jsx-runtime";
3397
3607
  var gridTableFeatures = tableFeatures({ columnSizingFeature, columnResizingFeature });
3398
3608
  var DEFAULT_COLUMN_SIZE = 150;
3399
3609
  var MIN_COLUMN_SIZE = 40;
@@ -3444,7 +3654,7 @@ function toTanstackColumns(columns, editingCtxRef, cellEditingCtxRef, atomicGest
3444
3654
  cell: (info) => {
3445
3655
  const cellEditingCtx = cellEditingCtxRef.current;
3446
3656
  if (!cellEditingCtx) return renderEditableCell(column, info.row.original, info.getValue(), editingCtxRef.current);
3447
- return cellEditingCtx.alwaysEdit ? /* @__PURE__ */ jsx33(
3657
+ return cellEditingCtx.alwaysEdit ? /* @__PURE__ */ jsx35(
3448
3658
  AlwaysEditCell,
3449
3659
  {
3450
3660
  column,
@@ -3481,6 +3691,9 @@ function DataGrid({
3481
3691
  onSelectedIdsChange,
3482
3692
  testId,
3483
3693
  loading: loadingProp = false,
3694
+ error,
3695
+ onRetry,
3696
+ statusLabels,
3484
3697
  groupBy,
3485
3698
  renderGroupHeader,
3486
3699
  defaultGroupsExpanded = true,
@@ -3489,8 +3702,10 @@ function DataGrid({
3489
3702
  zebra = true,
3490
3703
  showTotals = false,
3491
3704
  editing,
3492
- cellEditing
3705
+ cellEditing,
3706
+ filterLabels
3493
3707
  }) {
3708
+ const resolvedFilterLabels = useFilterLabels(filterLabels);
3494
3709
  const hasCellEditing = Boolean(cellEditing) && !groupBy;
3495
3710
  const { state, filtersByColumn, setColumnFilter, toggleSort, setPage } = useGridState(
3496
3711
  dataSource,
@@ -3536,11 +3751,11 @@ function DataGrid({
3536
3751
  const atomicGestureRef = useRef13(void 0);
3537
3752
  const visibleColumns = useVisibleColumns(columns, columnVisibility);
3538
3753
  const serverRowCount = dataSource.mode === "server" ? dataSource.rowCount : void 0;
3539
- const { rows, rowCount } = useMemo8(() => {
3754
+ const { rows, rowCount } = useMemo9(() => {
3540
3755
  if (dataSource.mode === "client") return processClientData(dataSource.data, state);
3541
3756
  return { rows: dataSource.data, rowCount: serverRowCount ?? 0 };
3542
3757
  }, [dataSource.mode, dataSource.data, serverRowCount, state]);
3543
- const selectedRows = useMemo8(
3758
+ const selectedRows = useMemo9(
3544
3759
  () => rows.filter((row) => selectedIds.has(getRowId(row))),
3545
3760
  [rows, selectedIds, getRowId]
3546
3761
  );
@@ -3559,11 +3774,11 @@ function DataGrid({
3559
3774
  }
3560
3775
  updateSelectedIds(next);
3561
3776
  }
3562
- const tanstackColumns = useMemo8(
3777
+ const tanstackColumns = useMemo9(
3563
3778
  () => toTanstackColumns(visibleColumns, editingCtxRef, cellEditingCtxRef, atomicGestureRef),
3564
3779
  [visibleColumns]
3565
3780
  );
3566
- const columnById = useMemo8(() => {
3781
+ const columnById = useMemo9(() => {
3567
3782
  const map = /* @__PURE__ */ new Map();
3568
3783
  for (const column of visibleColumns) map.set(column.id, column);
3569
3784
  return map;
@@ -3585,13 +3800,15 @@ function DataGrid({
3585
3800
  return table.getRowModel().rowsById[rowId]?.original;
3586
3801
  }
3587
3802
  const pageCount = Math.max(1, Math.ceil(rowCount / state.pageSize));
3803
+ const labels = resolveStatusLabels(statusLabels);
3804
+ const hasError = toErrorInfo(error) !== void 0;
3588
3805
  const loading = loadingProp || (dataSource.mode === "server" ? Boolean(dataSource.loading) : false);
3589
3806
  const showSelectionColumn = Boolean(headerActions?.length) || controlledSelectedIds !== void 0;
3590
3807
  const showRowActionsColumn = Boolean(rowActions?.length);
3591
3808
  const showDetailColumn = Boolean(renderDetail);
3592
3809
  const totalColumnCount = visibleColumns.length + (showDetailColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0) + (showRowActionsColumn ? 1 : 0);
3593
3810
  const hasFilterRow = visibleColumns.some((column) => isFilterable(column) && column.filterDisplay === "row");
3594
- const headerRuns = useMemo8(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
3811
+ const headerRuns = useMemo9(() => computeHeaderRuns(visibleColumns), [visibleColumns]);
3595
3812
  const hasHeaderGroups = visibleColumns.some((column) => column.headerGroup);
3596
3813
  const hasColumnSummary = visibleColumns.some((column) => column.summary);
3597
3814
  function renderFilterWidget(column) {
@@ -3602,11 +3819,11 @@ function DataGrid({
3602
3819
  const columnSize = (columnId) => table.getColumn(columnId)?.getSize() ?? DEFAULT_COLUMN_SIZE;
3603
3820
  const leadingStructuralWidth = (showDetailColumn ? DETAIL_COLUMN_WIDTH : 0) + (showSelectionColumn ? SELECTION_COLUMN_WIDTH : 0);
3604
3821
  const trailingStructuralWidth = showRowActionsColumn ? ROW_ACTIONS_COLUMN_WIDTH : 0;
3605
- const leftPinnedOffsets = useMemo8(
3822
+ const leftPinnedOffsets = useMemo9(
3606
3823
  () => pinnedOffsets(visibleColumns, "left", columnSize, leadingStructuralWidth),
3607
3824
  [visibleColumns, columnSizing, leadingStructuralWidth]
3608
3825
  );
3609
- const rightPinnedOffsets = useMemo8(
3826
+ const rightPinnedOffsets = useMemo9(
3610
3827
  () => pinnedOffsets(visibleColumns, "right", columnSize, trailingStructuralWidth),
3611
3828
  [visibleColumns, columnSizing, trailingStructuralWidth]
3612
3829
  );
@@ -3656,7 +3873,7 @@ function DataGrid({
3656
3873
  style: pinnedProps?.style
3657
3874
  };
3658
3875
  }
3659
- const bodyCellPropsByColumn = useMemo8(() => {
3876
+ const bodyCellPropsByColumn = useMemo9(() => {
3660
3877
  const map = /* @__PURE__ */ new Map();
3661
3878
  for (const column of visibleColumns) map.set(column.id, bodyCellClassAndStyle(column));
3662
3879
  return map;
@@ -3672,7 +3889,7 @@ function DataGrid({
3672
3889
  );
3673
3890
  return { className, style: pinnedProps.style };
3674
3891
  }
3675
- const leafHeaderCellPropsByColumn = useMemo8(() => {
3892
+ const leafHeaderCellPropsByColumn = useMemo9(() => {
3676
3893
  const map = /* @__PURE__ */ new Map();
3677
3894
  for (const column of visibleColumns) {
3678
3895
  map.set(
@@ -3682,7 +3899,7 @@ function DataGrid({
3682
3899
  }
3683
3900
  return map;
3684
3901
  }, [visibleColumns, enableColumnResizing, leftPinnedOffsets, rightPinnedOffsets, columnSizing]);
3685
- const filterHeaderCellPropsByColumn = useMemo8(() => {
3902
+ const filterHeaderCellPropsByColumn = useMemo9(() => {
3686
3903
  const map = /* @__PURE__ */ new Map();
3687
3904
  for (const column of visibleColumns) {
3688
3905
  map.set(column.id, headerCellClassAndStyle(column, "border-b border-border p-2 align-top font-normal"));
@@ -3702,13 +3919,13 @@ function DataGrid({
3702
3919
  `[DataGrid] showPagination={false} but only ${tableRows.length} of ${rowCount} row(s) are rendered, with no pagination UI to reach the rest. Pass an explicit pageSize (via initialState or gridState) that covers every row you want visible, or a dataSource that returns all of them.`
3703
3920
  );
3704
3921
  }, [showPagination, gridState, initialState?.pageSize, tableRows.length, rowCount]);
3705
- const groupedBuckets = useMemo8(
3922
+ const groupedBuckets = useMemo9(
3706
3923
  () => groupBy ? groupRows(tableRows, (row) => groupBy(row.original)) : void 0,
3707
3924
  [tableRows, groupBy]
3708
3925
  );
3709
3926
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
3710
3927
  const shouldVirtualize = Boolean(virtualize) && tableRows.length > (virtualize?.threshold ?? 100);
3711
- const flatItems = useMemo8(() => {
3928
+ const flatItems = useMemo9(() => {
3712
3929
  if (!shouldVirtualize) return [];
3713
3930
  if (!groupedBuckets) return tableRows.map((row) => ({ kind: "row", row }));
3714
3931
  const items = [];
@@ -3745,15 +3962,15 @@ function DataGrid({
3745
3962
  notifiedForLengthRef.current = tableRows.length;
3746
3963
  virtualize.onEndReached();
3747
3964
  }, [lastVisibleIndex, flatItems.length, tableRows.length, virtualize]);
3748
- const cellEditingRowIds = useMemo8(
3965
+ const cellEditingRowIds = useMemo9(
3749
3966
  () => hasCellEditing ? tableRows.map((row) => row.id) : EMPTY_ID_ARRAY,
3750
3967
  [hasCellEditing, tableRows]
3751
3968
  );
3752
- const cellEditingColumnIds = useMemo8(
3969
+ const cellEditingColumnIds = useMemo9(
3753
3970
  () => hasCellEditing ? visibleColumns.map((column) => column.id) : EMPTY_ID_ARRAY,
3754
3971
  [hasCellEditing, visibleColumns]
3755
3972
  );
3756
- const cellEditingRowIndex = useMemo8(() => buildIndexMap(cellEditingRowIds), [cellEditingRowIds]);
3973
+ const cellEditingRowIndex = useMemo9(() => buildIndexMap(cellEditingRowIds), [cellEditingRowIds]);
3757
3974
  const cellSelection = useCellSelection({
3758
3975
  rowIds: cellEditingRowIds,
3759
3976
  columnIds: cellEditingColumnIds,
@@ -3992,20 +4209,20 @@ function DataGrid({
3992
4209
  const rowProps = getRowProps?.(row.original);
3993
4210
  const rowTestId = getRowTestId?.(row.original) ?? `row-${row.id}`;
3994
4211
  const isOddRow = zebra && row.index % 2 === 1;
3995
- return /* @__PURE__ */ jsxs20("tbody", { ref: measureRef, "data-index": dataIndex, children: [
3996
- /* @__PURE__ */ jsxs20(
4212
+ return /* @__PURE__ */ jsxs21("tbody", { ref: measureRef, "data-index": dataIndex, children: [
4213
+ /* @__PURE__ */ jsxs21(
3997
4214
  "tr",
3998
4215
  {
3999
4216
  ...rowProps,
4000
4217
  "data-testid": rowTestId,
4001
4218
  className: rowProps?.className ? cn("divide-x divide-border", rowProps.className, isOddRow && "bg-foreground/5") : `divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
4002
4219
  children: [
4003
- showDetailColumn && /* @__PURE__ */ jsx33(
4220
+ showDetailColumn && /* @__PURE__ */ jsx35(
4004
4221
  "td",
4005
4222
  {
4006
4223
  className: isOddRow ? detailCellProps.classNameOdd : detailCellProps.className,
4007
4224
  style: detailCellProps.style,
4008
- children: /* @__PURE__ */ jsx33(
4225
+ children: /* @__PURE__ */ jsx35(
4009
4226
  Button,
4010
4227
  {
4011
4228
  type: "button",
@@ -4017,8 +4234,8 @@ function DataGrid({
4017
4234
  stopRowClick(event);
4018
4235
  toggleRowExpanded(row.id);
4019
4236
  },
4020
- children: /* @__PURE__ */ jsx33(
4021
- ChevronRight,
4237
+ children: /* @__PURE__ */ jsx35(
4238
+ ChevronRightIcon2,
4022
4239
  {
4023
4240
  className: `h-4 w-4 transition-transform${isExpanded ? " rotate-90" : ""}`,
4024
4241
  "aria-hidden": true
@@ -4028,12 +4245,12 @@ function DataGrid({
4028
4245
  )
4029
4246
  }
4030
4247
  ),
4031
- showSelectionColumn && /* @__PURE__ */ jsx33(
4248
+ showSelectionColumn && /* @__PURE__ */ jsx35(
4032
4249
  "td",
4033
4250
  {
4034
4251
  className: isOddRow ? selectionCellProps.classNameOdd : selectionCellProps.className,
4035
4252
  style: selectionCellProps.style,
4036
- children: /* @__PURE__ */ jsx33(
4253
+ children: /* @__PURE__ */ jsx35(
4037
4254
  "input",
4038
4255
  {
4039
4256
  type: "checkbox",
@@ -4047,7 +4264,7 @@ function DataGrid({
4047
4264
  ),
4048
4265
  row.getAllCells().map((cell) => {
4049
4266
  const cellProps = bodyCellPropsByColumn.get(cell.column.id);
4050
- return /* @__PURE__ */ jsx33(
4267
+ return /* @__PURE__ */ jsx35(
4051
4268
  "td",
4052
4269
  {
4053
4270
  style: cellProps?.style,
@@ -4059,24 +4276,24 @@ function DataGrid({
4059
4276
  cell.id
4060
4277
  );
4061
4278
  }),
4062
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx33(
4279
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx35(
4063
4280
  "td",
4064
4281
  {
4065
4282
  className: isOddRow ? rowActionsCellProps.classNameOdd : rowActionsCellProps.className,
4066
4283
  style: rowActionsCellProps.style,
4067
- children: /* @__PURE__ */ jsx33(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
4284
+ children: /* @__PURE__ */ jsx35(ActionsMenu, { items: rowActions, ctx: { row: row.original }, triggerLabel: `Row actions for ${row.id}` })
4068
4285
  }
4069
4286
  )
4070
4287
  ]
4071
4288
  }
4072
4289
  ),
4073
- showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx33("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
4290
+ showDetailColumn && isExpanded && renderDetail && /* @__PURE__ */ jsx35("tr", { "data-testid": `${rowTestId}-detail`, children: /* @__PURE__ */ jsx35("td", { colSpan: totalColumnCount, className: "border-b border-border p-2", children: renderDetail(row.original) }) })
4074
4291
  ] }, row.id);
4075
4292
  }
4076
4293
  function renderGroupSummaryCells(rows2, bgClassName) {
4077
4294
  return visibleColumns.map((column) => {
4078
4295
  const cellProps = bodyCellPropsByColumn.get(column.id);
4079
- return /* @__PURE__ */ jsx33(
4296
+ return /* @__PURE__ */ jsx35(
4080
4297
  "td",
4081
4298
  {
4082
4299
  className: cn(cellProps?.className, bgClassName, "font-medium", alignClassName(column)),
@@ -4094,10 +4311,10 @@ function DataGrid({
4094
4311
  const leadingSpan = summaryLeadingCellSpan();
4095
4312
  const bgClassName = zebra ? "bg-muted" : "bg-background";
4096
4313
  const fillerClass = cn("border-b border-border p-2", bgClassName);
4097
- return /* @__PURE__ */ jsxs20("tr", { "data-testid": "group-summary-row", className: "divide-x divide-border", children: [
4098
- leadingSpan > 0 && /* @__PURE__ */ jsx33("td", { colSpan: leadingSpan, className: fillerClass }),
4314
+ return /* @__PURE__ */ jsxs21("tr", { "data-testid": "group-summary-row", className: "divide-x divide-border", children: [
4315
+ leadingSpan > 0 && /* @__PURE__ */ jsx35("td", { colSpan: leadingSpan, className: fillerClass }),
4099
4316
  renderGroupSummaryCells(rows2, bgClassName),
4100
- showRowActionsColumn && /* @__PURE__ */ jsx33("td", { className: fillerClass, "aria-hidden": true })
4317
+ showRowActionsColumn && /* @__PURE__ */ jsx35("td", { className: fillerClass, "aria-hidden": true })
4101
4318
  ] });
4102
4319
  }
4103
4320
  function renderTotalsFooter() {
@@ -4105,11 +4322,11 @@ function DataGrid({
4105
4322
  const summaryRows = tableRows.map((row) => row.original);
4106
4323
  const leadingSpan = summaryLeadingCellSpan();
4107
4324
  const fillerClass = "sticky bottom-0 z-20 border-t border-border bg-muted p-2";
4108
- return /* @__PURE__ */ jsx33("tfoot", { children: /* @__PURE__ */ jsxs20("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
4109
- leadingSpan > 0 && /* @__PURE__ */ jsx33("td", { colSpan: leadingSpan, className: fillerClass }),
4325
+ return /* @__PURE__ */ jsx35("tfoot", { children: /* @__PURE__ */ jsxs21("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
4326
+ leadingSpan > 0 && /* @__PURE__ */ jsx35("td", { colSpan: leadingSpan, className: fillerClass }),
4110
4327
  visibleColumns.map((column) => {
4111
4328
  const pinnedProps = pinnedCellProps(column, "header");
4112
- return /* @__PURE__ */ jsx33(
4329
+ return /* @__PURE__ */ jsx35(
4113
4330
  "td",
4114
4331
  {
4115
4332
  className: cn(
@@ -4124,19 +4341,19 @@ function DataGrid({
4124
4341
  column.id
4125
4342
  );
4126
4343
  }),
4127
- showRowActionsColumn && /* @__PURE__ */ jsx33("td", { className: fillerClass, "aria-hidden": true })
4344
+ showRowActionsColumn && /* @__PURE__ */ jsx35("td", { className: fillerClass, "aria-hidden": true })
4128
4345
  ] }) });
4129
4346
  }
4130
4347
  function renderGroupHeaderTbody(key, rows2, measureRef, dataIndex) {
4131
4348
  const expanded = isGroupExpanded(key);
4132
- return /* @__PURE__ */ jsxs20("tbody", { ref: measureRef, "data-index": dataIndex, children: [
4133
- /* @__PURE__ */ jsx33("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx33(
4349
+ return /* @__PURE__ */ jsxs21("tbody", { ref: measureRef, "data-index": dataIndex, children: [
4350
+ /* @__PURE__ */ jsx35("tr", { "data-testid": `group-header-${key}`, children: /* @__PURE__ */ jsx35(
4134
4351
  "td",
4135
4352
  {
4136
4353
  colSpan: totalColumnCount,
4137
4354
  className: cn("sticky z-20 border-b p-2 font-medium", zebra ? "bg-muted" : "bg-background"),
4138
4355
  style: { top: groupHeaderTop },
4139
- children: /* @__PURE__ */ jsxs20(
4356
+ children: /* @__PURE__ */ jsxs21(
4140
4357
  "button",
4141
4358
  {
4142
4359
  type: "button",
@@ -4144,8 +4361,8 @@ function DataGrid({
4144
4361
  onClick: () => toggleGroupExpanded(key),
4145
4362
  "aria-expanded": expanded,
4146
4363
  children: [
4147
- /* @__PURE__ */ jsx33(
4148
- ChevronRight,
4364
+ /* @__PURE__ */ jsx35(
4365
+ ChevronRightIcon2,
4149
4366
  {
4150
4367
  className: `h-4 w-4 shrink-0 transition-transform${expanded ? " rotate-90" : ""}`,
4151
4368
  "aria-hidden": true
@@ -4163,7 +4380,7 @@ function DataGrid({
4163
4380
  function renderGroupedBucket(bucket) {
4164
4381
  const expanded = isGroupExpanded(bucket.key);
4165
4382
  const originalRows = bucket.items.map((row) => row.original);
4166
- return /* @__PURE__ */ jsxs20(Fragment3, { children: [
4383
+ return /* @__PURE__ */ jsxs21(Fragment3, { children: [
4167
4384
  renderGroupHeaderTbody(bucket.key, originalRows),
4168
4385
  expanded && bucket.items.map((row) => renderRow(row))
4169
4386
  ] }, `group-${bucket.key}`);
@@ -4172,7 +4389,7 @@ function DataGrid({
4172
4389
  if (item.kind === "header") return renderGroupHeaderTbody(item.key, item.rows, measureRef, index);
4173
4390
  return renderRow(item.row, measureRef, index);
4174
4391
  }
4175
- return (
4392
+ const grid = (
4176
4393
  // `h-full`/`min-h-0` here and on the two wrappers below are no-ops unless
4177
4394
  // a consumer itself gives <DataGrid> a bounded height (e.g. wraps it in
4178
4395
  // its own `flex-1 min-h-0` container) -- percentage heights fall back to
@@ -4185,8 +4402,8 @@ function DataGrid({
4185
4402
  // containers (that one scrolling vertically, this component's own
4186
4403
  // `overflow-x-auto` div scrolling horizontally) fighting over which one
4187
4404
  // a sticky header/pinned column is actually stuck relative to.
4188
- /* @__PURE__ */ jsxs20("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
4189
- editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx33(
4405
+ /* @__PURE__ */ jsxs21("div", { className: "flex h-full min-h-0 flex-col gap-2", "data-testid": "datagrid", children: [
4406
+ editing && pendingEdits.size > 0 && /* @__PURE__ */ jsx35(
4190
4407
  EditingBar,
4191
4408
  {
4192
4409
  editing,
@@ -4196,28 +4413,25 @@ function DataGrid({
4196
4413
  onDiscard: handleDiscardEdits
4197
4414
  }
4198
4415
  ),
4199
- showSelectionColumn && headerActions && /* @__PURE__ */ jsxs20("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
4200
- /* @__PURE__ */ jsxs20("span", { children: [
4416
+ showSelectionColumn && headerActions && /* @__PURE__ */ jsxs21("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
4417
+ /* @__PURE__ */ jsxs21("span", { children: [
4201
4418
  selectedRows.length,
4202
4419
  " selected"
4203
4420
  ] }),
4204
- /* @__PURE__ */ jsx33(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
4421
+ /* @__PURE__ */ jsx35(ActionsMenu, { items: headerActions, ctx: { selectedRows }, triggerLabel: "Bulk actions" })
4205
4422
  ] }),
4206
- /* @__PURE__ */ jsxs20("div", { className: "relative min-h-0 flex-1", children: [
4207
- loading && /* @__PURE__ */ jsx33(
4208
- "div",
4209
- {
4210
- "data-testid": "datagrid-loading-overlay",
4211
- className: "absolute inset-0 z-30 flex items-center justify-center bg-background/60",
4212
- children: /* @__PURE__ */ jsx33(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground", "aria-hidden": true })
4213
- }
4214
- ),
4215
- /* @__PURE__ */ jsxs20(
4423
+ hasError && rows.length > 0 && /* @__PURE__ */ jsx35("div", { className: "flex shrink-0 items-center rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1", children: /* @__PURE__ */ jsx35(GridInlineError, { error, onRetry, labels }) }),
4424
+ /* @__PURE__ */ jsxs21("div", { className: "relative min-h-0 flex-1", children: [
4425
+ loading && rows.length > 0 && /* @__PURE__ */ jsx35(RefreshBar, { "data-testid": "datagrid-loading-overlay" }),
4426
+ /* @__PURE__ */ jsxs21(
4216
4427
  "div",
4217
4428
  {
4218
4429
  ref: scrollRef,
4219
4430
  "data-testid": testId,
4220
- className: cn("h-full overflow-auto rounded-md border", hasCellEditing && "relative"),
4431
+ className: cn(
4432
+ "h-full overflow-auto rounded-md border",
4433
+ hasCellEditing && "relative"
4434
+ ),
4221
4435
  style: shouldVirtualize ? { maxHeight: virtualize?.maxBodyHeight ?? 480 } : void 0,
4222
4436
  tabIndex: hasCellEditing ? 0 : void 0,
4223
4437
  onMouseDown: hasCellEditing ? handleCellMouseDown : void 0,
@@ -4226,8 +4440,8 @@ function DataGrid({
4226
4440
  onCopy: hasCellEditing ? handleCopy : void 0,
4227
4441
  onPaste: hasCellEditing ? handlePaste : void 0,
4228
4442
  children: [
4229
- /* @__PURE__ */ jsxs20("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
4230
- /* @__PURE__ */ jsxs20("thead", { ref: theadRef, className: "bg-muted", children: [
4443
+ /* @__PURE__ */ jsxs21("table", { className: cn("w-full border-collapse text-sm", enableColumnResizing && "table-fixed"), children: [
4444
+ /* @__PURE__ */ jsxs21("thead", { ref: theadRef, className: "bg-muted", children: [
4231
4445
  table.getHeaderGroups().map((headerGroup) => {
4232
4446
  const headerById = new Map(headerGroup.headers.map((header) => [header.id, header]));
4233
4447
  function renderLeafHeaderCell(column, rowSpan) {
@@ -4239,7 +4453,7 @@ function DataGrid({
4239
4453
  const cellProps = leafHeaderCellPropsByColumn.get(column.id);
4240
4454
  const headerContent = column.renderHeader ? column.renderHeader(column) : column.header;
4241
4455
  const headerLabelId = `${header.id}-label`;
4242
- return /* @__PURE__ */ jsxs20(
4456
+ return /* @__PURE__ */ jsxs21(
4243
4457
  "th",
4244
4458
  {
4245
4459
  rowSpan,
@@ -4248,8 +4462,8 @@ function DataGrid({
4248
4462
  "aria-sort": sortable ? sortDirToAriaSort(sortEntry?.dir) : void 0,
4249
4463
  "aria-labelledby": headerLabelId,
4250
4464
  children: [
4251
- /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-1", children: [
4252
- sortable ? /* @__PURE__ */ jsxs20(
4465
+ /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", children: [
4466
+ sortable ? /* @__PURE__ */ jsxs21(
4253
4467
  "button",
4254
4468
  {
4255
4469
  type: "button",
@@ -4259,8 +4473,8 @@ function DataGrid({
4259
4473
  onClick: (event) => toggleSort(column.id, enableMultiSort && event.shiftKey, column.sortDescFirst === true),
4260
4474
  children: [
4261
4475
  headerContent,
4262
- sortEntry?.dir === "asc" && /* @__PURE__ */ jsx33(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
4263
- sortEntry?.dir === "desc" && /* @__PURE__ */ jsx33(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
4476
+ sortEntry?.dir === "asc" && /* @__PURE__ */ jsx35(BarsArrowUpIcon, { className: "h-3 w-3", "aria-hidden": true }),
4477
+ sortEntry?.dir === "desc" && /* @__PURE__ */ jsx35(BarsArrowDownIcon, { className: "h-3 w-3", "aria-hidden": true })
4264
4478
  ]
4265
4479
  }
4266
4480
  ) : (
@@ -4269,17 +4483,17 @@ function DataGrid({
4269
4483
  // silently breaking any interactive content a column's own `renderHeader`
4270
4484
  // embeds directly (bare filter inputs, "select/reject all" buttons) —
4271
4485
  // clicks never reach them at all, even though nothing looks wrong visually.
4272
- /* @__PURE__ */ jsx33("div", { id: headerLabelId, className: "flex items-center gap-1 px-1", children: headerContent })
4486
+ /* @__PURE__ */ jsx35("div", { id: headerLabelId, className: "flex items-center gap-1 px-1", children: headerContent })
4273
4487
  ),
4274
- filterable && /* @__PURE__ */ jsxs20(Popover, { children: [
4275
- /* @__PURE__ */ jsx33(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx33(
4488
+ filterable && /* @__PURE__ */ jsxs21(Popover, { children: [
4489
+ /* @__PURE__ */ jsx35(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx35(
4276
4490
  Button,
4277
4491
  {
4278
4492
  variant: "ghost",
4279
4493
  size: "icon",
4280
4494
  "data-testid": `filter-trigger-${column.id}`,
4281
- "aria-label": `Filter ${column.header}`,
4282
- children: /* @__PURE__ */ jsx33(
4495
+ "aria-label": resolvedFilterLabels.filterAriaLabel(column.header),
4496
+ children: /* @__PURE__ */ jsx35(
4283
4497
  FunnelIcon7,
4284
4498
  {
4285
4499
  className: cn(
@@ -4291,10 +4505,10 @@ function DataGrid({
4291
4505
  )
4292
4506
  }
4293
4507
  ) }),
4294
- /* @__PURE__ */ jsx33(PopoverContent, { className: "w-auto", children: renderFilterWidget(column) })
4508
+ /* @__PURE__ */ jsx35(PopoverContent, { className: "w-auto max-w-[var(--radix-popover-content-available-width)]", children: renderFilterWidget(column) })
4295
4509
  ] })
4296
4510
  ] }),
4297
- enableColumnResizing && /* @__PURE__ */ jsx33(
4511
+ enableColumnResizing && /* @__PURE__ */ jsx35(
4298
4512
  "div",
4299
4513
  {
4300
4514
  "data-testid": `resize-handle-${column.id}`,
@@ -4319,9 +4533,9 @@ function DataGrid({
4319
4533
  header.id
4320
4534
  );
4321
4535
  }
4322
- return /* @__PURE__ */ jsxs20(Fragment3, { children: [
4323
- hasHeaderGroups && /* @__PURE__ */ jsxs20("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
4324
- showDetailColumn && /* @__PURE__ */ jsx33(
4536
+ return /* @__PURE__ */ jsxs21(Fragment3, { children: [
4537
+ hasHeaderGroups && /* @__PURE__ */ jsxs21("tr", { "data-testid": "header-group-row", className: "divide-x divide-border", children: [
4538
+ showDetailColumn && /* @__PURE__ */ jsx35(
4325
4539
  "th",
4326
4540
  {
4327
4541
  rowSpan: 2,
@@ -4330,13 +4544,13 @@ function DataGrid({
4330
4544
  "aria-hidden": true
4331
4545
  }
4332
4546
  ),
4333
- showSelectionColumn && /* @__PURE__ */ jsx33(
4547
+ showSelectionColumn && /* @__PURE__ */ jsx35(
4334
4548
  "th",
4335
4549
  {
4336
4550
  rowSpan: 2,
4337
4551
  className: selectionHeaderCellProps.className,
4338
4552
  style: selectionHeaderCellProps.style,
4339
- children: /* @__PURE__ */ jsx33(
4553
+ children: /* @__PURE__ */ jsx35(
4340
4554
  "input",
4341
4555
  {
4342
4556
  type: "checkbox",
@@ -4348,7 +4562,7 @@ function DataGrid({
4348
4562
  }
4349
4563
  ),
4350
4564
  headerRuns.map(
4351
- (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx33(
4565
+ (run) => run.kind === "single" ? renderLeafHeaderCell(run.column, 2) : /* @__PURE__ */ jsx35(
4352
4566
  "th",
4353
4567
  {
4354
4568
  colSpan: run.columns.length,
@@ -4358,7 +4572,7 @@ function DataGrid({
4358
4572
  `group-${run.label}-${run.columns[0].id}`
4359
4573
  )
4360
4574
  ),
4361
- showRowActionsColumn && /* @__PURE__ */ jsx33(
4575
+ showRowActionsColumn && /* @__PURE__ */ jsx35(
4362
4576
  "th",
4363
4577
  {
4364
4578
  rowSpan: 2,
@@ -4368,8 +4582,8 @@ function DataGrid({
4368
4582
  }
4369
4583
  )
4370
4584
  ] }, `${headerGroup.id}-groups`),
4371
- /* @__PURE__ */ jsxs20("tr", { className: "divide-x divide-border", children: [
4372
- !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx33(
4585
+ /* @__PURE__ */ jsxs21("tr", { className: "divide-x divide-border", children: [
4586
+ !hasHeaderGroups && showDetailColumn && /* @__PURE__ */ jsx35(
4373
4587
  "th",
4374
4588
  {
4375
4589
  className: detailHeaderCellProps.className,
@@ -4377,12 +4591,12 @@ function DataGrid({
4377
4591
  "aria-hidden": true
4378
4592
  }
4379
4593
  ),
4380
- !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx33(
4594
+ !hasHeaderGroups && showSelectionColumn && /* @__PURE__ */ jsx35(
4381
4595
  "th",
4382
4596
  {
4383
4597
  className: selectionHeaderCellProps.className,
4384
4598
  style: selectionHeaderCellProps.style,
4385
- children: /* @__PURE__ */ jsx33(
4599
+ children: /* @__PURE__ */ jsx35(
4386
4600
  "input",
4387
4601
  {
4388
4602
  type: "checkbox",
@@ -4398,7 +4612,7 @@ function DataGrid({
4398
4612
  if (!column || hasHeaderGroups && !column.headerGroup) return null;
4399
4613
  return renderLeafHeaderCell(column);
4400
4614
  }),
4401
- !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx33(
4615
+ !hasHeaderGroups && showRowActionsColumn && /* @__PURE__ */ jsx35(
4402
4616
  "th",
4403
4617
  {
4404
4618
  className: rowActionsHeaderCellProps.className,
@@ -4409,9 +4623,9 @@ function DataGrid({
4409
4623
  ] }, headerGroup.id)
4410
4624
  ] }, headerGroup.id);
4411
4625
  }),
4412
- hasFilterRow && /* @__PURE__ */ jsxs20("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
4413
- showDetailColumn && /* @__PURE__ */ jsx33("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
4414
- showSelectionColumn && /* @__PURE__ */ jsx33(
4626
+ hasFilterRow && /* @__PURE__ */ jsxs21("tr", { "data-testid": "filter-row", className: "divide-x divide-border", children: [
4627
+ showDetailColumn && /* @__PURE__ */ jsx35("th", { className: detailHeaderCellProps.className, style: detailHeaderCellProps.style }),
4628
+ showSelectionColumn && /* @__PURE__ */ jsx35(
4415
4629
  "th",
4416
4630
  {
4417
4631
  className: selectionHeaderCellProps.className,
@@ -4420,9 +4634,9 @@ function DataGrid({
4420
4634
  ),
4421
4635
  visibleColumns.map((column) => {
4422
4636
  const cellProps = filterHeaderCellPropsByColumn.get(column.id);
4423
- return /* @__PURE__ */ jsx33("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
4637
+ return /* @__PURE__ */ jsx35("th", { style: cellProps?.style, className: cellProps?.className, children: isFilterable(column) && column.filterDisplay === "row" ? renderFilterWidget(column) : null }, column.id);
4424
4638
  }),
4425
- showRowActionsColumn && /* @__PURE__ */ jsx33(
4639
+ showRowActionsColumn && /* @__PURE__ */ jsx35(
4426
4640
  "th",
4427
4641
  {
4428
4642
  className: rowActionsHeaderCellProps.className,
@@ -4431,8 +4645,8 @@ function DataGrid({
4431
4645
  )
4432
4646
  ] })
4433
4647
  ] }),
4434
- rows.length === 0 ? /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? "Loading..." : "No results." }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs20(Fragment4, { children: [
4435
- paddingTop > 0 && /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
4648
+ rows.length === 0 ? /* @__PURE__ */ jsx35("tbody", { children: /* @__PURE__ */ jsx35("tr", { children: /* @__PURE__ */ jsx35("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? /* @__PURE__ */ jsx35(LoadingRows, { label: labels.loading }) : hasError ? /* @__PURE__ */ jsx35(GridErrorState, { error, onRetry, labels }) : labels.noResults }) }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs21(Fragment4, { children: [
4649
+ paddingTop > 0 && /* @__PURE__ */ jsx35("tbody", { children: /* @__PURE__ */ jsx35("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx35("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }) }),
4436
4650
  virtualItems.map(
4437
4651
  (virtualItem) => renderFlatItem(
4438
4652
  flatItems[virtualItem.index],
@@ -4440,11 +4654,11 @@ function DataGrid({
4440
4654
  (el) => virtualizer.measureElement(el)
4441
4655
  )
4442
4656
  ),
4443
- paddingBottom > 0 && /* @__PURE__ */ jsx33("tbody", { children: /* @__PURE__ */ jsx33("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx33("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
4657
+ paddingBottom > 0 && /* @__PURE__ */ jsx35("tbody", { children: /* @__PURE__ */ jsx35("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx35("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingBottom } }) }) })
4444
4658
  ] }) : groupedBuckets ? groupedBuckets.map((bucket) => renderGroupedBucket(bucket)) : tableRows.map((row) => renderRow(row)),
4445
4659
  renderTotalsFooter()
4446
4660
  ] }),
4447
- hasCellEditing && /* @__PURE__ */ jsx33(
4661
+ hasCellEditing && /* @__PURE__ */ jsx35(
4448
4662
  SelectionOverlay,
4449
4663
  {
4450
4664
  containerRef: scrollRef,
@@ -4457,8 +4671,8 @@ function DataGrid({
4457
4671
  }
4458
4672
  )
4459
4673
  ] }),
4460
- showPagination && /* @__PURE__ */ jsxs20("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
4461
- /* @__PURE__ */ jsxs20("span", { children: [
4674
+ showPagination && /* @__PURE__ */ jsxs21("div", { className: "flex shrink-0 items-center justify-between text-sm", children: [
4675
+ /* @__PURE__ */ jsxs21("span", { children: [
4462
4676
  "Page ",
4463
4677
  state.page + 1,
4464
4678
  " of ",
@@ -4469,8 +4683,8 @@ function DataGrid({
4469
4683
  rowCount === 1 ? "" : "s",
4470
4684
  ")"
4471
4685
  ] }),
4472
- /* @__PURE__ */ jsxs20("div", { className: "flex gap-2", children: [
4473
- /* @__PURE__ */ jsx33(
4686
+ /* @__PURE__ */ jsxs21("div", { className: "flex gap-2", children: [
4687
+ /* @__PURE__ */ jsx35(
4474
4688
  Button,
4475
4689
  {
4476
4690
  variant: "outline",
@@ -4480,7 +4694,7 @@ function DataGrid({
4480
4694
  children: "Previous"
4481
4695
  }
4482
4696
  ),
4483
- /* @__PURE__ */ jsx33(
4697
+ /* @__PURE__ */ jsx35(
4484
4698
  Button,
4485
4699
  {
4486
4700
  variant: "outline",
@@ -4494,12 +4708,17 @@ function DataGrid({
4494
4708
  ] })
4495
4709
  ] })
4496
4710
  );
4711
+ return /* @__PURE__ */ jsx35(FilterLabelsProvider, { labels: filterLabels, children: grid });
4497
4712
  }
4498
4713
 
4499
4714
  // src/tree/TreeDataGrid.tsx
4500
4715
  import { useVirtualizer as useVirtualizer2 } from "@tanstack/react-virtual";
4501
- import { AlertCircle, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2, Loader2 as Loader22 } from "lucide-react";
4502
- import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo10, useRef as useRef14 } from "react";
4716
+ import {
4717
+ ArrowPathIcon as ArrowPathIcon2,
4718
+ ChevronDownIcon as ChevronDownIcon3,
4719
+ ChevronRightIcon as ChevronRightIcon3
4720
+ } from "@heroicons/react/24/outline";
4721
+ import { Fragment as Fragment5, useEffect as useEffect13, useMemo as useMemo11, useRef as useRef14 } from "react";
4503
4722
 
4504
4723
  // src/tree/flattenTree.ts
4505
4724
  function flattenTree(roots, accessors, expanded, childrenOverride) {
@@ -4544,7 +4763,7 @@ function computeSelectionStates(roots, accessors, childrenOverride, selectedIds)
4544
4763
  }
4545
4764
 
4546
4765
  // src/tree/useTreeState.ts
4547
- import { useCallback as useCallback5, useMemo as useMemo9, useState as useState17 } from "react";
4766
+ import { useCallback as useCallback5, useMemo as useMemo10, useState as useState17 } from "react";
4548
4767
  function useTreeState({
4549
4768
  data,
4550
4769
  getRowId,
@@ -4582,7 +4801,7 @@ function useTreeState({
4582
4801
  );
4583
4802
  const [loadingIds, setLoadingIds] = useState17(/* @__PURE__ */ new Set());
4584
4803
  const [errorIds, setErrorIds] = useState17(/* @__PURE__ */ new Map());
4585
- const resolveHasChildren = useMemo9(
4804
+ const resolveHasChildren = useMemo10(
4586
4805
  () => hasChildren ?? ((row) => (getChildren(row)?.length ?? 0) > 0),
4587
4806
  [hasChildren, getChildren]
4588
4807
  );
@@ -4661,7 +4880,7 @@ function useTreeState({
4661
4880
  }
4662
4881
 
4663
4882
  // src/tree/TreeDataGrid.tsx
4664
- import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
4883
+ import { Fragment as Fragment6, jsx as jsx36, jsxs as jsxs22 } from "react/jsx-runtime";
4665
4884
  var EMPTY_SELECTED_IDS = /* @__PURE__ */ new Set();
4666
4885
  var UNCHECKED_STATE = { checked: false, indeterminate: false };
4667
4886
  function renderCell(column, row, editingCtx) {
@@ -4673,12 +4892,13 @@ function TreeCell({
4673
4892
  indentSize,
4674
4893
  isLoading,
4675
4894
  error,
4895
+ labels,
4676
4896
  onToggle,
4677
4897
  onRetry,
4678
4898
  editingCtx
4679
4899
  }) {
4680
- return /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4681
- flatRow.hasChildren ? /* @__PURE__ */ jsx34(
4900
+ return /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1", style: { paddingLeft: flatRow.depth * indentSize }, children: [
4901
+ flatRow.hasChildren ? /* @__PURE__ */ jsx36(
4682
4902
  "button",
4683
4903
  {
4684
4904
  type: "button",
@@ -4690,21 +4910,28 @@ function TreeCell({
4690
4910
  "aria-label": flatRow.isExpanded ? "Collapse" : "Expand",
4691
4911
  "aria-expanded": flatRow.isExpanded,
4692
4912
  className: "flex h-4 w-4 shrink-0 items-center justify-center text-muted-foreground hover:text-foreground disabled:opacity-50",
4693
- children: isLoading ? /* @__PURE__ */ jsx34(Loader22, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx34(ChevronDown2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx34(ChevronRight2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4913
+ children: isLoading ? /* @__PURE__ */ jsx36(ArrowPathIcon2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": "true" }) : flatRow.isExpanded ? /* @__PURE__ */ jsx36(ChevronDownIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx36(ChevronRightIcon3, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
4694
4914
  }
4695
- ) : /* @__PURE__ */ jsx34("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4696
- /* @__PURE__ */ jsx34("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4697
- flatRow.isExpanded && error ? /* @__PURE__ */ jsxs21("span", { className: "ml-1 flex shrink-0 items-center gap-1 text-xs text-destructive", children: [
4698
- /* @__PURE__ */ jsx34(AlertCircle, { className: "h-3 w-3", "aria-hidden": "true" }),
4699
- "Failed to load.",
4700
- /* @__PURE__ */ jsx34("button", { type: "button", className: "underline underline-offset-2", onClick: onRetry, children: "Retry" })
4701
- ] }) : null
4915
+ ) : /* @__PURE__ */ jsx36("span", { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
4916
+ /* @__PURE__ */ jsx36("span", { className: "min-w-0 flex-1 truncate", children: renderCell(column, flatRow.row, editingCtx) }),
4917
+ flatRow.isExpanded && error ? /* @__PURE__ */ jsx36(
4918
+ GridInlineError,
4919
+ {
4920
+ error: { message: labels.nodeLoadFailed, details: error },
4921
+ onRetry,
4922
+ labels,
4923
+ className: "ml-1 shrink-0"
4924
+ }
4925
+ ) : null
4702
4926
  ] });
4703
4927
  }
4704
4928
  function TreeDataGrid({
4705
4929
  columns,
4706
4930
  data,
4707
4931
  loading = false,
4932
+ error,
4933
+ onRetry,
4934
+ statusLabels,
4708
4935
  treeColumnId,
4709
4936
  columnVisibility,
4710
4937
  getRowId,
@@ -4737,7 +4964,9 @@ function TreeDataGrid({
4737
4964
  showTotals = false,
4738
4965
  editing
4739
4966
  }) {
4740
- const accessors = useMemo10(
4967
+ const labels = resolveStatusLabels(statusLabels);
4968
+ const hasError = toErrorInfo(error) !== void 0;
4969
+ const accessors = useMemo11(
4741
4970
  () => ({ getRowId, getChildren, hasChildren }),
4742
4971
  [getRowId, getChildren, hasChildren]
4743
4972
  );
@@ -4765,11 +4994,11 @@ function TreeDataGrid({
4765
4994
  controlledExpandedGroups,
4766
4995
  onExpandedGroupsChange
4767
4996
  );
4768
- const flatRows = useMemo10(
4997
+ const flatRows = useMemo11(
4769
4998
  () => groupBy ? [] : flattenTree(data, accessors, expanded, childrenMap),
4770
4999
  [groupBy, data, accessors, expanded, childrenMap]
4771
5000
  );
4772
- const groupedBuckets = useMemo10(
5001
+ const groupedBuckets = useMemo11(
4773
5002
  () => groupBy ? groupRows(data, groupBy).map((bucket) => ({
4774
5003
  key: bucket.key,
4775
5004
  roots: bucket.items,
@@ -4777,7 +5006,7 @@ function TreeDataGrid({
4777
5006
  })) : void 0,
4778
5007
  [groupBy, data, accessors, expanded, childrenMap]
4779
5008
  );
4780
- const groupedRender = useMemo10(() => {
5009
+ const groupedRender = useMemo11(() => {
4781
5010
  if (!groupedBuckets) return void 0;
4782
5011
  const startIndexByBucket = /* @__PURE__ */ new Map();
4783
5012
  const rows = [];
@@ -4794,7 +5023,7 @@ function TreeDataGrid({
4794
5023
  const showSelectionColumn = controlledSelectedIds !== void 0;
4795
5024
  const totalColumnCount = visibleColumns.length + (showRowActionsColumn ? 1 : 0) + (showSelectionColumn ? 1 : 0);
4796
5025
  const { theadRef, groupHeaderTop } = useStickyGroupHeaderTop(Boolean(groupBy));
4797
- const selectionStates = useMemo10(
5026
+ const selectionStates = useMemo11(
4798
5027
  () => showSelectionColumn ? computeSelectionStates(data, accessors, childrenMap, controlledSelectedIds ?? EMPTY_SELECTED_IDS) : void 0,
4799
5028
  [showSelectionColumn, data, accessors, childrenMap, controlledSelectedIds]
4800
5029
  );
@@ -4810,7 +5039,7 @@ function TreeDataGrid({
4810
5039
  else next.add(id);
4811
5040
  onSelectedIdsChange?.(next);
4812
5041
  }
4813
- const selectableFlatRows = useMemo10(
5042
+ const selectableFlatRows = useMemo11(
4814
5043
  () => allFlatRows.filter((flatRow) => !(isRowSelectionDisabled?.(flatRow.row) ?? false)),
4815
5044
  [allFlatRows, isRowSelectionDisabled]
4816
5045
  );
@@ -4824,14 +5053,14 @@ function TreeDataGrid({
4824
5053
  }
4825
5054
  onSelectedIdsChange?.(next);
4826
5055
  }
4827
- const bodyTdClassByColumn = useMemo10(() => {
5056
+ const bodyTdClassByColumn = useMemo11(() => {
4828
5057
  const map = /* @__PURE__ */ new Map();
4829
5058
  for (const column of visibleColumns) map.set(column.id, `p-2 ${alignClassName(column)}`);
4830
5059
  return map;
4831
5060
  }, [visibleColumns]);
4832
5061
  const hasColumnSummary = visibleColumns.some((column) => column.summary);
4833
5062
  function renderGroupSummaryCells(rows) {
4834
- return visibleColumns.map((column) => /* @__PURE__ */ jsx34("td", { className: cn(bodyTdClassByColumn.get(column.id), "font-medium"), children: column.summary?.(rows) }, column.id));
5063
+ return visibleColumns.map((column) => /* @__PURE__ */ jsx36("td", { className: cn(bodyTdClassByColumn.get(column.id), "font-medium"), children: column.summary?.(rows) }, column.id));
4835
5064
  }
4836
5065
  const scrollRef = useRef14(null);
4837
5066
  const shouldVirtualize = !groupBy && flatRows.length > virtualizeThreshold;
@@ -4846,7 +5075,7 @@ function TreeDataGrid({
4846
5075
  const rowProps = getRowProps?.(flatRow.row, flatRow.depth, index);
4847
5076
  const rowTestId = getRowTestId?.(flatRow.row) ?? `tree-row-${flatRow.id}`;
4848
5077
  const isOddRow = zebra && index % 2 === 1;
4849
- return /* @__PURE__ */ jsxs21(
5078
+ return /* @__PURE__ */ jsxs22(
4850
5079
  "tr",
4851
5080
  {
4852
5081
  ref: measureRef,
@@ -4859,9 +5088,9 @@ function TreeDataGrid({
4859
5088
  isOddRow && "bg-foreground/5"
4860
5089
  ) : `border-b border-border divide-x divide-border${isOddRow ? " bg-foreground/5" : ""}`,
4861
5090
  children: [
4862
- showSelectionColumn && /* @__PURE__ */ jsx34("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
5091
+ showSelectionColumn && /* @__PURE__ */ jsx36("td", { className: "p-1", style: { width: SELECTION_COLUMN_WIDTH }, children: (() => {
4863
5092
  const state = resolveSelectionState(flatRow.row, flatRow.id);
4864
- return /* @__PURE__ */ jsx34(
5093
+ return /* @__PURE__ */ jsx36(
4865
5094
  Checkbox,
4866
5095
  {
4867
5096
  "aria-label": `Select row ${flatRow.id}`,
@@ -4872,7 +5101,7 @@ function TreeDataGrid({
4872
5101
  }
4873
5102
  );
4874
5103
  })() }),
4875
- visibleColumns.map((column) => /* @__PURE__ */ jsx34("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx34(
5104
+ visibleColumns.map((column) => /* @__PURE__ */ jsx36("td", { className: bodyTdClassByColumn.get(column.id), children: column.id === treeColId ? /* @__PURE__ */ jsx36(
4876
5105
  TreeCell,
4877
5106
  {
4878
5107
  flatRow,
@@ -4880,12 +5109,13 @@ function TreeDataGrid({
4880
5109
  indentSize,
4881
5110
  isLoading: loadingIds.has(flatRow.id),
4882
5111
  error: errorIds.get(flatRow.id),
5112
+ labels,
4883
5113
  onToggle: () => toggleExpand(flatRow.row),
4884
5114
  onRetry: () => retry(flatRow.row),
4885
5115
  editingCtx: editingState.ctx
4886
5116
  }
4887
5117
  ) : renderCell(column, flatRow.row, editingState.ctx) }, column.id)),
4888
- showRowActionsColumn && rowActions && /* @__PURE__ */ jsx34("td", { className: "p-2", children: /* @__PURE__ */ jsx34(
5118
+ showRowActionsColumn && rowActions && /* @__PURE__ */ jsx36("td", { className: "p-2", children: /* @__PURE__ */ jsx36(
4889
5119
  ActionsMenu,
4890
5120
  {
4891
5121
  items: rowActions,
@@ -4901,8 +5131,9 @@ function TreeDataGrid({
4901
5131
  const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];
4902
5132
  const paddingTop = virtualItems.length > 0 ? virtualItems[0].start : 0;
4903
5133
  const paddingBottom = virtualItems.length > 0 ? virtualizer.getTotalSize() - virtualItems[virtualItems.length - 1].end : 0;
4904
- return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
4905
- editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx34(
5134
+ return /* @__PURE__ */ jsxs22("div", { className: "flex flex-col gap-2", "data-testid": "tree-datagrid", children: [
5135
+ hasError && data.length > 0 && /* @__PURE__ */ jsx36("div", { className: "flex shrink-0 items-center rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1", children: /* @__PURE__ */ jsx36(GridInlineError, { error, onRetry, labels }) }),
5136
+ editing && editingState.pendingEdits.size > 0 && /* @__PURE__ */ jsx36(
4906
5137
  EditingBar,
4907
5138
  {
4908
5139
  editing,
@@ -4913,21 +5144,21 @@ function TreeDataGrid({
4913
5144
  testIdPrefix: "tree-datagrid"
4914
5145
  }
4915
5146
  ),
4916
- /* @__PURE__ */ jsx34(
5147
+ /* @__PURE__ */ jsx36(
4917
5148
  "div",
4918
5149
  {
4919
5150
  ref: scrollRef,
4920
5151
  "data-testid": testId,
4921
5152
  className: "overflow-y-auto",
4922
5153
  style: { maxHeight: maxBodyHeight },
4923
- children: /* @__PURE__ */ jsxs21("table", { className: "w-full border-collapse text-sm", children: [
4924
- /* @__PURE__ */ jsx34("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs21("tr", { className: "divide-x divide-border", children: [
4925
- showSelectionColumn && /* @__PURE__ */ jsx34(
5154
+ children: /* @__PURE__ */ jsxs22("table", { className: "w-full border-collapse text-sm", children: [
5155
+ /* @__PURE__ */ jsx36("thead", { ref: theadRef, children: /* @__PURE__ */ jsxs22("tr", { className: "divide-x divide-border", children: [
5156
+ showSelectionColumn && /* @__PURE__ */ jsx36(
4926
5157
  "th",
4927
5158
  {
4928
5159
  className: "sticky top-0 z-20 border-b border-border bg-muted p-1",
4929
5160
  style: { width: SELECTION_COLUMN_WIDTH },
4930
- children: /* @__PURE__ */ jsx34(
5161
+ children: /* @__PURE__ */ jsx36(
4931
5162
  Checkbox,
4932
5163
  {
4933
5164
  "aria-label": "Select all visible rows",
@@ -4937,7 +5168,7 @@ function TreeDataGrid({
4937
5168
  )
4938
5169
  }
4939
5170
  ),
4940
- visibleColumns.map((column) => /* @__PURE__ */ jsx34(
5171
+ visibleColumns.map((column) => /* @__PURE__ */ jsx36(
4941
5172
  "th",
4942
5173
  {
4943
5174
  style: column.width ? { width: column.width } : void 0,
@@ -4949,15 +5180,12 @@ function TreeDataGrid({
4949
5180
  },
4950
5181
  column.id
4951
5182
  )),
4952
- showRowActionsColumn && /* @__PURE__ */ jsx34("th", { className: "sticky top-0 z-20 border-b border-border bg-muted p-2", "aria-label": "Row actions" })
5183
+ showRowActionsColumn && /* @__PURE__ */ jsx36("th", { className: "sticky top-0 z-20 border-b border-border bg-muted p-2", "aria-label": "Row actions" })
4953
5184
  ] }) }),
4954
- /* @__PURE__ */ jsx34("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx34("tr", { children: /* @__PURE__ */ jsx34("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? /* @__PURE__ */ jsxs21("span", { className: "inline-flex items-center gap-2", children: [
4955
- /* @__PURE__ */ jsx34(Loader22, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }),
4956
- "Loading..."
4957
- ] }) : "No results." }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
5185
+ /* @__PURE__ */ jsx36("tbody", { children: data.length === 0 ? /* @__PURE__ */ jsx36("tr", { children: /* @__PURE__ */ jsx36("td", { colSpan: totalColumnCount, className: "p-4 text-center text-muted-foreground", children: loading ? /* @__PURE__ */ jsx36(LoadingRows, { label: labels.loading }) : hasError ? /* @__PURE__ */ jsx36(GridErrorState, { error, onRetry, labels }) : labels.noResults }) }) : groupedBuckets ? groupedBuckets.map((bucket) => {
4958
5186
  const groupExpanded = isGroupExpanded(bucket.key);
4959
- return /* @__PURE__ */ jsxs21(Fragment5, { children: [
4960
- /* @__PURE__ */ jsx34("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx34(
5187
+ return /* @__PURE__ */ jsxs22(Fragment5, { children: [
5188
+ /* @__PURE__ */ jsx36("tr", { "data-testid": `group-header-${bucket.key}`, children: /* @__PURE__ */ jsx36(
4961
5189
  "td",
4962
5190
  {
4963
5191
  colSpan: totalColumnCount,
@@ -4966,7 +5194,7 @@ function TreeDataGrid({
4966
5194
  zebra ? "bg-muted" : "bg-background"
4967
5195
  ),
4968
5196
  style: { top: groupHeaderTop },
4969
- children: /* @__PURE__ */ jsxs21(
5197
+ children: /* @__PURE__ */ jsxs22(
4970
5198
  "button",
4971
5199
  {
4972
5200
  type: "button",
@@ -4974,8 +5202,8 @@ function TreeDataGrid({
4974
5202
  onClick: () => toggleGroupExpanded(bucket.key),
4975
5203
  "aria-expanded": groupExpanded,
4976
5204
  children: [
4977
- /* @__PURE__ */ jsx34(
4978
- ChevronRight2,
5205
+ /* @__PURE__ */ jsx36(
5206
+ ChevronRightIcon3,
4979
5207
  {
4980
5208
  className: `h-4 w-4 shrink-0 transition-transform${groupExpanded ? " rotate-90" : ""}`,
4981
5209
  "aria-hidden": true
@@ -4987,13 +5215,13 @@ function TreeDataGrid({
4987
5215
  )
4988
5216
  }
4989
5217
  ) }),
4990
- hasColumnSummary && /* @__PURE__ */ jsxs21(
5218
+ hasColumnSummary && /* @__PURE__ */ jsxs22(
4991
5219
  "tr",
4992
5220
  {
4993
5221
  "data-testid": `group-summary-${bucket.key}`,
4994
5222
  className: "border-b border-border divide-x divide-border",
4995
5223
  children: [
4996
- showSelectionColumn && /* @__PURE__ */ jsx34(
5224
+ showSelectionColumn && /* @__PURE__ */ jsx36(
4997
5225
  "td",
4998
5226
  {
4999
5227
  className: cn("p-2", zebra ? "bg-muted" : "bg-background"),
@@ -5001,7 +5229,7 @@ function TreeDataGrid({
5001
5229
  }
5002
5230
  ),
5003
5231
  renderGroupSummaryCells(bucket.roots),
5004
- showRowActionsColumn && /* @__PURE__ */ jsx34("td", { className: cn("p-2", zebra ? "bg-muted" : "bg-background"), "aria-hidden": true })
5232
+ showRowActionsColumn && /* @__PURE__ */ jsx36("td", { className: cn("p-2", zebra ? "bg-muted" : "bg-background"), "aria-hidden": true })
5005
5233
  ]
5006
5234
  }
5007
5235
  ),
@@ -5010,12 +5238,12 @@ function TreeDataGrid({
5010
5238
  return bucket.flatRows.map((flatRow, i) => renderRow(flatRow, startIndex + i));
5011
5239
  })()
5012
5240
  ] }, `group-${bucket.key}`);
5013
- }) : shouldVirtualize ? /* @__PURE__ */ jsxs21(Fragment6, { children: [
5014
- paddingTop > 0 && /* @__PURE__ */ jsx34("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx34("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
5241
+ }) : shouldVirtualize ? /* @__PURE__ */ jsxs22(Fragment6, { children: [
5242
+ paddingTop > 0 && /* @__PURE__ */ jsx36("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx36("td", { colSpan: totalColumnCount, style: { padding: 0, border: "none", height: paddingTop } }) }),
5015
5243
  virtualItems.map(
5016
5244
  (virtualItem) => renderRow(flatRows[virtualItem.index], virtualItem.index, (el) => virtualizer.measureElement(el))
5017
5245
  ),
5018
- paddingBottom > 0 && /* @__PURE__ */ jsx34("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx34(
5246
+ paddingBottom > 0 && /* @__PURE__ */ jsx36("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx36(
5019
5247
  "td",
5020
5248
  {
5021
5249
  colSpan: totalColumnCount,
@@ -5030,15 +5258,15 @@ function TreeDataGrid({
5030
5258
  // "roots only" scope `groupBy` itself uses, and for the same
5031
5259
  // reason (a child's value is usually already folded into its
5032
5260
  // parent's).
5033
- /* @__PURE__ */ jsx34("tfoot", { children: /* @__PURE__ */ jsxs21("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
5034
- showSelectionColumn && /* @__PURE__ */ jsx34(
5261
+ /* @__PURE__ */ jsx36("tfoot", { children: /* @__PURE__ */ jsxs22("tr", { "data-testid": "totals-row", className: "divide-x divide-border", children: [
5262
+ showSelectionColumn && /* @__PURE__ */ jsx36(
5035
5263
  "td",
5036
5264
  {
5037
5265
  className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2",
5038
5266
  style: { width: SELECTION_COLUMN_WIDTH }
5039
5267
  }
5040
5268
  ),
5041
- visibleColumns.map((column) => /* @__PURE__ */ jsx34(
5269
+ visibleColumns.map((column) => /* @__PURE__ */ jsx36(
5042
5270
  "td",
5043
5271
  {
5044
5272
  style: column.width ? { width: column.width } : void 0,
@@ -5050,7 +5278,7 @@ function TreeDataGrid({
5050
5278
  },
5051
5279
  column.id
5052
5280
  )),
5053
- showRowActionsColumn && /* @__PURE__ */ jsx34("td", { className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2", "aria-hidden": true })
5281
+ showRowActionsColumn && /* @__PURE__ */ jsx36("td", { className: "sticky bottom-0 z-20 border-t border-border bg-muted p-2", "aria-hidden": true })
5054
5282
  ] }) })
5055
5283
  ] })
5056
5284
  }
@@ -5069,6 +5297,7 @@ export {
5069
5297
  EditingBar,
5070
5298
  EnumEditor,
5071
5299
  EnumFilter,
5300
+ FilterLabelsProvider,
5072
5301
  MultilineStringEditor,
5073
5302
  NumberComparisonFilter,
5074
5303
  NumberEditor,
@@ -5089,7 +5318,9 @@ export {
5089
5318
  computeFillChanges,
5090
5319
  countVisible,
5091
5320
  defaultAlign,
5321
+ defaultFilterLabels,
5092
5322
  defaultFormat,
5323
+ defaultGridStatusLabels,
5093
5324
  editErrorId,
5094
5325
  evaluateFilter,
5095
5326
  extendRangeForFill,
@@ -5105,6 +5336,7 @@ export {
5105
5336
  isFilterDescriptor,
5106
5337
  isFilterable,
5107
5338
  isSortable,
5339
+ mergeFilterLabels,
5108
5340
  moveColumnBefore,
5109
5341
  normalizeRange,
5110
5342
  orderStorageKeyFor,
@@ -5118,10 +5350,12 @@ export {
5118
5350
  resolveMenuItems,
5119
5351
  storageKeyFor,
5120
5352
  toDate,
5353
+ toErrorInfo,
5121
5354
  useCellEditingState,
5122
5355
  useCellSelection,
5123
5356
  useDebouncedCallback,
5124
5357
  useEditingState,
5358
+ useFilterLabels,
5125
5359
  useGridState,
5126
5360
  useGroupExpansion,
5127
5361
  useStickyGroupHeaderTop,