@dododog/ui 0.4.0 → 0.5.1

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.
@@ -0,0 +1,148 @@
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 AnchorTabs = React__namespace.forwardRef(
28
+ ({
29
+ items,
30
+ className,
31
+ ariaLabel = "Navigation dans la fiche",
32
+ offsetTop = 96,
33
+ onActiveChange,
34
+ pinWhileSectionsVisible = false,
35
+ pinTop = 72,
36
+ containerClassName
37
+ }, ref) => {
38
+ const [activeKey, setActiveKey] = React__namespace.useState(items[0]?.key ?? "");
39
+ const [pinned, setPinned] = React__namespace.useState(false);
40
+ const [pinBox, setPinBox] = React__namespace.useState(null);
41
+ const wrapperRef = React__namespace.useRef(null);
42
+ React__namespace.useEffect(() => {
43
+ if (items.length === 0) return;
44
+ const ids = items.map((i) => i.key);
45
+ let raf = 0;
46
+ const update = () => {
47
+ raf = 0;
48
+ let current = ids[0];
49
+ let bestTop = -Infinity;
50
+ for (const id of ids) {
51
+ const el = document.getElementById(id);
52
+ if (!el) continue;
53
+ const top = el.getBoundingClientRect().top - offsetTop;
54
+ if (top <= 8 && top > bestTop) {
55
+ bestTop = top;
56
+ current = id;
57
+ }
58
+ }
59
+ setActiveKey((prev) => prev === current ? prev : current);
60
+ if (pinWhileSectionsVisible && wrapperRef.current) {
61
+ const wrap = wrapperRef.current.getBoundingClientRect();
62
+ let lastBottom = -Infinity;
63
+ for (const id of ids) {
64
+ const el = document.getElementById(id);
65
+ if (el) lastBottom = Math.max(lastBottom, el.getBoundingClientRect().bottom);
66
+ }
67
+ const barHeight = wrap.height || 46;
68
+ const shouldPin = wrap.top <= pinTop && lastBottom > pinTop + barHeight;
69
+ setPinned((prev) => prev === shouldPin ? prev : shouldPin);
70
+ setPinBox((prev) => {
71
+ if (!shouldPin) return prev;
72
+ if (prev && prev.left === wrap.left && prev.width === wrap.width && prev.height === barHeight) return prev;
73
+ return { left: wrap.left, width: wrap.width, height: barHeight };
74
+ });
75
+ }
76
+ };
77
+ const onScroll = () => {
78
+ if (raf) return;
79
+ raf = window.requestAnimationFrame(update);
80
+ };
81
+ update();
82
+ window.addEventListener("scroll", onScroll, { passive: true });
83
+ window.addEventListener("resize", onScroll);
84
+ return () => {
85
+ window.removeEventListener("scroll", onScroll);
86
+ window.removeEventListener("resize", onScroll);
87
+ if (raf) window.cancelAnimationFrame(raf);
88
+ };
89
+ }, [items, offsetTop, pinWhileSectionsVisible, pinTop]);
90
+ React__namespace.useEffect(() => {
91
+ if (activeKey) onActiveChange?.(activeKey);
92
+ }, [activeKey, onActiveChange]);
93
+ const handleClick = (event, key) => {
94
+ const el = typeof document !== "undefined" ? document.getElementById(key) : null;
95
+ if (!el) return;
96
+ event.preventDefault();
97
+ const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
98
+ const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
99
+ window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
100
+ setActiveKey(key);
101
+ };
102
+ const isPinned = pinWhileSectionsVisible && pinned && pinBox !== null;
103
+ return /* @__PURE__ */ jsxRuntime.jsx(
104
+ "div",
105
+ {
106
+ ref: wrapperRef,
107
+ className: containerClassName,
108
+ style: isPinned && pinBox ? { minHeight: pinBox.height } : void 0,
109
+ children: /* @__PURE__ */ jsxRuntime.jsx(
110
+ "nav",
111
+ {
112
+ ref,
113
+ "aria-label": ariaLabel,
114
+ className: chunkADIDI7AJ_js.cn(
115
+ "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
116
+ isPinned && "z-40 shadow-sm",
117
+ className
118
+ ),
119
+ style: isPinned && pinBox ? { position: "fixed", top: pinTop, left: pinBox.left, width: pinBox.width } : void 0,
120
+ children: items.map((item) => {
121
+ const isActive = item.key === activeKey;
122
+ return /* @__PURE__ */ jsxRuntime.jsx(
123
+ "a",
124
+ {
125
+ href: `#${item.key}`,
126
+ "aria-current": isActive ? "location" : void 0,
127
+ onClick: (event) => handleClick(event, item.key),
128
+ className: chunkADIDI7AJ_js.cn(
129
+ // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
130
+ // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
131
+ // via opacité (transform/opacity, aucun layout shift).
132
+ "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",
133
+ isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
134
+ ),
135
+ children: item.label
136
+ },
137
+ item.key
138
+ );
139
+ })
140
+ }
141
+ )
142
+ }
143
+ );
144
+ }
145
+ );
146
+ AnchorTabs.displayName = "AnchorTabs";
147
+
148
+ exports.AnchorTabs = AnchorTabs;
@@ -0,0 +1,126 @@
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
+ ({
7
+ items,
8
+ className,
9
+ ariaLabel = "Navigation dans la fiche",
10
+ offsetTop = 96,
11
+ onActiveChange,
12
+ pinWhileSectionsVisible = false,
13
+ pinTop = 72,
14
+ containerClassName
15
+ }, ref) => {
16
+ const [activeKey, setActiveKey] = React.useState(items[0]?.key ?? "");
17
+ const [pinned, setPinned] = React.useState(false);
18
+ const [pinBox, setPinBox] = React.useState(null);
19
+ const wrapperRef = React.useRef(null);
20
+ React.useEffect(() => {
21
+ if (items.length === 0) return;
22
+ const ids = items.map((i) => i.key);
23
+ let raf = 0;
24
+ const update = () => {
25
+ raf = 0;
26
+ let current = ids[0];
27
+ let bestTop = -Infinity;
28
+ for (const id of ids) {
29
+ const el = document.getElementById(id);
30
+ if (!el) continue;
31
+ const top = el.getBoundingClientRect().top - offsetTop;
32
+ if (top <= 8 && top > bestTop) {
33
+ bestTop = top;
34
+ current = id;
35
+ }
36
+ }
37
+ setActiveKey((prev) => prev === current ? prev : current);
38
+ if (pinWhileSectionsVisible && wrapperRef.current) {
39
+ const wrap = wrapperRef.current.getBoundingClientRect();
40
+ let lastBottom = -Infinity;
41
+ for (const id of ids) {
42
+ const el = document.getElementById(id);
43
+ if (el) lastBottom = Math.max(lastBottom, el.getBoundingClientRect().bottom);
44
+ }
45
+ const barHeight = wrap.height || 46;
46
+ const shouldPin = wrap.top <= pinTop && lastBottom > pinTop + barHeight;
47
+ setPinned((prev) => prev === shouldPin ? prev : shouldPin);
48
+ setPinBox((prev) => {
49
+ if (!shouldPin) return prev;
50
+ if (prev && prev.left === wrap.left && prev.width === wrap.width && prev.height === barHeight) return prev;
51
+ return { left: wrap.left, width: wrap.width, height: barHeight };
52
+ });
53
+ }
54
+ };
55
+ const onScroll = () => {
56
+ if (raf) return;
57
+ raf = window.requestAnimationFrame(update);
58
+ };
59
+ update();
60
+ window.addEventListener("scroll", onScroll, { passive: true });
61
+ window.addEventListener("resize", onScroll);
62
+ return () => {
63
+ window.removeEventListener("scroll", onScroll);
64
+ window.removeEventListener("resize", onScroll);
65
+ if (raf) window.cancelAnimationFrame(raf);
66
+ };
67
+ }, [items, offsetTop, pinWhileSectionsVisible, pinTop]);
68
+ React.useEffect(() => {
69
+ if (activeKey) onActiveChange?.(activeKey);
70
+ }, [activeKey, onActiveChange]);
71
+ const handleClick = (event, key) => {
72
+ const el = typeof document !== "undefined" ? document.getElementById(key) : null;
73
+ if (!el) return;
74
+ event.preventDefault();
75
+ const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
76
+ const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
77
+ window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
78
+ setActiveKey(key);
79
+ };
80
+ const isPinned = pinWhileSectionsVisible && pinned && pinBox !== null;
81
+ return /* @__PURE__ */ jsx(
82
+ "div",
83
+ {
84
+ ref: wrapperRef,
85
+ className: containerClassName,
86
+ style: isPinned && pinBox ? { minHeight: pinBox.height } : void 0,
87
+ children: /* @__PURE__ */ jsx(
88
+ "nav",
89
+ {
90
+ ref,
91
+ "aria-label": ariaLabel,
92
+ className: cn(
93
+ "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
94
+ isPinned && "z-40 shadow-sm",
95
+ className
96
+ ),
97
+ style: isPinned && pinBox ? { position: "fixed", top: pinTop, left: pinBox.left, width: pinBox.width } : void 0,
98
+ children: items.map((item) => {
99
+ const isActive = item.key === activeKey;
100
+ return /* @__PURE__ */ jsx(
101
+ "a",
102
+ {
103
+ href: `#${item.key}`,
104
+ "aria-current": isActive ? "location" : void 0,
105
+ onClick: (event) => handleClick(event, item.key),
106
+ className: cn(
107
+ // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
108
+ // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
109
+ // via opacité (transform/opacity, aucun layout shift).
110
+ "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",
111
+ isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
112
+ ),
113
+ children: item.label
114
+ },
115
+ item.key
116
+ );
117
+ })
118
+ }
119
+ )
120
+ }
121
+ );
122
+ }
123
+ );
124
+ AnchorTabs.displayName = "AnchorTabs";
125
+
126
+ export { AnchorTabs };
@@ -87,9 +87,13 @@ function RateRow({
87
87
  rate,
88
88
  selected,
89
89
  onSelect,
90
+ onReserve,
90
91
  selectLabel,
92
+ reserveLabel,
93
+ ctaNote,
91
94
  withTopBorder
92
95
  }) {
96
+ const showReserveCta = selected && !!onReserve;
93
97
  return /* @__PURE__ */ jsxs(
94
98
  "div",
95
99
  {
@@ -110,7 +114,15 @@ function RateRow({
110
114
  rate.originalTotal && rate.originalTotal > rate.totalPrice && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-text-muted line-through leading-none", children: formatPrice(rate.originalTotal) }),
111
115
  /* @__PURE__ */ jsx("div", { className: "font-heading text-lg font-bold text-primary leading-tight", children: formatPrice(rate.totalPrice) }),
112
116
  rate.totalLabel && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-text-muted", children: rate.totalLabel }),
113
- /* @__PURE__ */ jsx(
117
+ showReserveCta ? /* @__PURE__ */ jsx(
118
+ "button",
119
+ {
120
+ type: "button",
121
+ onClick: () => onReserve?.(rate.id),
122
+ className: "mt-1.5 text-[13px] font-semibold px-5 py-1.5 rounded-full bg-secondary text-white transition-colors hover:bg-secondary-dark focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-1",
123
+ children: reserveLabel
124
+ }
125
+ ) : /* @__PURE__ */ jsx(
114
126
  "button",
115
127
  {
116
128
  type: "button",
@@ -122,7 +134,8 @@ function RateRow({
122
134
  ),
123
135
  children: selected ? "S\xE9lectionn\xE9" : selectLabel
124
136
  }
125
- )
137
+ ),
138
+ showReserveCta && ctaNote && /* @__PURE__ */ jsx("div", { className: "mt-1 text-[11px] leading-tight text-text-muted", children: ctaNote })
126
139
  ] })
127
140
  ]
128
141
  }
@@ -134,6 +147,9 @@ var RoomTypeCard = React.forwardRef(
134
147
  rates,
135
148
  selectedRateId,
136
149
  onSelectRate,
150
+ onReserveRate,
151
+ reserveLabel = "R\xE9server",
152
+ ctaNote,
137
153
  initialVisibleRates = 2,
138
154
  selectLabel = "Choisir",
139
155
  variant = "default",
@@ -266,7 +282,10 @@ var RoomTypeCard = React.forwardRef(
266
282
  rate,
267
283
  selected: rate.id === selectedRateId,
268
284
  onSelect: onSelectRate,
285
+ onReserve: onReserveRate,
269
286
  selectLabel,
287
+ reserveLabel,
288
+ ctaNote,
270
289
  withTopBorder: i > 0
271
290
  },
272
291
  rate.id
@@ -109,9 +109,13 @@ function RateRow({
109
109
  rate,
110
110
  selected,
111
111
  onSelect,
112
+ onReserve,
112
113
  selectLabel,
114
+ reserveLabel,
115
+ ctaNote,
113
116
  withTopBorder
114
117
  }) {
118
+ const showReserveCta = selected && !!onReserve;
115
119
  return /* @__PURE__ */ jsxRuntime.jsxs(
116
120
  "div",
117
121
  {
@@ -132,7 +136,15 @@ function RateRow({
132
136
  rate.originalTotal && rate.originalTotal > rate.totalPrice && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-text-muted line-through leading-none", children: formatPrice(rate.originalTotal) }),
133
137
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-heading text-lg font-bold text-primary leading-tight", children: formatPrice(rate.totalPrice) }),
134
138
  rate.totalLabel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-text-muted", children: rate.totalLabel }),
135
- /* @__PURE__ */ jsxRuntime.jsx(
139
+ showReserveCta ? /* @__PURE__ */ jsxRuntime.jsx(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: () => onReserve?.(rate.id),
144
+ className: "mt-1.5 text-[13px] font-semibold px-5 py-1.5 rounded-full bg-secondary text-white transition-colors hover:bg-secondary-dark focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-1",
145
+ children: reserveLabel
146
+ }
147
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
136
148
  "button",
137
149
  {
138
150
  type: "button",
@@ -144,7 +156,8 @@ function RateRow({
144
156
  ),
145
157
  children: selected ? "S\xE9lectionn\xE9" : selectLabel
146
158
  }
147
- )
159
+ ),
160
+ showReserveCta && ctaNote && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 text-[11px] leading-tight text-text-muted", children: ctaNote })
148
161
  ] })
149
162
  ]
150
163
  }
@@ -156,6 +169,9 @@ var RoomTypeCard = React__namespace.forwardRef(
156
169
  rates,
157
170
  selectedRateId,
158
171
  onSelectRate,
172
+ onReserveRate,
173
+ reserveLabel = "R\xE9server",
174
+ ctaNote,
159
175
  initialVisibleRates = 2,
160
176
  selectLabel = "Choisir",
161
177
  variant = "default",
@@ -288,7 +304,10 @@ var RoomTypeCard = React__namespace.forwardRef(
288
304
  rate,
289
305
  selected: rate.id === selectedRateId,
290
306
  onSelect: onSelectRate,
307
+ onReserve: onReserveRate,
291
308
  selectLabel,
309
+ reserveLabel,
310
+ ctaNote,
292
311
  withTopBorder: i > 0
293
312
  },
294
313
  rate.id
@@ -23,6 +23,17 @@ interface AnchorTabsProps {
23
23
  offsetTop?: number;
24
24
  /** Notifié à chaque changement de section active (scroll ou clic). */
25
25
  onActiveChange?: (key: string) => void;
26
+ /**
27
+ * Épingle la barre (position fixed) tant qu'au moins une des sections
28
+ * ancrées reste dans le viewport — au-delà de la portée d'un simple
29
+ * `position: sticky` limité au parent. Un espace réservé conserve la
30
+ * hauteur de la barre (aucun layout shift).
31
+ */
32
+ pinWhileSectionsVisible?: boolean;
33
+ /** Ordonnée (px) de l'épinglage sous le header. Défaut : 72. */
34
+ pinTop?: number;
35
+ /** Classes du conteneur/sentinelle (ex. `hidden lg:block`). */
36
+ containerClassName?: string;
26
37
  }
27
38
  /**
28
39
  * Barre d'onglets d'ancrage (desktop) avec scroll-spy.
@@ -23,6 +23,17 @@ interface AnchorTabsProps {
23
23
  offsetTop?: number;
24
24
  /** Notifié à chaque changement de section active (scroll ou clic). */
25
25
  onActiveChange?: (key: string) => void;
26
+ /**
27
+ * Épingle la barre (position fixed) tant qu'au moins une des sections
28
+ * ancrées reste dans le viewport — au-delà de la portée d'un simple
29
+ * `position: sticky` limité au parent. Un espace réservé conserve la
30
+ * hauteur de la barre (aucun layout shift).
31
+ */
32
+ pinWhileSectionsVisible?: boolean;
33
+ /** Ordonnée (px) de l'épinglage sous le header. Défaut : 72. */
34
+ pinTop?: number;
35
+ /** Classes du conteneur/sentinelle (ex. `hidden lg:block`). */
36
+ containerClassName?: string;
26
37
  }
27
38
  /**
28
39
  * Barre d'onglets d'ancrage (desktop) avec scroll-spy.
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunk5A3MVRZJ_js = require('../../chunk-5A3MVRZJ.js');
3
+ var chunk2J73BRC5_js = require('../../chunk-2J73BRC5.js');
4
4
  require('../../chunk-ADIDI7AJ.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "AnchorTabs", {
9
9
  enumerable: true,
10
- get: function () { return chunk5A3MVRZJ_js.AnchorTabs; }
10
+ get: function () { return chunk2J73BRC5_js.AnchorTabs; }
11
11
  });
@@ -1,2 +1,2 @@
1
- export { AnchorTabs } from '../../chunk-PU4CWOWU.mjs';
1
+ export { AnchorTabs } from '../../chunk-MKUUHYGQ.mjs';
2
2
  import '../../chunk-IMKLN273.mjs';
@@ -53,6 +53,16 @@ interface RoomTypeCardProps extends React.HTMLAttributes<HTMLDivElement> {
53
53
  /** id de la formule actuellement sélectionnée */
54
54
  selectedRateId?: string;
55
55
  onSelectRate?: (rateId: string) => void;
56
+ /**
57
+ * Callback de réservation directe (pattern Expedia) : quand fourni, la formule
58
+ * sélectionnée affiche un bouton PLEIN « Réserver » qui l'appelle. Absent →
59
+ * comportement historique préservé (bouton « Sélectionné », API additive).
60
+ */
61
+ onReserveRate?: (rateId: string) => void;
62
+ /** Libellé du CTA de réservation directe (défaut « Réserver »). */
63
+ reserveLabel?: string;
64
+ /** Note grise sous le CTA « Réserver » (ex: « Le montant ne vous sera pas encore facturé »). */
65
+ ctaNote?: string;
56
66
  /** Nombre de formules visibles avant le bouton "X autres formules" (défaut 2) */
57
67
  initialVisibleRates?: number;
58
68
  selectLabel?: string;
@@ -53,6 +53,16 @@ interface RoomTypeCardProps extends React.HTMLAttributes<HTMLDivElement> {
53
53
  /** id de la formule actuellement sélectionnée */
54
54
  selectedRateId?: string;
55
55
  onSelectRate?: (rateId: string) => void;
56
+ /**
57
+ * Callback de réservation directe (pattern Expedia) : quand fourni, la formule
58
+ * sélectionnée affiche un bouton PLEIN « Réserver » qui l'appelle. Absent →
59
+ * comportement historique préservé (bouton « Sélectionné », API additive).
60
+ */
61
+ onReserveRate?: (rateId: string) => void;
62
+ /** Libellé du CTA de réservation directe (défaut « Réserver »). */
63
+ reserveLabel?: string;
64
+ /** Note grise sous le CTA « Réserver » (ex: « Le montant ne vous sera pas encore facturé »). */
65
+ ctaNote?: string;
56
66
  /** Nombre de formules visibles avant le bouton "X autres formules" (défaut 2) */
57
67
  initialVisibleRates?: number;
58
68
  selectLabel?: string;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk36JNXXGH_js = require('../../chunk-36JNXXGH.js');
3
+ var chunkYDTBGEAB_js = require('../../chunk-YDTBGEAB.js');
4
4
  require('../../chunk-NCU5PY34.js');
5
5
  require('../../chunk-ADIDI7AJ.js');
6
6
 
@@ -8,9 +8,9 @@ require('../../chunk-ADIDI7AJ.js');
8
8
 
9
9
  Object.defineProperty(exports, "RoomTypeCard", {
10
10
  enumerable: true,
11
- get: function () { return chunk36JNXXGH_js.RoomTypeCard; }
11
+ get: function () { return chunkYDTBGEAB_js.RoomTypeCard; }
12
12
  });
13
13
  Object.defineProperty(exports, "RoomTypeCardSkeleton", {
14
14
  enumerable: true,
15
- get: function () { return chunk36JNXXGH_js.RoomTypeCardSkeleton; }
15
+ get: function () { return chunkYDTBGEAB_js.RoomTypeCardSkeleton; }
16
16
  });
@@ -1,3 +1,3 @@
1
- export { RoomTypeCard, RoomTypeCardSkeleton } from '../../chunk-XH2MVC4W.mjs';
1
+ export { RoomTypeCard, RoomTypeCardSkeleton } from '../../chunk-QJDKVDK2.mjs';
2
2
  import '../../chunk-PAHSQMXV.mjs';
3
3
  import '../../chunk-IMKLN273.mjs';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var chunk7IJOHVNJ_js = require('./chunk-7IJOHVNJ.js');
14
14
  var chunkL7OYR6U3_js = require('./chunk-L7OYR6U3.js');
15
15
  var chunkGXKON34B_js = require('./chunk-GXKON34B.js');
16
16
  var chunkTZHT7NZU_js = require('./chunk-TZHT7NZU.js');
17
- var chunk36JNXXGH_js = require('./chunk-36JNXXGH.js');
17
+ var chunkYDTBGEAB_js = require('./chunk-YDTBGEAB.js');
18
18
  var chunkHS3MAW3G_js = require('./chunk-HS3MAW3G.js');
19
19
  var chunkQWDKSY6Y_js = require('./chunk-QWDKSY6Y.js');
20
20
  var chunk3R7PT3JG_js = require('./chunk-3R7PT3JG.js');
@@ -29,27 +29,27 @@ var chunk6ASRTUXO_js = require('./chunk-6ASRTUXO.js');
29
29
  var chunkPG4I4NWO_js = require('./chunk-PG4I4NWO.js');
30
30
  var chunkPRDIS2VR_js = require('./chunk-PRDIS2VR.js');
31
31
  var chunkIRKM5UF4_js = require('./chunk-IRKM5UF4.js');
32
- var chunkYN4HPIRC_js = require('./chunk-YN4HPIRC.js');
33
32
  var chunkODUY7NXD_js = require('./chunk-ODUY7NXD.js');
33
+ var chunkYN4HPIRC_js = require('./chunk-YN4HPIRC.js');
34
34
  var chunkMKOKWME3_js = require('./chunk-MKOKWME3.js');
35
35
  var chunkDLOHAW74_js = require('./chunk-DLOHAW74.js');
36
- var chunkU3XWNFHH_js = require('./chunk-U3XWNFHH.js');
37
36
  var chunk77WKG2D7_js = require('./chunk-77WKG2D7.js');
37
+ var chunkU3XWNFHH_js = require('./chunk-U3XWNFHH.js');
38
38
  var chunkW7DZZS6L_js = require('./chunk-W7DZZS6L.js');
39
39
  var chunkTDGU5Y2P_js = require('./chunk-TDGU5Y2P.js');
40
40
  var chunkIEHX4DU6_js = require('./chunk-IEHX4DU6.js');
41
41
  var chunkS5TLUDNM_js = require('./chunk-S5TLUDNM.js');
42
42
  var chunk7W3TFPIF_js = require('./chunk-7W3TFPIF.js');
43
- var chunkS732LTPT_js = require('./chunk-S732LTPT.js');
44
43
  var chunkN2Q4Z2FU_js = require('./chunk-N2Q4Z2FU.js');
44
+ var chunkS732LTPT_js = require('./chunk-S732LTPT.js');
45
45
  var chunkC4RZBOKH_js = require('./chunk-C4RZBOKH.js');
46
46
  var chunkKOO5ZXLD_js = require('./chunk-KOO5ZXLD.js');
47
47
  var chunkP7GAKOCX_js = require('./chunk-P7GAKOCX.js');
48
48
  var chunkIJIUUBFT_js = require('./chunk-IJIUUBFT.js');
49
+ var chunkT4AT3YCT_js = require('./chunk-T4AT3YCT.js');
50
+ var chunkAIA3NHCK_js = require('./chunk-AIA3NHCK.js');
49
51
  var chunkE24VNM6S_js = require('./chunk-E24VNM6S.js');
50
52
  var chunkXXC2YD3D_js = require('./chunk-XXC2YD3D.js');
51
- var chunkAIA3NHCK_js = require('./chunk-AIA3NHCK.js');
52
- var chunkT4AT3YCT_js = require('./chunk-T4AT3YCT.js');
53
53
  var chunkVWFY24XF_js = require('./chunk-VWFY24XF.js');
54
54
  var chunkT5FLQQP6_js = require('./chunk-T5FLQQP6.js');
55
55
  var chunkEY4ZIR3P_js = require('./chunk-EY4ZIR3P.js');
@@ -62,7 +62,7 @@ var chunkZAUYE2EI_js = require('./chunk-ZAUYE2EI.js');
62
62
  var chunkMY6BYO5F_js = require('./chunk-MY6BYO5F.js');
63
63
  var chunkMCF3EQTV_js = require('./chunk-MCF3EQTV.js');
64
64
  var chunkWBRVUWGC_js = require('./chunk-WBRVUWGC.js');
65
- var chunk5A3MVRZJ_js = require('./chunk-5A3MVRZJ.js');
65
+ var chunk2J73BRC5_js = require('./chunk-2J73BRC5.js');
66
66
  var chunkNHB2TI2B_js = require('./chunk-NHB2TI2B.js');
67
67
  var chunkADIDI7AJ_js = require('./chunk-ADIDI7AJ.js');
68
68
  var React = require('react');
@@ -570,11 +570,11 @@ Object.defineProperty(exports, "Rating", {
570
570
  });
571
571
  Object.defineProperty(exports, "RoomTypeCard", {
572
572
  enumerable: true,
573
- get: function () { return chunk36JNXXGH_js.RoomTypeCard; }
573
+ get: function () { return chunkYDTBGEAB_js.RoomTypeCard; }
574
574
  });
575
575
  Object.defineProperty(exports, "RoomTypeCardSkeleton", {
576
576
  enumerable: true,
577
- get: function () { return chunk36JNXXGH_js.RoomTypeCardSkeleton; }
577
+ get: function () { return chunkYDTBGEAB_js.RoomTypeCardSkeleton; }
578
578
  });
579
579
  Object.defineProperty(exports, "SearchBar", {
580
580
  enumerable: true,
@@ -660,6 +660,10 @@ Object.defineProperty(exports, "Header", {
660
660
  enumerable: true,
661
661
  get: function () { return chunkIRKM5UF4_js.Header; }
662
662
  });
663
+ Object.defineProperty(exports, "HotelCard", {
664
+ enumerable: true,
665
+ get: function () { return chunkODUY7NXD_js.HotelCard; }
666
+ });
663
667
  Object.defineProperty(exports, "IconBadge", {
664
668
  enumerable: true,
665
669
  get: function () { return chunkYN4HPIRC_js.IconBadge; }
@@ -668,10 +672,6 @@ Object.defineProperty(exports, "iconBadgeVariants", {
668
672
  enumerable: true,
669
673
  get: function () { return chunkYN4HPIRC_js.iconBadgeVariants; }
670
674
  });
671
- Object.defineProperty(exports, "HotelCard", {
672
- enumerable: true,
673
- get: function () { return chunkODUY7NXD_js.HotelCard; }
674
- });
675
675
  Object.defineProperty(exports, "ImageGallery", {
676
676
  enumerable: true,
677
677
  get: function () { return chunkMKOKWME3_js.ImageGallery; }
@@ -680,10 +680,6 @@ Object.defineProperty(exports, "ImageWithFallback", {
680
680
  enumerable: true,
681
681
  get: function () { return chunkDLOHAW74_js.ImageWithFallback; }
682
682
  });
683
- Object.defineProperty(exports, "Lightbox", {
684
- enumerable: true,
685
- get: function () { return chunkU3XWNFHH_js.Lightbox; }
686
- });
687
683
  Object.defineProperty(exports, "Input", {
688
684
  enumerable: true,
689
685
  get: function () { return chunk77WKG2D7_js.Input; }
@@ -692,6 +688,10 @@ Object.defineProperty(exports, "inputVariants", {
692
688
  enumerable: true,
693
689
  get: function () { return chunk77WKG2D7_js.inputVariants; }
694
690
  });
691
+ Object.defineProperty(exports, "Lightbox", {
692
+ enumerable: true,
693
+ get: function () { return chunkU3XWNFHH_js.Lightbox; }
694
+ });
695
695
  Object.defineProperty(exports, "LinkCard", {
696
696
  enumerable: true,
697
697
  get: function () { return chunkW7DZZS6L_js.LinkCard; }
@@ -712,14 +712,14 @@ Object.defineProperty(exports, "FilterAccordion", {
712
712
  enumerable: true,
713
713
  get: function () { return chunk7W3TFPIF_js.FilterAccordion; }
714
714
  });
715
- Object.defineProperty(exports, "FloatingDock", {
716
- enumerable: true,
717
- get: function () { return chunkS732LTPT_js.FloatingDock; }
718
- });
719
715
  Object.defineProperty(exports, "FilterPill", {
720
716
  enumerable: true,
721
717
  get: function () { return chunkN2Q4Z2FU_js.FilterPill; }
722
718
  });
719
+ Object.defineProperty(exports, "FloatingDock", {
720
+ enumerable: true,
721
+ get: function () { return chunkS732LTPT_js.FloatingDock; }
722
+ });
723
723
  Object.defineProperty(exports, "Footer", {
724
724
  enumerable: true,
725
725
  get: function () { return chunkC4RZBOKH_js.Footer; }
@@ -736,13 +736,9 @@ Object.defineProperty(exports, "Checkbox", {
736
736
  enumerable: true,
737
737
  get: function () { return chunkIJIUUBFT_js.Checkbox; }
738
738
  });
739
- Object.defineProperty(exports, "DetailList", {
740
- enumerable: true,
741
- get: function () { return chunkE24VNM6S_js.DetailList; }
742
- });
743
- Object.defineProperty(exports, "Divider", {
739
+ Object.defineProperty(exports, "Counter", {
744
740
  enumerable: true,
745
- get: function () { return chunkXXC2YD3D_js.Divider; }
741
+ get: function () { return chunkT4AT3YCT_js.Counter; }
746
742
  });
747
743
  Object.defineProperty(exports, "DatePickerInput", {
748
744
  enumerable: true,
@@ -752,9 +748,13 @@ Object.defineProperty(exports, "DateRangePicker", {
752
748
  enumerable: true,
753
749
  get: function () { return chunkAIA3NHCK_js.DateRangePicker; }
754
750
  });
755
- Object.defineProperty(exports, "Counter", {
751
+ Object.defineProperty(exports, "DetailList", {
756
752
  enumerable: true,
757
- get: function () { return chunkT4AT3YCT_js.Counter; }
753
+ get: function () { return chunkE24VNM6S_js.DetailList; }
754
+ });
755
+ Object.defineProperty(exports, "Divider", {
756
+ enumerable: true,
757
+ get: function () { return chunkXXC2YD3D_js.Divider; }
758
758
  });
759
759
  Object.defineProperty(exports, "Drawer", {
760
760
  enumerable: true,
@@ -846,7 +846,7 @@ Object.defineProperty(exports, "Alert", {
846
846
  });
847
847
  Object.defineProperty(exports, "AnchorTabs", {
848
848
  enumerable: true,
849
- get: function () { return chunk5A3MVRZJ_js.AnchorTabs; }
849
+ get: function () { return chunk2J73BRC5_js.AnchorTabs; }
850
850
  });
851
851
  Object.defineProperty(exports, "Autocomplete", {
852
852
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ export { Tag, tagVariants } from './chunk-WOG6V7OW.mjs';
12
12
  export { Textarea, textareaVariants } from './chunk-ZZ4EWC53.mjs';
13
13
  export { RadioGroup } from './chunk-F2BUMJYW.mjs';
14
14
  export { Rating } from './chunk-SGDC4IVX.mjs';
15
- export { RoomTypeCard, RoomTypeCardSkeleton } from './chunk-XH2MVC4W.mjs';
15
+ export { RoomTypeCard, RoomTypeCardSkeleton } from './chunk-QJDKVDK2.mjs';
16
16
  export { SearchBar } from './chunk-KQK7LFWM.mjs';
17
17
  export { SegmentedControl } from './chunk-36Y7UU32.mjs';
18
18
  export { Select, selectVariants } from './chunk-333YD5QI.mjs';
@@ -27,27 +27,27 @@ export { PriceDisplay } from './chunk-DGEXT7PN.mjs';
27
27
  export { ProgressBar, progressBarVariants, progressFillVariants } from './chunk-UGYMWWKT.mjs';
28
28
  export { PromoBanner, promoBannerVariants } from './chunk-FNDR3WOZ.mjs';
29
29
  export { Header } from './chunk-PPDKQ3FF.mjs';
30
- export { IconBadge, iconBadgeVariants } from './chunk-I4WCNTNP.mjs';
31
30
  export { HotelCard } from './chunk-PXHZ4CXG.mjs';
31
+ export { IconBadge, iconBadgeVariants } from './chunk-I4WCNTNP.mjs';
32
32
  export { ImageGallery } from './chunk-QU6ZRLKO.mjs';
33
33
  export { ImageWithFallback } from './chunk-BUXVK2HE.mjs';
34
- export { Lightbox } from './chunk-GBWVIY3C.mjs';
35
34
  export { Input, inputVariants } from './chunk-GRG4USTC.mjs';
35
+ export { Lightbox } from './chunk-GBWVIY3C.mjs';
36
36
  export { LinkCard } from './chunk-UYDZKAGZ.mjs';
37
37
  export { DualRangeSlider } from './chunk-ERL3WXNY.mjs';
38
38
  export { EmptyState } from './chunk-OCUHCDAQ.mjs';
39
39
  export { ErrorBoundary } from './chunk-5257MWFI.mjs';
40
40
  export { FilterAccordion } from './chunk-VQVRKRSM.mjs';
41
- export { FloatingDock } from './chunk-PBDPZTHK.mjs';
42
41
  export { FilterPill } from './chunk-TNWMTKNR.mjs';
42
+ export { FloatingDock } from './chunk-PBDPZTHK.mjs';
43
43
  export { Footer } from './chunk-SZ3SV4SJ.mjs';
44
44
  export { GuestPicker } from './chunk-EJ7LDW7E.mjs';
45
45
  export { Carousel } from './chunk-OOPP4ES2.mjs';
46
46
  export { Checkbox } from './chunk-EEIPCJQ2.mjs';
47
+ export { Counter } from './chunk-LRNSVRUN.mjs';
48
+ export { DatePickerInput, DateRangePicker } from './chunk-FL3GD5FJ.mjs';
47
49
  export { DetailList } from './chunk-N6THLJIG.mjs';
48
50
  export { Divider } from './chunk-E4B6LXK7.mjs';
49
- export { DatePickerInput, DateRangePicker } from './chunk-FL3GD5FJ.mjs';
50
- export { Counter } from './chunk-LRNSVRUN.mjs';
51
51
  export { Drawer } from './chunk-ZLF7IL3Y.mjs';
52
52
  export { DropdownMenu } from './chunk-Q7BKR6O7.mjs';
53
53
  export { Avatar } from './chunk-2POGTS27.mjs';
@@ -60,7 +60,7 @@ export { Card, CardContent, CardDescription, CardImage, CardTitle, cardImageVari
60
60
  export { CardList } from './chunk-RJWHPHHX.mjs';
61
61
  export { Accordion } from './chunk-NZ7GF6RF.mjs';
62
62
  export { Alert } from './chunk-BQWVWK74.mjs';
63
- export { AnchorTabs } from './chunk-PU4CWOWU.mjs';
63
+ export { AnchorTabs } from './chunk-MKUUHYGQ.mjs';
64
64
  export { Autocomplete, autocompleteInputVariants } from './chunk-B47HQHX3.mjs';
65
65
  import { cn } from './chunk-IMKLN273.mjs';
66
66
  export { cn } from './chunk-IMKLN273.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dododog/ui",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "React UI component library for DodoDog — pet-friendly travel platform",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -1,109 +0,0 @@
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 AnchorTabs = React__namespace.forwardRef(
28
- ({ items, className, ariaLabel = "Navigation dans la fiche", offsetTop = 96, onActiveChange }, ref) => {
29
- const [activeKey, setActiveKey] = React__namespace.useState(items[0]?.key ?? "");
30
- React__namespace.useEffect(() => {
31
- if (items.length === 0) return;
32
- const ids = items.map((i) => i.key);
33
- let raf = 0;
34
- const update = () => {
35
- raf = 0;
36
- let current = ids[0];
37
- let bestTop = -Infinity;
38
- for (const id of ids) {
39
- const el = document.getElementById(id);
40
- if (!el) continue;
41
- const top = el.getBoundingClientRect().top - offsetTop;
42
- if (top <= 1 && top > bestTop) {
43
- bestTop = top;
44
- current = id;
45
- }
46
- }
47
- setActiveKey((prev) => prev === current ? prev : current);
48
- };
49
- const onScroll = () => {
50
- if (raf) return;
51
- raf = window.requestAnimationFrame(update);
52
- };
53
- update();
54
- window.addEventListener("scroll", onScroll, { passive: true });
55
- window.addEventListener("resize", onScroll);
56
- return () => {
57
- window.removeEventListener("scroll", onScroll);
58
- window.removeEventListener("resize", onScroll);
59
- if (raf) window.cancelAnimationFrame(raf);
60
- };
61
- }, [items, offsetTop]);
62
- React__namespace.useEffect(() => {
63
- if (activeKey) onActiveChange?.(activeKey);
64
- }, [activeKey, onActiveChange]);
65
- const handleClick = (event, key) => {
66
- const el = typeof document !== "undefined" ? document.getElementById(key) : null;
67
- if (!el) return;
68
- event.preventDefault();
69
- const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
70
- const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
71
- window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
72
- setActiveKey(key);
73
- };
74
- return /* @__PURE__ */ jsxRuntime.jsx(
75
- "nav",
76
- {
77
- ref,
78
- "aria-label": ariaLabel,
79
- className: chunkADIDI7AJ_js.cn(
80
- "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
81
- className
82
- ),
83
- children: items.map((item) => {
84
- const isActive = item.key === activeKey;
85
- return /* @__PURE__ */ jsxRuntime.jsx(
86
- "a",
87
- {
88
- href: `#${item.key}`,
89
- "aria-current": isActive ? "location" : void 0,
90
- onClick: (event) => handleClick(event, item.key),
91
- className: chunkADIDI7AJ_js.cn(
92
- // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
93
- // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
94
- // via opacité (transform/opacity, aucun layout shift).
95
- "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",
96
- isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
97
- ),
98
- children: item.label
99
- },
100
- item.key
101
- );
102
- })
103
- }
104
- );
105
- }
106
- );
107
- AnchorTabs.displayName = "AnchorTabs";
108
-
109
- exports.AnchorTabs = AnchorTabs;
@@ -1,87 +0,0 @@
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 };