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