@eintrek/erp-theme 1.4.9 → 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/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import require$$1, { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { ChevronDownIcon, ChevronRight, ChevronLeftIcon, ChevronRightIcon, Calendar as Calendar$1, ArrowRight, ArrowLeft, CheckIcon, XIcon, SearchIcon, CircleIcon, ChevronsUpDown, Check, Paperclip, X as X$1, AlertCircle, MoreHorizontalIcon, GripVerticalIcon, ChevronUpIcon, PanelLeftIcon, ChevronLeft, MoreHorizontal, ChevronDown, ChevronUp, EyeOff, Filter, Settings2, ListChecks, ArrowDownUp, Trash2, GripVertical, ListFilter, CalendarIcon, Copy, Code2, FileSpreadsheetIcon, CommandIcon } from 'lucide-react';
2
+ import { ChevronDownIcon, ChevronRight, CheckIcon, ChevronUpIcon, ChevronLeftIcon, ChevronRightIcon, Calendar as Calendar$1, ArrowRight, ArrowLeft, XIcon, SearchIcon, CircleIcon, ChevronsUpDown, Check, Paperclip, X as X$1, AlertCircle, MoreHorizontalIcon, GripVerticalIcon, PanelLeftIcon, ChevronLeft, MoreHorizontal, ChevronDown, ChevronUp, EyeOff, Filter, Settings2, ListChecks, ArrowDownUp, Trash2, GripVertical, ListFilter, CalendarIcon, Copy, Code2, FileSpreadsheetIcon, CommandIcon } from 'lucide-react';
3
3
  import '@radix-ui/react-accessible-icon';
4
4
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
5
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
@@ -213,133 +213,266 @@ function BreadcrumbSeparator({ children, className, ...props }) {
213
213
  return (jsx("li", { "data-slot": "breadcrumb-separator", role: "presentation", "aria-hidden": "true", className: cn$1("[&>svg]:size-3.5", className), ...props, children: children ?? jsx(ChevronRight, {}) }));
214
214
  }
215
215
 
216
- function Calendar({ className, selected, onSelect, mode = "single", disabled, }) {
217
- const [currentMonth, setCurrentMonth] = React.useState(new Date());
218
- const today = new Date();
219
- const year = currentMonth.getFullYear();
220
- const month = currentMonth.getMonth();
221
- const firstDayOfMonth = new Date(year, month, 1);
222
- const lastDayOfMonth = new Date(year, month + 1, 0);
223
- const firstDayOfWeek = firstDayOfMonth.getDay();
224
- const daysInMonth = lastDayOfMonth.getDate();
225
- const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
226
- const getDaysInMonth = () => {
227
- const days = [];
228
- // Add empty cells for days before the first day of the month
229
- for (let i = 0; i < firstDayOfWeek; i++) {
230
- days.push(null);
231
- }
232
- // Add days of the month
233
- for (let day = 1; day <= daysInMonth; day++) {
234
- days.push(new Date(year, month, day));
235
- }
236
- return days;
237
- };
238
- const isSelected = (date) => {
239
- if (!selected)
240
- return false;
241
- if (mode === "single" && selected instanceof Date) {
242
- return date.toDateString() === selected.toDateString();
243
- }
244
- if (mode === "range" && selected && typeof selected === "object" && "from" in selected) {
245
- const range = selected;
246
- if (range.from && range.to) {
247
- return date >= range.from && date <= range.to;
248
- }
249
- if (range.from) {
250
- return date.toDateString() === range.from.toDateString();
251
- }
252
- }
253
- if (mode === "multiple" && Array.isArray(selected)) {
254
- return selected.some(d => d.toDateString() === date.toDateString());
255
- }
256
- return false;
257
- };
258
- const isToday = (date) => {
259
- return date.toDateString() === today.toDateString();
260
- };
261
- const isDisabled = (date) => {
262
- if (disabled)
263
- return disabled(date);
264
- return false;
265
- };
266
- const handleDateClick = (date) => {
267
- if (isDisabled(date))
268
- return;
269
- if (mode === "single") {
270
- onSelect?.(date);
271
- }
272
- else if (mode === "range") {
273
- // Simple range selection - click to set start, click again to set end
274
- if (!selected || typeof selected !== "object" || !("from" in selected)) {
275
- onSelect?.({ from: date, to: undefined });
276
- }
277
- else {
278
- const range = selected;
279
- if (!range.from) {
280
- onSelect?.({ from: date, to: undefined });
281
- }
282
- else if (!range.to) {
283
- if (date >= range.from) {
284
- onSelect?.({ from: range.from, to: date });
285
- }
286
- else {
287
- onSelect?.({ from: date, to: range.from });
288
- }
289
- }
290
- else {
291
- onSelect?.({ from: date, to: undefined });
292
- }
293
- }
294
- }
295
- else if (mode === "multiple") {
296
- const currentSelection = Array.isArray(selected) ? selected : [];
297
- const isAlreadySelected = currentSelection.some(d => d.toDateString() === date.toDateString());
298
- if (isAlreadySelected) {
299
- onSelect?.(currentSelection.filter(d => d.toDateString() !== date.toDateString()));
300
- }
301
- else {
302
- onSelect?.([...currentSelection, date]);
303
- }
304
- }
305
- };
306
- const navigateMonth = (direction) => {
307
- setCurrentMonth(prev => {
308
- const newMonth = new Date(prev);
309
- if (direction === "prev") {
310
- newMonth.setMonth(prev.getMonth() - 1);
311
- }
312
- else {
313
- newMonth.setMonth(prev.getMonth() + 1);
314
- }
315
- return newMonth;
316
- });
317
- };
318
- const days = getDaysInMonth();
319
- return (jsxs("div", { "data-slot": "calendar", className: cn$1("bg-background group/calendar p-3 w-fit", className), children: [jsxs("div", { className: "flex items-center justify-between mb-4", children: [jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigateMonth("prev"), className: "h-8 w-8 p-0", children: jsx(ChevronLeftIcon, { className: "h-4 w-4" }) }), jsx("div", { className: "text-sm font-medium", children: currentMonth.toLocaleDateString("default", { month: "long", year: "numeric" }) }), jsx(Button, { variant: "ghost", size: "icon", onClick: () => navigateMonth("next"), className: "h-8 w-8 p-0", children: jsx(ChevronRightIcon, { className: "h-4 w-4" }) })] }), jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: weekDays.map((day) => (jsx("div", { className: "text-muted-foreground text-xs font-normal text-center h-8 flex items-center justify-center", children: day }, day))) }), jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((date, index) => {
320
- if (!date) {
321
- return jsx("div", { className: "h-8" }, index);
322
- }
323
- const isDateSelected = isSelected(date);
324
- const isDateToday = isToday(date);
325
- const isDateDisabled = isDisabled(date);
326
- return (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()));
327
- }) })] }));
216
+ function Select({ onValueChange, onOpenChange, open, defaultOpen, ...props }) {
217
+ // Tracked in a ref, not state: this only needs to be readable inside the
218
+ // callback below, and putting it in state would re-render on every open.
219
+ const isOpenRef = React.useRef(defaultOpen ?? false);
220
+ if (open !== undefined)
221
+ isOpenRef.current = open;
222
+ return (jsx(SelectPrimitive.Root, { "data-slot": "select", ...props, open: open, defaultOpen: defaultOpen, onOpenChange: next => {
223
+ isOpenRef.current = next;
224
+ onOpenChange?.(next);
225
+ }, onValueChange: value => {
226
+ // Drop ONLY the empty value Radix echoes back on its own.
227
+ //
228
+ // Radix keeps a hidden native <select> so the control works inside
229
+ // real forms. Whenever the controlled value changes it assigns that
230
+ // value to the native node and dispatches a synthetic change event,
231
+ // which comes straight back out through onValueChange:
232
+ //
233
+ // setValue.call(select, selectValue);
234
+ // select.dispatchEvent(new Event("change", { bubbles: true }));
235
+ // ...
236
+ // onChange: (event) => onValueChange(event.target.value)
237
+ //
238
+ // The native <option> list is registered by SelectItem, and the items
239
+ // live inside SelectContent — portalled, and only mounted while the
240
+ // menu is open. On a closed select the option for the incoming value
241
+ // usually does not exist yet, the DOM refuses the assignment,
242
+ // `select.value` collapses to "", and that empty string reaches the
243
+ // consumer. In a form this lands exactly when saved data arrives: the
244
+ // field is set to its stored value, Radix echoes "", and the handler
245
+ // writes that back — enum fields then snap to their fallback and zod
246
+ // rejects the submit with "received ''".
247
+ //
248
+ // The open check is what keeps a real "none" option working. Radix
249
+ // does NOT forbid <SelectItem value="">, and picking one is a
250
+ // legitimate way to clear a selection. A user pick always arrives
251
+ // while the menu is still open — SelectItem's handleSelect calls
252
+ // onValueChange(value) BEFORE onOpenChange(false) — whereas the echo
253
+ // above fires from an effect with the menu closed. So only the closed
254
+ // case is suppressed.
255
+ if (value === "" && !isOpenRef.current)
256
+ return;
257
+ onValueChange?.(value);
258
+ } }));
328
259
  }
