@dododog/ui 0.14.1 → 0.16.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/dist/chunk-5KOWMIWK.mjs +110 -0
- package/dist/{chunk-MCF3EQTV.js → chunk-73BHQJW6.js} +13 -1
- package/dist/{chunk-U3XWNFHH.js → chunk-A3M3XQ35.js} +96 -29
- package/dist/chunk-CPBDDGRX.mjs +572 -0
- package/dist/chunk-DJTFRMRY.js +595 -0
- package/dist/{chunk-NHB2TI2B.js → chunk-FHAWYXPX.js} +1 -1
- package/dist/{chunk-5UNG2O64.js → chunk-FR46MMNP.js} +7 -1
- package/dist/{chunk-I4S3R6C6.js → chunk-JUB3NGJR.js} +51 -6
- package/dist/chunk-JWQ2OUEG.js +132 -0
- package/dist/{chunk-EEIPCJQ2.mjs → chunk-KWQKC5TG.mjs} +1 -1
- package/dist/{chunk-IJIUUBFT.js → chunk-OUN5BU4Z.js} +1 -1
- package/dist/{chunk-DMCMWOBJ.mjs → chunk-PBXH4I5N.mjs} +51 -6
- package/dist/{chunk-ERL3WXNY.mjs → chunk-PZZWQ6IG.mjs} +5 -5
- package/dist/{chunk-L7OYR6U3.js → chunk-R4G5LFPN.js} +1 -1
- package/dist/{chunk-F2BUMJYW.mjs → chunk-RBVTV3LA.mjs} +1 -1
- package/dist/{chunk-B47HQHX3.mjs → chunk-RGZHSI2G.mjs} +1 -1
- package/dist/{chunk-GXKON34B.js → chunk-RTI25IBR.js} +1 -1
- package/dist/{chunk-GBWVIY3C.mjs → chunk-UJEIFKSK.mjs} +96 -29
- package/dist/{chunk-3R7PT3JG.js → chunk-USJDHMKS.js} +40 -6
- package/dist/{chunk-333YD5QI.mjs → chunk-WNBZXXFA.mjs} +40 -6
- package/dist/chunk-WY24TZ4R.mjs +94 -0
- package/dist/{chunk-JWOV5H6G.mjs → chunk-X53C3IQC.mjs} +7 -1
- package/dist/chunk-XBNIHUSG.js +116 -0
- package/dist/{chunk-TDGU5Y2P.js → chunk-XOFTU3T7.js} +5 -5
- package/dist/{chunk-NZ7GF6RF.mjs → chunk-YOMOULLR.mjs} +13 -1
- package/dist/{chunk-ZZ4EWC53.mjs → chunk-Z7CF7EQJ.mjs} +1 -1
- package/dist/components/Accordion/index.js +2 -2
- package/dist/components/Accordion/index.mjs +1 -1
- package/dist/components/Autocomplete/index.js +3 -3
- package/dist/components/Autocomplete/index.mjs +1 -1
- package/dist/components/Checkbox/index.js +2 -2
- package/dist/components/Checkbox/index.mjs +1 -1
- package/dist/components/Counter/index.d.mts +12 -0
- package/dist/components/Counter/index.d.ts +12 -0
- package/dist/components/Counter/index.js +2 -2
- package/dist/components/Counter/index.mjs +1 -1
- package/dist/components/DateRangePicker/index.d.mts +7 -1
- package/dist/components/DateRangePicker/index.d.ts +7 -1
- package/dist/components/DateRangePicker/index.js +3 -3
- package/dist/components/DateRangePicker/index.mjs +1 -1
- package/dist/components/Drawer/index.d.mts +21 -1
- package/dist/components/Drawer/index.d.ts +21 -1
- package/dist/components/Drawer/index.js +2 -2
- package/dist/components/Drawer/index.mjs +1 -1
- package/dist/components/DualRangeSlider/index.js +2 -2
- package/dist/components/DualRangeSlider/index.mjs +1 -1
- package/dist/components/Input/index.js +3 -3
- package/dist/components/Input/index.mjs +1 -1
- package/dist/components/Lightbox/index.d.mts +2 -0
- package/dist/components/Lightbox/index.d.ts +2 -0
- package/dist/components/Lightbox/index.js +2 -2
- package/dist/components/Lightbox/index.mjs +1 -1
- package/dist/components/RadioGroup/index.js +2 -2
- package/dist/components/RadioGroup/index.mjs +1 -1
- package/dist/components/Select/index.d.mts +8 -0
- package/dist/components/Select/index.d.ts +8 -0
- package/dist/components/Select/index.js +3 -3
- package/dist/components/Select/index.mjs +1 -1
- package/dist/components/Switch/index.d.mts +41 -1
- package/dist/components/Switch/index.d.ts +41 -1
- package/dist/components/Switch/index.js +4 -4
- package/dist/components/Switch/index.mjs +1 -1
- package/dist/components/Textarea/index.js +3 -3
- package/dist/components/Textarea/index.mjs +1 -1
- package/dist/index.js +81 -81
- package/dist/index.mjs +20 -20
- package/package.json +1 -1
- package/dist/chunk-AIA3NHCK.js +0 -373
- package/dist/chunk-FL3GD5FJ.mjs +0 -350
- package/dist/chunk-LRNSVRUN.mjs +0 -55
- package/dist/chunk-T4AT3YCT.js +0 -77
- package/dist/chunk-VWFY24XF.js +0 -89
- package/dist/chunk-ZLF7IL3Y.mjs +0 -67
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { cn } from './chunk-IMKLN273.mjs';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function startOfDay(d) {
|
|
6
|
+
const r = new Date(d);
|
|
7
|
+
r.setHours(0, 0, 0, 0);
|
|
8
|
+
return r;
|
|
9
|
+
}
|
|
10
|
+
function addMonths(d, n) {
|
|
11
|
+
const year = d.getFullYear();
|
|
12
|
+
const month = d.getMonth() + n;
|
|
13
|
+
const lastDayOfTargetMonth = new Date(year, month + 1, 0).getDate();
|
|
14
|
+
const r = new Date(d);
|
|
15
|
+
r.setFullYear(year, month, Math.min(d.getDate(), lastDayOfTargetMonth));
|
|
16
|
+
return r;
|
|
17
|
+
}
|
|
18
|
+
function addDays(d, n) {
|
|
19
|
+
const r = startOfDay(d);
|
|
20
|
+
r.setDate(r.getDate() + n);
|
|
21
|
+
return r;
|
|
22
|
+
}
|
|
23
|
+
function startOfWeek(d) {
|
|
24
|
+
const r = startOfDay(d);
|
|
25
|
+
r.setDate(r.getDate() - (r.getDay() + 6) % 7);
|
|
26
|
+
return r;
|
|
27
|
+
}
|
|
28
|
+
function firstDayOfMonth(d) {
|
|
29
|
+
return new Date(d.getFullYear(), d.getMonth(), 1);
|
|
30
|
+
}
|
|
31
|
+
function isSameDay(a, b) {
|
|
32
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
33
|
+
}
|
|
34
|
+
function isBefore(a, b) {
|
|
35
|
+
return startOfDay(a).getTime() < startOfDay(b).getTime();
|
|
36
|
+
}
|
|
37
|
+
function isAfter(a, b) {
|
|
38
|
+
return startOfDay(a).getTime() > startOfDay(b).getTime();
|
|
39
|
+
}
|
|
40
|
+
function isSameMonth(a, b) {
|
|
41
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
42
|
+
}
|
|
43
|
+
var MONTH_NAMES = [
|
|
44
|
+
"janvier",
|
|
45
|
+
"f\xE9vrier",
|
|
46
|
+
"mars",
|
|
47
|
+
"avril",
|
|
48
|
+
"mai",
|
|
49
|
+
"juin",
|
|
50
|
+
"juillet",
|
|
51
|
+
"ao\xFBt",
|
|
52
|
+
"septembre",
|
|
53
|
+
"octobre",
|
|
54
|
+
"novembre",
|
|
55
|
+
"d\xE9cembre"
|
|
56
|
+
];
|
|
57
|
+
var SHORT_MONTHS = [
|
|
58
|
+
"janv.",
|
|
59
|
+
"f\xE9vr.",
|
|
60
|
+
"mars",
|
|
61
|
+
"avr.",
|
|
62
|
+
"mai",
|
|
63
|
+
"juin",
|
|
64
|
+
"juil.",
|
|
65
|
+
"ao\xFBt",
|
|
66
|
+
"sept.",
|
|
67
|
+
"oct.",
|
|
68
|
+
"nov.",
|
|
69
|
+
"d\xE9c."
|
|
70
|
+
];
|
|
71
|
+
var WEEK_DAYS = ["lu", "ma", "me", "je", "ve", "sa", "di"];
|
|
72
|
+
var WEEK_DAY_NAMES = [
|
|
73
|
+
"lundi",
|
|
74
|
+
"mardi",
|
|
75
|
+
"mercredi",
|
|
76
|
+
"jeudi",
|
|
77
|
+
"vendredi",
|
|
78
|
+
"samedi",
|
|
79
|
+
"dimanche"
|
|
80
|
+
];
|
|
81
|
+
var DIALOG_TITLE = "S\xE9lectionnez vos dates";
|
|
82
|
+
function formatMonthYear(d) {
|
|
83
|
+
return `${MONTH_NAMES[d.getMonth()]} ${d.getFullYear()}`;
|
|
84
|
+
}
|
|
85
|
+
function formatShortDate(d) {
|
|
86
|
+
return `${d.getDate()} ${SHORT_MONTHS[d.getMonth()]}`;
|
|
87
|
+
}
|
|
88
|
+
function formatFullDate(d) {
|
|
89
|
+
return `${d.getDate()} ${MONTH_NAMES[d.getMonth()]} ${d.getFullYear()}`;
|
|
90
|
+
}
|
|
91
|
+
function dayKey(d) {
|
|
92
|
+
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
|
|
93
|
+
}
|
|
94
|
+
function getCalendarDays(month) {
|
|
95
|
+
const first = new Date(month.getFullYear(), month.getMonth(), 1);
|
|
96
|
+
const dayOfWeek = (first.getDay() + 6) % 7;
|
|
97
|
+
const start = new Date(first);
|
|
98
|
+
start.setDate(start.getDate() - dayOfWeek);
|
|
99
|
+
const last = new Date(month.getFullYear(), month.getMonth() + 1, 0);
|
|
100
|
+
const dayOfWeekEnd = (last.getDay() + 6) % 7;
|
|
101
|
+
const end = new Date(last);
|
|
102
|
+
end.setDate(end.getDate() + (6 - dayOfWeekEnd));
|
|
103
|
+
const days = [];
|
|
104
|
+
const cursor = new Date(start);
|
|
105
|
+
while (cursor <= end) {
|
|
106
|
+
days.push(new Date(cursor));
|
|
107
|
+
cursor.setDate(cursor.getDate() + 1);
|
|
108
|
+
}
|
|
109
|
+
return days;
|
|
110
|
+
}
|
|
111
|
+
function toWeeks(days) {
|
|
112
|
+
const weeks = [];
|
|
113
|
+
for (let index = 0; index < days.length; index += 7) {
|
|
114
|
+
weeks.push(days.slice(index, index + 7));
|
|
115
|
+
}
|
|
116
|
+
return weeks;
|
|
117
|
+
}
|
|
118
|
+
function describeRange(checkIn, checkOut) {
|
|
119
|
+
if (checkIn && checkOut) {
|
|
120
|
+
return `Arriv\xE9e le ${formatFullDate(checkIn)}, d\xE9part le ${formatFullDate(checkOut)}.`;
|
|
121
|
+
}
|
|
122
|
+
if (checkIn) {
|
|
123
|
+
return `Arriv\xE9e le ${formatFullDate(checkIn)}. S\xE9lectionnez la date de d\xE9part.`;
|
|
124
|
+
}
|
|
125
|
+
if (checkOut) {
|
|
126
|
+
return `D\xE9part le ${formatFullDate(checkOut)}. S\xE9lectionnez la date d'arriv\xE9e.`;
|
|
127
|
+
}
|
|
128
|
+
return "Aucune date s\xE9lectionn\xE9e.";
|
|
129
|
+
}
|
|
130
|
+
var FOCUSABLE_SELECTOR = [
|
|
131
|
+
"a[href]",
|
|
132
|
+
"button:not([disabled])",
|
|
133
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
134
|
+
"select:not([disabled])",
|
|
135
|
+
"textarea:not([disabled])",
|
|
136
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
137
|
+
].join(", ");
|
|
138
|
+
function getFocusableElements(panel) {
|
|
139
|
+
return Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
|
|
140
|
+
(element) => element.getAttribute("tabindex") !== "-1" && !element.closest("[hidden], [aria-hidden='true']")
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function DatePickerInput({
|
|
144
|
+
label,
|
|
145
|
+
value,
|
|
146
|
+
placeholder = "Quand ?",
|
|
147
|
+
isActive,
|
|
148
|
+
onClick,
|
|
149
|
+
onClear,
|
|
150
|
+
showClear = false,
|
|
151
|
+
isOpen
|
|
152
|
+
}) {
|
|
153
|
+
return /* @__PURE__ */ jsx("div", { className: "relative flex-1 px-4 py-2 border-b md:border-b-0 md:border-r border-gray-200 transition-all text-left", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
154
|
+
/* @__PURE__ */ jsxs(
|
|
155
|
+
"button",
|
|
156
|
+
{
|
|
157
|
+
type: "button",
|
|
158
|
+
onClick,
|
|
159
|
+
"aria-haspopup": "dialog",
|
|
160
|
+
"aria-expanded": isOpen ?? isActive,
|
|
161
|
+
className: "flex-1 appearance-none bg-transparent text-left cursor-pointer",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs font-medium text-gray-500 mb-0.5", children: label }),
|
|
164
|
+
/* @__PURE__ */ jsx(
|
|
165
|
+
"span",
|
|
166
|
+
{
|
|
167
|
+
className: cn(
|
|
168
|
+
"block text-base font-medium text-primary whitespace-nowrap",
|
|
169
|
+
isActive && value && "underline decoration-secondary decoration-2 underline-offset-4"
|
|
170
|
+
),
|
|
171
|
+
children: value ? formatShortDate(value) : /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: placeholder })
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
),
|
|
177
|
+
showClear && value && isActive && onClear && /* @__PURE__ */ jsx(
|
|
178
|
+
"button",
|
|
179
|
+
{
|
|
180
|
+
type: "button",
|
|
181
|
+
"aria-label": `Effacer la date : ${label}`,
|
|
182
|
+
onClick: (e) => {
|
|
183
|
+
e.stopPropagation();
|
|
184
|
+
onClear();
|
|
185
|
+
},
|
|
186
|
+
className: "p-1 hover:bg-gray-100 rounded-full transition-colors",
|
|
187
|
+
children: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-400", "aria-hidden": "true", focusable: "false", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
189
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
190
|
+
] })
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
] }) });
|
|
194
|
+
}
|
|
195
|
+
var DateRangePicker = React.forwardRef(
|
|
196
|
+
({
|
|
197
|
+
checkIn,
|
|
198
|
+
checkOut,
|
|
199
|
+
onCheckInChange,
|
|
200
|
+
onCheckOutChange,
|
|
201
|
+
isOpen,
|
|
202
|
+
onClose,
|
|
203
|
+
position = "center",
|
|
204
|
+
minWidth = 800
|
|
205
|
+
}, forwardedRef) => {
|
|
206
|
+
const [calendarMonth, setCalendarMonth] = React.useState(() => checkIn || /* @__PURE__ */ new Date());
|
|
207
|
+
const [activeDateField, setActiveDateField] = React.useState("arrival");
|
|
208
|
+
const [isMobile, setIsMobile] = React.useState(false);
|
|
209
|
+
const [isNarrow, setIsNarrow] = React.useState(false);
|
|
210
|
+
const [focusedDate, setFocusedDate] = React.useState(() => startOfDay(checkIn || /* @__PURE__ */ new Date()));
|
|
211
|
+
const internalRef = React.useRef(null);
|
|
212
|
+
const panelRef = React.useRef(null);
|
|
213
|
+
const pendingFocusRef = React.useRef(false);
|
|
214
|
+
const titleId = React.useId();
|
|
215
|
+
const setRefs = React.useCallback(
|
|
216
|
+
(node) => {
|
|
217
|
+
internalRef.current = node;
|
|
218
|
+
panelRef.current = node;
|
|
219
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
220
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
221
|
+
},
|
|
222
|
+
[forwardedRef]
|
|
223
|
+
);
|
|
224
|
+
React.useEffect(() => {
|
|
225
|
+
const mq = window.matchMedia("(max-width: 767px)");
|
|
226
|
+
setIsMobile(mq.matches);
|
|
227
|
+
const handler = (e) => setIsMobile(e.matches);
|
|
228
|
+
mq.addEventListener("change", handler);
|
|
229
|
+
return () => mq.removeEventListener("change", handler);
|
|
230
|
+
}, []);
|
|
231
|
+
React.useEffect(() => {
|
|
232
|
+
const checkNarrow = () => setIsNarrow(window.innerWidth < minWidth + 64);
|
|
233
|
+
checkNarrow();
|
|
234
|
+
window.addEventListener("resize", checkNarrow);
|
|
235
|
+
return () => window.removeEventListener("resize", checkNarrow);
|
|
236
|
+
}, [minWidth]);
|
|
237
|
+
React.useEffect(() => {
|
|
238
|
+
if (!isOpen) return;
|
|
239
|
+
const trigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
240
|
+
return () => {
|
|
241
|
+
if (trigger && trigger.isConnected) trigger.focus();
|
|
242
|
+
};
|
|
243
|
+
}, [isOpen]);
|
|
244
|
+
React.useEffect(() => {
|
|
245
|
+
if (isOpen) {
|
|
246
|
+
setActiveDateField("arrival");
|
|
247
|
+
if (checkIn) setCalendarMonth(checkIn);
|
|
248
|
+
setFocusedDate(startOfDay(checkIn || /* @__PURE__ */ new Date()));
|
|
249
|
+
pendingFocusRef.current = true;
|
|
250
|
+
}
|
|
251
|
+
}, [isOpen]);
|
|
252
|
+
React.useEffect(() => {
|
|
253
|
+
if (isOpen && isMobile) {
|
|
254
|
+
document.body.style.overflow = "hidden";
|
|
255
|
+
return () => {
|
|
256
|
+
document.body.style.overflow = "";
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}, [isOpen, isMobile]);
|
|
260
|
+
React.useEffect(() => {
|
|
261
|
+
if (isMobile || !isOpen) return;
|
|
262
|
+
const handleClickOutside = (event) => {
|
|
263
|
+
if (internalRef.current && !internalRef.current.contains(event.target)) {
|
|
264
|
+
onClose();
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
268
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
269
|
+
}, [isOpen, onClose, isMobile]);
|
|
270
|
+
React.useEffect(() => {
|
|
271
|
+
if (!isOpen) return;
|
|
272
|
+
const handleKeyDown = (event) => {
|
|
273
|
+
const panel = panelRef.current;
|
|
274
|
+
if (!panel) return;
|
|
275
|
+
if (event.key === "Escape") {
|
|
276
|
+
event.preventDefault();
|
|
277
|
+
onClose();
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (event.key !== "Tab") return;
|
|
281
|
+
const focusable = getFocusableElements(panel);
|
|
282
|
+
const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
283
|
+
if (focusable.length === 0) {
|
|
284
|
+
event.preventDefault();
|
|
285
|
+
panel.focus();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const first = focusable[0];
|
|
289
|
+
const last = focusable[focusable.length - 1];
|
|
290
|
+
if (!active || !panel.contains(active)) {
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
(event.shiftKey ? last : first).focus();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (event.shiftKey && active === first) {
|
|
296
|
+
event.preventDefault();
|
|
297
|
+
last.focus();
|
|
298
|
+
} else if (!event.shiftKey && active === last) {
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
first.focus();
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
304
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
305
|
+
}, [isOpen, onClose]);
|
|
306
|
+
const isDateDisabled = (date) => {
|
|
307
|
+
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
308
|
+
return isBefore(date, today);
|
|
309
|
+
};
|
|
310
|
+
const singleMonth = isMobile || isNarrow;
|
|
311
|
+
const visibleMonths = singleMonth ? [calendarMonth] : [calendarMonth, addMonths(calendarMonth, 1)];
|
|
312
|
+
const isDateVisible = (date) => visibleMonths.some((month) => isSameMonth(date, month));
|
|
313
|
+
const firstSelectableVisibleDay = () => {
|
|
314
|
+
for (const month of visibleMonths) {
|
|
315
|
+
for (const day of getCalendarDays(month)) {
|
|
316
|
+
if (isSameMonth(day, month) && !isDateDisabled(day)) return day;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return firstDayOfMonth(calendarMonth);
|
|
320
|
+
};
|
|
321
|
+
const activeFocusDate = isDateVisible(focusedDate) && !isDateDisabled(focusedDate) ? focusedDate : firstSelectableVisibleDay();
|
|
322
|
+
React.useEffect(() => {
|
|
323
|
+
if (!isOpen || !pendingFocusRef.current) return;
|
|
324
|
+
const panel = panelRef.current;
|
|
325
|
+
if (!panel) return;
|
|
326
|
+
const target = panel.querySelector('[role="gridcell"][tabindex="0"]');
|
|
327
|
+
if (target) {
|
|
328
|
+
target.focus();
|
|
329
|
+
pendingFocusRef.current = false;
|
|
330
|
+
}
|
|
331
|
+
}, [isOpen, focusedDate, calendarMonth, isMobile, isNarrow]);
|
|
332
|
+
const moveFocusTo = (target) => {
|
|
333
|
+
const next = startOfDay(target);
|
|
334
|
+
if (isDateDisabled(next)) return;
|
|
335
|
+
setFocusedDate(next);
|
|
336
|
+
pendingFocusRef.current = true;
|
|
337
|
+
if (!isDateVisible(next)) setCalendarMonth(firstDayOfMonth(next));
|
|
338
|
+
};
|
|
339
|
+
const handleDayKeyDown = (event, day) => {
|
|
340
|
+
let target = null;
|
|
341
|
+
switch (event.key) {
|
|
342
|
+
case "ArrowLeft":
|
|
343
|
+
target = addDays(day, -1);
|
|
344
|
+
break;
|
|
345
|
+
case "ArrowRight":
|
|
346
|
+
target = addDays(day, 1);
|
|
347
|
+
break;
|
|
348
|
+
case "ArrowUp":
|
|
349
|
+
target = addDays(day, -7);
|
|
350
|
+
break;
|
|
351
|
+
case "ArrowDown":
|
|
352
|
+
target = addDays(day, 7);
|
|
353
|
+
break;
|
|
354
|
+
case "Home":
|
|
355
|
+
target = startOfWeek(day);
|
|
356
|
+
break;
|
|
357
|
+
case "End":
|
|
358
|
+
target = addDays(startOfWeek(day), 6);
|
|
359
|
+
break;
|
|
360
|
+
case "PageUp":
|
|
361
|
+
target = addMonths(day, -1);
|
|
362
|
+
break;
|
|
363
|
+
case "PageDown":
|
|
364
|
+
target = addMonths(day, 1);
|
|
365
|
+
break;
|
|
366
|
+
default:
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
event.preventDefault();
|
|
370
|
+
moveFocusTo(target);
|
|
371
|
+
};
|
|
372
|
+
const handleDateClick = (date) => {
|
|
373
|
+
setFocusedDate(startOfDay(date));
|
|
374
|
+
if (activeDateField === "arrival") {
|
|
375
|
+
onCheckInChange(date);
|
|
376
|
+
if (checkOut && isBefore(checkOut, date)) onCheckOutChange(null);
|
|
377
|
+
setActiveDateField("departure");
|
|
378
|
+
} else {
|
|
379
|
+
if (checkIn && isBefore(date, checkIn)) {
|
|
380
|
+
onCheckInChange(date);
|
|
381
|
+
onCheckOutChange(null);
|
|
382
|
+
setActiveDateField("departure");
|
|
383
|
+
} else {
|
|
384
|
+
onCheckOutChange(date);
|
|
385
|
+
if (isMobile) onClose();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
const clearDates = () => {
|
|
390
|
+
onCheckInChange(null);
|
|
391
|
+
onCheckOutChange(null);
|
|
392
|
+
setActiveDateField("arrival");
|
|
393
|
+
};
|
|
394
|
+
const isDateInRange = (date) => {
|
|
395
|
+
if (!checkIn || !checkOut) return false;
|
|
396
|
+
return isAfter(date, checkIn) && isBefore(date, checkOut);
|
|
397
|
+
};
|
|
398
|
+
const renderMonth = (monthDate, compact = false) => {
|
|
399
|
+
const weeks = toWeeks(getCalendarDays(monthDate));
|
|
400
|
+
const monthLabel = formatMonthYear(monthDate);
|
|
401
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
402
|
+
!compact && /* @__PURE__ */ jsx("h3", { className: "text-center font-semibold text-2xl text-primary mb-6 capitalize", children: monthLabel }),
|
|
403
|
+
/* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": monthLabel, children: [
|
|
404
|
+
/* @__PURE__ */ jsx(
|
|
405
|
+
"div",
|
|
406
|
+
{
|
|
407
|
+
role: "rowgroup",
|
|
408
|
+
className: cn("grid grid-cols-7", compact ? "mb-2" : "mb-3"),
|
|
409
|
+
children: /* @__PURE__ */ jsx("div", { role: "row", className: "contents", children: WEEK_DAYS.map((day, dayIndex) => /* @__PURE__ */ jsx(
|
|
410
|
+
"div",
|
|
411
|
+
{
|
|
412
|
+
role: "columnheader",
|
|
413
|
+
"aria-label": WEEK_DAY_NAMES[dayIndex],
|
|
414
|
+
className: cn(
|
|
415
|
+
"text-center font-medium text-gray-500 py-2",
|
|
416
|
+
compact ? "text-xs" : "text-base"
|
|
417
|
+
),
|
|
418
|
+
children: day
|
|
419
|
+
},
|
|
420
|
+
day
|
|
421
|
+
)) })
|
|
422
|
+
}
|
|
423
|
+
),
|
|
424
|
+
/* @__PURE__ */ jsx("div", { role: "rowgroup", className: "grid grid-cols-7", children: weeks.map((week) => /* @__PURE__ */ jsx("div", { role: "row", className: "contents", children: week.map((day) => {
|
|
425
|
+
const isCurrentMonth = isSameMonth(day, monthDate);
|
|
426
|
+
const isSelected = Boolean(
|
|
427
|
+
checkIn && isSameDay(day, checkIn) || checkOut && isSameDay(day, checkOut)
|
|
428
|
+
);
|
|
429
|
+
const isStart = checkIn && isSameDay(day, checkIn);
|
|
430
|
+
const isEnd = checkOut && isSameDay(day, checkOut);
|
|
431
|
+
const inRange = isDateInRange(day) && isCurrentMonth;
|
|
432
|
+
const disabled = isDateDisabled(day) || !isCurrentMonth;
|
|
433
|
+
return /* @__PURE__ */ jsx(
|
|
434
|
+
"button",
|
|
435
|
+
{
|
|
436
|
+
type: "button",
|
|
437
|
+
role: "gridcell",
|
|
438
|
+
"aria-label": formatFullDate(day),
|
|
439
|
+
"aria-selected": isSelected,
|
|
440
|
+
tabIndex: isCurrentMonth && isSameDay(day, activeFocusDate) ? 0 : -1,
|
|
441
|
+
onClick: () => !disabled && handleDateClick(day),
|
|
442
|
+
onKeyDown: (event) => handleDayKeyDown(event, day),
|
|
443
|
+
disabled,
|
|
444
|
+
className: cn(
|
|
445
|
+
"relative font-medium transition-all",
|
|
446
|
+
compact ? "h-10 text-sm" : "h-12 text-base",
|
|
447
|
+
!isCurrentMonth && "text-gray-300",
|
|
448
|
+
disabled && isCurrentMonth && "text-gray-300 cursor-not-allowed",
|
|
449
|
+
inRange && "bg-orange-100",
|
|
450
|
+
isSelected && "bg-secondary text-white z-10",
|
|
451
|
+
isStart && checkOut && "rounded-l-lg",
|
|
452
|
+
isEnd && checkIn && "rounded-r-lg",
|
|
453
|
+
isSelected && (!checkOut || !checkIn) && "rounded-lg",
|
|
454
|
+
isSelected && checkIn && checkOut && isSameDay(checkIn, checkOut) && "rounded-lg",
|
|
455
|
+
!disabled && !isSelected && !inRange && isCurrentMonth && "text-primary hover:bg-gray-100 hover:rounded-lg"
|
|
456
|
+
),
|
|
457
|
+
children: isCurrentMonth && day.getDate()
|
|
458
|
+
},
|
|
459
|
+
dayKey(day)
|
|
460
|
+
);
|
|
461
|
+
}) }, dayKey(week[0]))) })
|
|
462
|
+
] })
|
|
463
|
+
] });
|
|
464
|
+
};
|
|
465
|
+
if (!isOpen) return null;
|
|
466
|
+
const liveRegion = /* @__PURE__ */ jsx("p", { "aria-live": "polite", className: "sr-only", children: describeRange(checkIn, checkOut) });
|
|
467
|
+
if (isMobile) {
|
|
468
|
+
return /* @__PURE__ */ jsxs(
|
|
469
|
+
"div",
|
|
470
|
+
{
|
|
471
|
+
ref: setRefs,
|
|
472
|
+
role: "dialog",
|
|
473
|
+
"aria-modal": "true",
|
|
474
|
+
"aria-labelledby": titleId,
|
|
475
|
+
className: "fixed inset-0 z-[60] bg-white flex flex-col",
|
|
476
|
+
children: [
|
|
477
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-100", children: [
|
|
478
|
+
/* @__PURE__ */ jsx("h2", { id: titleId, className: "font-heading font-bold text-lg text-primary", children: DIALOG_TITLE }),
|
|
479
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, "aria-label": "Fermer", className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-600", "aria-hidden": "true", focusable: "false", children: [
|
|
480
|
+
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
481
|
+
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
482
|
+
] }) })
|
|
483
|
+
] }),
|
|
484
|
+
liveRegion,
|
|
485
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 px-4 py-3 border-b border-gray-100", children: [
|
|
486
|
+
/* @__PURE__ */ jsxs(
|
|
487
|
+
"button",
|
|
488
|
+
{
|
|
489
|
+
type: "button",
|
|
490
|
+
onClick: () => setActiveDateField("arrival"),
|
|
491
|
+
"aria-pressed": activeDateField === "arrival",
|
|
492
|
+
className: cn(
|
|
493
|
+
"flex-1 px-3 py-2 rounded-xl text-left transition-all",
|
|
494
|
+
activeDateField === "arrival" ? "ring-2 ring-secondary bg-secondary/5" : "bg-gray-100"
|
|
495
|
+
),
|
|
496
|
+
children: [
|
|
497
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs font-medium text-gray-500", children: "Arriv\xE9e" }),
|
|
498
|
+
/* @__PURE__ */ jsx("span", { className: "block text-sm font-semibold text-primary", children: checkIn ? `${checkIn.getDate()} ${SHORT_MONTHS[checkIn.getMonth()]} ${checkIn.getFullYear()}` : "S\xE9lectionner" })
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
),
|
|
502
|
+
/* @__PURE__ */ jsxs(
|
|
503
|
+
"button",
|
|
504
|
+
{
|
|
505
|
+
type: "button",
|
|
506
|
+
onClick: () => setActiveDateField("departure"),
|
|
507
|
+
"aria-pressed": activeDateField === "departure",
|
|
508
|
+
className: cn(
|
|
509
|
+
"flex-1 px-3 py-2 rounded-xl text-left transition-all",
|
|
510
|
+
activeDateField === "departure" ? "ring-2 ring-secondary bg-secondary/5" : "bg-gray-100"
|
|
511
|
+
),
|
|
512
|
+
children: [
|
|
513
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs font-medium text-gray-500", children: "D\xE9part" }),
|
|
514
|
+
/* @__PURE__ */ jsx("span", { className: "block text-sm font-semibold text-primary", children: checkOut ? `${checkOut.getDate()} ${SHORT_MONTHS[checkOut.getMonth()]} ${checkOut.getFullYear()}` : "S\xE9lectionner" })
|
|
515
|
+
]
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
] }),
|
|
519
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: [
|
|
520
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
521
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCalendarMonth(addMonths(calendarMonth, -1)), "aria-label": "Mois pr\xE9c\xE9dent", className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-600", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
|
|
522
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg text-primary capitalize", children: formatMonthYear(calendarMonth) }),
|
|
523
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCalendarMonth(addMonths(calendarMonth, 1)), "aria-label": "Mois suivant", className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-600", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) })
|
|
524
|
+
] }),
|
|
525
|
+
renderMonth(calendarMonth, true)
|
|
526
|
+
] }),
|
|
527
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-gray-100 px-4 py-3 flex items-center justify-between bg-white", children: [
|
|
528
|
+
checkIn || checkOut ? /* @__PURE__ */ jsx("button", { type: "button", onClick: clearDates, className: "text-sm text-primary underline hover:text-secondary transition-colors", children: "Effacer" }) : /* @__PURE__ */ jsx("div", {}),
|
|
529
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, className: "px-6 py-2.5 bg-secondary text-white rounded-xl font-semibold text-sm hover:bg-secondary-dark transition-colors", children: "Valider" })
|
|
530
|
+
] })
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const positionClasses = {
|
|
536
|
+
left: "left-0",
|
|
537
|
+
center: "left-1/2 -translate-x-1/2",
|
|
538
|
+
right: "right-0"
|
|
539
|
+
};
|
|
540
|
+
return /* @__PURE__ */ jsxs(
|
|
541
|
+
"div",
|
|
542
|
+
{
|
|
543
|
+
ref: setRefs,
|
|
544
|
+
role: "dialog",
|
|
545
|
+
"aria-modal": "true",
|
|
546
|
+
"aria-labelledby": titleId,
|
|
547
|
+
className: cn(
|
|
548
|
+
"absolute top-full mt-2 bg-white rounded-3xl shadow-2xl border z-[60] p-8 pb-10",
|
|
549
|
+
positionClasses[position]
|
|
550
|
+
),
|
|
551
|
+
style: singleMonth ? { width: "100%", maxWidth: "400px" } : { minWidth: `${minWidth}px` },
|
|
552
|
+
children: [
|
|
553
|
+
/* @__PURE__ */ jsx("h2", { id: titleId, className: "sr-only", children: DIALOG_TITLE }),
|
|
554
|
+
liveRegion,
|
|
555
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
556
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCalendarMonth(addMonths(calendarMonth, -1)), "aria-label": "Mois pr\xE9c\xE9dent", className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-600", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) }) }),
|
|
557
|
+
singleMonth && /* @__PURE__ */ jsx("h3", { className: "font-semibold text-lg text-primary capitalize", children: formatMonthYear(calendarMonth) }),
|
|
558
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCalendarMonth(addMonths(calendarMonth, 1)), "aria-label": "Mois suivant", className: "p-2 hover:bg-gray-100 rounded-full transition-colors", children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-600", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) }) })
|
|
559
|
+
] }),
|
|
560
|
+
/* @__PURE__ */ jsxs("div", { className: singleMonth ? "" : "flex gap-12", children: [
|
|
561
|
+
renderMonth(calendarMonth, singleMonth),
|
|
562
|
+
!singleMonth && renderMonth(addMonths(calendarMonth, 1))
|
|
563
|
+
] }),
|
|
564
|
+
(checkIn || checkOut) && /* @__PURE__ */ jsx("div", { className: "text-right mt-4 pt-4 border-t border-gray-100", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: clearDates, className: "text-sm text-primary underline hover:text-secondary transition-colors", children: "Effacer les dates" }) })
|
|
565
|
+
]
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
DateRangePicker.displayName = "DateRangePicker";
|
|
571
|
+
|
|
572
|
+
export { DatePickerInput, DateRangePicker };
|