@dododog/ui 0.2.1 → 0.4.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 (47) hide show
  1. package/dist/{chunk-OK3E73OK.js → chunk-2XH74PQR.js} +14 -0
  2. package/dist/chunk-36JNXXGH.js +360 -0
  3. package/dist/chunk-5A3MVRZJ.js +109 -0
  4. package/dist/{chunk-KVVXKSMQ.mjs → chunk-7JYJ7ICL.mjs} +14 -0
  5. package/dist/{chunk-NWLJ7VQF.js → chunk-AIA3NHCK.js} +1 -2
  6. package/dist/{chunk-YSJAEDMG.js → chunk-E24VNM6S.js} +8 -5
  7. package/dist/{chunk-RQGC6RPI.mjs → chunk-FL3GD5FJ.mjs} +1 -2
  8. package/dist/{chunk-TNGW36OC.mjs → chunk-LRNSVRUN.mjs} +2 -0
  9. package/dist/{chunk-76DZXGKJ.mjs → chunk-N6THLJIG.mjs} +8 -5
  10. package/dist/chunk-PBDPZTHK.mjs +53 -0
  11. package/dist/chunk-PU4CWOWU.mjs +87 -0
  12. package/dist/chunk-S732LTPT.js +75 -0
  13. package/dist/{chunk-3BPC4LNH.js → chunk-T4AT3YCT.js} +2 -0
  14. package/dist/chunk-XH2MVC4W.mjs +337 -0
  15. package/dist/components/AnchorTabs/index.d.mts +41 -0
  16. package/dist/components/AnchorTabs/index.d.ts +41 -0
  17. package/dist/components/AnchorTabs/index.js +11 -0
  18. package/dist/components/AnchorTabs/index.mjs +2 -0
  19. package/dist/components/Counter/index.js +2 -2
  20. package/dist/components/Counter/index.mjs +1 -1
  21. package/dist/components/DateRangePicker/index.js +3 -3
  22. package/dist/components/DateRangePicker/index.mjs +1 -1
  23. package/dist/components/DetailList/index.d.mts +8 -2
  24. package/dist/components/DetailList/index.d.ts +8 -2
  25. package/dist/components/DetailList/index.js +2 -2
  26. package/dist/components/DetailList/index.mjs +1 -1
  27. package/dist/components/FloatingDock/index.d.mts +44 -0
  28. package/dist/components/FloatingDock/index.d.ts +44 -0
  29. package/dist/components/FloatingDock/index.js +11 -0
  30. package/dist/components/FloatingDock/index.mjs +2 -0
  31. package/dist/components/RoomTypeCard/index.d.mts +92 -0
  32. package/dist/components/RoomTypeCard/index.d.ts +92 -0
  33. package/dist/components/RoomTypeCard/index.js +16 -0
  34. package/dist/components/RoomTypeCard/index.mjs +3 -0
  35. package/dist/index.d.mts +125 -4
  36. package/dist/index.d.ts +125 -4
  37. package/dist/index.js +542 -102
  38. package/dist/index.mjs +426 -22
  39. package/dist/tailwind-preset.d.mts +11 -0
  40. package/dist/tailwind-preset.d.ts +11 -0
  41. package/dist/tailwind-preset.js +7 -7
  42. package/dist/tailwind-preset.mjs +1 -1
  43. package/dist/tokens/index.d.mts +11 -0
  44. package/dist/tokens/index.d.ts +11 -0
  45. package/dist/tokens/index.js +8 -8
  46. package/dist/tokens/index.mjs +1 -1
  47. package/package.json +16 -1