329
- function DateInput({ value, onChange, placeholder = "Select date", className, disabled = false }) {
330
- const handleChange = (event) => {
331
- const dateValue = event.target.value;
332
- if (dateValue) {
333
- onChange?.(new Date(dateValue));
334
- }
335
- else {
336
- onChange?.(undefined);
337
- }
338
- };
339
- const formatDateForInput = (date) => {
340
- return date.toISOString().split('T')[0];
341
- };
342
- return (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) }));
260
+ function SelectGroup({ ...props }) {
261
+ return jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
262
+ }
263
+ function SelectValue({ ...props }) {
264
+ return jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
265
+ }
266
+ function SelectTrigger({ className, size = "default", children, ...props }) {
267
+ return (jsxs(SelectPrimitive.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, jsx(SelectPrimitive.Icon, { asChild: true, children: jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
268
+ }
269
+ function SelectContent({ className, children, position = "popper", ...props }) {
270
+ return (jsx(SelectPrimitive.Portal, { children: jsxs(SelectPrimitive.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" &&
271
+ "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: [jsx(SelectScrollUpButton, {}), jsx(SelectPrimitive.Viewport, { className: cn$1("p-1", position === "popper" &&
272
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), jsx(SelectScrollDownButton, {})] }) }));
273
+ }
274
+ function SelectLabel({ className, ...props }) {
275
+ return (jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn$1("text-muted-foreground px-2 py-1.5 text-xs", className), ...props }));
276
+ }
277
+ function SelectItem({ className, children, ...props }) {
278
+ return (jsxs(SelectPrimitive.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: [jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: jsx(SelectPrimitive.ItemIndicator, { children: jsx(CheckIcon, { className: "size-4" }) }) }), jsx(SelectPrimitive.ItemText, { children: children })] }));
279
+ }
280
+ function SelectSeparator({ className, ...props }) {
281
+ return (jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn$1("bg-border pointer-events-none -mx-1 my-1 h-px", className), ...props }));
282
+ }
283
+ function SelectScrollUpButton({ className, ...props }) {
284
+ return (jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: jsx(ChevronUpIcon, { className: "size-4" }) }));
285
+ }
286
+ function SelectScrollDownButton({ className, ...props }) {
287
+ return (jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: jsx(ChevronDownIcon, { className: "size-4" }) }));
288
+ }
289
+
290
+ /**
291
+ * @module constants
292
+ * @summary Useful constants
293
+ * @description
294
+ * Collection of useful date constants.
295
+ *
296
+ * The constants could be imported from `date-fns/constants`:
297
+ *
298
+ * ```ts
299
+ * import { maxTime, minTime } from "./constants/date-fns/constants";
300
+ *
301
+ * function isAllowedTime(time) {
302
+ * return time <= maxTime && time >= minTime;
303
+ * }
304
+ * ```
305
+ */
306
+
307
+
308
+ /**
309
+ * @constant
310
+ * @name constructFromSymbol
311
+ * @summary Symbol enabling Date extensions to inherit properties from the reference date.
312
+ *
313
+ * The symbol is used to enable the `constructFrom` function to construct a date
314
+ * using a reference date and a value. It allows to transfer extra properties
315
+ * from the reference date to the new date. It's useful for extensions like
316
+ * [`TZDate`](https://github.com/date-fns/tz) that accept a time zone as
317
+ * a constructor argument.
318
+ */
319
+ const constructFromSymbol = Symbol.for("constructDateFrom");
320
+
321
+ /**
322
+ * @name constructFrom
323
+ * @category Generic Helpers
324
+ * @summary Constructs a date using the reference date and the value
325
+ *
326
+ * @description
327
+ * The function constructs a new date using the constructor from the reference
328
+ * date and the given value. It helps to build generic functions that accept
329
+ * date extensions.
330
+ *
331
+ * It defaults to `Date` if the passed reference date is a number or a string.
332
+ *
333
+ * Starting from v3.7.0, it allows to construct a date using `[Symbol.for("constructDateFrom")]`
334
+ * enabling to transfer extra properties from the reference date to the new date.
335
+ * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz)
336
+ * that accept a time zone as a constructor argument.
337
+ *
338
+ * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
339
+ *
340
+ * @param date - The reference date to take constructor from
341
+ * @param value - The value to create the date
342
+ *
343
+ * @returns Date initialized using the given date and value
344
+ *
345
+ * @example
346
+ * import { constructFrom } from "./constructFrom/date-fns";
347
+ *
348
+ * // A function that clones a date preserving the original type
349
+ * function cloneDate<DateType extends Date>(date: DateType): DateType {
350
+ * return constructFrom(
351
+ * date, // Use constructor from the given date
352
+ * date.getTime() // Use the date value to create a new date
353
+ * );
354
+ * }
355
+ */
356
+ function constructFrom(date, value) {
357
+ if (typeof date === "function") return date(value);
358
+
359
+ if (date && typeof date === "object" && constructFromSymbol in date)
360
+ return date[constructFromSymbol](value);
361
+
362
+ if (date instanceof Date) return new date.constructor(value);
363
+
364
+ return new Date(value);
365
+ }
366
+
367
+ /**
368
+ * @name toDate
369
+ * @category Common Helpers
370
+ * @summary Convert the given argument to an instance of Date.
371
+ *
372
+ * @description
373
+ * Convert the given argument to an instance of Date.
374
+ *
375
+ * If the argument is an instance of Date, the function returns its clone.
376
+ *
377
+ * If the argument is a number, it is treated as a timestamp.
378
+ *
379
+ * If the argument is none of the above, the function returns Invalid Date.
380
+ *
381
+ * Starting from v3.7.0, it clones a date using `[Symbol.for("constructDateFrom")]`
382
+ * enabling to transfer extra properties from the reference date to the new date.
383
+ * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz)
384
+ * that accept a time zone as a constructor argument.
385
+ *
386
+ * **Note**: *all* Date arguments passed to any *date-fns* function is processed by `toDate`.
387
+ *
388
+ * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
389
+ * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
390
+ *
391
+ * @param argument - The value to convert
392
+ *
393
+ * @returns The parsed date in the local time zone
394
+ *
395
+ * @example
396
+ * // Clone the date:
397
+ * const result = toDate(new Date(2014, 1, 11, 11, 30, 30))
398
+ * //=> Tue Feb 11 2014 11:30:30
399
+ *
400
+ * @example
401
+ * // Convert the timestamp to date:
402
+ * const result = toDate(1392098430000)
403
+ * //=> Tue Feb 11 2014 11:30:30
404
+ */
405
+ function toDate(argument, context) {
406
+ // [TODO] Get rid of `toDate` or `constructFrom`?
407
+ return constructFrom(argument, argument);
408
+ }
409
+
410
+ /**
411
+ * The {@link endOfMonth} function options.
412
+ */
413
+
414
+ /**
415
+ * @name endOfMonth
416
+ * @category Month Helpers
417
+ * @summary Return the end of a month for the given date.
418
+ *
419
+ * @description
420
+ * Return the end of a month for the given date.
421
+ * The result will be in the local timezone.
422
+ *
423
+ * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
424
+ * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
425
+ *
426
+ * @param date - The original date
427
+ * @param options - An object with options
428
+ *
429
+ * @returns The end of a month
430
+ *
431
+ * @example
432
+ * // The end of a month for 2 September 2014 11:55:00:
433
+ * const result = endOfMonth(new Date(2014, 8, 2, 11, 55, 0))
434
+ * //=> Tue Sep 30 2014 23:59:59.999
435
+ */
436
+ function endOfMonth(date, options) {
437
+ const _date = toDate(date);
438
+ const month = _date.getMonth();
439
+ _date.setFullYear(_date.getFullYear(), month + 1, 0);
440
+ _date.setHours(23, 59, 59, 999);
441
+ return _date;
442
+ }
443
+
444
+ /**
445
+ * The {@link startOfMonth} function options.
446
+ */
447
+
448
+ /**
449
+ * @name startOfMonth
450
+ * @category Month Helpers
451
+ * @summary Return the start of a month for the given date.
452
+ *
453
+ * @description
454
+ * Return the start of a month for the given date. The result will be in the local timezone.
455
+ *
456
+ * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments.
457
+ * Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
458
+ * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed,
459
+ * or inferred from the arguments.
460
+ *
461
+ * @param date - The original date
462
+ * @param options - An object with options
463
+ *
464
+ * @returns The start of a month
465
+ *
466
+ * @example
467
+ * // The start of a month for 2 September 2014 11:55:00:
468
+ * const result = startOfMonth(new Date(2014, 8, 2, 11, 55, 0))
469
+ * //=> Mon Sep 01 2014 00:00:00
470
+ */
471
+ function startOfMonth(date, options) {
472
+ const _date = toDate(date);
473
+ _date.setDate(1);
474
+ _date.setHours(0, 0, 0, 0);
475
+ return _date;
343
476
  }
