@pichetch08/trip-ui 0.1.0

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.
Files changed (145) hide show
  1. package/README.md +46 -0
  2. package/dist/accordion.d.ts +18 -0
  3. package/dist/accordion.js +76 -0
  4. package/dist/agreement-modal.d.ts +16 -0
  5. package/dist/agreement-modal.js +67 -0
  6. package/dist/alert.d.ts +13 -0
  7. package/dist/alert.js +47 -0
  8. package/dist/auth-hero.d.ts +7 -0
  9. package/dist/auth-hero.js +63 -0
  10. package/dist/avatar.d.ts +21 -0
  11. package/dist/avatar.js +114 -0
  12. package/dist/badge.d.ts +13 -0
  13. package/dist/badge.js +36 -0
  14. package/dist/banner.d.ts +14 -0
  15. package/dist/banner.js +57 -0
  16. package/dist/breadcrumb.d.ts +15 -0
  17. package/dist/breadcrumb.js +37 -0
  18. package/dist/button.d.ts +15 -0
  19. package/dist/button.js +81 -0
  20. package/dist/card.d.ts +30 -0
  21. package/dist/card.js +66 -0
  22. package/dist/change-summary-modal.d.ts +35 -0
  23. package/dist/change-summary-modal.js +128 -0
  24. package/dist/channel-badge.d.ts +8 -0
  25. package/dist/channel-badge.js +17 -0
  26. package/dist/checkbox.d.ts +28 -0
  27. package/dist/checkbox.js +108 -0
  28. package/dist/chunk-ORMEWXMH.js +37 -0
  29. package/dist/color-picker.d.ts +15 -0
  30. package/dist/color-picker.js +159 -0
  31. package/dist/confirm-dialog.d.ts +23 -0
  32. package/dist/confirm-dialog.js +108 -0
  33. package/dist/copy-button.d.ts +13 -0
  34. package/dist/copy-button.js +69 -0
  35. package/dist/dashed-add-button.d.ts +8 -0
  36. package/dist/dashed-add-button.js +24 -0
  37. package/dist/data-table.d.ts +27 -0
  38. package/dist/data-table.js +152 -0
  39. package/dist/date-picker.d.ts +19 -0
  40. package/dist/date-picker.js +234 -0
  41. package/dist/date-range-picker.d.ts +25 -0
  42. package/dist/date-range-picker.js +456 -0
  43. package/dist/dev-auto-fill.d.ts +12 -0
  44. package/dist/dev-auto-fill.js +22 -0
  45. package/dist/divider.d.ts +10 -0
  46. package/dist/divider.js +44 -0
  47. package/dist/drawer.d.ts +16 -0
  48. package/dist/drawer.js +111 -0
  49. package/dist/dropdown-menu.d.ts +20 -0
  50. package/dist/dropdown-menu.js +94 -0
  51. package/dist/empty-state.d.ts +13 -0
  52. package/dist/empty-state.js +24 -0
  53. package/dist/file-upload.d.ts +32 -0
  54. package/dist/file-upload.js +212 -0
  55. package/dist/filter-tabs.d.ts +16 -0
  56. package/dist/filter-tabs.js +30 -0
  57. package/dist/footer-action-bar.d.ts +21 -0
  58. package/dist/footer-action-bar.js +95 -0
  59. package/dist/form-input.d.ts +16 -0
  60. package/dist/form-input.js +58 -0
  61. package/dist/form-textarea.d.ts +13 -0
  62. package/dist/form-textarea.js +41 -0
  63. package/dist/icon-button.d.ts +12 -0
  64. package/dist/icon-button.js +54 -0
  65. package/dist/icon-picker.d.ts +15 -0
  66. package/dist/icon-picker.js +311 -0
  67. package/dist/icon-wrapper.d.ts +15 -0
  68. package/dist/icon-wrapper.js +52 -0
  69. package/dist/image-upload.d.ts +24 -0
  70. package/dist/image-upload.js +122 -0
  71. package/dist/index.d.ts +71 -0
  72. package/dist/index.js +155 -0
  73. package/dist/kbd.d.ts +15 -0
  74. package/dist/kbd.js +27 -0
  75. package/dist/mobile-preview.d.ts +36 -0
  76. package/dist/mobile-preview.js +167 -0
  77. package/dist/modal.d.ts +19 -0
  78. package/dist/modal.js +110 -0
  79. package/dist/multi-select.d.ts +30 -0
  80. package/dist/multi-select.js +261 -0
  81. package/dist/number-input.d.ts +21 -0
  82. package/dist/number-input.js +129 -0
  83. package/dist/otp-input.d.ts +13 -0
  84. package/dist/otp-input.js +114 -0
  85. package/dist/page-header.d.ts +15 -0
  86. package/dist/page-header.js +43 -0
  87. package/dist/page-state.d.ts +14 -0
  88. package/dist/page-state.js +29 -0
  89. package/dist/pagination.d.ts +20 -0
  90. package/dist/pagination.js +87 -0
  91. package/dist/popover.d.ts +11 -0
  92. package/dist/popover.js +70 -0
  93. package/dist/preview-drawer.d.ts +33 -0
  94. package/dist/preview-drawer.js +74 -0
  95. package/dist/progress-bar.d.ts +15 -0
  96. package/dist/progress-bar.js +56 -0
  97. package/dist/qr-code-display.d.ts +10 -0
  98. package/dist/qr-code-display.js +43 -0
  99. package/dist/radio-group.d.ts +19 -0
  100. package/dist/radio-group.js +78 -0
  101. package/dist/rating.d.ts +12 -0
  102. package/dist/rating.js +123 -0
  103. package/dist/rich-editor.d.ts +13 -0
  104. package/dist/rich-editor.js +97 -0
  105. package/dist/search-bar.d.ts +14 -0
  106. package/dist/search-bar.js +64 -0
  107. package/dist/section-header.d.ts +12 -0
  108. package/dist/section-header.js +41 -0
  109. package/dist/segmented-control.d.ts +24 -0
  110. package/dist/segmented-control.js +38 -0
  111. package/dist/select-picker.d.ts +24 -0
  112. package/dist/select-picker.js +157 -0
  113. package/dist/skeleton.d.ts +14 -0
  114. package/dist/skeleton.js +53 -0
  115. package/dist/slider.d.ts +17 -0
  116. package/dist/slider.js +151 -0
  117. package/dist/spinner.d.ts +13 -0
  118. package/dist/spinner.js +38 -0
  119. package/dist/stat-card.d.ts +20 -0
  120. package/dist/stat-card.js +87 -0
  121. package/dist/stats-summary.d.ts +13 -0
  122. package/dist/stats-summary.js +28 -0
  123. package/dist/status-badge.d.ts +19 -0
  124. package/dist/status-badge.js +41 -0
  125. package/dist/stepper.d.ts +12 -0
  126. package/dist/stepper.js +89 -0
  127. package/dist/tabs.d.ts +18 -0
  128. package/dist/tabs.js +70 -0
  129. package/dist/tag.d.ts +23 -0
  130. package/dist/tag.js +158 -0
  131. package/dist/time-picker.d.ts +19 -0
  132. package/dist/time-picker.js +222 -0
  133. package/dist/timeline.d.ts +15 -0
  134. package/dist/timeline.js +49 -0
  135. package/dist/toast.d.ts +18 -0
  136. package/dist/toast.js +108 -0
  137. package/dist/toggle-switch.d.ts +12 -0
  138. package/dist/toggle-switch.js +34 -0
  139. package/dist/tooltip.d.ts +9 -0
  140. package/dist/tooltip.js +69 -0
  141. package/dist/trip-day-map-lazy.d.ts +15 -0
  142. package/dist/trip-day-map-lazy.js +16 -0
  143. package/dist/trip-day-map.d.ts +15 -0
  144. package/dist/trip-day-map.js +62 -0
  145. package/package.json +73 -0
