@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.
- package/README.md +46 -0
- package/dist/accordion.d.ts +18 -0
- package/dist/accordion.js +76 -0
- package/dist/agreement-modal.d.ts +16 -0
- package/dist/agreement-modal.js +67 -0
- package/dist/alert.d.ts +13 -0
- package/dist/alert.js +47 -0
- package/dist/auth-hero.d.ts +7 -0
- package/dist/auth-hero.js +63 -0
- package/dist/avatar.d.ts +21 -0
- package/dist/avatar.js +114 -0
- package/dist/badge.d.ts +13 -0
- package/dist/badge.js +36 -0
- package/dist/banner.d.ts +14 -0
- package/dist/banner.js +57 -0
- package/dist/breadcrumb.d.ts +15 -0
- package/dist/breadcrumb.js +37 -0
- package/dist/button.d.ts +15 -0
- package/dist/button.js +81 -0
- package/dist/card.d.ts +30 -0
- package/dist/card.js +66 -0
- package/dist/change-summary-modal.d.ts +35 -0
- package/dist/change-summary-modal.js +128 -0
- package/dist/channel-badge.d.ts +8 -0
- package/dist/channel-badge.js +17 -0
- package/dist/checkbox.d.ts +28 -0
- package/dist/checkbox.js +108 -0
- package/dist/chunk-ORMEWXMH.js +37 -0
- package/dist/color-picker.d.ts +15 -0
- package/dist/color-picker.js +159 -0
- package/dist/confirm-dialog.d.ts +23 -0
- package/dist/confirm-dialog.js +108 -0
- package/dist/copy-button.d.ts +13 -0
- package/dist/copy-button.js +69 -0
- package/dist/dashed-add-button.d.ts +8 -0
- package/dist/dashed-add-button.js +24 -0
- package/dist/data-table.d.ts +27 -0
- package/dist/data-table.js +152 -0
- package/dist/date-picker.d.ts +19 -0
- package/dist/date-picker.js +234 -0
- package/dist/date-range-picker.d.ts +25 -0
- package/dist/date-range-picker.js +456 -0
- package/dist/dev-auto-fill.d.ts +12 -0
- package/dist/dev-auto-fill.js +22 -0
- package/dist/divider.d.ts +10 -0
- package/dist/divider.js +44 -0
- package/dist/drawer.d.ts +16 -0
- package/dist/drawer.js +111 -0
- package/dist/dropdown-menu.d.ts +20 -0
- package/dist/dropdown-menu.js +94 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +24 -0
- package/dist/file-upload.d.ts +32 -0
- package/dist/file-upload.js +212 -0
- package/dist/filter-tabs.d.ts +16 -0
- package/dist/filter-tabs.js +30 -0
- package/dist/footer-action-bar.d.ts +21 -0
- package/dist/footer-action-bar.js +95 -0
- package/dist/form-input.d.ts +16 -0
- package/dist/form-input.js +58 -0
- package/dist/form-textarea.d.ts +13 -0
- package/dist/form-textarea.js +41 -0
- package/dist/icon-button.d.ts +12 -0
- package/dist/icon-button.js +54 -0
- package/dist/icon-picker.d.ts +15 -0
- package/dist/icon-picker.js +311 -0
- package/dist/icon-wrapper.d.ts +15 -0
- package/dist/icon-wrapper.js +52 -0
- package/dist/image-upload.d.ts +24 -0
- package/dist/image-upload.js +122 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +155 -0
- package/dist/kbd.d.ts +15 -0
- package/dist/kbd.js +27 -0
- package/dist/mobile-preview.d.ts +36 -0
- package/dist/mobile-preview.js +167 -0
- package/dist/modal.d.ts +19 -0
- package/dist/modal.js +110 -0
- package/dist/multi-select.d.ts +30 -0
- package/dist/multi-select.js +261 -0
- package/dist/number-input.d.ts +21 -0
- package/dist/number-input.js +129 -0
- package/dist/otp-input.d.ts +13 -0
- package/dist/otp-input.js +114 -0
- package/dist/page-header.d.ts +15 -0
- package/dist/page-header.js +43 -0
- package/dist/page-state.d.ts +14 -0
- package/dist/page-state.js +29 -0
- package/dist/pagination.d.ts +20 -0
- package/dist/pagination.js +87 -0
- package/dist/popover.d.ts +11 -0
- package/dist/popover.js +70 -0
- package/dist/preview-drawer.d.ts +33 -0
- package/dist/preview-drawer.js +74 -0
- package/dist/progress-bar.d.ts +15 -0
- package/dist/progress-bar.js +56 -0
- package/dist/qr-code-display.d.ts +10 -0
- package/dist/qr-code-display.js +43 -0
- package/dist/radio-group.d.ts +19 -0
- package/dist/radio-group.js +78 -0
- package/dist/rating.d.ts +12 -0
- package/dist/rating.js +123 -0
- package/dist/rich-editor.d.ts +13 -0
- package/dist/rich-editor.js +97 -0
- package/dist/search-bar.d.ts +14 -0
- package/dist/search-bar.js +64 -0
- package/dist/section-header.d.ts +12 -0
- package/dist/section-header.js +41 -0
- package/dist/segmented-control.d.ts +24 -0
- package/dist/segmented-control.js +38 -0
- package/dist/select-picker.d.ts +24 -0
- package/dist/select-picker.js +157 -0
- package/dist/skeleton.d.ts +14 -0
- package/dist/skeleton.js +53 -0
- package/dist/slider.d.ts +17 -0
- package/dist/slider.js +151 -0
- package/dist/spinner.d.ts +13 -0
- package/dist/spinner.js +38 -0
- package/dist/stat-card.d.ts +20 -0
- package/dist/stat-card.js +87 -0
- package/dist/stats-summary.d.ts +13 -0
- package/dist/stats-summary.js +28 -0
- package/dist/status-badge.d.ts +19 -0
- package/dist/status-badge.js +41 -0
- package/dist/stepper.d.ts +12 -0
- package/dist/stepper.js +89 -0
- package/dist/tabs.d.ts +18 -0
- package/dist/tabs.js +70 -0
- package/dist/tag.d.ts +23 -0
- package/dist/tag.js +158 -0
- package/dist/time-picker.d.ts +19 -0
- package/dist/time-picker.js +222 -0
- package/dist/timeline.d.ts +15 -0
- package/dist/timeline.js +49 -0
- package/dist/toast.d.ts +18 -0
- package/dist/toast.js +108 -0
- package/dist/toggle-switch.d.ts +12 -0
- package/dist/toggle-switch.js +34 -0
- package/dist/tooltip.d.ts +9 -0
- package/dist/tooltip.js +69 -0
- package/dist/trip-day-map-lazy.d.ts +15 -0
- package/dist/trip-day-map-lazy.js +16 -0
- package/dist/trip-day-map.d.ts +15 -0
- package/dist/trip-day-map.js +62 -0
- 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 };
|
package/dist/divider.js
ADDED
|
@@ -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
|
+
};
|
package/dist/drawer.d.ts
ADDED
|
@@ -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
|
+
};
|