344
477
 
345
478
  function buildFormatLongFn(args) {
@@ -494,123 +627,290 @@ function buildMatchPatternFn(args) {
494
627
  }
495
628
 
496
629
  /**
497
- * @module constants
498
- * @summary Useful constants
499
- * @description
500
- * Collection of useful date constants.
501
- *
502
- * The constants could be imported from `date-fns/constants`:
503
- *
504
- * ```ts
505
- * import { maxTime, minTime } from "./constants/date-fns/constants";
506
- *
507
- * function isAllowedTime(time) {
508
- * return time <= maxTime && time >= minTime;
509
- * }
510
- * ```
630
+ * Thai month names in Buddhist calendar
511
631
  */
512
-
513
-
632
+ const THAI_MONTH_NAMES = [
633
+ "มกราคม",
634
+ "กุมภาพันธ์",
635
+ "มีนาคม",
636
+ "เมษายน",
637
+ "พฤษภาคม",
638
+ "มิถุนายน",
639
+ "กรกฎาคม",
640
+ "สิงหาคม",
641
+ "กันยายน",
642
+ "ตุลาคม",
643
+ "พฤศจิกายน",
644
+ "ธันวาคม",
645
+ ];
514
646
  /**
515
- * @constant
516
- * @name constructFromSymbol
517
- * @summary Symbol enabling Date extensions to inherit properties from the reference date.
647
+ * Convert Gregorian year to Buddhist year
648
+ * Buddhist calendar is 543 years ahead of Gregorian calendar
518
649
  *
519
- * The symbol is used to enable the `constructFrom` function to construct a date
520
- * using a reference date and a value. It allows to transfer extra properties
521
- * from the reference date to the new date. It's useful for extensions like
522
- * [`TZDate`](https://github.com/date-fns/tz) that accept a time zone as
523
- * a constructor argument.
650
+ * @param gregorianYear - The Gregorian year (e.g., 2024)
651
+ * @returns The Buddhist year (e.g., 2567)
652
+ *
653
+ * @example
654
+ * toBuddhistYear(2024); // returns 2567
524
655
  */
525
- const constructFromSymbol = Symbol.for("constructDateFrom");
526
-
656
+ function toBuddhistYear(gregorianYear) {
657
+ return gregorianYear + 543;
658
+ }
527
659
  /**
528
- * @name constructFrom
529
- * @category Generic Helpers
530
- * @summary Constructs a date using the reference date and the value
531
- *
532
- * @description
533
- * The function constructs a new date using the constructor from the reference
534
- * date and the given value. It helps to build generic functions that accept
535
- * date extensions.
536
- *
537
- * It defaults to `Date` if the passed reference date is a number or a string.
538
- *
539
- * Starting from v3.7.0, it allows to construct a date using `[Symbol.for("constructDateFrom")]`
540
- * enabling to transfer extra properties from the reference date to the new date.
541
- * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz)
542
- * that accept a time zone as a constructor argument.
543
- *
544
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
545
- *
546
- * @param date - The reference date to take constructor from
547
- * @param value - The value to create the date
660
+ * Convert Buddhist year to Gregorian year
548
661
  *
549
- * @returns Date initialized using the given date and value
662
+ * @param buddhistYear - The Buddhist year (e.g., 2567)
663
+ * @returns The Gregorian year (e.g., 2024)
550
664
  *
551
665
  * @example
552
- * import { constructFrom } from "./constructFrom/date-fns";
553
- *
554
- * // A function that clones a date preserving the original type
555
- * function cloneDate<DateType extends Date>(date: DateType): DateType {
556
- * return constructFrom(
557
- * date, // Use constructor from the given date
558
- * date.getTime() // Use the date value to create a new date
559
- * );
560
- * }
666
+ * toGregorianYear(2567); // returns 2024
561
667
  */
562
- function constructFrom(date, value) {
563
- if (typeof date === "function") return date(value);
564
-
565
- if (date && typeof date === "object" && constructFromSymbol in date)
566
- return date[constructFromSymbol](value);
567
-
568
- if (date instanceof Date) return new date.constructor(value);
569
-
570
- return new Date(value);
668
+ function toGregorianYear(buddhistYear) {
669
+ return buddhistYear - 543;
571
670
  }
572
-
573
671
  /**
574
- * @name toDate
575
- * @category Common Helpers
576
- * @summary Convert the given argument to an instance of Date.
577
- *
578
- * @description
579
- * Convert the given argument to an instance of Date.
580
- *
581
- * If the argument is an instance of Date, the function returns its clone.
582
- *
583
- * If the argument is a number, it is treated as a timestamp.
584
- *
585
- * If the argument is none of the above, the function returns Invalid Date.
672
+ * Get the first and last day of a month
586
673
  *
587
- * Starting from v3.7.0, it clones a date using `[Symbol.for("constructDateFrom")]`
588
- * enabling to transfer extra properties from the reference date to the new date.
589
- * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz)
590
- * that accept a time zone as a constructor argument.
674
+ * @param date - The date within the month
675
+ * @returns Object with start and end dates of the month
591
676
  *
592
- * **Note**: *all* Date arguments passed to any *date-fns* function is processed by `toDate`.
677
+ * @example
678
+ * getMonthDateRange(new Date('2024-06-15'));
679
+ * // returns { start: Date('2024-06-01'), end: Date('2024-06-30') }
680
+ */
681
+ function getMonthDateRange(date) {
682
+ const start = startOfMonth(date);
683
+ const end = endOfMonth(date);
684
+ return { start, end };
685
+ }
686
+ /**
687
+ * Format a date to Thai Buddhist calendar month display
593
688
  *
594
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
595
- * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
689
+ * @param date - The date to format
690
+ * @returns Formatted string in Thai Buddhist calendar (e.g., "มกราคม 2568")
596
691
  *
597
- * @param argument - The value to convert
692
+ * @example
693
+ * formatThaiMonth(new Date('2024-01-15')); // returns "มกราคม 2567"
694
+ * formatThaiMonth(new Date('2024-12-25')); // returns "ธันวาคม 2567"
695
+ */
696
+ function formatThaiMonth(date) {
697
+ const monthIndex = date.getMonth();
698
+ const gregorianYear = date.getFullYear();
699
+ const buddhistYear = toBuddhistYear(gregorianYear);
700
+ return `${THAI_MONTH_NAMES[monthIndex]} ${buddhistYear}`;
701
+ }
702
+ /**
703
+ * Format a date to short Thai Buddhist calendar format
598
704
  *
599
- * @returns The parsed date in the local time zone
705
+ * @param date - The date to format
706
+ * @returns Formatted string (e.g., "01/2568")
600
707
  *
601
708
  * @example
602
- * // Clone the date:
603
- * const result = toDate(new Date(2014, 1, 11, 11, 30, 30))
604
- * //=> Tue Feb 11 2014 11:30:30
709
+ * formatThaiMonthShort(new Date('2024-01-15')); // returns "01/2567"
710
+ */
711
+ function formatThaiMonthShort(date) {
712
+ const month = String(date.getMonth() + 1).padStart(2, "0");
713
+ const gregorianYear = date.getFullYear();
714
+ const buddhistYear = toBuddhistYear(gregorianYear);
715
+ return `${month}/${buddhistYear}`;
716
+ }
717
+ /**
718
+ * Get Thai month name by index (0-11)
719
+ *
720
+ * @param monthIndex - Month index (0 = January, 11 = December)
721
+ * @returns Thai month name
605
722
  *
606
723
  * @example
607
- * // Convert the timestamp to date:
608
- * const result = toDate(1392098430000)
609
- * //=> Tue Feb 11 2014 11:30:30
724
+ * getThaiMonthName(0); // returns "มกราคม"
725
+ * getThaiMonthName(11); // returns "ธันวาคม"
610
726
  */
611
- function toDate(argument, context) {
612
- // [TODO] Get rid of `toDate` or `constructFrom`?
613
- return constructFrom(argument, argument);
727
+ function getThaiMonthName(monthIndex) {
728
+ if (monthIndex < 0 || monthIndex > 11) {
729
+ throw new Error("Month index must be between 0 and 11");
730
+ }
731
+ return THAI_MONTH_NAMES[monthIndex];
732
+ }
733
+
734
+ /** Thai weekday abbreviations (Sunday-first, matching JS getDay()). */
735
+ const WEEK_DAYS_TH = ["อา.", "จ.", "อ.", "พ.", "พฤ.", "ศ.", "ส."];
736
+ function getInitialMonth(selected) {
737
+ if (selected instanceof Date && !Number.isNaN(selected.getTime())) {
738
+ return new Date(selected.getFullYear(), selected.getMonth(), 1);
739
+ }
740
+ if (Array.isArray(selected) && selected[0] instanceof Date) {
741
+ return new Date(selected[0].getFullYear(), selected[0].getMonth(), 1);
742
+ }
743
+ if (selected &&
744
+ typeof selected === "object" &&
745
+ "from" in selected &&
746
+ selected.from instanceof Date) {
747
+ return new Date(selected.from.getFullYear(), selected.from.getMonth(), 1);
748
+ }
749
+ return new Date();
750
+ }
751
+ function Calendar({ className, selected, onSelect, mode = "single", disabled, fromYear = 1900, toYear, }) {
752
+ const maxYear = toYear ?? new Date().getFullYear() + 10;
753
+ const [currentMonth, setCurrentMonth] = React.useState(() => getInitialMonth(selected));
754
+ // Jump the grid to the selected date when it changes (e.g. form load).
755
+ React.useEffect(() => {
756
+ const next = getInitialMonth(selected);
757
+ setCurrentMonth((prev) => {
758
+ if (prev.getFullYear() === next.getFullYear() &&
759
+ prev.getMonth() === next.getMonth()) {
760
+ return prev;
761
+ }
762
+ return next;
763
+ });
764
+ }, [selected]);
765
+ const today = new Date();
766
+ const year = currentMonth.getFullYear();
767
+ const month = currentMonth.getMonth();
768
+ const firstDayOfMonth = new Date(year, month, 1);
769
+ const lastDayOfMonth = new Date(year, month + 1, 0);
770
+ const firstDayOfWeek = firstDayOfMonth.getDay();
771
+ const daysInMonth = lastDayOfMonth.getDate();
772
+ const yearOptions = React.useMemo(() => {
773
+ const years = [];
774
+ const start = Math.min(fromYear, maxYear);
775
+ const end = Math.max(fromYear, maxYear);
776
+ for (let y = end; y >= start; y--) {
777
+ years.push(y);
778
+ }
779
+ return years;
780
+ }, [fromYear, maxYear]);
781
+ const getDaysInMonth = () => {
782
+ const days = [];
783
+ for (let i = 0; i < firstDayOfWeek; i++) {
784
+ days.push(null);
785
+ }
786
+ for (let day = 1; day <= daysInMonth; day++) {
787
+ days.push(new Date(year, month, day));
788
+ }
789
+ return days;
790
+ };
791
+ const isSelected = (date) => {
792
+ if (!selected)
793
+ return false;
794
+ if (mode === "single" && selected instanceof Date) {
795
+ return date.toDateString() === selected.toDateString();
796
+ }
797
+ if (mode === "range" &&
798
+ selected &&
799
+ typeof selected === "object" &&
800
+ "from" in selected) {
801
+ const range = selected;
802
+ if (range.from && range.to) {
803
+ return date >= range.from && date <= range.to;
804
+ }
805
+ if (range.from) {
806
+ return date.toDateString() === range.from.toDateString();
807
+ }
808
+ }
809
+ if (mode === "multiple" && Array.isArray(selected)) {
810
+ return selected.some((d) => d.toDateString() === date.toDateString());
811
+ }
812
+ return false;
813
+ };
814
+ const isToday = (date) => {
815
+ return date.toDateString() === today.toDateString();
816
+ };
817
+ const isDisabled = (date) => {
818
+ if (disabled)
819
+ return disabled(date);
820
+ return false;
821
+ };
822
+ const handleDateClick = (date) => {
823
+ if (isDisabled(date))
824
+ return;
825
+ if (mode === "single") {
826
+ onSelect?.(date);
827
+ }
828
+ else if (mode === "range") {
829
+ if (!selected || typeof selected !== "object" || !("from" in selected)) {
830
+ onSelect?.({ from: date, to: undefined });
831
+ }
832
+ else {
833
+ const range = selected;
834
+ if (!range.from) {
835
+ onSelect?.({ from: date, to: undefined });
836
+ }
837
+ else if (!range.to) {
838
+ if (date >= range.from) {
839
+ onSelect?.({ from: range.from, to: date });
840
+ }
841
+ else {
842
+ onSelect?.({ from: date, to: range.from });
843
+ }
844
+ }
845
+ else {
846
+ onSelect?.({ from: date, to: undefined });
847
+ }
848
+ }
849
+ }
850
+ else if (mode === "multiple") {
851
+ const currentSelection = Array.isArray(selected) ? selected : [];
852
+ const isAlreadySelected = currentSelection.some((d) => d.toDateString() === date.toDateString());
853
+ if (isAlreadySelected) {
854
+ onSelect?.(currentSelection.filter((d) => d.toDateString() !== date.toDateString()));
855
+ }
856
+ else {
857
+ onSelect?.([...currentSelection, date]);
858
+ }
859
+ }
860
+ };
861
+ const navigateMonth = (direction) => {
862
+ setCurrentMonth((prev) => {
863
+ const newMonth = new Date(prev);
864
+ if (direction === "prev") {
865
+ newMonth.setMonth(prev.getMonth() - 1);
866
+ }
867
+ else {
868
+ newMonth.setMonth(prev.getMonth() + 1);
869
+ }
870
+ return newMonth;
871
+ });
872
+ };
873
+ const handleMonthChange = (monthStr) => {
874
+ const nextMonth = Number.parseInt(monthStr, 10);
875
+ if (Number.isNaN(nextMonth))
876
+ return;
877
+ setCurrentMonth((prev) => new Date(prev.getFullYear(), nextMonth, 1));
878
+ };
879
+ const handleYearChange = (buddhistYearStr) => {
880
+ const gregorian = toGregorianYear(Number.parseInt(buddhistYearStr, 10));
881
+ if (Number.isNaN(gregorian))
882
+ return;
883
+ setCurrentMonth((prev) => new Date(gregorian, prev.getMonth(), 1));
884
+ };
885
+ const days = getDaysInMonth();
886
+ const canGoPrev = year > fromYear || (year === fromYear && month > 0);
887
+ const canGoNext = year < maxYear || (year === maxYear && month < 11);
888
+ return (jsxs("div", { "data-slot": "calendar", className: cn$1("bg-background group/calendar p-3 w-fit", className), children: [jsxs("div", { className: "mb-4 flex items-center justify-between gap-1", children: [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: jsx(ChevronLeftIcon, { className: "h-4 w-4" }) }), jsxs("div", { className: "flex min-w-0 flex-1 items-center justify-center gap-1", children: [jsxs(Select, { value: String(month), onValueChange: handleMonthChange, children: [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: jsx(SelectValue, { children: THAI_MONTH_NAMES[month] }) }), jsx(SelectContent, { className: "z-[100] max-h-60", children: THAI_MONTH_NAMES.map((name, index) => (jsx(SelectItem, { value: String(index), children: name }, name))) })] }), jsxs(Select, { value: String(toBuddhistYear(year)), onValueChange: handleYearChange, children: [jsx(SelectTrigger, { size: "sm", className: "h-8 w-[5.5rem] px-2 text-xs", "aria-label": "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E1B\u0E35", children: jsx(SelectValue, { children: toBuddhistYear(year) }) }), jsx(SelectContent, { className: "z-[100] max-h-60", children: yearOptions.map((y) => (jsx(SelectItem, { value: String(toBuddhistYear(y)), children: toBuddhistYear(y) }, y))) })] })] }), 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: jsx(ChevronRightIcon, { className: "h-4 w-4" }) })] }), jsx("div", { className: "mb-2 grid grid-cols-7 gap-1", children: WEEK_DAYS_TH.map((day) => (jsx("div", { className: "text-muted-foreground flex h-8 items-center justify-center text-center text-xs font-normal", children: day }, day))) }), jsx("div", { className: "grid grid-cols-7 gap-1", children: days.map((date, index) => {
889
+ if (!date) {
890
+ return jsx("div", { className: "h-8" }, index);
891
+ }
892
+ const isDateSelected = isSelected(date);
893
+ const isDateToday = isToday(date);
894
+ const isDateDisabled = isDisabled(date);
895
+ return (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 &&
896
+ !isDateSelected &&
897
+ "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()));
898
+ }) })] }));
899
+ }
900
+ function DateInput({ value, onChange, placeholder = "Select date", className, disabled = false, }) {
901
+ const handleChange = (event) => {
902
+ const dateValue = event.target.value;
903
+ if (dateValue) {
904
+ onChange?.(new Date(dateValue));
905
+ }
906
+ else {
907
+ onChange?.(undefined);
908
+ }
909
+ };
910
+ const formatDateForInput = (date) => {
911
+ return date.toISOString().split("T")[0];
912
+ };
913
+ return (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) }));
614
914
  }