@@ -0,0 +1,53 @@
1
+ import { cn } from './chunk-IMKLN273.mjs';
2
+ import * as React from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var FloatingDock = React.forwardRef(
6
+ ({ items, className, ariaLabel = "Navigation rapide", onNavigate }, ref) => {
7
+ return /* @__PURE__ */ jsx(
8
+ "nav",
9
+ {
10
+ ref,
11
+ "aria-label": ariaLabel,
12
+ className: cn(
13
+ "inline-flex items-center gap-1 rounded-full border border-black/5 bg-white/95 px-2 py-2 shadow-[0_8px_30px_rgba(0,0,0,0.12)] backdrop-blur",
14
+ className
15
+ ),
16
+ children: items.map((item) => /* @__PURE__ */ jsxs(
17
+ "a",
18
+ {
19
+ href: item.href,
20
+ "aria-label": item.label,
21
+ "aria-current": item.isActive ? "page" : void 0,
22
+ title: item.label,
23
+ onClick: onNavigate ? (event) => {
24
+ event.preventDefault();
25
+ onNavigate(item);
26
+ } : void 0,
27
+ className: cn(
28
+ "flex min-h-[44px] items-center justify-center rounded-full outline-none transition-colors focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-2",
29
+ item.isActive ? "bg-secondary px-4 text-white" : "w-11 text-primary/70 hover:bg-secondary/5 hover:text-secondary"
30
+ ),
31
+ children: [
32
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", "aria-hidden": "true", children: item.icon }),
33
+ /* @__PURE__ */ jsx(
34
+ "span",
35
+ {
36
+ className: cn(
37
+ "overflow-hidden whitespace-nowrap text-sm font-medium transition-all duration-300",
38
+ item.isActive ? "ml-2 max-w-[8rem] opacity-100" : "ml-0 max-w-0 opacity-0"
39
+ ),
40
+ children: item.label
41
+ }
42
+ )
43
+ ]
44
+ },
45
+ item.key
46
+ ))
47
+ }
48
+ );
49
+ }
50
+ );
51
+ FloatingDock.displayName = "FloatingDock";
52
+
53
+ export { FloatingDock };
@@ -0,0 +1,87 @@
1
+ import { cn } from './chunk-IMKLN273.mjs';
2
+ import * as React from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var AnchorTabs = React.forwardRef(
6
+ ({ items, className, ariaLabel = "Navigation dans la fiche", offsetTop = 96, onActiveChange }, ref) => {
7
+ const [activeKey, setActiveKey] = React.useState(items[0]?.key ?? "");
8
+ React.useEffect(() => {
9
+ if (items.length === 0) return;
10
+ const ids = items.map((i) => i.key);
11
+ let raf = 0;
12
+ const update = () => {
13
+ raf = 0;
14
+ let current = ids[0];
15
+ let bestTop = -Infinity;
16
+ for (const id of ids) {
17
+ const el = document.getElementById(id);
18
+ if (!el) continue;
19
+ const top = el.getBoundingClientRect().top - offsetTop;
20
+ if (top <= 1 && top > bestTop) {
21
+ bestTop = top;
22
+ current = id;
23
+ }
24
+ }
25
+ setActiveKey((prev) => prev === current ? prev : current);
26
+ };
27
+ const onScroll = () => {
28
+ if (raf) return;
29
+ raf = window.requestAnimationFrame(update);
30
+ };
31
+ update();
32
+ window.addEventListener("scroll", onScroll, { passive: true });
33
+ window.addEventListener("resize", onScroll);
34
+ return () => {
35
+ window.removeEventListener("scroll", onScroll);
36
+ window.removeEventListener("resize", onScroll);
37
+ if (raf) window.cancelAnimationFrame(raf);
38
+ };
39
+ }, [items, offsetTop]);
40
+ React.useEffect(() => {
41
+ if (activeKey) onActiveChange?.(activeKey);
42
+ }, [activeKey, onActiveChange]);
43
+ const handleClick = (event, key) => {
44
+ const el = typeof document !== "undefined" ? document.getElementById(key) : null;
45
+ if (!el) return;
46
+ event.preventDefault();
47
+ const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
48
+ const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
49
+ window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
50
+ setActiveKey(key);
51
+ };
52
+ return /* @__PURE__ */ jsx(
53
+ "nav",
54
+ {
55
+ ref,
56
+ "aria-label": ariaLabel,
57
+ className: cn(
58
+ "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
59
+ className
60
+ ),
61
+ children: items.map((item) => {
62
+ const isActive = item.key === activeKey;
63
+ return /* @__PURE__ */ jsx(
64
+ "a",
65
+ {
66
+ href: `#${item.key}`,
67
+ "aria-current": isActive ? "location" : void 0,
68
+ onClick: (event) => handleClick(event, item.key),
69
+ className: cn(
70
+ // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
71
+ // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
72
+ // via opacité (transform/opacity, aucun layout shift).
73
+ "flex min-h-[44px] items-center whitespace-nowrap border-b-2 px-4 text-sm outline-none transition-colors duration-200 -mb-px active:opacity-70 focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-2",
74
+ isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
75
+ ),
76
+ children: item.label
77
+ },
78
+ item.key
79
+ );
80
+ })
81
+ }
82
+ );
83
+ }
84
+ );
85
+ AnchorTabs.displayName = "AnchorTabs";
86
+
87
+ export { AnchorTabs };
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ var chunkADIDI7AJ_js = require('./chunk-ADIDI7AJ.js');
4
+ var React = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ function _interopNamespace(e) {
8
+ if (e && e.__esModule) return e;
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
26
+
27
+ var FloatingDock = React__namespace.forwardRef(
28
+ ({ items, className, ariaLabel = "Navigation rapide", onNavigate }, ref) => {
29
+ return /* @__PURE__ */ jsxRuntime.jsx(
30
+ "nav",
31
+ {
32
+ ref,
33
+ "aria-label": ariaLabel,
34
+ className: chunkADIDI7AJ_js.cn(
35
+ "inline-flex items-center gap-1 rounded-full border border-black/5 bg-white/95 px-2 py-2 shadow-[0_8px_30px_rgba(0,0,0,0.12)] backdrop-blur",
36
+ className
37
+ ),
38
+ children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
39
+ "a",
40
+ {
41
+ href: item.href,
42
+ "aria-label": item.label,
43
+ "aria-current": item.isActive ? "page" : void 0,
44
+ title: item.label,
45
+ onClick: onNavigate ? (event) => {
46
+ event.preventDefault();
47
+ onNavigate(item);
48
+ } : void 0,
49
+ className: chunkADIDI7AJ_js.cn(
50
+ "flex min-h-[44px] items-center justify-center rounded-full outline-none transition-colors focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-2",
51
+ item.isActive ? "bg-secondary px-4 text-white" : "w-11 text-primary/70 hover:bg-secondary/5 hover:text-secondary"
52
+ ),
53
+ children: [
54
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", "aria-hidden": "true", children: item.icon }),
55
+ /* @__PURE__ */ jsxRuntime.jsx(
56
+ "span",
57
+ {
58
+ className: chunkADIDI7AJ_js.cn(
59
+ "overflow-hidden whitespace-nowrap text-sm font-medium transition-all duration-300",
60
+ item.isActive ? "ml-2 max-w-[8rem] opacity-100" : "ml-0 max-w-0 opacity-0"
61
+ ),
62
+ children: item.label
63
+ }
64
+ )
65
+ ]
66
+ },
67
+ item.key
68
+ ))
69
+ }
70
+ );
71
+ }
72
+ );
73
+ FloatingDock.displayName = "FloatingDock";
74
+
75
+ exports.FloatingDock = FloatingDock;
@@ -39,6 +39,7 @@ var Counter = React__namespace.forwardRef(
39
39
  {
40
40
  type: "button",
41
41
  disabled: !canDecrement,
42
+ onPointerDown: (e) => e.stopPropagation(),
42
43
  onClick: () => canDecrement && onChange?.(value - 1),
43
44
  className: chunkADIDI7AJ_js.cn(
44
45
  "w-8 h-8 rounded-full border flex items-center justify-center transition-colors",
@@ -54,6 +55,7 @@ var Counter = React__namespace.forwardRef(
54
55
  {
55
56
  type: "button",
56
57
  disabled: !canIncrement,
58
+ onPointerDown: (e) => e.stopPropagation(),
57
59
  onClick: () => canIncrement && onChange?.(value + 1),
58
60
  className: chunkADIDI7AJ_js.cn(
59
61
  "w-8 h-8 rounded-full border flex items-center justify-center transition-colors",
@@ -0,0 +1,337 @@
1
+ import { Skeleton, SkeletonText } from './chunk-PAHSQMXV.mjs';
2
+ import { cn } from './chunk-IMKLN273.mjs';
3
+ import * as React from 'react';
4
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
+
6
+ function BedIcon({ className }) {
7
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
8
+ /* @__PURE__ */ jsx("path", { d: "M2 4v16" }),
9
+ /* @__PURE__ */ jsx("path", { d: "M2 8h18a2 2 0 0 1 2 2v10" }),
10
+ /* @__PURE__ */ jsx("path", { d: "M2 17h20" }),
11
+ /* @__PURE__ */ jsx("path", { d: "M6 8v9" })
12
+ ] });
13
+ }
14
+ function RulerIcon({ className }) {
15
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
16
+ /* @__PURE__ */ jsx("path", { d: "M21.3 8.7 8.7 21.3a1 1 0 0 1-1.4 0l-4.6-4.6a1 1 0 0 1 0-1.4L15.3 2.7a1 1 0 0 1 1.4 0l4.6 4.6a1 1 0 0 1 0 1.4Z" }),
17
+ /* @__PURE__ */ jsx("path", { d: "m7.5 10.5 2 2" }),
18
+ /* @__PURE__ */ jsx("path", { d: "m10.5 7.5 2 2" }),
19
+ /* @__PURE__ */ jsx("path", { d: "m13.5 4.5 2 2" }),
20
+ /* @__PURE__ */ jsx("path", { d: "m4.5 13.5 2 2" })
21
+ ] });
22
+ }
23
+ function UsersIcon({ className }) {
24
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
25
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
26
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" }),
27
+ /* @__PURE__ */ jsx("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }),
28
+ /* @__PURE__ */ jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })
29
+ ] });
30
+ }
31
+ function CheckIcon({ className }) {
32
+ return /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
33
+ }
34
+ function NoRefundIcon({ className }) {
35
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
36
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
37
+ /* @__PURE__ */ jsx("path", { d: "m5 5 14 14" })
38
+ ] });
39
+ }
40
+ function DogIcon({ className }) {
41
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
42
+ /* @__PURE__ */ jsx("path", { d: "M10 5.172C10 3.782 8.423 2.679 6.5 3c-2.823.47-4.113 6.006-4 7 .08.703 1.725 1.722 3.656 1 1.261-.472 1.96-1.45 2.344-2.5" }),
43
+ /* @__PURE__ */ jsx("path", { d: "M14.267 5.172c0-1.39 1.577-2.493 3.5-2.172 2.823.47 4.113 6.006 4 7-.08.703-1.725 1.722-3.656 1-1.261-.472-1.855-1.45-2.239-2.5" }),
44
+ /* @__PURE__ */ jsx("path", { d: "M8 14v.5" }),
45
+ /* @__PURE__ */ jsx("path", { d: "M16 14v.5" }),
46
+ /* @__PURE__ */ jsx("path", { d: "M11.25 16.25h1.5L12 17l-.75-.75Z" }),
47
+ /* @__PURE__ */ jsx("path", { d: "M4.42 11.247A13.152 13.152 0 0 0 4 14.556C4 18.728 7.582 21 12 21s8-2.272 8-6.444c0-1.061-.162-2.2-.493-3.309m-9.243-6.082A8.801 8.801 0 0 1 12 5c.78 0 1.5.108 2.161.306" })
48
+ ] });
49
+ }
50
+ function ChevronDownIcon({ className }) {
51
+ return /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
52
+ }
53
+ function GalleryIcon({ className }) {
54
+ return /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": "true", children: [
55
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
56
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
57
+ /* @__PURE__ */ jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
58
+ ] });
59
+ }
60
+ function formatPrice(price) {
61
+ return new Intl.NumberFormat("fr-FR", {
62
+ style: "currency",
63
+ currency: "EUR",
64
+ minimumFractionDigits: 0,
65
+ maximumFractionDigits: 0
66
+ }).format(price);
67
+ }
68
+ function CancellationChip({ cancellation }) {
69
+ if (cancellation.kind === "free") {
70
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs font-medium text-emerald-700", children: [
71
+ /* @__PURE__ */ jsx(CheckIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
72
+ cancellation.label
73
+ ] });
74
+ }
75
+ if (cancellation.kind === "partial") {
76
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs font-medium text-amber-700", children: [
77
+ /* @__PURE__ */ jsx(CheckIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
78
+ cancellation.label
79
+ ] });
80
+ }
81
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs text-text-muted", children: [
82
+ /* @__PURE__ */ jsx(NoRefundIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
83
+ cancellation.label
84
+ ] });
85
+ }
86
+ function RateRow({
87
+ rate,
88
+ selected,
89
+ onSelect,
90
+ selectLabel,
91
+ withTopBorder
92
+ }) {
93
+ return /* @__PURE__ */ jsxs(
94
+ "div",
95
+ {
96
+ className: cn(
97
+ "flex items-end justify-between gap-3 py-3",
98
+ withTopBorder && "border-t border-dashed border-sand-dark"
99
+ ),
100
+ children: [
101
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
102
+ rate.mealLabel && /* @__PURE__ */ jsx("div", { className: "text-[13px] font-medium text-primary", children: rate.mealLabel }),
103
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx(CancellationChip, { cancellation: rate.cancellation }) }),
104
+ rate.dogFeeLabel && /* @__PURE__ */ jsxs("div", { className: "mt-1 inline-flex items-center gap-1.5 text-xs font-medium text-secondary", children: [
105
+ /* @__PURE__ */ jsx(DogIcon, { className: "w-3.5 h-3.5 flex-shrink-0" }),
106
+ rate.dogFeeLabel
107
+ ] })
108
+ ] }),
109
+ /* @__PURE__ */ jsxs("div", { className: "text-right flex-shrink-0", children: [
110
+ rate.originalTotal && rate.originalTotal > rate.totalPrice && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-text-muted line-through leading-none", children: formatPrice(rate.originalTotal) }),
111
+ /* @__PURE__ */ jsx("div", { className: "font-heading text-lg font-bold text-primary leading-tight", children: formatPrice(rate.totalPrice) }),
112
+ rate.totalLabel && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-text-muted", children: rate.totalLabel }),
113
+ /* @__PURE__ */ jsx(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ onClick: () => onSelect?.(rate.id),
118
+ "aria-pressed": selected,
119
+ className: cn(
120
+ "mt-1.5 text-[13px] font-medium px-4 py-1.5 rounded-full transition-colors",
121
+ selected ? "bg-secondary text-white" : "border border-secondary text-secondary hover:bg-secondary hover:text-white"
122
+ ),
123
+ children: selected ? "S\xE9lectionn\xE9" : selectLabel
124
+ }
125
+ )
126
+ ] })
127
+ ]
128
+ }
129
+ );
130
+ }
131
+ var RoomTypeCard = React.forwardRef(
132
+ ({
133
+ room,
134
+ rates,
135
+ selectedRateId,
136
+ onSelectRate,
137
+ initialVisibleRates = 2,
138
+ selectLabel = "Choisir",
139
+ variant = "default",
140
+ availability = "available",
141
+ onSeeOtherDates,
142
+ unavailableLabel = "Aucune disponibilit\xE9 sur notre site",
143
+ renderImage,
144
+ className,
145
+ ...props
146
+ }, ref) => {
147
+ const [expanded, setExpanded] = React.useState(false);
148
+ const visibleRates = expanded ? rates : rates.slice(0, initialVisibleRates);
149
+ const hiddenCount = rates.length - visibleRates.length;
150
+ const isCompact = variant === "compact";
151
+ const isUnavailable = availability === "unavailable";
152
+ const meta = [];
153
+ if (room.beds) {
154
+ meta.push(
155
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
156
+ /* @__PURE__ */ jsx(BedIcon, { className: "w-3.5 h-3.5" }),
157
+ room.beds
158
+ ] }, "beds")
159
+ );
160
+ }
161
+ if (room.sizeSqm) {
162
+ meta.push(
163
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
164
+ /* @__PURE__ */ jsx(RulerIcon, { className: "w-3.5 h-3.5" }),
165
+ room.sizeSqm,
166
+ " m\xB2"
167
+ ] }, "size")
168
+ );
169
+ }
170
+ if (room.maxOccupancy) {
171
+ meta.push(
172
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
173
+ /* @__PURE__ */ jsx(UsersIcon, { className: "w-3.5 h-3.5" }),
174
+ room.maxOccupancy,
175
+ " voyageur",
176
+ room.maxOccupancy > 1 ? "s" : ""
177
+ ] }, "occ")
178
+ );
179
+ }
180
+ return /* @__PURE__ */ jsxs(
181
+ "div",
182
+ {
183
+ ref,
184
+ className: cn(
185
+ "bg-white rounded-xl border border-sand-dark overflow-hidden",
186
+ className
187
+ ),
188
+ ...props,
189
+ children: [
190
+ /* @__PURE__ */ jsx(
191
+ "div",
192
+ {
193
+ className: cn(
194
+ "relative overflow-hidden bg-sand",
195
+ isCompact ? "h-32" : "aspect-[16/9]"
196
+ ),
197
+ children: room.image ? /* @__PURE__ */ jsxs(Fragment, { children: [
198
+ renderImage ? renderImage({
199
+ src: room.image,
200
+ alt: room.establishmentPhotoLabel ?? room.name,
201
+ className: "object-cover w-full h-full"
202
+ }) : /* @__PURE__ */ jsx(
203
+ "img",
204
+ {
205
+ src: room.image,
206
+ alt: room.establishmentPhotoLabel ?? room.name,
207
+ className: "object-cover w-full h-full"
208
+ }
209
+ ),
210
+ room.establishmentPhotoLabel ? /* @__PURE__ */ jsxs("span", { className: "absolute bottom-2 left-2 inline-flex items-center gap-1 bg-black/55 text-white text-[11px] px-2 py-0.5 rounded-md", children: [
211
+ /* @__PURE__ */ jsx(GalleryIcon, { className: "w-3 h-3" }),
212
+ room.establishmentPhotoLabel
213
+ ] }) : room.imageCount != null && room.imageCount > 1 && /* @__PURE__ */ jsxs("span", { className: "absolute bottom-2 left-2 inline-flex items-center gap-1 bg-black/55 text-white text-[11px] px-2 py-0.5 rounded-md", children: [
214
+ /* @__PURE__ */ jsx(GalleryIcon, { className: "w-3 h-3" }),
215
+ room.imageCount,
216
+ " photos"
217
+ ] })
218
+ ] }) : /* @__PURE__ */ jsx(
219
+ "div",
220
+ {
221
+ className: "flex h-full w-full items-center justify-center text-text-muted",
222
+ role: "img",
223
+ "aria-label": "Photo de l'\xE9tablissement indisponible",
224
+ children: /* @__PURE__ */ jsx(GalleryIcon, { className: "h-8 w-8" })
225
+ }
226
+ )
227
+ }
228
+ ),
229
+ /* @__PURE__ */ jsxs("div", { className: cn(isCompact ? "p-3" : "p-4"), children: [
230
+ /* @__PURE__ */ jsx(
231
+ "h3",
232
+ {
233
+ className: cn(
234
+ "font-heading font-bold text-primary",
235
+ isCompact ? "text-base" : "text-lg"
236
+ ),
237
+ children: room.name
238
+ }
239
+ ),
240
+ meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-x-3 gap-y-1 text-[12.5px] text-text-muted", children: meta }),
241
+ room.highlight && /* @__PURE__ */ jsx("div", { className: "mt-1.5 text-[12.5px] font-medium text-positive", children: room.highlight }),
242
+ room.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[13px] leading-snug text-text-secondary", children: room.description }),
243
+ room.amenities && room.amenities.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2.5 flex flex-wrap gap-1.5", children: room.amenities.map((a, i) => /* @__PURE__ */ jsx(
244
+ "span",
245
+ {
246
+ className: "text-[11px] bg-sand-light text-text-muted px-2 py-1 rounded-full",
247
+ children: a
248
+ },
249
+ i
250
+ )) }),
251
+ isUnavailable ? /* @__PURE__ */ jsxs("div", { className: "mt-3.5 border-t border-sand-dark pt-3.5", children: [
252
+ /* @__PURE__ */ jsx("div", { className: "text-[13px] font-semibold leading-snug text-danger", children: unavailableLabel }),
253
+ onSeeOtherDates && /* @__PURE__ */ jsx(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ onClick: onSeeOtherDates,
258
+ className: "mt-2 inline-block text-[12.5px] font-semibold text-secondary-dark",
259
+ children: "Voir d'autres dates \u2192"
260
+ }
261
+ )
262
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "mt-3.5 border-t border-sand-dark pt-1", children: [
263
+ visibleRates.map((rate, i) => /* @__PURE__ */ jsx(
264
+ RateRow,
265
+ {
266
+ rate,
267
+ selected: rate.id === selectedRateId,
268
+ onSelect: onSelectRate,
269
+ selectLabel,
270
+ withTopBorder: i > 0
271
+ },
272
+ rate.id
273
+ )),
274
+ hiddenCount > 0 && /* @__PURE__ */ jsxs(
275
+ "button",
276
+ {
277
+ type: "button",
278
+ onClick: () => setExpanded(true),
279
+ className: "mt-1 inline-flex items-center gap-1.5 text-[12.5px] font-medium text-secondary",
280
+ children: [
281
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "w-3.5 h-3.5" }),
282
+ hiddenCount,
283
+ " autre",
284
+ hiddenCount > 1 ? "s" : "",
285
+ " formule",
286
+ hiddenCount > 1 ? "s" : ""
287
+ ]
288
+ }
289
+ )
290
+ ] })
291
+ ] })
292
+ ]
293
+ }
294
+ );
295
+ }
296
+ );
297
+ RoomTypeCard.displayName = "RoomTypeCard";
298
+ var RoomTypeCardSkeleton = React.forwardRef(({ variant = "default", className, ...props }, ref) => {
299
+ const isCompact = variant === "compact";
300
+ return /* @__PURE__ */ jsxs(
301
+ "div",
302
+ {
303
+ "aria-hidden": "true",
304
+ className: cn(
305
+ "bg-white rounded-xl border border-sand-dark overflow-hidden",
306
+ className
307
+ ),
308
+ ref,
309
+ ...props,
310
+ children: [
311
+ /* @__PURE__ */ jsx(
312
+ Skeleton,
313
+ {
314
+ className: cn("w-full rounded-none", isCompact ? "h-32" : "aspect-[16/9]")
315
+ }
316
+ ),
317
+ /* @__PURE__ */ jsxs("div", { className: cn(isCompact ? "p-3" : "p-4", "space-y-3"), children: [
318
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-2/3" }),
319
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
320
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16" }),
321
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-20" })
322
+ ] }),
323
+ /* @__PURE__ */ jsxs("div", { className: "border-t border-sand-dark pt-3 space-y-2", children: [
324
+ /* @__PURE__ */ jsx(SkeletonText, { lines: 2 }),
325
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3", children: [
326
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-24" }),
327
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-28 rounded-lg" })
328
+ ] })
329
+ ] })
330
+ ] })
331
+ ]
332
+ }
333
+ );
334
+ });
335
+ RoomTypeCardSkeleton.displayName = "RoomTypeCardSkeleton";
336
+
337
+ export { RoomTypeCard, RoomTypeCardSkeleton };
@@ -0,0 +1,41 @@
1
+ import * as React from 'react';
2
+
3
+ interface AnchorTabItem {
4
+ /**
5
+ * Identifiant de la section cible : doit correspondre à l'attribut `id` de
6
+ * l'élément de section dans le DOM (le lien pointe sur `#{key}`).
7
+ */
8
+ key: string;
9
+ /** Libellé affiché de l'onglet. */
10
+ label: string;
11
+ }
12
+ interface AnchorTabsProps {
13
+ /** Onglets d'ancrage, dans l'ordre d'apparition des sections. */
14
+ items: AnchorTabItem[];
15
+ /** Classes supplémentaires appliquées à la barre `<nav>`. */
16
+ className?: string;
17
+ /** Libellé ARIA de la navigation. */
18
+ ariaLabel?: string;
19
+ /**
20
+ * Décalage (px) réservé en haut de page pour le scroll-spy ET le défilement
21
+ * au clic (barre sticky / header). Défaut : 96.
22
+ */
23
+ offsetTop?: number;
24
+ /** Notifié à chaque changement de section active (scroll ou clic). */
25
+ onActiveChange?: (key: string) => void;
26
+ }
27
+ /**
28
+ * Barre d'onglets d'ancrage (desktop) avec scroll-spy.
29
+ *
30
+ * Présente une rangée de liens `<a href="#{key}">` ; l'onglet dont la section
31
+ * est actuellement en haut du viewport (au-delà de `offsetTop`) est souligné.
32
+ * Le composant observe le défilement de la fenêtre (via `requestAnimationFrame`)
33
+ * pour déterminer la section courante et défile en douceur au clic.
34
+ *
35
+ * Les sections cibles doivent porter l'`id` correspondant à chaque `key`. Le
36
+ * positionnement (sticky, largeur, visibilité responsive) reste à la charge de
37
+ * l'appelant via `className`.
38
+ */
39
+ declare const AnchorTabs: React.ForwardRefExoticComponent<AnchorTabsProps & React.RefAttributes<HTMLElement>>;
40
+
41
+ export { type AnchorTabItem, AnchorTabs, type AnchorTabsProps };
@@ -0,0 +1,41 @@
1
+ import * as React from 'react';
2
+
3
+ interface AnchorTabItem {
4
+ /**
5
+ * Identifiant de la section cible : doit correspondre à l'attribut `id` de
6
+ * l'élément de section dans le DOM (le lien pointe sur `#{key}`).
7
+ */
8
+ key: string;
9
+ /** Libellé affiché de l'onglet. */
10
+ label: string;
11
+ }
12
+ interface AnchorTabsProps {
13
+ /** Onglets d'ancrage, dans l'ordre d'apparition des sections. */
14
+ items: AnchorTabItem[];
15
+ /** Classes supplémentaires appliquées à la barre `<nav>`. */
16
+ className?: string;
17
+ /** Libellé ARIA de la navigation. */
18
+ ariaLabel?: string;
19
+ /**
20
+ * Décalage (px) réservé en haut de page pour le scroll-spy ET le défilement
21
+ * au clic (barre sticky / header). Défaut : 96.
22
+ */
23
+ offsetTop?: number;
24
+ /** Notifié à chaque changement de section active (scroll ou clic). */
25
+ onActiveChange?: (key: string) => void;
26
+ }
27
+ /**
28
+ * Barre d'onglets d'ancrage (desktop) avec scroll-spy.
29
+ *
30
+ * Présente une rangée de liens `<a href="#{key}">` ; l'onglet dont la section
31
+ * est actuellement en haut du viewport (au-delà de `offsetTop`) est souligné.
32
+ * Le composant observe le défilement de la fenêtre (via `requestAnimationFrame`)
33
+ * pour déterminer la section courante et défile en douceur au clic.
34
+ *
35
+ * Les sections cibles doivent porter l'`id` correspondant à chaque `key`. Le
36
+ * positionnement (sticky, largeur, visibilité responsive) reste à la charge de
37
+ * l'appelant via `className`.
38
+ */
39
+ declare const AnchorTabs: React.ForwardRefExoticComponent<AnchorTabsProps & React.RefAttributes<HTMLElement>>;
40
+
41
+ export { type AnchorTabItem, AnchorTabs, type AnchorTabsProps };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var chunk5A3MVRZJ_js = require('../../chunk-5A3MVRZJ.js');
4
+ require('../../chunk-ADIDI7AJ.js');
5
+
6
+
7
+
8
+ Object.defineProperty(exports, "AnchorTabs", {
9
+ enumerable: true,
10
+ get: function () { return chunk5A3MVRZJ_js.AnchorTabs; }
11
+ });
@@ -0,0 +1,2 @@
1
+ export { AnchorTabs } from '../../chunk-PU4CWOWU.mjs';
2
+ import '../../chunk-IMKLN273.mjs';