@wordrhyme/auto-crud 1.5.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -420,13 +420,15 @@ function hasValidCanonicalDateTimePrefix(value) {
420
420
  const match = canonicalDateTimePrefixPattern.exec(value);
421
421
  return !match || isValidCalendarDate(match[1], match[2], match[3]);
422
422
  }
423
+ function getDateLocaleOptions() {
424
+ return hostDateFormatters[hostDateFormatters.length - 1]?.options;
425
+ }
423
426
  /**
424
- * Register the host's process-wide date presentation policy without coupling
425
- * AutoCrud to locale or timezone selection. The returned cleanup removes only
426
- * this registration, so overlapping registrations may be disposed out of order.
427
+ * Register the host's date formatter and optional calendar locale policy.
428
+ * Cleanup removes only this registration, including out-of-order cleanup.
427
429
  * Passing undefined clears every registration.
428
430
  */
429
- function setDateFormatter(formatter) {
431
+ function setDateFormatter(formatter, options) {
430
432
  if (formatter === void 0) {
431
433
  const hadFormatter = hostDateFormatters.length > 0;
432
434
  hostDateFormatters = [];
@@ -435,15 +437,16 @@ function setDateFormatter(formatter) {
435
437
  }
436
438
  const registration = {
437
439
  formatter,
440
+ ...options ? { options } : {},
438
441
  token: Symbol("date-formatter")
439
442
  };
440
443
  hostDateFormatters.push(registration);
441
444
  notifyDateFormatterChange();
442
445
  return () => {
443
- const currentFormatter = getHostDateFormatter();
446
+ const currentRegistration = hostDateFormatters[hostDateFormatters.length - 1];
444
447
  const previousLength = hostDateFormatters.length;
445
448
  hostDateFormatters = hostDateFormatters.filter(({ token }) => token !== registration.token);
446
- if (hostDateFormatters.length !== previousLength && getHostDateFormatter() !== currentFormatter) notifyDateFormatterChange();
449
+ if (hostDateFormatters.length !== previousLength && hostDateFormatters[hostDateFormatters.length - 1] !== currentRegistration) notifyDateFormatterChange();
447
450
  };
448
451
  }
449
452
  function formatDate(date, opts = {}, preset) {
@@ -1043,6 +1046,71 @@ function useDebouncedCallback(callback, delay) {
1043
1046
  }, [handleCallback, delay]);
1044
1047
  }
1045
1048
 
1049
+ //#endregion
1050
+ //#region src/lib/calendar-date.ts
1051
+ /** Calendar days are wall dates, never instants in the host's time zone. */
1052
+ function serializeCalendarDate(date) {
1053
+ return `${String(date.getFullYear()).padStart(4, "0")}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
1054
+ }
1055
+ function parseCalendarDate(value) {
1056
+ if (value === void 0 || value === "") return void 0;
1057
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
1058
+ const date$1 = /* @__PURE__ */ new Date(`${value}T00:00:00`);
1059
+ return !Number.isNaN(date$1.getTime()) && serializeCalendarDate(date$1) === value ? date$1 : void 0;
1060
+ }
1061
+ const date = new Date(Number(value));
1062
+ return Number.isNaN(date.getTime()) ? void 0 : date;
1063
+ }
1064
+ function calendarPresentation() {
1065
+ const localeOptions = getDateLocaleOptions();
1066
+ const locale = localeOptions?.locale ?? "en-US";
1067
+ const chinese = locale.toLowerCase().startsWith("zh");
1068
+ const localeInfo = new Intl.Locale(locale);
1069
+ const firstDay = localeInfo.getWeekInfo?.().firstDay ?? localeInfo.weekInfo?.firstDay ?? (chinese ? 1 : 7);
1070
+ const format = (date, options) => new Intl.DateTimeFormat(locale, {
1071
+ ...options,
1072
+ timeZone: "UTC"
1073
+ }).format(/* @__PURE__ */ new Date(`${serializeCalendarDate(date)}T00:00:00Z`));
1074
+ return {
1075
+ locale,
1076
+ weekStartsOn: firstDay % 7,
1077
+ formatDate: (date) => {
1078
+ if (!localeOptions) return formatDate(date);
1079
+ return date ? format(date, {
1080
+ year: "numeric",
1081
+ month: "long",
1082
+ day: "numeric"
1083
+ }) : "";
1084
+ },
1085
+ clearLabel: (title) => chinese ? `清除${title ?? ""}筛选` : `Clear ${title ?? ""} filter`,
1086
+ formatters: {
1087
+ formatCaption: (date) => format(date, {
1088
+ year: "numeric",
1089
+ month: "long"
1090
+ }),
1091
+ formatMonthDropdown: (date) => format(date, { month: "long" }),
1092
+ formatWeekdayName: (date) => format(date, { weekday: "short" })
1093
+ },
1094
+ labels: {
1095
+ labelNext: () => chinese ? "下个月" : "Go to the Next Month",
1096
+ labelPrevious: () => chinese ? "上个月" : "Go to the Previous Month",
1097
+ labelMonthDropdown: () => chinese ? "选择月份" : "Choose the Month",
1098
+ labelYearDropdown: () => chinese ? "选择年份" : "Choose the Year",
1099
+ labelGrid: (date) => format(date, {
1100
+ year: "numeric",
1101
+ month: "long"
1102
+ }),
1103
+ labelWeekday: (date) => format(date, { weekday: "long" }),
1104
+ labelDayButton: (date) => format(date, {
1105
+ year: "numeric",
1106
+ month: "long",
1107
+ day: "numeric",
1108
+ weekday: "long"
1109
+ })
1110
+ }
1111
+ };
1112
+ }
1113
+
1046
1114
  //#endregion
1047
1115
  //#region src/components/data-table/data-table-filter-list.tsx
1048
1116
  const DEBOUNCE_MS$2 = 300;
@@ -1214,6 +1282,7 @@ function DataTableFilterList({ table, debounceMs = DEBOUNCE_MS$2, throttleMs = T
1214
1282
  });
1215
1283
  }
1216
1284
  function DataTableFilterItem$1({ filter, index: index$1, filterItemId, joinOperator, setJoinOperator, columns, onFilterUpdate, onFilterRemove }) {
1285
+ useDateFormatterVersion();
1217
1286
  const [showFieldSelector, setShowFieldSelector] = react.useState(false);
1218
1287
  const [showOperatorSelector, setShowOperatorSelector] = react.useState(false);
1219
1288
  const [showValueSelector, setShowValueSelector] = react.useState(false);
@@ -1502,10 +1571,10 @@ function onFilterInputRender$1({ filter, inputId, column, columnMeta, onFilterUp
1502
1571
  case "dateRange": {
1503
1572
  const inputListboxId = `${inputId}-listbox`;
1504
1573
  const dateValue = Array.isArray(filter.value) ? filter.value.filter(Boolean) : [filter.value, filter.value].filter(Boolean);
1505
- const startDate = dateValue[0] ? new Date(Number(dateValue[0])) : void 0;
1506
- const endDate = dateValue[1] ? new Date(Number(dateValue[1])) : void 0;
1574
+ const startDate = dateValue[0] ? parseCalendarDate(dateValue[0]) : void 0;
1575
+ const endDate = dateValue[1] ? parseCalendarDate(dateValue[1]) : void 0;
1507
1576
  const isSameDate = startDate && endDate && startDate.toDateString() === endDate.toDateString();
1508
- const displayValue = filter.operator === "isBetween" && dateValue.length === 2 && !isSameDate ? `${formatDate(startDate, { month: "short" })} - ${formatDate(endDate, { month: "short" })}` : startDate ? formatDate(startDate, { month: "short" }) : "Pick a date";
1577
+ const displayValue = filter.operator === "isBetween" && dateValue.length === 2 && !isSameDate ? `${calendarPresentation().formatDate(startDate)} - ${calendarPresentation().formatDate(endDate)}` : startDate ? calendarPresentation().formatDate(startDate) : "Pick a date";
1509
1578
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Popover, {
1510
1579
  open: showValueSelector,
1511
1580
  onOpenChange: setShowValueSelector,
@@ -1528,28 +1597,36 @@ function onFilterInputRender$1({ filter, inputId, column, columnMeta, onFilterUp
1528
1597
  align: "start",
1529
1598
  className: "w-auto p-0",
1530
1599
  children: filter.operator === "isBetween" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
1600
+ lang: calendarPresentation().locale,
1601
+ weekStartsOn: calendarPresentation().weekStartsOn,
1602
+ formatters: calendarPresentation().formatters,
1603
+ labels: calendarPresentation().labels,
1531
1604
  "aria-label": `Select ${columnMeta?.label} date range`,
1532
1605
  autoFocus: true,
1533
1606
  captionLayout: "dropdown",
1534
1607
  mode: "range",
1535
1608
  selected: dateValue.length === 2 ? {
1536
- from: new Date(Number(dateValue[0])),
1537
- to: new Date(Number(dateValue[1]))
1609
+ from: parseCalendarDate(dateValue[0]),
1610
+ to: parseCalendarDate(dateValue[1])
1538
1611
  } : {
1539
1612
  from: /* @__PURE__ */ new Date(),
1540
1613
  to: /* @__PURE__ */ new Date()
1541
1614
  },
1542
1615
  onSelect: (date) => {
1543
- onFilterUpdate(filter.filterId, { value: date ? [(date.from?.getTime() ?? "").toString(), (date.to?.getTime() ?? "").toString()] : [] });
1616
+ onFilterUpdate(filter.filterId, { value: date ? [date.from ? serializeCalendarDate(date.from) : "", date.to ? serializeCalendarDate(date.to) : ""] : [] });
1544
1617
  }
1545
1618
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
1619
+ lang: calendarPresentation().locale,
1620
+ weekStartsOn: calendarPresentation().weekStartsOn,
1621
+ formatters: calendarPresentation().formatters,
1622
+ labels: calendarPresentation().labels,
1546
1623
  "aria-label": `Select ${columnMeta?.label} date`,
1547
1624
  autoFocus: true,
1548
1625
  captionLayout: "dropdown",
1549
1626
  mode: "single",
1550
- selected: dateValue[0] ? new Date(Number(dateValue[0])) : void 0,
1627
+ selected: dateValue[0] ? parseCalendarDate(dateValue[0]) : void 0,
1551
1628
  onSelect: (date) => {
1552
- onFilterUpdate(filter.filterId, { value: (date?.getTime() ?? "").toString() });
1629
+ onFilterUpdate(filter.filterId, { value: date ? serializeCalendarDate(date) : "" });
1553
1630
  setShowValueSelector(false);
1554
1631
  }
1555
1632
  })
@@ -1761,6 +1838,7 @@ function DataTableFilterMenu({ table, debounceMs = DEBOUNCE_MS$1, throttleMs = T
1761
1838
  }
1762
1839
  function DataTableFilterItem({ filter, filterItemId, columns, onFilterUpdate, onFilterRemove }) {
1763
1840
  {
1841
+ useDateFormatterVersion();
1764
1842
  const [showFieldSelector, setShowFieldSelector] = react.useState(false);
1765
1843
  const [showOperatorSelector, setShowOperatorSelector] = react.useState(false);
1766
1844
  const [showValueSelector, setShowValueSelector] = react.useState(false);
@@ -1871,6 +1949,7 @@ function DataTableFilterItem({ filter, filterItemId, columns, onFilterUpdate, on
1871
1949
  }
1872
1950
  }
1873
1951
  function FilterValueSelector({ column, value, onSelect }) {
1952
+ useDateFormatterVersion();
1874
1953
  switch (column.columnDef.meta?.variant ?? "text") {
1875
1954
  case "boolean": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.CommandGroup, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.CommandItem, {
1876
1955
  value: "true",
@@ -1899,11 +1978,15 @@ function FilterValueSelector({ column, value, onSelect }) {
1899
1978
  }, option.value)) });
1900
1979
  case "date":
1901
1980
  case "dateRange": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
1981
+ lang: calendarPresentation().locale,
1982
+ weekStartsOn: calendarPresentation().weekStartsOn,
1983
+ formatters: calendarPresentation().formatters,
1984
+ labels: calendarPresentation().labels,
1902
1985
  autoFocus: true,
1903
1986
  captionLayout: "dropdown",
1904
1987
  mode: "single",
1905
- selected: value ? new Date(value) : void 0,
1906
- onSelect: (date) => onSelect(date?.getTime().toString() ?? "")
1988
+ selected: parseCalendarDate(value),
1989
+ onSelect: (date) => onSelect(date ? serializeCalendarDate(date) : "")
1907
1990
  });
1908
1991
  default: {
1909
1992
  const isEmpty = !value.trim();
@@ -2057,10 +2140,10 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
2057
2140
  case "dateRange": {
2058
2141
  const inputListboxId = `${inputId}-listbox`;
2059
2142
  const dateValue = Array.isArray(filter.value) ? filter.value.filter(Boolean) : [filter.value, filter.value].filter(Boolean);
2060
- const startDate = dateValue[0] ? new Date(Number(dateValue[0])) : void 0;
2061
- const endDate = dateValue[1] ? new Date(Number(dateValue[1])) : void 0;
2143
+ const startDate = dateValue[0] ? parseCalendarDate(dateValue[0]) : void 0;
2144
+ const endDate = dateValue[1] ? parseCalendarDate(dateValue[1]) : void 0;
2062
2145
  const isSameDate = startDate && endDate && startDate.toDateString() === endDate.toDateString();
2063
- const displayValue = filter.operator === "isBetween" && dateValue.length === 2 && !isSameDate ? `${formatDate(startDate, { month: "short" })} - ${formatDate(endDate, { month: "short" })}` : startDate ? formatDate(startDate, { month: "short" }) : "Pick date...";
2146
+ const displayValue = filter.operator === "isBetween" && dateValue.length === 2 && !isSameDate ? `${calendarPresentation().formatDate(startDate)} - ${calendarPresentation().formatDate(endDate)}` : startDate ? calendarPresentation().formatDate(startDate) : "Pick date...";
2064
2147
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Popover, {
2065
2148
  open: showValueSelector,
2066
2149
  onOpenChange: setShowValueSelector,
@@ -2082,26 +2165,34 @@ function onFilterInputRender({ filter, column, inputId, onFilterUpdate, showValu
2082
2165
  align: "start",
2083
2166
  className: "w-auto p-0",
2084
2167
  children: filter.operator === "isBetween" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
2168
+ lang: calendarPresentation().locale,
2169
+ weekStartsOn: calendarPresentation().weekStartsOn,
2170
+ formatters: calendarPresentation().formatters,
2171
+ labels: calendarPresentation().labels,
2085
2172
  autoFocus: true,
2086
2173
  captionLayout: "dropdown",
2087
2174
  mode: "range",
2088
2175
  selected: dateValue.length === 2 ? {
2089
- from: new Date(Number(dateValue[0])),
2090
- to: new Date(Number(dateValue[1]))
2176
+ from: parseCalendarDate(dateValue[0]),
2177
+ to: parseCalendarDate(dateValue[1])
2091
2178
  } : {
2092
2179
  from: /* @__PURE__ */ new Date(),
2093
2180
  to: /* @__PURE__ */ new Date()
2094
2181
  },
2095
2182
  onSelect: (date) => {
2096
- onFilterUpdate(filter.filterId, { value: date ? [(date.from?.getTime() ?? "").toString(), (date.to?.getTime() ?? "").toString()] : [] });
2183
+ onFilterUpdate(filter.filterId, { value: date ? [date.from ? serializeCalendarDate(date.from) : "", date.to ? serializeCalendarDate(date.to) : ""] : [] });
2097
2184
  }
2098
2185
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
2186
+ lang: calendarPresentation().locale,
2187
+ weekStartsOn: calendarPresentation().weekStartsOn,
2188
+ formatters: calendarPresentation().formatters,
2189
+ labels: calendarPresentation().labels,
2099
2190
  autoFocus: true,
2100
2191
  captionLayout: "dropdown",
2101
2192
  mode: "single",
2102
- selected: dateValue[0] ? new Date(Number(dateValue[0])) : void 0,
2193
+ selected: dateValue[0] ? parseCalendarDate(dateValue[0]) : void 0,
2103
2194
  onSelect: (date) => {
2104
- onFilterUpdate(filter.filterId, { value: (date?.getTime() ?? "").toString() });
2195
+ onFilterUpdate(filter.filterId, { value: date ? serializeCalendarDate(date) : "" });
2105
2196
  }
2106
2197
  })
2107
2198
  })]
@@ -2850,11 +2941,17 @@ function SimpleSliderFilter({ title, range, unit, value, onChange }) {
2850
2941
  })] });
2851
2942
  }
2852
2943
  function SimpleDateFilter({ title, multiple, value, onChange }) {
2944
+ const [open, setOpen] = react.useState(false);
2945
+ const commit = (next) => {
2946
+ setOpen(false);
2947
+ onChange(next);
2948
+ };
2949
+ const formatterVersion = useDateFormatterVersion();
2950
+ const presentation = react.useMemo(() => calendarPresentation(), [formatterVersion]);
2951
+ const { formatDate: formatDate$1 } = presentation;
2853
2952
  const timestamps = Array.isArray(value) ? value : value ? [value] : [];
2854
- const fromTimestamp = timestamps[0] ? Number(timestamps[0]) : void 0;
2855
- const toTimestamp = timestamps[1] ? Number(timestamps[1]) : void 0;
2856
- const fromDate = fromTimestamp ? new Date(fromTimestamp) : void 0;
2857
- const toDate = toTimestamp ? new Date(toTimestamp) : void 0;
2953
+ const fromDate = parseCalendarDate(timestamps[0]);
2954
+ const toDate = parseCalendarDate(timestamps[1]);
2858
2955
  const [localRange, setLocalRange] = react.useState({
2859
2956
  from: void 0,
2860
2957
  to: void 0
@@ -2873,100 +2970,114 @@ function SimpleDateFilter({ title, multiple, value, onChange }) {
2873
2970
  const hasValue = multiple ? fromDate || toDate : fromDate;
2874
2971
  const onReset = (e) => {
2875
2972
  e.stopPropagation();
2876
- onChange(void 0);
2973
+ commit(void 0);
2877
2974
  };
2878
- const label = multiple ? fromDate && toDate ? `${formatDate(fromDate)} - ${formatDate(toDate)}` : fromDate ? formatDate(fromDate) : void 0 : fromDate ? formatDate(fromDate) : void 0;
2879
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Popover, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.PopoverTrigger, {
2880
- asChild: true,
2881
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Button, {
2882
- variant: "outline",
2883
- size: "sm",
2884
- className: "border-dashed font-normal",
2885
- children: [
2886
- hasValue ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2887
- role: "button",
2888
- tabIndex: 0,
2889
- className: "rounded-sm opacity-70 hover:opacity-100",
2890
- onClick: onReset,
2891
- onKeyDown: (e) => {
2892
- if (e.key === "Enter" || e.key === " ") {
2893
- e.preventDefault();
2894
- onReset(e);
2975
+ const label = multiple ? fromDate && toDate ? `${formatDate$1(fromDate)} - ${formatDate$1(toDate)}` : fromDate ? formatDate$1(fromDate) : void 0 : fromDate ? formatDate$1(fromDate) : void 0;
2976
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Popover, {
2977
+ open,
2978
+ onOpenChange: setOpen,
2979
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.PopoverTrigger, {
2980
+ asChild: true,
2981
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Button, {
2982
+ variant: "outline",
2983
+ size: "sm",
2984
+ className: "border-dashed font-normal",
2985
+ children: [
2986
+ hasValue ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2987
+ role: "button",
2988
+ tabIndex: 0,
2989
+ className: "rounded-sm opacity-70 hover:opacity-100",
2990
+ onClick: onReset,
2991
+ onKeyDown: (e) => {
2992
+ if (e.key === "Enter" || e.key === " ") {
2993
+ e.preventDefault();
2994
+ onReset(e);
2995
+ }
2996
+ },
2997
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
2998
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CalendarIcon, {}),
2999
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }),
3000
+ label && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Separator, {
3001
+ orientation: "vertical",
3002
+ className: "mx-0.5 h-4"
3003
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })] })
3004
+ ]
3005
+ })
3006
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.PopoverContent, {
3007
+ className: "w-auto p-0",
3008
+ align: "start",
3009
+ children: multiple ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
3010
+ lang: presentation.locale,
3011
+ weekStartsOn: presentation.weekStartsOn,
3012
+ formatters: presentation.formatters,
3013
+ labels: presentation.labels,
3014
+ defaultMonth: fromDate,
3015
+ autoFocus: true,
3016
+ captionLayout: "dropdown",
3017
+ mode: "range",
3018
+ selected: localRange,
3019
+ onSelect: (range) => {
3020
+ if (!range?.from && !range?.to) {
3021
+ commit(void 0);
3022
+ setLocalRange({
3023
+ from: void 0,
3024
+ to: void 0
3025
+ });
3026
+ return;
3027
+ }
3028
+ const hadFrom = Boolean(localRange.from);
3029
+ const hadTo = Boolean(localRange.to);
3030
+ const from = range.from?.getTime();
3031
+ const to = range.to?.getTime();
3032
+ if (from != null && to != null && from === to) {
3033
+ if (!hadFrom || hadTo) {
3034
+ setLocalRange({
3035
+ from: range.from,
3036
+ to: void 0
3037
+ });
3038
+ return;
2895
3039
  }
2896
- },
2897
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, {})
2898
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CalendarIcon, {}),
2899
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }),
2900
- label && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Separator, {
2901
- orientation: "vertical",
2902
- className: "mx-0.5 h-4"
2903
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })] })
2904
- ]
2905
- })
2906
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.PopoverContent, {
2907
- className: "w-auto p-0",
2908
- align: "start",
2909
- children: multiple ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
2910
- autoFocus: true,
2911
- captionLayout: "dropdown",
2912
- mode: "range",
2913
- selected: localRange,
2914
- onSelect: (range) => {
2915
- if (!range?.from && !range?.to) {
2916
- onChange(void 0);
2917
- setLocalRange({
2918
- from: void 0,
2919
- to: void 0
2920
- });
2921
- return;
2922
- }
2923
- const hadFrom = Boolean(localRange.from);
2924
- const hadTo = Boolean(localRange.to);
2925
- const from = range.from?.getTime();
2926
- const to = range.to?.getTime();
2927
- if (from != null && to != null && from === to) {
2928
- if (!hadFrom || hadTo) {
3040
+ if (hadFrom && !hadTo && localRange.from) {
3041
+ const prevFrom = localRange.from;
3042
+ const newDate = range.from;
3043
+ const [finalFrom, finalTo] = prevFrom <= newDate ? [prevFrom, newDate] : [newDate, prevFrom];
3044
+ setLocalRange({
3045
+ from: finalFrom,
3046
+ to: finalTo
3047
+ });
3048
+ commit([serializeCalendarDate(finalFrom), serializeCalendarDate(finalTo)]);
3049
+ return;
3050
+ }
3051
+ }
3052
+ if (from != null && to != null && from !== to) {
2929
3053
  setLocalRange({
2930
3054
  from: range.from,
2931
- to: void 0
3055
+ to: range.to
2932
3056
  });
3057
+ commit([serializeCalendarDate(range.from), serializeCalendarDate(range.to)]);
2933
3058
  return;
2934
3059
  }
2935
- if (hadFrom && !hadTo && localRange.from) {
2936
- const prevFrom = localRange.from;
2937
- const newDate = range.from;
2938
- const [finalFrom, finalTo] = prevFrom <= newDate ? [prevFrom, newDate] : [newDate, prevFrom];
3060
+ if (range.from && !range.to) {
2939
3061
  setLocalRange({
2940
- from: finalFrom,
2941
- to: finalTo
3062
+ from: range.from,
3063
+ to: void 0
2942
3064
  });
2943
- onChange([finalFrom.getTime().toString(), finalTo.getTime().toString()]);
2944
3065
  return;
2945
3066
  }
2946
3067
  }
2947
- if (from != null && to != null && from !== to) {
2948
- setLocalRange({
2949
- from: range.from,
2950
- to: range.to
2951
- });
2952
- onChange([range.from.getTime().toString(), range.to.getTime().toString()]);
2953
- return;
2954
- }
2955
- if (range.from && !range.to) {
2956
- setLocalRange({
2957
- from: range.from,
2958
- to: void 0
2959
- });
2960
- return;
2961
- }
2962
- }
2963
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
2964
- captionLayout: "dropdown",
2965
- mode: "single",
2966
- selected: fromDate,
2967
- onSelect: (date) => onChange(date ? date.getTime().toString() : void 0)
2968
- })
2969
- })] });
3068
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
3069
+ lang: presentation.locale,
3070
+ weekStartsOn: presentation.weekStartsOn,
3071
+ formatters: presentation.formatters,
3072
+ labels: presentation.labels,
3073
+ defaultMonth: fromDate,
3074
+ captionLayout: "dropdown",
3075
+ mode: "single",
3076
+ selected: fromDate,
3077
+ onSelect: (date) => commit(date ? serializeCalendarDate(date) : void 0)
3078
+ })
3079
+ })]
3080
+ });
2970
3081
  }
2971
3082
 
2972
3083
  //#endregion
@@ -3974,7 +4085,8 @@ function useDataTable(props) {
3974
4085
  if (enableAdvancedFilter) return [];
3975
4086
  return Object.entries(filterValues).reduce((filters, [key, value]) => {
3976
4087
  if (value !== null) {
3977
- const processedValue = Array.isArray(value) ? value : typeof value === "string" && /[^a-zA-Z0-9]/.test(value) ? value.split(/[^a-zA-Z0-9]+/).filter(Boolean) : [value];
4088
+ const variant = filterableColumns.find((column) => column.id === key)?.meta?.variant;
4089
+ const processedValue = Array.isArray(value) ? value : typeof value === "string" && (variant === "date" || variant === "dateRange") ? value.split(",").filter(Boolean) : typeof value === "string" && /[^a-zA-Z0-9]/.test(value) ? value.split(/[^a-zA-Z0-9]+/).filter(Boolean) : [value];
3978
4090
  filters.push({
3979
4091
  id: key,
3980
4092
  value: processedValue
@@ -3982,7 +4094,11 @@ function useDataTable(props) {
3982
4094
  }
3983
4095
  return filters;
3984
4096
  }, []);
3985
- }, [filterValues, enableAdvancedFilter]);
4097
+ }, [
4098
+ filterValues,
4099
+ enableAdvancedFilter,
4100
+ filterableColumns
4101
+ ]);
3986
4102
  const [columnFilters, setColumnFilters] = react.useState(initialColumnFilters);
3987
4103
  const onColumnFiltersChange = react.useCallback((updaterOrValue) => {
3988
4104
  if (enableAdvancedFilter) return;
@@ -6136,13 +6252,6 @@ function mergeFieldOptions(current, incoming) {
6136
6252
  if ((!current || current.length === 0) && (!incoming || incoming.length === 0)) return;
6137
6253
  return mergeFilterOptions(current, incoming ?? []);
6138
6254
  }
6139
- function mergeOptionsByField(left, right) {
6140
- const keys = new Set([...Object.keys(left ?? {}), ...Object.keys(right ?? {})]);
6141
- return Object.fromEntries(Array.from(keys).flatMap((key) => {
6142
- const options = mergeFieldOptions(left?.[key], right?.[key]);
6143
- return options ? [[key, options]] : [];
6144
- }));
6145
- }
6146
6255
  function shouldResolveOptions(field, config, hiddenColumns) {
6147
6256
  if (!config) return false;
6148
6257
  if (hiddenColumns.has(field)) return false;
@@ -6679,7 +6788,7 @@ function buildBatchUpdateFields(schema, batchFields, fields) {
6679
6788
  /**
6680
6789
  * 渲染字段值
6681
6790
  */
6682
- function renderFieldValue(value, type, booleanLocale, options, display = "auto") {
6791
+ function renderFieldValue(value, type, booleanLocale, options, display = "auto", maxItems = 5) {
6683
6792
  if (value === null || value === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6684
6793
  className: "text-muted-foreground",
6685
6794
  children: "-"
@@ -6694,12 +6803,12 @@ function renderFieldValue(value, type, booleanLocale, options, display = "auto")
6694
6803
  if (display === "badge" && (!options || options.length === 0)) {
6695
6804
  if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6696
6805
  className: "flex gap-1 flex-wrap",
6697
- children: [value.slice(0, 5).map((item, index$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6806
+ children: [value.slice(0, maxItems).map((item, index$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6698
6807
  variant: "secondary",
6699
6808
  children: String(item)
6700
- }, index$1)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6809
+ }, index$1)), value.length > maxItems && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6701
6810
  variant: "outline",
6702
- children: ["+", value.length - 5]
6811
+ children: ["+", value.length - maxItems]
6703
6812
  })]
6704
6813
  });
6705
6814
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
@@ -6711,12 +6820,12 @@ function renderFieldValue(value, type, booleanLocale, options, display = "auto")
6711
6820
  if ((display === "badge" || display === "auto") && options && options.length > 0) {
6712
6821
  if (Array.isArray(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6713
6822
  className: "flex gap-1 flex-wrap",
6714
- children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6823
+ children: [value.slice(0, maxItems).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6715
6824
  variant: "secondary",
6716
6825
  children: getOptionLabel(v, options)
6717
- }, i)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6826
+ }, i)), value.length > maxItems && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6718
6827
  variant: "outline",
6719
- children: ["+", value.length - 5]
6828
+ children: ["+", value.length - maxItems]
6720
6829
  })]
6721
6830
  });
6722
6831
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
@@ -6735,48 +6844,81 @@ function renderFieldValue(value, type, booleanLocale, options, display = "auto")
6735
6844
  });
6736
6845
  case "array": return Array.isArray(value) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6737
6846
  className: "flex gap-1 flex-wrap",
6738
- children: [value.slice(0, 5).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6847
+ children: [value.slice(0, maxItems).map((v, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Badge, {
6739
6848
  variant: "secondary",
6740
6849
  children: String(v)
6741
- }, i)), value.length > 5 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6850
+ }, i)), value.length > maxItems && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Badge, {
6742
6851
  variant: "outline",
6743
- children: ["+", value.length - 5]
6852
+ children: ["+", value.length - maxItems]
6744
6853
  })]
6745
6854
  }) : null;
6746
6855
  default: return String(value);
6747
6856
  }
6748
6857
  }
6749
- /**
6750
- * ViewModal 组件 - 详情查看弹窗
6751
- */
6752
- function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, dynamicOptions, denyFields, locale }) {
6753
- if (!data) return null;
6754
- const shape = schema.shape;
6755
- const denySet = new Set(denyFields ?? []);
6858
+ function ViewModal(props) {
6859
+ if (!props.open || !props.data) return null;
6860
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewModalContent, { ...props });
6861
+ }
6862
+ function ViewModalContent({ open, onOpenChange, variant, data, schema, fields: fieldConfig, dynamicOptions, denyFields, locale, tableOverrides, view }) {
6863
+ const excluded = react.useMemo(() => {
6864
+ const denied = new Set(denyFields ?? []);
6865
+ return Object.keys(schema.shape).filter((key) => {
6866
+ const config = fieldConfig?.[key];
6867
+ return denied.has(key) || config?.hidden || view?.overrides?.[key]?.hidden || isDefaultHiddenAuditField(key) && !isFieldTableExplicitlyVisible(config) && view?.overrides?.[key]?.hidden !== false;
6868
+ });
6869
+ }, [
6870
+ schema,
6871
+ fieldConfig,
6872
+ denyFields,
6873
+ view?.overrides
6874
+ ]);
6875
+ const rows = react.useMemo(() => data ? [data] : [], [data]);
6876
+ const resolvedOptions = useDynamicResolveOptions(fieldConfig, rows, excluded);
6756
6877
  const content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dl", {
6757
6878
  className: "grid gap-4 py-4",
6758
- children: Object.entries(shape).filter(([key]) => {
6759
- if (denySet.has(key)) return false;
6760
- const config = fieldConfig?.[key];
6761
- if (config?.hidden) return false;
6762
- if (isDefaultHiddenAuditField(key) && !isFieldTableExplicitlyVisible(config)) return false;
6763
- return true;
6764
- }).map(([key, fieldSchema]) => {
6765
- const parsed = parseZodField(fieldSchema);
6766
- const label = fieldConfig?.[key]?.label ?? humanize(key);
6767
- const value = data[key];
6768
- const options = normalizeFieldOptions(fieldConfig?.[key]?.enum) ?? normalizeFieldOptions(dynamicOptions?.[key]);
6769
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6770
- className: "grid grid-cols-3 items-start gap-4",
6771
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
6772
- className: "text-sm font-medium text-muted-foreground",
6773
- children: label
6774
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
6775
- className: "col-span-2 text-sm",
6776
- children: renderFieldValue(value, parsed.type, locale.boolean, options)
6777
- })]
6778
- }, key);
6779
- })
6879
+ children: (0, __tanstack_react_table.useReactTable)({
6880
+ data: rows,
6881
+ columns: react.useMemo(() => {
6882
+ const overrides = {};
6883
+ for (const [key, fieldSchema] of Object.entries(schema.shape).filter(([fieldKey]) => !excluded.includes(fieldKey))) {
6884
+ const config = fieldConfig?.[key] ?? {};
6885
+ const tableConfig = getTableConfig(config);
6886
+ const listOverride = tableOverrides?.[key];
6887
+ const options = getTableOptions(config) ?? resolvedOptions.optionsByField[key] ?? (tableConfig?.dataSource !== void 0 ? void 0 : dynamicOptions?.[key]);
6888
+ overrides[key] = {
6889
+ label: tableConfig?.label ?? config.label ?? listOverride?.label ?? humanize(key),
6890
+ index: tableConfig?.index ?? listOverride?.index,
6891
+ cell: ({ getValue }) => renderFieldValue(getValue(), parseZodField(fieldSchema).type, locale.boolean, options, tableConfig?.display ?? "auto", Infinity),
6892
+ ...view?.presentation === "table" ? listOverride : void 0,
6893
+ hidden: false,
6894
+ ...view?.overrides?.[key]
6895
+ };
6896
+ }
6897
+ return createTableSchema(schema, {
6898
+ overrides,
6899
+ exclude: excluded
6900
+ });
6901
+ }, [
6902
+ schema,
6903
+ excluded,
6904
+ fieldConfig,
6905
+ tableOverrides,
6906
+ view,
6907
+ resolvedOptions.optionsByField,
6908
+ dynamicOptions,
6909
+ locale.boolean
6910
+ ]),
6911
+ getCoreRowModel: (0, __tanstack_react_table.getCoreRowModel)()
6912
+ }).getRowModel().rows[0]?.getAllCells().map((cell) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6913
+ className: "grid grid-cols-3 items-start gap-4",
6914
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
6915
+ className: "text-sm font-medium text-muted-foreground",
6916
+ children: cell.column.columnDef.meta?.label ?? humanize(cell.column.id)
6917
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", {
6918
+ className: "col-span-2 min-w-0 whitespace-pre-wrap break-words text-sm",
6919
+ children: (0, __tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext())
6920
+ })]
6921
+ }, cell.id))
6780
6922
  });
6781
6923
  if (variant === "sheet") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Sheet, {
6782
6924
  open,
@@ -6897,7 +7039,7 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale, context) {
6897
7039
  *
6898
7040
  * 高级 CRUD 表格组件,封装了完整的增删改查流程
6899
7041
  */
6900
- function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
7042
+ function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, view: viewConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
6901
7043
  const locale = resolveLocale(localeProp);
6902
7044
  const resolvedSchema = resource.schema ?? schema;
6903
7045
  const resolvedFields = react.useMemo(() => applyResourceTablePresentation(mergeFields(resource.fields, fields), resource.fields) ?? {}, [fields, resource.fields]);
@@ -6923,18 +7065,6 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
6923
7065
  tableConfig?.overrides
6924
7066
  ]);
6925
7067
  const dynamicResolveOptions = useDynamicResolveOptions(resolvedFields, resource.tableData.data, hiddenColumns);
6926
- const sharedResolveOptions = useDynamicResolveOptions(react.useMemo(() => Object.fromEntries(Object.entries(resolvedFields).flatMap(([field, config]) => {
6927
- const table = getTableConfig(config);
6928
- if (table?.options === void 0 && table?.dataSource === void 0) return [];
6929
- const { table: _table,...sharedConfig } = config;
6930
- return [[field, sharedConfig]];
6931
- })), [resolvedFields]), resource.tableData.data, hiddenColumns);
6932
- const viewDynamicOptionsByField = react.useMemo(() => mergeOptionsByField(mergeOptionsByField(dynamicFilterOptions.labelOptionsByField, sharedResolveOptions.optionsByField), Object.fromEntries(Object.entries(dynamicResolveOptions.optionsByField).filter(([field]) => !getTableConfig(resolvedFields[field] ?? {})?.dataSource))), [
6933
- dynamicFilterOptions.labelOptionsByField,
6934
- dynamicResolveOptions.optionsByField,
6935
- sharedResolveOptions.optionsByField,
6936
- resolvedFields
6937
- ]);
6938
7068
  const resourceIdKey = resource.idKey ?? "id";
6939
7069
  const actionRegistryVersion = useCrudActionsVersion();
6940
7070
  const unifiedActions = react.useMemo(() => {
@@ -7266,9 +7396,11 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
7266
7396
  data: resource.modal.selected,
7267
7397
  schema: resolvedSchema,
7268
7398
  fields: resolvedFields,
7269
- dynamicOptions: viewDynamicOptionsByField,
7399
+ dynamicOptions: dynamicFilterOptions.labelOptionsByField,
7270
7400
  denyFields,
7271
- locale
7401
+ locale,
7402
+ tableOverrides,
7403
+ view: viewConfig
7272
7404
  }),
7273
7405
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.AlertDialog, {
7274
7406
  open: resource.modal.deleteOpen,
@@ -7465,10 +7597,7 @@ function getIsDateRange(value) {
7465
7597
  return value && typeof value === "object" && !Array.isArray(value);
7466
7598
  }
7467
7599
  function parseAsDate(timestamp) {
7468
- if (!timestamp) return void 0;
7469
- const numericTimestamp = typeof timestamp === "string" ? Number(timestamp) : timestamp;
7470
- const date = new Date(numericTimestamp);
7471
- return !Number.isNaN(date.getTime()) ? date : void 0;
7600
+ return parseCalendarDate(timestamp);
7472
7601
  }
7473
7602
  function parseColumnFilterValue(value) {
7474
7603
  if (value === null || value === void 0) return [];
@@ -7479,6 +7608,9 @@ function parseColumnFilterValue(value) {
7479
7608
  return [];
7480
7609
  }
7481
7610
  function DataTableDateFilter({ column, title, multiple }) {
7611
+ const formatterVersion = useDateFormatterVersion();
7612
+ const presentation = react.useMemo(() => calendarPresentation(), [formatterVersion]);
7613
+ const { formatDate: formatDate$1 } = presentation;
7482
7614
  const columnFilterValue = column.getFilterValue();
7483
7615
  const selectedDates = react.useMemo(() => {
7484
7616
  if (!columnFilterValue) return multiple ? {
@@ -7552,12 +7684,12 @@ function DataTableDateFilter({ column, title, multiple }) {
7552
7684
  from: nextFrom,
7553
7685
  to: nextTo
7554
7686
  });
7555
- if (nextTo) column.setFilterValue([nextFrom.getTime(), nextTo.getTime()]);
7687
+ if (nextTo) column.setFilterValue([serializeCalendarDate(nextFrom), serializeCalendarDate(nextTo)]);
7556
7688
  return;
7557
7689
  }
7558
7690
  setLocalRange(date);
7559
- if (from && to) column.setFilterValue([from, to]);
7560
- } else if (!multiple && "getTime" in date) column.setFilterValue(date.getTime());
7691
+ if (from && to) column.setFilterValue([serializeCalendarDate(new Date(from)), serializeCalendarDate(new Date(to))]);
7692
+ } else if (!multiple && "getTime" in date) column.setFilterValue(serializeCalendarDate(date));
7561
7693
  }, [
7562
7694
  column,
7563
7695
  multiple,
@@ -7577,9 +7709,9 @@ function DataTableDateFilter({ column, title, multiple }) {
7577
7709
  }, [multiple, selectedDates]);
7578
7710
  const formatDateRange = react.useCallback((range) => {
7579
7711
  if (!range.from && !range.to) return "";
7580
- if (range.from && range.to) return `${formatDate(range.from)} - ${formatDate(range.to)}`;
7581
- return formatDate(range.from ?? range.to);
7582
- }, []);
7712
+ if (range.from && range.to) return `${formatDate$1(range.from)} - ${formatDate$1(range.to)}`;
7713
+ return formatDate$1(range.from ?? range.to);
7714
+ }, [formatDate$1]);
7583
7715
  const label = react.useMemo(() => {
7584
7716
  if (multiple) {
7585
7717
  if (!getIsDateRange(selectedDates)) return null;
@@ -7595,7 +7727,7 @@ function DataTableDateFilter({ column, title, multiple }) {
7595
7727
  }
7596
7728
  if (getIsDateRange(selectedDates)) return null;
7597
7729
  const hasSelectedDate = selectedDates.length > 0;
7598
- const dateText = hasSelectedDate ? formatDate(selectedDates[0]) : "Select date";
7730
+ const dateText = hasSelectedDate ? formatDate$1(selectedDates[0]) : "Select date";
7599
7731
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
7600
7732
  className: "flex items-center gap-2",
7601
7733
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }), hasSelectedDate && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Separator, {
@@ -7607,7 +7739,8 @@ function DataTableDateFilter({ column, title, multiple }) {
7607
7739
  selectedDates,
7608
7740
  multiple,
7609
7741
  formatDateRange,
7610
- title
7742
+ title,
7743
+ formatDate$1
7611
7744
  ]);
7612
7745
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Popover, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.PopoverTrigger, {
7613
7746
  asChild: true,
@@ -7617,7 +7750,7 @@ function DataTableDateFilter({ column, title, multiple }) {
7617
7750
  className: "border-dashed font-normal",
7618
7751
  children: [hasValue ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7619
7752
  role: "button",
7620
- "aria-label": `Clear ${title} filter`,
7753
+ "aria-label": presentation.clearLabel(title),
7621
7754
  tabIndex: 0,
7622
7755
  onClick: onReset,
7623
7756
  className: "rounded-sm opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
@@ -7628,14 +7761,24 @@ function DataTableDateFilter({ column, title, multiple }) {
7628
7761
  className: "w-auto p-0",
7629
7762
  align: "start",
7630
7763
  children: multiple ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
7764
+ lang: presentation.locale,
7765
+ weekStartsOn: presentation.weekStartsOn,
7766
+ formatters: presentation.formatters,
7767
+ labels: presentation.labels,
7631
7768
  autoFocus: true,
7632
7769
  captionLayout: "dropdown",
7633
7770
  mode: "range",
7771
+ defaultMonth: localRange.from,
7634
7772
  selected: localRange,
7635
7773
  onSelect
7636
7774
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Calendar, {
7775
+ lang: presentation.locale,
7776
+ weekStartsOn: presentation.weekStartsOn,
7777
+ formatters: presentation.formatters,
7778
+ labels: presentation.labels,
7637
7779
  captionLayout: "dropdown",
7638
7780
  mode: "single",
7781
+ defaultMonth: !getIsDateRange(selectedDates) ? selectedDates[0] : void 0,
7639
7782
  selected: !getIsDateRange(selectedDates) ? selectedDates[0] : void 0,
7640
7783
  onSelect
7641
7784
  })