615
915
 
616
916
  const formatDistanceLocale = {
@@ -1112,10 +1412,11 @@ const defaultFormatDate = (date) => new Intl.DateTimeFormat("th-TH", {
1112
1412
  year: "numeric",
1113
1413
  timeZone: "Asia/Bangkok",
1114
1414
  }).format(date);
1115
- function DatePicker({ date, onDateChange, placeholder = "เลือกวันที่", disabled = false, disabledDates, formatDate = defaultFormatDate, className, id, ariaLabel, "aria-required": ariaRequired, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, }) {
1415
+ 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, }) {
1116
1416
  const [open, setOpen] = React.useState(false);
1117
1417
  const isDateDisabled = disabledDates ?? defaultDisabledDates;
1118
- 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-[280px] overflow-hidden p-0", align: "start", children: jsx(Calendar, { ...{ locale: th }, mode: "single", selected: date, onSelect: (selectedDate) => {
1418
+ const resolvedToYear = toYear ?? new Date().getFullYear() + 10;
1419
+ 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) => {
1119
1420
  if (selectedDate instanceof Date) {
1120
1421
  onDateChange?.(selectedDate);
1121
1422
  setOpen(false);
@@ -1124,7 +1425,7 @@ function DatePicker({ date, onDateChange, placeholder = "เลือกวั
1124
1425
  onDateChange?.(undefined);
1125
1426
  setOpen(false);
1126
1427
  }
1127
- }, disabled: isDateDisabled, initialFocus: true }) })] }));
1428
+ }, disabled: isDateDisabled, fromYear: fromYear, toYear: resolvedToYear, initialFocus: true }) })] }));
1128
1429
  }
