@eintrek/erp-theme 1.4.10 → 1.4.11
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/components/ui/calendar.d.ts +6 -2
- package/dist/components/ui/date-picker.d.ts +5 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +145 -95
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +144 -94
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
708
|
-
|
|
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
|
|
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" &&
|
|
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
|
-
|
|
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 &&
|
|
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(
|
|
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
|
|
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 = {
|
|
@@ -1334,10 +1457,11 @@ const defaultFormatDate = (date) => new Intl.DateTimeFormat("th-TH", {
|
|
|
1334
1457
|
year: "numeric",
|
|
1335
1458
|
timeZone: "Asia/Bangkok",
|
|
1336
1459
|
}).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, }) {
|
|
1460
|
+
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
1461
|
const [open, setOpen] = React__namespace.useState(false);
|
|
1339
1462
|
const isDateDisabled = disabledDates ?? defaultDisabledDates;
|
|
1340
|
-
|
|
1463
|
+
const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
|
|
1464
|
+
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
1465
|
if (selectedDate instanceof Date) {
|
|
1342
1466
|
onDateChange?.(selectedDate);
|
|
1343
1467
|
setOpen(false);
|
|
@@ -1346,7 +1470,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
|
|
|
1346
1470
|
onDateChange?.(undefined);
|
|
1347
1471
|
setOpen(false);
|
|
1348
1472
|
}
|
|
1349
|
-
}, disabled: isDateDisabled, initialFocus: true }) })] }));
|
|
1473
|
+
}, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
|
|
1350
1474
|
}
|
|
1351
1475
|
|
|
1352
1476
|
const labelVariants = classVarianceAuthority.cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
|
|
@@ -16429,80 +16553,6 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
|
|
|
16429
16553
|
"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
16554
|
}
|
|
16431
16555
|
|
|
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
16556
|
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
16557
|
const reactId = React__namespace.useId();
|
|
16508
16558
|
const inputId = id ?? reactId;
|