@eintrek/erp-theme 1.4.13 → 1.4.15

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
@@ -776,22 +776,64 @@ function getThaiMonthName(monthIndex) {
776
776
  return THAI_MONTH_NAMES[monthIndex];
777
777
  }
778
778
 
779
+ /**
780
+ * Month arithmetic for the calendar header.
781
+ *
782
+ * This lives apart from the component for one reason: the bug it fixes is
783
+ * invisible on 28 days out of 31. Driving the calendar through the UI proves
784
+ * nothing unless the machine's clock happens to read the 29th or later, so the
785
+ * behaviour has to be reachable by a test that supplies its own date.
786
+ */
787
+ /**
788
+ * stepMonth moves one month in either direction, always landing on the 1st.
789
+ *
790
+ * The obvious implementation — copy the date and call setMonth(m + 1) — keeps
791
+ * the day of the month. From the 31st that asks for a day the target month
792
+ * does not have, and JavaScript rolls forward rather than complaining: 31
793
+ * August plus one month is 1 October, so September cannot be reached at all.
794
+ *
795
+ * It only ever bit a calendar opened with nothing selected, because that
796
+ * starting point is today — including today's day-of-month. On the 3rd the old
797
+ * code is indistinguishable from this one.
798
+ */
799
+ function stepMonth(from, direction) {
800
+ return new Date(from.getFullYear(), from.getMonth() + (direction === "next" ? 1 : -1), 1);
801
+ }
802
+ /**
803
+ * monthAnchor is the month a calendar should open on.
804
+ *
805
+ * A field holding a value opens on that value's month. A field holding nothing
806
+ * opens on the current month — not on whatever month a different field was
807
+ * last browsed to, which is what a shared, unreset state produces.
808
+ */
809
+ function monthAnchor(selected, today) {
810
+ const basis = selected ?? today;
811
+ return new Date(basis.getFullYear(), basis.getMonth(), 1);
812
+ }
813
+
779
814
  /** Thai weekday abbreviations (Sunday-first, matching JS getDay()). */
780
815
  const WEEK_DAYS_TH = ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
781
816
  function getInitialMonth(selected) {
817
+ // Work out which date the calendar should open around, then let monthAnchor
818
+ // pin it to the first. Pinning in one place is what stops an untouched
819
+ // calendar from carrying today's day-of-month into the month arrows —
820
+ // the bug that made the arrows skip a month from the 29th onward.
821
+ return monthAnchor(selectionBasis(selected), new Date());
822
+ }
823
+ function selectionBasis(selected) {
782
824
  if (selected instanceof Date && !Number.isNaN(selected.getTime())) {
783
- return new Date(selected.getFullYear(), selected.getMonth(), 1);
825
+ return selected;
784
826
  }
785
827
  if (Array.isArray(selected) && selected[0] instanceof Date) {
786
- return new Date(selected[0].getFullYear(), selected[0].getMonth(), 1);
828
+ return selected[0];
787
829
  }
788
830
  if (selected &&
789
831
  typeof selected === "object" &&
790
832
  "from" in selected &&
791
833
  selected.from instanceof Date) {
792
- return new Date(selected.from.getFullYear(), selected.from.getMonth(), 1);
834
+ return selected.from;
793
835
  }
794
- return new Date();
836
+ return undefined;
795
837
  }
796
838
  function Calendar({ className, selected, onSelect, mode = "single", disabled, fromYear = 1900, toYear, }) {
797
839
  const maxYear = toYear ?? new Date().getFullYear() + 10;
@@ -904,16 +946,10 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, fr
904
946
  }
905
947
  };
906
948
  const navigateMonth = (direction) => {
907
- setCurrentMonth((prev) => {
908
- const newMonth = new Date(prev);
909
- if (direction === "prev") {
910
- newMonth.setMonth(prev.getMonth() - 1);
911
- }
912
- else {
913
- newMonth.setMonth(prev.getMonth() + 1);
914
- }
915
- return newMonth;
916
- });
949
+ // stepMonth, not setMonth — see the note in lib/calendar-month. The
950
+ // arithmetic lives there because it is only wrong on the 29th and later,
951
+ // and a test has to be able to say what day it is.
952
+ setCurrentMonth((prev) => stepMonth(prev, direction));
917
953
  };
918
954
  const handleMonthChange = (monthStr) => {
919
955
  const nextMonth = Number.parseInt(monthStr, 10);
@@ -1488,7 +1524,13 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1488
1524
  const [open, setOpen] = React__namespace.useState(false);
1489
1525
  const isDateDisabled = disabledDates ?? defaultDisabledDates;
1490
1526
  const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
1491
- 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) => {
1527
+ 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
1528
+ // Remount on each open so an empty field always starts on this
1529
+ // month. Without it the grid keeps whichever month was last browsed
1530
+ // — and when React reuses this instance for a different field, that
1531
+ // month is one the user was looking at in a *different* box, which
1532
+ // reads as the calendar opening on a random date.
1533
+ , { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1492
1534
  if (selectedDate instanceof Date) {
1493
1535
  onDateChange?.(selectedDate);
1494
1536
  setOpen(false);
@@ -1497,7 +1539,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1497
1539
  onDateChange?.(undefined);
1498
1540
  setOpen(false);
1499
1541
  }
1500
- }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
1542
+ }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }, open ? "open" : "closed") })] }));
1501
1543
  }
1502
1544
 
1503
1545
  const labelVariants = classVarianceAuthority.cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");