@@ -0,0 +1,456 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState, useRef, useEffect } from "react";
5
+ const THAI_MONTHS_SHORT = [
6
+ "\u0E21.\u0E04.",
7
+ "\u0E01.\u0E1E.",
8
+ "\u0E21\u0E35.\u0E04.",
9
+ "\u0E40\u0E21.\u0E22.",
10
+ "\u0E1E.\u0E04.",
11
+ "\u0E21\u0E34.\u0E22.",
12
+ "\u0E01.\u0E04.",
13
+ "\u0E2A.\u0E04.",
14
+ "\u0E01.\u0E22.",
15
+ "\u0E15.\u0E04.",
16
+ "\u0E1E.\u0E22.",
17
+ "\u0E18.\u0E04."
18
+ ];
19
+ const WEEKDAY_LABELS = ["\u0E2D\u0E32", "\u0E08", "\u0E2D", "\u0E1E", "\u0E1E\u0E24", "\u0E28", "\u0E2A"];
20
+ function parseDate(str) {
21
+ if (!str) return null;
22
+ const [y, m, d] = str.split("-").map(Number);
23
+ if (!y || !m || !d) return null;
24
+ return new Date(y, m - 1, d);
25
+ }
26
+ function toDateString(date) {
27
+ const y = date.getFullYear();
28
+ const m = String(date.getMonth() + 1).padStart(2, "0");
29
+ const d = String(date.getDate()).padStart(2, "0");
30
+ return `${y}-${m}-${d}`;
31
+ }
32
+ function formatDisplay(str, months) {
33
+ const d = parseDate(str);
34
+ if (!d) return "";
35
+ const buddhistYear = d.getFullYear() + 543;
36
+ return `${d.getDate()} ${months[d.getMonth()]} ${buddhistYear}`;
37
+ }
38
+ function isSameDay(a, b) {
39
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
40
+ }
41
+ function startOfDay(d) {
42
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
43
+ }
44
+ function getDaysInMonth(year, month) {
45
+ return new Date(year, month + 1, 0).getDate();
46
+ }
47
+ function getFirstDayOfMonth(year, month) {
48
+ return new Date(year, month, 1).getDay();
49
+ }
50
+ function CalendarMonth({
51
+ year,
52
+ month,
53
+ startDate,
54
+ endDate,
55
+ hoverDate,
56
+ picking,
57
+ minDate,
58
+ maxDate,
59
+ onSelectDate,
60
+ onHoverDate,
61
+ onPrevMonth,
62
+ onNextMonth,
63
+ showPrev,
64
+ showNext,
65
+ monthNames,
66
+ dayNames
67
+ }) {
68
+ const buddhistYear = year + 543;
69
+ const daysInMonth = getDaysInMonth(year, month);
70
+ const firstDay = getFirstDayOfMonth(year, month);
71
+ const rangeEnd = picking === "end" && !endDate && hoverDate ? hoverDate : endDate;
72
+ const cells = [];
73
+ for (let i = 0; i < firstDay; i++) cells.push(null);
74
+ for (let d = 1; d <= daysInMonth; d++) {
75
+ cells.push(new Date(year, month, d));
76
+ }
77
+ function isDisabled(date) {
78
+ const s = startOfDay(date);
79
+ if (minDate && s < startOfDay(minDate)) return true;
80
+ if (maxDate && s > startOfDay(maxDate)) return true;
81
+ return false;
82
+ }
83
+ function isStart(date) {
84
+ return !!startDate && isSameDay(date, startDate);
85
+ }
86
+ function isEnd(date) {
87
+ return !!rangeEnd && isSameDay(date, rangeEnd);
88
+ }
89
+ function isInRange(date) {
90
+ if (!startDate || !rangeEnd) return false;
91
+ const s = startOfDay(startDate);
92
+ const e = startOfDay(rangeEnd);
93
+ const d = startOfDay(date);
94
+ if (s > e) return d > e && d < s;
95
+ return d > s && d < e;
96
+ }
97
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-[240px]", children: [
98
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3 px-1", children: [
99
+ /* @__PURE__ */ jsx(
100
+ "button",
101
+ {
102
+ type: "button",
103
+ onClick: onPrevMonth,
104
+ className: `w-8 h-8 flex items-center justify-center rounded-full hover:bg-surface-variant transition-colors text-on-surface-variant active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${!showPrev ? "invisible" : ""}`,
105
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-lg", children: "chevron_left" })
106
+ }
107
+ ),
108
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-bold text-on-surface", children: [
109
+ monthNames[month],
110
+ " ",
111
+ buddhistYear
112
+ ] }),
113
+ /* @__PURE__ */ jsx(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ onClick: onNextMonth,
118
+ className: `w-8 h-8 flex items-center justify-center rounded-full hover:bg-surface-variant transition-colors text-on-surface-variant active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${!showNext ? "invisible" : ""}`,
119
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-lg", children: "chevron_right" })
120
+ }
121
+ )
122
+ ] }),
123
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 mb-1", children: dayNames.map((d) => /* @__PURE__ */ jsx(
124
+ "div",
125
+ {
126
+ className: "text-center text-xs font-semibold text-on-surface-variant py-1",
127
+ children: d
128
+ },
129
+ d
130
+ )) }),
131
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: cells.map((date, idx) => {
132
+ if (!date) {
133
+ return /* @__PURE__ */ jsx("div", {}, `empty-${idx}`);
134
+ }
135
+ const disabled = isDisabled(date);
136
+ const start = isStart(date);
137
+ const end = isEnd(date);
138
+ const inRange = isInRange(date);
139
+ let cellBg = "";
140
+ if (start || end) {
141
+ cellBg = "bg-primary text-on-primary rounded-full";
142
+ } else if (inRange) {
143
+ cellBg = "bg-primary/10 text-on-surface";
144
+ } else {
145
+ cellBg = "text-on-surface hover:bg-surface-variant/60 rounded-full";
146
+ }
147
+ let rangeRound = "";
148
+ if (inRange) {
149
+ const isFirst = date.getDay() === 0;
150
+ const isLast = date.getDay() === 6;
151
+ if (isFirst) rangeRound = "rounded-l-full";
152
+ if (isLast) rangeRound = "rounded-r-full";
153
+ }
154
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
155
+ inRange && /* @__PURE__ */ jsx("div", { className: `absolute inset-y-0 inset-x-0 bg-primary/10 ${rangeRound}` }),
156
+ start && rangeEnd && !isSameDay(date, rangeEnd) && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 w-1/2 bg-primary/10" }),
157
+ end && startDate && !isSameDay(date, startDate) && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 w-1/2 bg-primary/10" }),
158
+ /* @__PURE__ */ jsx(
159
+ "button",
160
+ {
161
+ type: "button",
162
+ disabled,
163
+ onClick: () => !disabled && onSelectDate(date),
164
+ onMouseEnter: () => !disabled && onHoverDate(date),
165
+ onMouseLeave: () => onHoverDate(null),
166
+ className: `relative z-10 w-8 h-8 text-xs font-medium transition-all duration-150 ${disabled ? "opacity-30 cursor-not-allowed" : "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30"} ${cellBg}`,
167
+ children: date.getDate()
168
+ }
169
+ )
170
+ ] }, date.toISOString());
171
+ }) })
172
+ ] });
173
+ }
174
+ function DateRangePicker({
175
+ label,
176
+ startDate,
177
+ endDate,
178
+ onStartChange,
179
+ onEndChange,
180
+ required,
181
+ error,
182
+ placeholder = { start: "\u0E27\u0E31\u0E19\u0E40\u0E23\u0E34\u0E48\u0E21\u0E15\u0E49\u0E19", end: "\u0E27\u0E31\u0E19\u0E2A\u0E34\u0E49\u0E19\u0E2A\u0E38\u0E14" },
183
+ clearLabel = "\u0E25\u0E49\u0E32\u0E07",
184
+ confirmLabel = "\u0E15\u0E01\u0E25\u0E07",
185
+ minDate: minDateStr,
186
+ maxDate: maxDateStr,
187
+ icon = "calendar_month",
188
+ nameStart,
189
+ nameEnd,
190
+ monthNames = THAI_MONTHS_SHORT,
191
+ dayNames = WEEKDAY_LABELS
192
+ }) {
193
+ var _a, _b, _c, _d, _e;
194
+ const [open, setOpen] = useState(false);
195
+ const [picking, setPicking] = useState("start");
196
+ const [hoverDate, setHoverDate] = useState(null);
197
+ const [popupStyle, setPopupStyle] = useState({});
198
+ const containerRef = useRef(null);
199
+ const triggerRef = useRef(null);
200
+ const dropdownRef = useRef(null);
201
+ const now = /* @__PURE__ */ new Date();
202
+ const initialMonth = startDate ? (_a = parseDate(startDate)) != null ? _a : now : now;
203
+ const [leftMonth, setLeftMonth] = useState({
204
+ year: initialMonth.getFullYear(),
205
+ month: initialMonth.getMonth()
206
+ });
207
+ const rightMonth = (() => {
208
+ const d = new Date(leftMonth.year, leftMonth.month + 1, 1);
209
+ return { year: d.getFullYear(), month: d.getMonth() };
210
+ })();
211
+ const parsedStart = parseDate(startDate);
212
+ const parsedEnd = parseDate(endDate);
213
+ const minDate = parseDate(minDateStr != null ? minDateStr : "");
214
+ const maxDate = parseDate(maxDateStr != null ? maxDateStr : "");
215
+ const displayText = startDate || endDate ? `${startDate ? formatDisplay(startDate, monthNames) : (_b = placeholder.start) != null ? _b : ""} \u2192 ${endDate ? formatDisplay(endDate, monthNames) : (_c = placeholder.end) != null ? _c : ""}` : null;
216
+ function calculatePopupStyle() {
217
+ const btn = triggerRef.current;
218
+ if (!btn) return;
219
+ const rect = btn.getBoundingClientRect();
220
+ const popupWidth = 560;
221
+ const spaceBelow = window.innerHeight - rect.bottom;
222
+ const showAbove = spaceBelow < 420 && rect.top > 420;
223
+ let left = rect.left;
224
+ if (left + popupWidth > window.innerWidth - 8) {
225
+ left = Math.max(8, window.innerWidth - popupWidth - 8);
226
+ }
227
+ if (showAbove) {
228
+ setPopupStyle({ position: "fixed", bottom: window.innerHeight - rect.top + 4, left, width: popupWidth, zIndex: 9999 });
229
+ } else {
230
+ setPopupStyle({ position: "fixed", top: rect.bottom + 4, left, width: popupWidth, zIndex: 9999 });
231
+ }
232
+ }
233
+ useEffect(() => {
234
+ if (!open) return;
235
+ function handleClick(e) {
236
+ var _a2, _b2;
237
+ const target = e.target;
238
+ const insideTrigger = (_a2 = containerRef.current) == null ? void 0 : _a2.contains(target);
239
+ const insidePopup = (_b2 = dropdownRef.current) == null ? void 0 : _b2.contains(target);
240
+ if (!insideTrigger && !insidePopup) setOpen(false);
241
+ }
242
+ function handleKeyDown(e) {
243
+ if (e.key === "Escape") setOpen(false);
244
+ }
245
+ function handleScroll() {
246
+ calculatePopupStyle();
247
+ }
248
+ document.addEventListener("mousedown", handleClick);
249
+ document.addEventListener("keydown", handleKeyDown);
250
+ window.addEventListener("scroll", handleScroll, true);
251
+ window.addEventListener("resize", handleScroll);
252
+ return () => {
253
+ document.removeEventListener("mousedown", handleClick);
254
+ document.removeEventListener("keydown", handleKeyDown);
255
+ window.removeEventListener("scroll", handleScroll, true);
256
+ window.removeEventListener("resize", handleScroll);
257
+ };
258
+ }, [open]);
259
+ function handleOpen() {
260
+ if (!open) {
261
+ calculatePopupStyle();
262
+ setPicking("start");
263
+ const base = parsedStart != null ? parsedStart : /* @__PURE__ */ new Date();
264
+ setLeftMonth({ year: base.getFullYear(), month: base.getMonth() });
265
+ }
266
+ setOpen((v) => !v);
267
+ }
268
+ function handleSelectDate(date) {
269
+ if (picking === "start") {
270
+ onStartChange(toDateString(date));
271
+ if (parsedEnd && date > parsedEnd) {
272
+ onEndChange("");
273
+ }
274
+ setPicking("end");
275
+ } else {
276
+ if (parsedStart && date < parsedStart) {
277
+ onEndChange(toDateString(parsedStart));
278
+ onStartChange(toDateString(date));
279
+ } else {
280
+ onEndChange(toDateString(date));
281
+ }
282
+ setPicking("start");
283
+ }
284
+ }
285
+ function handleClear() {
286
+ onStartChange("");
287
+ onEndChange("");
288
+ setPicking("start");
289
+ }
290
+ function handleConfirm() {
291
+ setOpen(false);
292
+ }
293
+ function prevMonth() {
294
+ const d = new Date(leftMonth.year, leftMonth.month - 1, 1);
295
+ setLeftMonth({ year: d.getFullYear(), month: d.getMonth() });
296
+ }
297
+ function nextMonth() {
298
+ const d = new Date(leftMonth.year, leftMonth.month + 1, 1);
299
+ setLeftMonth({ year: d.getFullYear(), month: d.getMonth() });
300
+ }
301
+ const triggerId = label ? `daterange-${label.replace(/\s+/g, "-").toLowerCase()}` : void 0;
302
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", ref: containerRef, children: [
303
+ nameStart && /* @__PURE__ */ jsx("input", { type: "hidden", name: nameStart, value: startDate }),
304
+ nameEnd && /* @__PURE__ */ jsx("input", { type: "hidden", name: nameEnd, value: endDate }),
305
+ label && /* @__PURE__ */ jsxs("label", { htmlFor: triggerId, className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
306
+ label,
307
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
308
+ ] }),
309
+ /* @__PURE__ */ jsxs(
310
+ "button",
311
+ {
312
+ ref: triggerRef,
313
+ id: triggerId,
314
+ type: "button",
315
+ "aria-expanded": open,
316
+ "aria-haspopup": "dialog",
317
+ "aria-required": required,
318
+ onClick: handleOpen,
319
+ className: `relative w-full bg-surface-container-low border rounded-xl py-4 pl-12 pr-10 text-left transition-all font-medium outline-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${open ? "bg-surface ring-2 ring-primary/20 border-primary" : error ? "border-red-400 bg-red-50/30" : "border-transparent hover:border-outline-variant"}`,
320
+ children: [
321
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-on-surface-variant", children: icon }),
322
+ displayText ? /* @__PURE__ */ jsx("span", { className: "text-on-surface text-sm", children: displayText }) : /* @__PURE__ */ jsxs("span", { className: "text-outline/40 text-sm", children: [
323
+ placeholder.start,
324
+ " \u2192 ",
325
+ placeholder.end
326
+ ] }),
327
+ /* @__PURE__ */ jsx(
328
+ "span",
329
+ {
330
+ className: `material-symbols-outlined absolute right-3 top-1/2 -translate-y-1/2 text-on-surface-variant text-lg transition-transform ${open ? "rotate-180" : ""}`,
331
+ children: "expand_more"
332
+ }
333
+ )
334
+ ]
335
+ }
336
+ ),
337
+ error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 px-1 flex items-center gap-1", children: [
338
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", children: "error" }),
339
+ error
340
+ ] }),
341
+ open && /* @__PURE__ */ jsxs(
342
+ "div",
343
+ {
344
+ ref: dropdownRef,
345
+ style: popupStyle,
346
+ className: "bg-surface rounded-2xl shadow-2xl border border-outline-variant/30 overflow-hidden animate-in fade-in slide-in-from-top-2 duration-150",
347
+ children: [
348
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-5 pt-4 pb-3 border-b border-outline-variant/20", children: [
349
+ /* @__PURE__ */ jsxs(
350
+ "div",
351
+ {
352
+ className: `flex-1 flex flex-col items-center px-3 py-2 rounded-xl transition-all ${picking === "start" ? "bg-primary/10 ring-1 ring-primary/30" : "bg-surface-container-low"}`,
353
+ children: [
354
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant font-medium mb-0.5", children: (_d = placeholder.start) != null ? _d : "\u0E27\u0E31\u0E19\u0E40\u0E23\u0E34\u0E48\u0E21\u0E15\u0E49\u0E19" }),
355
+ /* @__PURE__ */ jsx(
356
+ "span",
357
+ {
358
+ className: `text-sm font-bold ${startDate ? "text-primary" : "text-outline/40"}`,
359
+ children: startDate ? formatDisplay(startDate, monthNames) : "\u2014"
360
+ }
361
+ )
362
+ ]
363
+ }
364
+ ),
365
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-on-surface-variant", children: "arrow_forward" }),
366
+ /* @__PURE__ */ jsxs(
367
+ "div",
368
+ {
369
+ className: `flex-1 flex flex-col items-center px-3 py-2 rounded-xl transition-all ${picking === "end" ? "bg-primary/10 ring-1 ring-primary/30" : "bg-surface-container-low"}`,
370
+ children: [
371
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant font-medium mb-0.5", children: (_e = placeholder.end) != null ? _e : "\u0E27\u0E31\u0E19\u0E2A\u0E34\u0E49\u0E19\u0E2A\u0E38\u0E14" }),
372
+ /* @__PURE__ */ jsx(
373
+ "span",
374
+ {
375
+ className: `text-sm font-bold ${endDate ? "text-primary" : "text-outline/40"}`,
376
+ children: endDate ? formatDisplay(endDate, monthNames) : "\u2014"
377
+ }
378
+ )
379
+ ]
380
+ }
381
+ )
382
+ ] }),
383
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4 p-4", children: [
384
+ /* @__PURE__ */ jsx(
385
+ CalendarMonth,
386
+ {
387
+ year: leftMonth.year,
388
+ month: leftMonth.month,
389
+ startDate: parsedStart,
390
+ endDate: parsedEnd,
391
+ hoverDate,
392
+ picking,
393
+ minDate,
394
+ maxDate,
395
+ onSelectDate: handleSelectDate,
396
+ onHoverDate: setHoverDate,
397
+ onPrevMonth: prevMonth,
398
+ onNextMonth: nextMonth,
399
+ showPrev: true,
400
+ showNext: false,
401
+ monthNames,
402
+ dayNames
403
+ }
404
+ ),
405
+ /* @__PURE__ */ jsx("div", { className: "w-px bg-outline-variant/20 self-stretch" }),
406
+ /* @__PURE__ */ jsx(
407
+ CalendarMonth,
408
+ {
409
+ year: rightMonth.year,
410
+ month: rightMonth.month,
411
+ startDate: parsedStart,
412
+ endDate: parsedEnd,
413
+ hoverDate,
414
+ picking,
415
+ minDate,
416
+ maxDate,
417
+ onSelectDate: handleSelectDate,
418
+ onHoverDate: setHoverDate,
419
+ onPrevMonth: prevMonth,
420
+ onNextMonth: nextMonth,
421
+ showPrev: false,
422
+ showNext: true,
423
+ monthNames,
424
+ dayNames
425
+ }
426
+ )
427
+ ] }),
428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-3 border-t border-outline-variant/20", children: [
429
+ /* @__PURE__ */ jsx(
430
+ "button",
431
+ {
432
+ type: "button",
433
+ onClick: handleClear,
434
+ className: "text-sm font-semibold text-red-500 hover:opacity-70 transition-opacity px-3 py-1.5 rounded-lg hover:bg-red-50 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
435
+ children: clearLabel
436
+ }
437
+ ),
438
+ /* @__PURE__ */ jsx(
439
+ "button",
440
+ {
441
+ type: "button",
442
+ onClick: handleConfirm,
443
+ disabled: !startDate || !endDate,
444
+ className: "text-sm font-bold bg-primary text-on-primary px-5 py-1.5 rounded-xl hover:opacity-90 transition-opacity disabled:opacity-40 disabled:cursor-not-allowed active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
445
+ children: confirmLabel
446
+ }
447
+ )
448
+ ] })
449
+ ]
450
+ }
451
+ )
452
+ ] });
453
+ }
454
+ export {
455
+ DateRangePicker
456
+ };
@@ -0,0 +1,12 @@
1
+ interface DevAutoFillProps {
2
+ onFill: () => void;
3
+ label?: string;
4
+ }
5
+ /**
6
+ * ปุ่ม Auto Fill สำหรับ dev mode เท่านั้น
7
+ * ไม่แสดงใน production
8
+ * ใช้ได้ทุกหน้าที่มี form
9
+ */
10
+ declare function DevAutoFill({ onFill, label }: DevAutoFillProps): React.ReactNode;
11
+
12
+ export { DevAutoFill };
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ const isDev = process.env.NODE_ENV === "development";
5
+ function DevAutoFill({ onFill, label = "Auto Fill" }) {
6
+ if (!isDev) return null;
7
+ return /* @__PURE__ */ jsxs(
8
+ "button",
9
+ {
10
+ type: "button",
11
+ onClick: onFill,
12
+ className: "fixed bottom-6 right-6 z-50 flex items-center gap-2 px-4 py-2.5 bg-amber-500 text-white rounded-full text-xs font-bold shadow-lg hover:bg-amber-600 active:scale-95 transition-all",
13
+ children: [
14
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", children: "auto_fix_high" }),
15
+ label
16
+ ]
17
+ }
18
+ );
19
+ }
20
+ export {
21
+ DevAutoFill
22
+ };
@@ -0,0 +1,10 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface DividerProps {
4
+ orientation?: "horizontal" | "vertical";
5
+ label?: string;
6
+ className?: string;
7
+ }
8
+ declare function Divider({ orientation, label, className, }: DividerProps): react_jsx_runtime.JSX.Element;
9
+
10
+ export { Divider };
@@ -0,0 +1,44 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ function Divider({
4
+ orientation = "horizontal",
5
+ label,
6
+ className = ""
7
+ }) {
8
+ if (orientation === "vertical") {
9
+ return /* @__PURE__ */ jsx(
10
+ "div",
11
+ {
12
+ className: `border-l border-outline-variant/30 h-full self-stretch ${className}`,
13
+ role: "separator",
14
+ "aria-orientation": "vertical"
15
+ }
16
+ );
17
+ }
18
+ if (label) {
19
+ return /* @__PURE__ */ jsxs(
20
+ "div",
21
+ {
22
+ className: `flex items-center gap-3 w-full ${className}`,
23
+ role: "separator",
24
+ "aria-orientation": "horizontal",
25
+ children: [
26
+ /* @__PURE__ */ jsx("div", { className: "flex-1 border-t border-outline-variant/30" }),
27
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant font-medium whitespace-nowrap", children: label }),
28
+ /* @__PURE__ */ jsx("div", { className: "flex-1 border-t border-outline-variant/30" })
29
+ ]
30
+ }
31
+ );
32
+ }
33
+ return /* @__PURE__ */ jsx(
34
+ "div",
35
+ {
36
+ className: `border-t border-outline-variant/30 w-full ${className}`,
37
+ role: "separator",
38
+ "aria-orientation": "horizontal"
39
+ }
40
+ );
41
+ }
42
+ export {
43
+ Divider
44
+ };
@@ -0,0 +1,16 @@
1
+ type DrawerPosition = "left" | "right";
2
+ type DrawerSize = "sm" | "md" | "lg" | "xl" | "full";
3
+ interface DrawerProps {
4
+ open: boolean;
5
+ onClose: () => void;
6
+ title: string;
7
+ subtitle?: string;
8
+ children: React.ReactNode;
9
+ footer?: React.ReactNode;
10
+ position?: DrawerPosition;
11
+ size?: DrawerSize;
12
+ closeAriaLabel?: string;
13
+ }
14
+ declare function Drawer({ open, onClose, title, subtitle, children, footer, position, size, closeAriaLabel, }: DrawerProps): React.ReactNode;
15
+
16
+ export { Drawer };
package/dist/drawer.js ADDED
@@ -0,0 +1,111 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useEffect, useRef, useCallback } from "react";
5
+ import { IconButton } from "./icon-button";
6
+ const SIZE_CLASSES = {
7
+ sm: "max-w-xs",
8
+ md: "max-w-lg",
9
+ lg: "max-w-2xl",
10
+ xl: "max-w-4xl",
11
+ full: "max-w-full"
12
+ };
13
+ function Drawer({
14
+ open,
15
+ onClose,
16
+ title,
17
+ subtitle,
18
+ children,
19
+ footer,
20
+ position = "right",
21
+ size = "md",
22
+ closeAriaLabel = "\u0E1B\u0E34\u0E14"
23
+ }) {
24
+ const closeButtonRef = useRef(null);
25
+ const panelRef = useRef(null);
26
+ useEffect(() => {
27
+ if (!open) return;
28
+ const handler = (e) => {
29
+ if (e.key === "Escape") onClose();
30
+ };
31
+ document.addEventListener("keydown", handler);
32
+ return () => document.removeEventListener("keydown", handler);
33
+ }, [open, onClose]);
34
+ useEffect(() => {
35
+ if (open) {
36
+ const id = setTimeout(() => {
37
+ var _a;
38
+ return (_a = closeButtonRef.current) == null ? void 0 : _a.focus();
39
+ }, 50);
40
+ return () => clearTimeout(id);
41
+ }
42
+ }, [open]);
43
+ const handleKeyDown = useCallback((e) => {
44
+ if (e.key !== "Tab" || !panelRef.current) return;
45
+ const focusable = panelRef.current.querySelectorAll(
46
+ 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
47
+ );
48
+ if (focusable.length === 0) return;
49
+ const first = focusable[0];
50
+ const last = focusable[focusable.length - 1];
51
+ if (e.shiftKey) {
52
+ if (document.activeElement === first) {
53
+ e.preventDefault();
54
+ last.focus();
55
+ }
56
+ } else {
57
+ if (document.activeElement === last) {
58
+ e.preventDefault();
59
+ first.focus();
60
+ }
61
+ }
62
+ }, []);
63
+ const isLeft = position === "left";
64
+ const slideIn = isLeft ? "-translate-x-full" : "translate-x-full";
65
+ const positionClasses = isLeft ? "top-0 left-0" : "top-0 right-0";
66
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
67
+ /* @__PURE__ */ jsx(
68
+ "div",
69
+ {
70
+ className: `fixed inset-0 z-60 bg-black/40 transition-opacity duration-300 ${open ? "opacity-100 pointer-events-auto" : "opacity-0 pointer-events-none"}`,
71
+ onClick: onClose
72
+ }
73
+ ),
74
+ /* @__PURE__ */ jsxs(
75
+ "div",
76
+ {
77
+ ref: panelRef,
78
+ role: "dialog",
79
+ "aria-modal": "true",
80
+ "aria-labelledby": "drawer-title",
81
+ onKeyDown: handleKeyDown,
82
+ className: `fixed ${positionClasses} z-70 h-full w-full ${SIZE_CLASSES[size]} bg-surface shadow-2xl flex flex-col transition-transform duration-300 ease-out ${open ? "translate-x-0" : slideIn}`,
83
+ children: [
84
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between p-6 border-b border-outline-variant", children: [
85
+ /* @__PURE__ */ jsxs("div", { children: [
86
+ /* @__PURE__ */ jsx("h2", { id: "drawer-title", className: "text-lg font-bold text-on-surface", children: title }),
87
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-on-surface-variant mt-0.5", children: subtitle })
88
+ ] }),
89
+ /* @__PURE__ */ jsx(
90
+ IconButton,
91
+ {
92
+ ref: closeButtonRef,
93
+ icon: "close",
94
+ variant: "ghost",
95
+ size: "sm",
96
+ onClick: onClose,
97
+ "aria-label": closeAriaLabel,
98
+ className: "-mr-1 -mt-1"
99
+ }
100
+ )
101
+ ] }),
102
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-6", children }),
103
+ footer && /* @__PURE__ */ jsx("div", { className: "border-t border-outline-variant p-4 bg-surface-container-low", children: footer })
104
+ ]
105
+ }
106
+ )
107
+ ] });
108
+ }
109
+ export {
110
+ Drawer
111
+ };