1129
1430
 
1130
1431
  const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
@@ -16207,80 +16508,6 @@ function ScrollBar({ className, orientation = "vertical", ...props }) {
16207
16508
  "h-2.5 flex-col border-t border-t-transparent", className), ...props, children: jsx(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-border relative flex-1 rounded-full" }) }));
16208
16509
  }
16209
16510
 
16210
- function Select({ onValueChange, onOpenChange, open, defaultOpen, ...props }) {
16211
- // Tracked in a ref, not state: this only needs to be readable inside the
16212
- // callback below, and putting it in state would re-render on every open.
16213
- const isOpenRef = React.useRef(defaultOpen ?? false);
16214
- if (open !== undefined)
16215
- isOpenRef.current = open;
16216
- return (jsx(SelectPrimitive.Root, { "data-slot": "select", ...props, open: open, defaultOpen: defaultOpen, onOpenChange: next => {
16217
- isOpenRef.current = next;
16218
- onOpenChange?.(next);
16219
- }, onValueChange: value => {
16220
- // Drop ONLY the empty value Radix echoes back on its own.
16221
- //
16222
- // Radix keeps a hidden native <select> so the control works inside
16223
- // real forms. Whenever the controlled value changes it assigns that
16224
- // value to the native node and dispatches a synthetic change event,
16225
- // which comes straight back out through onValueChange:
16226
- //
16227
- // setValue.call(select, selectValue);
16228
- // select.dispatchEvent(new Event("change", { bubbles: true }));
16229
- // ...
16230
- // onChange: (event) => onValueChange(event.target.value)
16231
- //
16232
- // The native <option> list is registered by SelectItem, and the items
16233
- // live inside SelectContent — portalled, and only mounted while the
16234
- // menu is open. On a closed select the option for the incoming value
16235
- // usually does not exist yet, the DOM refuses the assignment,
16236
- // `select.value` collapses to "", and that empty string reaches the
16237
- // consumer. In a form this lands exactly when saved data arrives: the
16238
- // field is set to its stored value, Radix echoes "", and the handler
16239
- // writes that back — enum fields then snap to their fallback and zod
16240
- // rejects the submit with "received ''".
16241
- //
16242
- // The open check is what keeps a real "none" option working. Radix
16243
- // does NOT forbid <SelectItem value="">, and picking one is a
16244
- // legitimate way to clear a selection. A user pick always arrives
16245
- // while the menu is still open — SelectItem's handleSelect calls
16246
- // onValueChange(value) BEFORE onOpenChange(false) — whereas the echo
16247
- // above fires from an effect with the menu closed. So only the closed
16248
- // case is suppressed.
16249
- if (value === "" && !isOpenRef.current)
16250
- return;
16251
- onValueChange?.(value);
16252
- } }));
16253
- }
16254
- function SelectGroup({ ...props }) {
16255
- return jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
16256
- }
16257
- function SelectValue({ ...props }) {
16258
- return jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
16259
- }
16260
- function SelectTrigger({ className, size = "default", children, ...props }) {
16261
- return (jsxs(SelectPrimitive.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, jsx(SelectPrimitive.Icon, { asChild: true, children: jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
16262
- }
16263
- function SelectContent({ className, children, position = "popper", ...props }) {
16264
- return (jsx(SelectPrimitive.Portal, { children: jsxs(SelectPrimitive.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" &&
16265
- "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: [jsx(SelectScrollUpButton, {}), jsx(SelectPrimitive.Viewport, { className: cn$1("p-1", position === "popper" &&
16266
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children: children }), jsx(SelectScrollDownButton, {})] }) }));
16267
- }
16268
- function SelectLabel({ className, ...props }) {
16269
- return (jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn$1("text-muted-foreground px-2 py-1.5 text-xs", className), ...props }));
16270
- }
16271
- function SelectItem({ className, children, ...props }) {
16272
- return (jsxs(SelectPrimitive.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: [jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: jsx(SelectPrimitive.ItemIndicator, { children: jsx(CheckIcon, { className: "size-4" }) }) }), jsx(SelectPrimitive.ItemText, { children: children })] }));
16273
- }
16274
- function SelectSeparator({ className, ...props }) {
16275
- return (jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn$1("bg-border pointer-events-none -mx-1 my-1 h-px", className), ...props }));
16276
- }
16277
- function SelectScrollUpButton({ className, ...props }) {
16278
- return (jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: jsx(ChevronUpIcon, { className: "size-4" }) }));
16279
- }
16280
- function SelectScrollDownButton({ className, ...props }) {
16281
- return (jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn$1("flex cursor-default items-center justify-center py-1", className), ...props, children: jsx(ChevronDownIcon, { className: "size-4" }) }));
16282
- }
16283
-
16284
16511
  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, }) {
16285
16512
  const reactId = React.useId();
16286
16513
  const inputId = id ?? reactId;
@@ -34387,179 +34614,6 @@ const RegistryItemRow = ({ item }) => {
34387
34614
  return (jsxs(TableRow, { children: [jsx(TableCell, { className: "w-50 font-medium", children: item.title }), jsx(TableCell, { children: jsx(Button, { variant: "link", asChild: true, children: jsx("a", { target: "_blank", href: `${getBaseUrl()}/registry/${item.name}`, children: "Link" }) }) }), jsx(TableCell, { children: jsx(Button, { variant: "link", asChild: true, children: jsx("a", { href: `${getBaseUrl()}/storybook/?path=/docs/${item.meta.story}--docs`, children: "Story" }) }) }), jsx(TableCell, { children: jsx(CommandBlock, { command: `npx shadcn@latest add ${getBaseUrl()}/registry/${item.name}` }) })] }, item.name));
34388
34615
  };
34389
34616
 
34390
- /**
34391
- * The {@link endOfMonth} function options.
34392
- */
34393
-
34394
- /**
34395
- * @name endOfMonth
34396
- * @category Month Helpers
34397
- * @summary Return the end of a month for the given date.
34398
- *
34399
- * @description
34400
- * Return the end of a month for the given date.
34401
- * The result will be in the local timezone.
34402
- *
34403
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
34404
- * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments.
34405
- *
34406
- * @param date - The original date
34407
- * @param options - An object with options
34408
- *
34409
- * @returns The end of a month
34410
- *
34411
- * @example
34412
- * // The end of a month for 2 September 2014 11:55:00:
34413
- * const result = endOfMonth(new Date(2014, 8, 2, 11, 55, 0))
34414
- * //=> Tue Sep 30 2014 23:59:59.999
34415
- */
34416
- function endOfMonth(date, options) {
34417
- const _date = toDate(date);
34418
- const month = _date.getMonth();
34419
- _date.setFullYear(_date.getFullYear(), month + 1, 0);
34420
- _date.setHours(23, 59, 59, 999);
34421
- return _date;
34422
- }
34423
-
34424
- /**
34425
- * The {@link startOfMonth} function options.
34426
- */
34427
-
34428
- /**
34429
- * @name startOfMonth
34430
- * @category Month Helpers
34431
- * @summary Return the start of a month for the given date.
34432
- *
34433
- * @description
34434
- * Return the start of a month for the given date. The result will be in the local timezone.
34435
- *
34436
- * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments.
34437
- * Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc).
34438
- * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed,
34439
- * or inferred from the arguments.
34440
- *
34441
- * @param date - The original date
34442
- * @param options - An object with options
34443
- *
34444
- * @returns The start of a month
34445
- *
34446
- * @example
34447
- * // The start of a month for 2 September 2014 11:55:00:
34448
- * const result = startOfMonth(new Date(2014, 8, 2, 11, 55, 0))
34449
- * //=> Mon Sep 01 2014 00:00:00
34450
- */
34451
- function startOfMonth(date, options) {
34452
- const _date = toDate(date);
34453
- _date.setDate(1);
34454
- _date.setHours(0, 0, 0, 0);
34455
- return _date;
34456
- }
34457
-
34458
- /**
34459
- * Thai month names in Buddhist calendar
34460
- */
34461
- const THAI_MONTH_NAMES = [
34462
- "มกราคม",
34463
- "กุมภาพันธ์",
34464
- "มีนาคม",
34465
- "เมษายน",
34466
- "พฤษภาคม",
34467
- "มิถุนายน",
34468
- "กรกฎาคม",
34469
- "สิงหาคม",
34470
- "กันยายน",
34471
- "ตุลาคม",
34472
- "พฤศจิกายน",
34473
- "ธันวาคม",
34474
- ];
34475
- /**
34476
- * Convert Gregorian year to Buddhist year
34477
- * Buddhist calendar is 543 years ahead of Gregorian calendar
34478
- *
34479
- * @param gregorianYear - The Gregorian year (e.g., 2024)
34480
- * @returns The Buddhist year (e.g., 2567)
34481
- *
34482
- * @example
34483
- * toBuddhistYear(2024); // returns 2567
34484
- */
34485
- function toBuddhistYear(gregorianYear) {
34486
- return gregorianYear + 543;
34487
- }
34488
- /**
34489
- * Convert Buddhist year to Gregorian year
34490
- *
34491
- * @param buddhistYear - The Buddhist year (e.g., 2567)
34492
- * @returns The Gregorian year (e.g., 2024)
34493
- *
34494
- * @example
34495
- * toGregorianYear(2567); // returns 2024
34496
- */
34497
- function toGregorianYear(buddhistYear) {
34498
- return buddhistYear - 543;
34499
- }
34500
- /**
34501
- * Get the first and last day of a month
34502
- *
34503
- * @param date - The date within the month
34504
- * @returns Object with start and end dates of the month
34505
- *
34506
- * @example
34507
- * getMonthDateRange(new Date('2024-06-15'));
34508
- * // returns { start: Date('2024-06-01'), end: Date('2024-06-30') }
34509
- */
34510
- function getMonthDateRange(date) {
34511
- const start = startOfMonth(date);
34512
- const end = endOfMonth(date);
34513
- return { start, end };
34514
- }
34515
- /**
34516
- * Format a date to Thai Buddhist calendar month display
34517
- *
34518
- * @param date - The date to format
34519
- * @returns Formatted string in Thai Buddhist calendar (e.g., "มกราคม 2568")
34520
- *
34521
- * @example
34522
- * formatThaiMonth(new Date('2024-01-15')); // returns "มกราคม 2567"
34523
- * formatThaiMonth(new Date('2024-12-25')); // returns "ธันวาคม 2567"
34524
- */
34525
- function formatThaiMonth(date) {
34526
- const monthIndex = date.getMonth();
34527
- const gregorianYear = date.getFullYear();
34528
- const buddhistYear = toBuddhistYear(gregorianYear);
34529
- return `${THAI_MONTH_NAMES[monthIndex]} ${buddhistYear}`;
34530
- }
34531
- /**
34532
- * Format a date to short Thai Buddhist calendar format
34533
- *
34534
- * @param date - The date to format
34535
- * @returns Formatted string (e.g., "01/2568")
34536
- *
34537
- * @example
34538
- * formatThaiMonthShort(new Date('2024-01-15')); // returns "01/2567"
34539
- */
34540
- function formatThaiMonthShort(date) {
34541
- const month = String(date.getMonth() + 1).padStart(2, "0");
34542
- const gregorianYear = date.getFullYear();
34543
- const buddhistYear = toBuddhistYear(gregorianYear);
34544
- return `${month}/${buddhistYear}`;
34545
- }
34546
- /**
34547
- * Get Thai month name by index (0-11)
34548
- *
34549
- * @param monthIndex - Month index (0 = January, 11 = December)
34550
- * @returns Thai month name
34551
- *
34552
- * @example
34553
- * getThaiMonthName(0); // returns "มกราคม"
34554
- * getThaiMonthName(11); // returns "ธันวาคม"
34555
- */
34556
- function getThaiMonthName(monthIndex) {
34557
- if (monthIndex < 0 || monthIndex > 11) {
34558
- throw new Error("Month index must be between 0 and 11");
34559
- }
34560
- return THAI_MONTH_NAMES[monthIndex];
34561
- }
34562
-
34563
34617
  /**
34564
34618
  * Convert a number to its Thai baht textual representation
34565
34619
  * (e.g. 1234.50 → "หนึ่งพันสองร้อยสามสิบสี่บาทห้าสิบสตางค์").