@eintrek/erp-theme 1.4.14 → 1.4.16

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
@@ -308,8 +308,19 @@ function SelectGroup({ ...props }) {
308
308
  function SelectValue({ ...props }) {
309
309
  return require$$1.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
310
310
  }
311
+ /**
312
+ * The trigger fills its container.
313
+ *
314
+ * Upstream ships w-fit, which sizes the control to whatever text happens to
315
+ * be in it — so a field reading "เลือก..." is narrow and the same field with
316
+ * a name in it is wide, and a form of them looks ragged. Across these apps
317
+ * 102 call sites had already written w-full to undo it and 158 had not,
318
+ * which is a default fighting almost everyone who uses it.
319
+ *
320
+ * A caller that wants the compact behaviour passes w-fit.
321
+ */
311
322
  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" }) })] }));
323
+ 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-full 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
324
  }
314
325
  function SelectContent({ className, children, position = "popper", ...props }) {
315
326
  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" &&
@@ -776,28 +787,64 @@ function getThaiMonthName(monthIndex) {
776
787
  return THAI_MONTH_NAMES[monthIndex];
777
788
  }
778
789
 
790
+ /**
791
+ * Month arithmetic for the calendar header.
792
+ *
793
+ * This lives apart from the component for one reason: the bug it fixes is
794
+ * invisible on 28 days out of 31. Driving the calendar through the UI proves
795
+ * nothing unless the machine's clock happens to read the 29th or later, so the
796
+ * behaviour has to be reachable by a test that supplies its own date.
797
+ */
798
+ /**
799
+ * stepMonth moves one month in either direction, always landing on the 1st.
800
+ *
801
+ * The obvious implementation — copy the date and call setMonth(m + 1) — keeps
802
+ * the day of the month. From the 31st that asks for a day the target month
803
+ * does not have, and JavaScript rolls forward rather than complaining: 31
804
+ * August plus one month is 1 October, so September cannot be reached at all.
805
+ *
806
+ * It only ever bit a calendar opened with nothing selected, because that
807
+ * starting point is today — including today's day-of-month. On the 3rd the old
808
+ * code is indistinguishable from this one.
809
+ */
810
+ function stepMonth(from, direction) {
811
+ return new Date(from.getFullYear(), from.getMonth() + (direction === "next" ? 1 : -1), 1);
812
+ }
813
+ /**
814
+ * monthAnchor is the month a calendar should open on.
815
+ *
816
+ * A field holding a value opens on that value's month. A field holding nothing
817
+ * opens on the current month — not on whatever month a different field was
818
+ * last browsed to, which is what a shared, unreset state produces.
819
+ */
820
+ function monthAnchor(selected, today) {
821
+ const basis = selected ?? today;
822
+ return new Date(basis.getFullYear(), basis.getMonth(), 1);
823
+ }
824
+
779
825
  /** Thai weekday abbreviations (Sunday-first, matching JS getDay()). */
780
826
  const WEEK_DAYS_TH = ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
781
827
  function getInitialMonth(selected) {
828
+ // Work out which date the calendar should open around, then let monthAnchor
829
+ // pin it to the first. Pinning in one place is what stops an untouched
830
+ // calendar from carrying today's day-of-month into the month arrows —
831
+ // the bug that made the arrows skip a month from the 29th onward.
832
+ return monthAnchor(selectionBasis(selected), new Date());
833
+ }
834
+ function selectionBasis(selected) {
782
835
  if (selected instanceof Date && !Number.isNaN(selected.getTime())) {
783
- return new Date(selected.getFullYear(), selected.getMonth(), 1);
836
+ return selected;
784
837
  }
785
838
  if (Array.isArray(selected) && selected[0] instanceof Date) {
786
- return new Date(selected[0].getFullYear(), selected[0].getMonth(), 1);
839
+ return selected[0];
787
840
  }
788
841
  if (selected &&
789
842
  typeof selected === "object" &&
790
843
  "from" in selected &&
791
844
  selected.from instanceof Date) {
792
- return new Date(selected.from.getFullYear(), selected.from.getMonth(), 1);
845
+ return selected.from;
793
846
  }
794
- // The first of this month, not today.
795
- //
796
- // Returning today carried its day-of-month into the month arrows, which is
797
- // what made an untouched calendar skip a month while one with a date
798
- // already chosen behaved — the selected branches above all pin the day to 1.
799
- const now = new Date();
800
- return new Date(now.getFullYear(), now.getMonth(), 1);
847
+ return undefined;
801
848
  }
802
849
  function Calendar({ className, selected, onSelect, mode = "single", disabled, fromYear = 1900, toYear, }) {
803
850
  const maxYear = toYear ?? new Date().getFullYear() + 10;
@@ -910,15 +957,10 @@ function Calendar({ className, selected, onSelect, mode = "single", disabled, fr
910
957
  }
911
958
  };
912
959
  const navigateMonth = (direction) => {
913
- // Built from year and month with the day pinned to 1, never by shifting
914
- // the month on a copy of the current date.
915
- //
916
- // setMonth keeps the day-of-month, so from the 31st it lands on a date
917
- // that does not exist in a 30-day month and JavaScript rolls it forward:
918
- // 31 Aug + 1 month became 1 Oct, and September could not be reached at
919
- // all. It bit every calendar opened with no value selected, because that
920
- // starting point was today — including today's day-of-month.
921
- setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + (direction === "next" ? 1 : -1), 1));
960
+ // stepMonth, not setMonth see the note in lib/calendar-month. The
961
+ // arithmetic lives there because it is only wrong on the 29th and later,
962
+ // and a test has to be able to say what day it is.
963
+ setCurrentMonth((prev) => stepMonth(prev, direction));
922
964
  };
923
965
  const handleMonthChange = (monthStr) => {
924
966
  const nextMonth = Number.parseInt(monthStr, 10);
@@ -1493,7 +1535,13 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1493
1535
  const [open, setOpen] = React__namespace.useState(false);
1494
1536
  const isDateDisabled = disabledDates ?? defaultDisabledDates;
1495
1537
  const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
1496
- 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) => {
1538
+ 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
1539
+ // Remount on each open so an empty field always starts on this
1540
+ // month. Without it the grid keeps whichever month was last browsed
1541
+ // — and when React reuses this instance for a different field, that
1542
+ // month is one the user was looking at in a *different* box, which
1543
+ // reads as the calendar opening on a random date.
1544
+ , { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1497
1545
  if (selectedDate instanceof Date) {
1498
1546
  onDateChange?.(selectedDate);
1499
1547
  setOpen(false);
@@ -1502,7 +1550,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1502
1550
  onDateChange?.(undefined);
1503
1551
  setOpen(false);
1504
1552
  }
1505
- }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
1553
+ }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }, open ? "open" : "closed") })] }));
1506
1554
  }
1507
1555
 
1508
1556
  const labelVariants = classVarianceAuthority.cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");