@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.
- package/dist/{chunk-OK3E73OK.js → chunk-2XH74PQR.js} +14 -0
- package/dist/chunk-36JNXXGH.js +360 -0
- package/dist/chunk-5A3MVRZJ.js +109 -0
- package/dist/{chunk-KVVXKSMQ.mjs → chunk-7JYJ7ICL.mjs} +14 -0
- package/dist/{chunk-NWLJ7VQF.js → chunk-AIA3NHCK.js} +1 -2
- package/dist/{chunk-YSJAEDMG.js → chunk-E24VNM6S.js} +8 -5
- package/dist/{chunk-RQGC6RPI.mjs → chunk-FL3GD5FJ.mjs} +1 -2
- package/dist/{chunk-TNGW36OC.mjs → chunk-LRNSVRUN.mjs} +2 -0
- package/dist/{chunk-76DZXGKJ.mjs → chunk-N6THLJIG.mjs} +8 -5
- package/dist/chunk-PBDPZTHK.mjs +53 -0
- package/dist/chunk-PU4CWOWU.mjs +87 -0
- package/dist/chunk-S732LTPT.js +75 -0
- package/dist/{chunk-3BPC4LNH.js → chunk-T4AT3YCT.js} +2 -0
- package/dist/chunk-XH2MVC4W.mjs +337 -0
- package/dist/components/AnchorTabs/index.d.mts +41 -0
- package/dist/components/AnchorTabs/index.d.ts +41 -0
- package/dist/components/AnchorTabs/index.js +11 -0
- package/dist/components/AnchorTabs/index.mjs +2 -0
- package/dist/components/Counter/index.js +2 -2
- package/dist/components/Counter/index.mjs +1 -1
- package/dist/components/DateRangePicker/index.js +3 -3
- package/dist/components/DateRangePicker/index.mjs +1 -1
- package/dist/components/DetailList/index.d.mts +8 -2
- package/dist/components/DetailList/index.d.ts +8 -2
- package/dist/components/DetailList/index.js +2 -2
- package/dist/components/DetailList/index.mjs +1 -1
- package/dist/components/FloatingDock/index.d.mts +44 -0
- package/dist/components/FloatingDock/index.d.ts +44 -0
- package/dist/components/FloatingDock/index.js +11 -0
- package/dist/components/FloatingDock/index.mjs +2 -0
- package/dist/components/RoomTypeCard/index.d.mts +92 -0
- package/dist/components/RoomTypeCard/index.d.ts +92 -0
- package/dist/components/RoomTypeCard/index.js +16 -0
- package/dist/components/RoomTypeCard/index.mjs +3 -0
- package/dist/index.d.mts +125 -4
- package/dist/index.d.ts +125 -4
- package/dist/index.js +542 -102
- package/dist/index.mjs +426 -22
- package/dist/tailwind-preset.d.mts +11 -0
- package/dist/tailwind-preset.d.ts +11 -0
- package/dist/tailwind-preset.js +7 -7
- package/dist/tailwind-preset.mjs +1 -1
- package/dist/tokens/index.d.mts +11 -0
- package/dist/tokens/index.d.ts +11 -0
- package/dist/tokens/index.js +8 -8
- package/dist/tokens/index.mjs +1 -1
- 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 };
|