@eintrek/erp-theme 1.4.14 → 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.esm.js CHANGED
@@ -731,28 +731,64 @@ function getThaiMonthName(monthIndex) {
731
731
  return THAI_MONTH_NAMES[monthIndex];
732
732
  }
733
733
 
734
+ /**
735
+ * Month arithmetic for the calendar header.
736
+ *
737
+ * This lives apart from the component for one reason: the bug it fixes is
738
+ * invisible on 28 days out of 31. Driving the calendar through the UI proves
739
+ * nothing unless the machine's clock happens to read the 29th or later, so the
740
+ * behaviour has to be reachable by a test that supplies its own date.
741
+ */
742
+ /**
743
+ * stepMonth moves one month in either direction, always landing on the 1st.
744
+ *
745
+ * The obvious implementation — copy the date and call setMonth(m + 1) — keeps
746
+ * the day of the month. From the 31st that asks for a day the target month
747
+ * does not have, and JavaScript rolls forward rather than complaining: 31
748
+ * August plus one month is 1 October, so September cannot be reached at all.
749
+ *
750
+ * It only ever bit a calendar opened with nothing selected, because that
751
+ * starting point is today — including today's day-of-month. On the 3rd the old
752
+ * code is indistinguishable from this one.
753
+ */
754
+ function stepMonth(from, direction) {
755
+ return new Date(from.getFullYear(), from.getMonth() + (direction === "next" ? 1 : -1), 1);
756
+ }
757
+ /**
758
+ * monthAnchor is the month a calendar should open on.
759
+ *
760
+ * A field holding a value opens on that value's month. A field holding nothing
761
+ * opens on the current month — not on whatever month a different field was
762
+ * last browsed to, which is what a shared, unreset state produces.
763
+ */
764
+ function monthAnchor(selected, today) {
765
+ const basis = selected ?? today;
766
+ return new Date(basis.getFullYear(), basis.getMonth(), 1);
767
+ }
768
+
734
769
  /** Thai weekday abbreviations (Sunday-first, matching JS getDay()). */
735
770
  const WEEK_DAYS_TH = ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
736
771
  function getInitialMonth(selected) {
772
+ // Work out which date the calendar should open around, then let monthAnchor
773
+ // pin it to the first. Pinning in one place is what stops an untouched
774
+ // calendar from carrying today's day-of-month into the month arrows —
775
+ // the bug that made the arrows skip a month from the 29th onward.
776
+ return monthAnchor(selectionBasis(selected), new Date());
777
+ }
778
+ function selectionBasis(selected) {
737
779
  if (selected instanceof Date && !Number.isNaN(selected.getTime())) {
738
- return new Date(selected.getFullYear(), selected.getMonth(), 1);
780
+ return selected;
739
781
  }
740
782
  if (Array.isArray(selected) && selected[0] instanceof Date) {
741
- return new Date(selected[0].getFullYear(), selected[0].getMonth(), 1);
783
+ return selected[0];
742
784
  }
743
785
  if (selected &&
744
786
  typeof selected === "object" &&
745
787
  "from" in selected &&
746
788
  selected.from instanceof Date) {
747
- return new Date(selected.from.getFullYear(), selected.from.getMonth(), 1);
789
+ return selected.from;
748
790
  }
749
- // The first of this month, not today.
750
- //
751
- // Returning today carried its day-of-month into the month arrows, which is
752
- // what made an untouched calendar skip a month while one with a date
753
- // already chosen behaved — the selected branches above all pin the day to 1.
754
- const now = new Date();
755
- return new Date(now.getFullYear(), now.getMonth(), 1);
791
+ return undefined;
756
792
  }
757
793
  function Calendar({ className, selected, onSelect, mode = "single", disabled, fromYear = 1900, toYear, }) {
758
794
  const maxYear = toYear ?? new Date().getFullYear() + 10;
@@ -865,15 +901,10 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, fr
865
901
  }
866
902
  };
867
903
  const navigateMonth = (direction) => {
868
- // Built from year and month with the day pinned to 1, never by shifting
869
- // the month on a copy of the current date.
870
- //
871
- // setMonth keeps the day-of-month, so from the 31st it lands on a date
872
- // that does not exist in a 30-day month and JavaScript rolls it forward:
873
- // 31 Aug + 1 month became 1 Oct, and September could not be reached at
874
- // all. It bit every calendar opened with no value selected, because that
875
- // starting point was today — including today's day-of-month.
876
- setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + (direction === "next" ? 1 : -1), 1));
904
+ // stepMonth, not setMonth see the note in lib/calendar-month. The
905
+ // arithmetic lives there because it is only wrong on the 29th and later,
906
+ // and a test has to be able to say what day it is.
907
+ setCurrentMonth((prev) => stepMonth(prev, direction));
877
908
  };
878
909
  const handleMonthChange = (monthStr) => {
879
910
  const nextMonth = Number.parseInt(monthStr, 10);
@@ -1448,7 +1479,13 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1448
1479
  const [open, setOpen] = React.useState(false);
1449
1480
  const isDateDisabled = disabledDates ?? defaultDisabledDates;
1450
1481
  const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
1451
- return (jsxs(Popover, { open: open && !disabled, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("div", { className: "relative w-full", children: [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 }), jsx(Calendar$1, { className: "text-muted-foreground pointer-events-none absolute top-1/2 right-3 h-4 w-4 -translate-y-1/2" })] }) }), jsx(PopoverContent, { className: "w-auto overflow-visible p-0", align: "start", children: jsx(Calendar, { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1482
+ return (jsxs(Popover, { open: open && !disabled, onOpenChange: setOpen, children: [jsx(PopoverTrigger, { asChild: true, children: jsxs("div", { className: "relative w-full", children: [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 }), jsx(Calendar$1, { className: "text-muted-foreground pointer-events-none absolute top-1/2 right-3 h-4 w-4 -translate-y-1/2" })] }) }), jsx(PopoverContent, { className: "w-auto overflow-visible p-0", align: "start", children: jsx(Calendar
1483
+ // Remount on each open so an empty field always starts on this
1484
+ // month. Without it the grid keeps whichever month was last browsed
1485
+ // — and when React reuses this instance for a different field, that
1486
+ // month is one the user was looking at in a *different* box, which
1487
+ // reads as the calendar opening on a random date.
1488
+ , { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1452
1489
  if (selectedDate instanceof Date) {
1453
1490
  onDateChange?.(selectedDate);
1454
1491
  setOpen(false);
@@ -1457,7 +1494,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1457
1494
  onDateChange?.(undefined);
1458
1495
  setOpen(false);
1459
1496
  }
1460
- }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
1497
+ }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }, open ? "open" : "closed") })] }));
1461
1498
  }
1462
1499
 
1463
1500
  const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");