@eintrek/erp-theme 1.4.10 → 1.4.12

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
@@ -258,6 +258,80 @@ function BreadcrumbSeparator({ children, className, ...props }) {
258
258
  return (require$$1.jsx("li", { "data-slot": "breadcrumb-separator", role: "presentation", "aria-hidden": "true", className: cn$1("[&>svg]:size-3.5", className), ...props, children: children ?? require$$1.jsx(lucideReact.ChevronRight, {}) }));
259
259
  }
260
260
 
261
+ function Select({ onValueChange, onOpenChange, open, defaultOpen, ...props }) {
262
+ // Tracked in a ref, not state: this only needs to be readable inside the
263
+ // callback below, and putting it in state would re-render on every open.
264
+ const isOpenRef = React__namespace.useRef(defaultOpen ?? false);
265
+ if (open !== undefined)
266
+ isOpenRef.current = open;
267
+ return (require$$1.jsx(SelectPrimitive__namespace.Root, { "data-slot": "select", ...props, open: open, defaultOpen: defaultOpen, onOpenChange: next => {
268
+ isOpenRef.current = next;
269
+ onOpenChange?.(next);
270
+ }, onValueChange: value => {
271
+ // Drop ONLY the empty value Radix echoes back on its own.
272
+ //
273
+ // Radix keeps a hidden native <select> so the control works inside
274
+ // real forms. Whenever the controlled value changes it assigns that
275
+ // value to the native node and dispatches a synthetic change event,
276
+ // which comes straight back out through onValueChange:
277
+ //
278
+ // setValue.call(select, selectValue);
279
+ // select.dispatchEvent(new Event("change", { bubbles: true }));
280
+ // ...
281
+ // onChange: (event) => onValueChange(event.target.value)
282
+ //
283
+ // The native <option> list is registered by SelectItem, and the items
284
+ // live inside SelectContent — portalled, and only mounted while the
285
+ // menu is open. On a closed select the option for the incoming value
286
+ // usually does not exist yet, the DOM refuses the assignment,
287
+ // `select.value` collapses to "", and that empty string reaches the
288
+ // consumer. In a form this lands exactly when saved data arrives: the
289
+ // field is set to its stored value, Radix echoes "", and the handler
290
+ // writes that back — enum fields then snap to their fallback and zod
291
+ // rejects the submit with "received ''".
292
+ //
293
+ // The open check is what keeps a real "none" option working. Radix
294
+ // does NOT forbid <SelectItem value="">, and picking one is a
295
+ // legitimate way to clear a selection. A user pick always arrives
296
+ // while the menu is still open — SelectItem's handleSelect calls
297
+ // onValueChange(value) BEFORE onOpenChange(false) — whereas the echo
298
+ // above fires from an effect with the menu closed. So only the closed
299
+ // case is suppressed.
300
+ if (value === "" && !isOpenRef.current)
301
+ return;
302
+ onValueChange?.(value);
303
+ } }));
304
+ }
305
+ function SelectGroup({ ...props }) {
306
+ return require$$1.jsx(SelectPrimitive__namespace.Group, { "data-slot": "select-group", ...props });
307
+ }
308
+ function SelectValue({ ...props }) {
309
+ return require$$1.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
310
+ }
311
+ function SelectTrigger({ className, size = "default", children, ...props }) {
312
+ return (require$$1.jsxs(SelectPrimitive__namespace.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn$1("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, require$$1.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: require$$1.jsx(lucideReact.ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
313
+ }
314
+ function SelectContent({ className, children, position = "popper", ...props }) {
315
+ return (require$$1.jsx(SelectPrimitive__namespace.Portal, { children: require$$1.jsxs(SelectPrimitive__namespace.Content, { "data-slot": "select-content", className: cn$1("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", position === "popper" &&
316
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [require$$1.jsx(SelectScrollUpButton, {}), require$$1.jsx(SelectPrimitive__namespace.Viewport, { className: cn$1("p-1", position === "popper" &&
317
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), require$$1.jsx(SelectScrollDownButton, {})] }) }));
318
+ }
319
+ function SelectLabel({ className, ...props }) {
320
+ return (require$$1.jsx(SelectPrimitive__namespace.Label, { "data-slot": "select-label", className: cn$1("text-muted-foreground px-2 py-1.5 text-xs", className), ...props }));
321
+ }
322
+ function SelectItem({ className, children, ...props }) {
323
+ return (require$$1.jsxs(SelectPrimitive__namespace.Item, { "data-slot": "select-item", className: cn$1("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [require$$1.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: require$$1.jsx(SelectPrimitive__namespace.ItemIndicator, { children: require$$1.jsx(lucideReact.CheckIcon, { className: "size-4" }) }) }), require$$1.jsx(SelectPrimitive__namespace.ItemText, { children: children })] }));
324
+ }
325
+ function SelectSeparator({ className, ...props }) {
326
+ return (require$$1.jsx(SelectPrimitive__namespace.Separator, { "data-slot": "select-separator", className: cn$1("bg-border pointer-events-none -mx-1 my-1 h-px", className), ...props }));
327
+ }
328
+ function SelectScrollUpButton({ className, ...props }) {
329
+ return (require$$1.jsx(SelectPrimitive__namespace.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: require$$1.jsx(lucideReact.ChevronUpIcon, { className: "size-4" }) }));
330
+ }
331
+ function SelectScrollDownButton({ className, ...props }) {
332
+ return (require$$1.jsx(SelectPrimitive__namespace.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: require$$1.jsx(lucideReact.ChevronDownIcon, { className: "size-4" }) }));
333
+ }
334
+
261
335
  /**
262
336
  * @module constants
263
337
  * @summary Useful constants
@@ -704,11 +778,35 @@ function getThaiMonthName(monthIndex) {
704
778
 
705
779
  /** Thai weekday abbreviations (Sunday-first, matching JS getDay()). */
706
780
  const WEEK_DAYS_TH = ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
707
- function formatMonthCaption(date) {
708
- return `${THAI_MONTH_NAMES[date.getMonth()]} ${toBuddhistYear(date.getFullYear())}`;
781
+ function getInitialMonth(selected) {
782
+ if (selected instanceof Date && !Number.isNaN(selected.getTime())) {
783
+ return new Date(selected.getFullYear(), selected.getMonth(), 1);
784
+ }
785
+ if (Array.isArray(selected) && selected[0] instanceof Date) {
786
+ return new Date(selected[0].getFullYear(), selected[0].getMonth(), 1);
787
+ }
788
+ if (selected &&
789
+ typeof selected === "object" &&
790
+ "from" in selected &&
791
+ selected.from instanceof Date) {
792
+ return new Date(selected.from.getFullYear(), selected.from.getMonth(), 1);
793
+ }
794
+ return new Date();
709
795
  }
710
- function Calendar({ className, selected, onSelect, mode = "single", disabled, }) {
711
- const [currentMonth, setCurrentMonth] = React__namespace.useState(new Date());
796
+ function Calendar({ className, selected, onSelect, mode = "single", disabled, fromYear = 1900, toYear, }) {
797
+ const maxYear = toYear ?? new Date().getFullYear() + 10;
798
+ const [currentMonth, setCurrentMonth] = React__namespace.useState(() => getInitialMonth(selected));
799
+ // Jump the grid to the selected date when it changes (e.g. form load).
800
+ React__namespace.useEffect(() => {
801
+ const next = getInitialMonth(selected);
802
+ setCurrentMonth((prev) => {
803
+ if (prev.getFullYear() === next.getFullYear() &&
804
+ prev.getMonth() === next.getMonth()) {
805
+ return prev;
806
+ }
807
+ return next;
808
+ });
809
+ }, [selected]);
712
810
  const today = new Date();
713
811
  const year = currentMonth.getFullYear();
714
812
  const month = currentMonth.getMonth();
@@ -716,13 +814,20 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
716
814
  const lastDayOfMonth = new Date(year, month + 1, 0);
717
815
  const firstDayOfWeek = firstDayOfMonth.getDay();
718
816
  const daysInMonth = lastDayOfMonth.getDate();
817
+ const yearOptions = React__namespace.useMemo(() => {
818
+ const years = [];
819
+ const start = Math.min(fromYear, maxYear);
820
+ const end = Math.max(fromYear, maxYear);
821
+ for (let y = end; y >= start; y--) {
822
+ years.push(y);
823
+ }
824
+ return years;
825
+ }, [fromYear, maxYear]);
719
826
  const getDaysInMonth = () => {
720
827
  const days = [];
721
- // Add empty cells for days before the first day of the month
722
828
  for (let i = 0; i < firstDayOfWeek; i++) {
723
829
  days.push(null);
724
830
  }
725
- // Add days of the month
726
831
  for (let day = 1; day <= daysInMonth; day++) {
727
832
  days.push(new Date(year, month, day));
728
833
  }
@@ -734,7 +839,10 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
734
839
  if (mode === "single" && selected instanceof Date) {
735
840
  return date.toDateString() === selected.toDateString();
736
841
  }
737
- if (mode === "range" && selected && typeof selected === "object" && "from" in selected) {
842
+ if (mode === "range" &&
843
+ selected &&
844
+ typeof selected === "object" &&
845
+ "from" in selected) {
738
846
  const range = selected;
739
847
  if (range.from && range.to) {
740
848
  return date >= range.from && date <= range.to;
@@ -744,7 +852,7 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
744
852
  }
745
853
  }
746
854
  if (mode === "multiple" && Array.isArray(selected)) {
747
- return selected.some(d => d.toDateString() === date.toDateString());
855
+ return selected.some((d) => d.toDateString() === date.toDateString());
748
856
  }
749
857
  return false;
750
858
  };
@@ -763,7 +871,6 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
763
871
  onSelect?.(date);
764
872
  }
765
873
  else if (mode === "range") {
766
- // Simple range selection - click to set start, click again to set end
767
874
  if (!selected || typeof selected !== "object" || !("from" in selected)) {
768
875
  onSelect?.({ from: date, to: undefined });
769
876
  }
@@ -787,9 +894,9 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
787
894
  }
788
895
  else if (mode === "multiple") {
789
896
  const currentSelection = Array.isArray(selected) ? selected : [];
790
- const isAlreadySelected = currentSelection.some(d => d.toDateString() === date.toDateString());
897
+ const isAlreadySelected = currentSelection.some((d) => d.toDateString() === date.toDateString());
791
898
  if (isAlreadySelected) {
792
- onSelect?.(currentSelection.filter(d => d.toDateString() !== date.toDateString()));
899
+ onSelect?.(currentSelection.filter((d) => d.toDateString() !== date.toDateString()));
793
900
  }
794
901
  else {
795
902
  onSelect?.([...currentSelection, date]);
@@ -797,7 +904,7 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
797
904
  }
798
905
  };
799
906
  const navigateMonth = (direction) => {
800
- setCurrentMonth(prev => {
907
+ setCurrentMonth((prev) => {
801
908
  const newMonth = new Date(prev);
802
909
  if (direction === "prev") {
803
910
  newMonth.setMonth(prev.getMonth() - 1);
@@ -808,18 +915,34 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, })
808
915
  return newMonth;
809
916
  });
810
917
  };
918
+ const handleMonthChange = (monthStr) => {
919
+ const nextMonth = Number.parseInt(monthStr, 10);
920
+ if (Number.isNaN(nextMonth))
921
+ return;
922
+ setCurrentMonth((prev) => new Date(prev.getFullYear(), nextMonth, 1));
923
+ };
924
+ const handleYearChange = (buddhistYearStr) => {
925
+ const gregorian = toGregorianYear(Number.parseInt(buddhistYearStr, 10));
926
+ if (Number.isNaN(gregorian))
927
+ return;
928
+ setCurrentMonth((prev) => new Date(gregorian, prev.getMonth(), 1));
929
+ };
811
930
  const days = getDaysInMonth();
812
- return (require$$1.jsxs("div", { "data-slot": "calendar", className: cn$1("bg-background group/calendar p-3 w-fit", className), children: [require$$1.jsxs("div", { className: "flex items-center justify-between mb-4", children: [require$$1.jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigateMonth("prev"), className: "h-8 w-8 p-0", children: require$$1.jsx(lucideReact.ChevronLeftIcon, { className: "h-4 w-4" }) }), require$$1.jsx("div", { className: "text-sm font-medium", children: formatMonthCaption(currentMonth) }), require$$1.jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigateMonth("next"), className: "h-8 w-8 p-0", children: require$$1.jsx(lucideReact.ChevronRightIcon, { className: "h-4 w-4" }) })] }), require$$1.jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEK_DAYS_TH.map((day) => (require$$1.jsx("div", { className: "text-muted-foreground text-xs font-normal text-center h-8 flex items-center justify-center", children: day }, day))) }), require$$1.jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((date, index) => {
931
+ const canGoPrev = year > fromYear || (year === fromYear && month > 0);
932
+ const canGoNext = year < maxYear || (year === maxYear && month < 11);
933
+ return (require$$1.jsxs("div", { "data-slot": "calendar", className: cn$1("bg-background group/calendar p-3 w-fit", className), children: [require$$1.jsxs("div", { className: "mb-4 flex items-center justify-between gap-1", children: [require$$1.jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => navigateMonth("prev"), disabled: !canGoPrev, className: "h-8 w-8 shrink-0 p-0", "aria-label": "\u0E40\u0E14\u0E37\u0E2D\u0E19\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32", children: require$$1.jsx(lucideReact.ChevronLeftIcon, { className: "h-4 w-4" }) }), require$$1.jsxs("div", { className: "flex min-w-0 flex-1 items-center justify-center gap-1", children: [require$$1.jsxs(Select, { value: String(month), onValueChange: handleMonthChange, children: [require$$1.jsx(SelectTrigger, { size: "sm", className: "h-8 w-[7.5rem] px-2 text-xs", "aria-label": "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E14\u0E37\u0E2D\u0E19", children: require$$1.jsx(SelectValue, { children: THAI_MONTH_NAMES[month] }) }), require$$1.jsx(SelectContent, { className: "z-[100] max-h-60", children: THAI_MONTH_NAMES.map((name, index) => (require$$1.jsx(SelectItem, { value: String(index), children: name }, name))) })] }), require$$1.jsxs(Select, { value: String(toBuddhistYear(year)), onValueChange: handleYearChange, children: [require$$1.jsx(SelectTrigger, { size: "sm", className: "h-8 w-[5.5rem] px-2 text-xs", "aria-label": "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E1B\u0E35", children: require$$1.jsx(SelectValue, { children: toBuddhistYear(year) }) }), require$$1.jsx(SelectContent, { className: "z-[100] max-h-60", children: yearOptions.map((y) => (require$$1.jsx(SelectItem, { value: String(toBuddhistYear(y)), children: toBuddhistYear(y) }, y))) })] })] }), require$$1.jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => navigateMonth("next"), disabled: !canGoNext, className: "h-8 w-8 shrink-0 p-0", "aria-label": "\u0E40\u0E14\u0E37\u0E2D\u0E19\u0E16\u0E31\u0E14\u0E44\u0E1B", children: require$$1.jsx(lucideReact.ChevronRightIcon, { className: "h-4 w-4" }) })] }), require$$1.jsx("div", { className: "mb-2 grid grid-cols-7 gap-1", children: WEEK_DAYS_TH.map((day) => (require$$1.jsx("div", { className: "text-muted-foreground flex h-8 items-center justify-center text-center text-xs font-normal", children: day }, day))) }), require$$1.jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((date, index) => {
813
934
  if (!date) {
814
935
  return require$$1.jsx("div", { className: "h-8" }, index);
815
936
  }
816
937
  const isDateSelected = isSelected(date);
817
938
  const isDateToday = isToday(date);
818
939
  const isDateDisabled = isDisabled(date);
819
- return (require$$1.jsx(Button, { variant: "ghost", size: "icon", className: cn$1("h-8 w-8 p-0 text-sm font-normal", isDateSelected && "bg-primary text-primary-foreground", isDateToday && !isDateSelected && "bg-accent text-accent-foreground", isDateDisabled && "opacity-50 cursor-not-allowed", "hover:bg-accent hover:text-accent-foreground"), onClick: () => handleDateClick(date), disabled: isDateDisabled, children: date.getDate() }, date.toISOString()));
940
+ return (require$$1.jsx(Button, { type: "button", variant: "ghost", size: "icon", className: cn$1("h-8 w-8 p-0 text-sm font-normal", isDateSelected && "bg-primary text-primary-foreground", isDateToday &&
941
+ !isDateSelected &&
942
+ "bg-accent text-accent-foreground", isDateDisabled && "cursor-not-allowed opacity-50", "hover:bg-accent hover:text-accent-foreground"), onClick: () => handleDateClick(date), disabled: isDateDisabled, children: date.getDate() }, date.toISOString()));
820
943
  }) })] }));
821
944
  }
822
- function DateInput({ value, onChange, placeholder = "Select date", className, disabled = false }) {
945
+ function DateInput({ value, onChange, placeholder = "Select date", className, disabled = false, }) {
823
946
  const handleChange = (event) => {
824
947
  const dateValue = event.target.value;
825
948
  if (dateValue) {
@@ -830,9 +953,9 @@ function DateInput({ value, onChange, placeholder = "Select date", className, di
830
953
  }
831
954
  };
832
955
  const formatDateForInput = (date) => {
833
- return date.toISOString().split('T')[0];
956
+ return date.toISOString().split("T")[0];
834
957
  };
835
- return (require$$1.jsx("input", { type: "date", value: value ? formatDateForInput(value) : "", onChange: handleChange, placeholder: placeholder, disabled: disabled, className: cn$1("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className) }));
958
+ return (require$$1.jsx("input", { type: "date", value: value ? formatDateForInput(value) : "", onChange: handleChange, placeholder: placeholder, disabled: disabled, className: cn$1("border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", className) }));
836
959
  }
837
960
 
838
961
  const formatDistanceLocale = {
@@ -1327,17 +1450,29 @@ function PopoverContent({ className, align = "center", sideOffset = 4, ...props
1327
1450
  return (require$$1.jsx(PopoverPrimitive__namespace.Portal, { children: require$$1.jsx(PopoverPrimitive__namespace.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, className: cn$1("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", className), ...props }) }));
1328
1451
  }
1329
1452
 
1330
- const defaultDisabledDates = (date) => date > new Date() || date < new Date("1900-01-01");
1453
+ /**
1454
+ * Only the lower bound is universal. Rejecting future dates by default made
1455
+ * every forward-looking field — due dates, expiry, delivery, installment
1456
+ * schedules — silently unusable unless the caller remembered to override it,
1457
+ * and several forms already carried `d => d < new Date("1900-01-01")` purely
1458
+ * to undo it. A field that must stay in the past says so with its own
1459
+ * `disabledDates`; the picker no longer guesses.
1460
+ */
1461
+ const defaultDisabledDates = (date) => date < new Date("1900-01-01");
1462
+ /** Ready-made matcher for fields that genuinely cannot be in the future
1463
+ * (date of birth, an issue date on a document already in hand). */
1464
+ const disallowFutureDates = (date) => date > new Date() || date < new Date("1900-01-01");
1331
1465
  const defaultFormatDate = (date) => new Intl.DateTimeFormat("th-TH", {
1332
1466
  day: "numeric",
1333
1467
  month: "long",
1334
1468
  year: "numeric",
1335
1469
  timeZone: "Asia/Bangkok",
1336
1470
  }).format(date);
1337
- function DatePicker({ date, onDateChange, placeholder = "เลือกวันที่", disabled = false, disabledDates, formatDate = defaultFormatDate, className, id, ariaLabel, "aria-required": ariaRequired, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }) {
1471
+ function DatePicker({ date, onDateChange, placeholder = "เลือกวันที่", disabled = false, disabledDates, formatDate = defaultFormatDate, fromYear = 1900, toYear, className, id, ariaLabel, "aria-required": ariaRequired, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }) {
1338
1472
  const [open, setOpen] = React__namespace.useState(false);
1339
1473
  const isDateDisabled = disabledDates ?? defaultDisabledDates;
1340
- return (require$$1.jsxs(Popover, { open: open && !disabled, onOpenChange: setOpen, children: [require$$1.jsx(PopoverTrigger, { asChild: true, children: require$$1.jsxs("div", { className: "relative w-full", children: [require$$1.jsx(Input, { readOnly: true, disabled: disabled, value: date ? formatDate(date) : "", placeholder: placeholder, className: cn$1("border-border bg-background text-foreground placeholder:text-muted-foreground w-full pr-9", disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer", className), id: id, "aria-label": date ? undefined : (ariaLabel ?? placeholder), "aria-required": ariaRequired, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby }), require$$1.jsx(lucideReact.Calendar, { className: "text-muted-foreground pointer-events-none absolute top-1/2 right-3 h-4 w-4 -translate-y-1/2" })] }) }), require$$1.jsx(PopoverContent, { className: "w-[280px] overflow-hidden p-0", align: "start", children: require$$1.jsx(Calendar, { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1474
+ const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
1475
+ return (require$$1.jsxs(Popover, { open: open && !disabled, onOpenChange: setOpen, children: [require$$1.jsx(PopoverTrigger, { asChild: true, children: require$$1.jsxs("div", { className: "relative w-full", children: [require$$1.jsx(Input, { readOnly: true, disabled: disabled, value: date ? formatDate(date) : "", placeholder: placeholder, className: cn$1("border-border bg-background text-foreground placeholder:text-muted-foreground w-full pr-9", disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer", className), id: id, "aria-label": date ? undefined : (ariaLabel ?? placeholder), "aria-required": ariaRequired, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby }), require$$1.jsx(lucideReact.Calendar, { className: "text-muted-foreground pointer-events-none absolute top-1/2 right-3 h-4 w-4 -translate-y-1/2" })] }) }), require$$1.jsx(PopoverContent, { className: "w-auto overflow-visible p-0", align: "start", children: require$$1.jsx(Calendar, { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1341
1476
  if (selectedDate instanceof Date) {
1342
1477
  onDateChange?.(selectedDate);
1343
1478
  setOpen(false);
@@ -1346,7 +1481,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1346
1481
  onDateChange?.(undefined);
1347
1482
  setOpen(false);
1348
1483
  }
1349
- }, disabled: isDateDisabled, initialFocus: true }) })] }));
1484
+ }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
1350
1485
  }
1351
1486
 
1352
1487
  const labelVariants = classVarianceAuthority.cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
@@ -16429,80 +16564,6 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
16429
16564
  "h-2.5 flex-col border-t border-t-transparent", className), ...props, children: require$$1.jsx(ScrollAreaPrimitive__namespace.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-border relative flex-1 rounded-full" }) }));
16430
16565
  }
16431
16566
 
16432
- function Select({ onValueChange, onOpenChange, open, defaultOpen, ...props }) {
16433
- // Tracked in a ref, not state: this only needs to be readable inside the
16434
- // callback below, and putting it in state would re-render on every open.
16435
- const isOpenRef = React__namespace.useRef(defaultOpen ?? false);
16436
- if (open !== undefined)
16437
- isOpenRef.current = open;
16438
- return (require$$1.jsx(SelectPrimitive__namespace.Root, { "data-slot": "select", ...props, open: open, defaultOpen: defaultOpen, onOpenChange: next => {
16439
- isOpenRef.current = next;
16440
- onOpenChange?.(next);
16441
- }, onValueChange: value => {
16442
- // Drop ONLY the empty value Radix echoes back on its own.
16443
- //
16444
- // Radix keeps a hidden native <select> so the control works inside
16445
- // real forms. Whenever the controlled value changes it assigns that
16446
- // value to the native node and dispatches a synthetic change event,
16447
- // which comes straight back out through onValueChange:
16448
- //
16449
- // setValue.call(select, selectValue);
16450
- // select.dispatchEvent(new Event("change", { bubbles: true }));
16451
- // ...
16452
- // onChange: (event) => onValueChange(event.target.value)
16453
- //
16454
- // The native <option> list is registered by SelectItem, and the items
16455
- // live inside SelectContent — portalled, and only mounted while the
16456
- // menu is open. On a closed select the option for the incoming value
16457
- // usually does not exist yet, the DOM refuses the assignment,
16458
- // `select.value` collapses to "", and that empty string reaches the
16459
- // consumer. In a form this lands exactly when saved data arrives: the
16460
- // field is set to its stored value, Radix echoes "", and the handler
16461
- // writes that back — enum fields then snap to their fallback and zod
16462
- // rejects the submit with "received ''".
16463
- //
16464
- // The open check is what keeps a real "none" option working. Radix
16465
- // does NOT forbid <SelectItem value="">, and picking one is a
16466
- // legitimate way to clear a selection. A user pick always arrives
16467
- // while the menu is still open — SelectItem's handleSelect calls
16468
- // onValueChange(value) BEFORE onOpenChange(false) — whereas the echo
16469
- // above fires from an effect with the menu closed. So only the closed
16470
- // case is suppressed.
16471
- if (value === "" && !isOpenRef.current)
16472
- return;
16473
- onValueChange?.(value);
16474
- } }));
16475
- }
16476
- function SelectGroup({ ...props }) {
16477
- return require$$1.jsx(SelectPrimitive__namespace.Group, { "data-slot": "select-group", ...props });
16478
- }
16479
- function SelectValue({ ...props }) {
16480
- return require$$1.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
16481
- }
16482
- function SelectTrigger({ className, size = "default", children, ...props }) {
16483
- return (require$$1.jsxs(SelectPrimitive__namespace.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn$1("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, require$$1.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: require$$1.jsx(lucideReact.ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
16484
- }
16485
- function SelectContent({ className, children, position = "popper", ...props }) {
16486
- return (require$$1.jsx(SelectPrimitive__namespace.Portal, { children: require$$1.jsxs(SelectPrimitive__namespace.Content, { "data-slot": "select-content", className: cn$1("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", position === "popper" &&
16487
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position: position, ...props, children: [require$$1.jsx(SelectScrollUpButton, {}), require$$1.jsx(SelectPrimitive__namespace.Viewport, { className: cn$1("p-1", position === "popper" &&
16488
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), require$$1.jsx(SelectScrollDownButton, {})] }) }));
16489
- }
16490
- function SelectLabel({ className, ...props }) {
16491
- return (require$$1.jsx(SelectPrimitive__namespace.Label, { "data-slot": "select-label", className: cn$1("text-muted-foreground px-2 py-1.5 text-xs", className), ...props }));
16492
- }
16493
- function SelectItem({ className, children, ...props }) {
16494
- return (require$$1.jsxs(SelectPrimitive__namespace.Item, { "data-slot": "select-item", className: cn$1("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [require$$1.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: require$$1.jsx(SelectPrimitive__namespace.ItemIndicator, { children: require$$1.jsx(lucideReact.CheckIcon, { className: "size-4" }) }) }), require$$1.jsx(SelectPrimitive__namespace.ItemText, { children: children })] }));
16495
- }
16496
- function SelectSeparator({ className, ...props }) {
16497
- return (require$$1.jsx(SelectPrimitive__namespace.Separator, { "data-slot": "select-separator", className: cn$1("bg-border pointer-events-none -mx-1 my-1 h-px", className), ...props }));
16498
- }
16499
- function SelectScrollUpButton({ className, ...props }) {
16500
- return (require$$1.jsx(SelectPrimitive__namespace.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: require$$1.jsx(lucideReact.ChevronUpIcon, { className: "size-4" }) }));
16501
- }
16502
- function SelectScrollDownButton({ className, ...props }) {
16503
- return (require$$1.jsx(SelectPrimitive__namespace.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: require$$1.jsx(lucideReact.ChevronDownIcon, { className: "size-4" }) }));
16504
- }
16505
-
16506
16567
  function SelectField({ id, label, helperText, error, required, placeholder, value, defaultValue, onValueChange, disabled, options, children, containerClassName, labelClassName, triggerClassName, messageClassName, size, "aria-label": ariaLabel, "aria-describedby": ariaDescribedby, }) {
16507
16568
  const reactId = React__namespace.useId();
16508
16569
  const inputId = id ?? reactId;
@@ -35115,6 +35176,7 @@ exports.convertFileToUploadedFile = convertFileToUploadedFile;
35115
35176
  exports.convertFilesToUploadedFiles = convertFilesToUploadedFiles;
35116
35177
  exports.dataTableConfig = dataTableConfig;
35117
35178
  exports.databasePrefix = databasePrefix;
35179
+ exports.disallowFutureDates = disallowFutureDates;
35118
35180
  exports.flagConfig = flagConfig;
35119
35181
  exports.formatCurrency = formatCurrency;
35120
35182
  exports.formatDate = formatDate;