@dododog/ui 0.13.1 → 0.14.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @dododog/ui
2
2
 
3
- React UI component library for [DodoDog](https://dododog.voyage) — the pet-friendly travel platform.
3
+ React UI component library for [ToutouTrip](https://toutoutrip.com) — the pet-friendly travel platform.
4
4
 
5
5
  Built with React, TypeScript, Tailwind CSS, and [class-variance-authority](https://cva.style).
6
6
 
@@ -14,7 +14,7 @@ npm install @dododog/ui
14
14
 
15
15
  ### Tailwind CSS
16
16
 
17
- Add the DodoDog preset and content path to your `tailwind.config.ts`:
17
+ Add the ToutouTrip preset and content path to your `tailwind.config.ts`:
18
18
 
19
19
  ```ts
20
20
  import type { Config } from "tailwindcss";
@@ -113,7 +113,7 @@ export default function App() {
113
113
  | Export | Import path | Description |
114
114
  |--------|-------------|-------------|
115
115
  | Tokens | `@dododog/ui/tokens` | Design tokens (colors, spacing, etc.) |
116
- | Tailwind Preset | `@dododog/ui/tailwind-preset` | Tailwind CSS preset with DodoDog theme |
116
+ | Tailwind Preset | `@dododog/ui/tailwind-preset` | Tailwind CSS preset with ToutouTrip theme |
117
117
  | Utils | `@dododog/ui/utils` | Utility functions (`cn`, etc.) |
118
118
 
119
119
  ## Requirements
@@ -106,7 +106,7 @@ var Footer = React.forwardRef(
106
106
  bottomExtra
107
107
  ] }),
108
108
  tagline && /* @__PURE__ */ jsx("div", { className: "text-sm text-white/60", children: tagline }),
109
- /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: copyright || `\xA9 ${year} DodoDog. Tous droits r\xE9serv\xE9s.` })
109
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: copyright || `\xA9 ${year} ToutouTrip. Tous droits r\xE9serv\xE9s.` })
110
110
  ] }) }) })
111
111
  ] });
112
112
  }
@@ -46,31 +46,61 @@ var inputVariants = classVarianceAuthority.cva(
46
46
  }
47
47
  );
48
48
  var Input = React__namespace.forwardRef(
49
- ({ className, size, variant, label, error, required, leftIcon, rightIcon, ...props }, ref) => {
49
+ ({
50
+ className,
51
+ size,
52
+ variant,
53
+ label,
54
+ error,
55
+ required,
56
+ leftIcon,
57
+ rightIcon,
58
+ id,
59
+ "aria-describedby": ariaDescribedBy,
60
+ "aria-invalid": ariaInvalid,
61
+ "aria-required": ariaRequired,
62
+ ...props
63
+ }, ref) => {
64
+ const generatedId = React__namespace.useId();
65
+ const inputId = id ?? generatedId;
66
+ const errorId = `${inputId}-error`;
50
67
  const resolvedVariant = error ? "error" : variant ?? "default";
68
+ const describedBy = [ariaDescribedBy, error ? errorId : null].filter(Boolean).join(" ") || void 0;
51
69
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
52
- label && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm font-medium text-primary mb-1.5", children: [
53
- label,
54
- required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-secondary", children: " *" })
55
- ] }),
70
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
71
+ "label",
72
+ {
73
+ htmlFor: inputId,
74
+ className: "block text-sm font-medium text-primary mb-1.5",
75
+ children: [
76
+ label,
77
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-secondary", "aria-hidden": "true", children: " *" })
78
+ ]
79
+ }
80
+ ),
56
81
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
57
82
  leftIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-muted", children: leftIcon }),
58
83
  /* @__PURE__ */ jsxRuntime.jsx(
59
84
  "input",
60
85
  {
86
+ ...props,
61
87
  ref,
88
+ id: inputId,
89
+ required,
90
+ "aria-required": ariaRequired ?? (required ? true : void 0),
91
+ "aria-invalid": ariaInvalid ?? (error ? true : void 0),
92
+ "aria-describedby": describedBy,
62
93
  className: chunkADIDI7AJ_js.cn(
63
94
  inputVariants({ size, variant: resolvedVariant }),
64
95
  leftIcon && "pl-10",
65
96
  rightIcon && "pr-10",
66
97
  className
67
- ),
68
- ...props
98
+ )
69
99
  }
70
100
  ),
71
101
  rightIcon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-text-muted", children: rightIcon })
72
102
  ] }),
73
- error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-red-500", children: error })
103
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "mt-1 text-sm text-red-500", children: error })
74
104
  ] });
75
105
  }
76
106
  );
@@ -128,7 +128,7 @@ var Footer = React__namespace.forwardRef(
128
128
  bottomExtra
129
129
  ] }),
130
130
  tagline && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-white/60", children: tagline }),
131
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60", children: copyright || `\xA9 ${year} DodoDog. Tous droits r\xE9serv\xE9s.` })
131
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/60", children: copyright || `\xA9 ${year} ToutouTrip. Tous droits r\xE9serv\xE9s.` })
132
132
  ] }) }) })
133
133
  ] });
134
134
  }
@@ -24,13 +24,89 @@ function _interopNamespace(e) {
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
26
26
 
27
+ var FOCUSABLE_SELECTOR = [
28
+ "a[href]",
29
+ "area[href]",
30
+ "button:not([disabled])",
31
+ "input:not([disabled]):not([type='hidden'])",
32
+ "select:not([disabled])",
33
+ "textarea:not([disabled])",
34
+ "iframe",
35
+ "audio[controls]",
36
+ "video[controls]",
37
+ "[contenteditable]:not([contenteditable='false'])",
38
+ '[tabindex]:not([tabindex="-1"])'
39
+ ].join(", ");
40
+ function getFocusableElements(panel) {
41
+ return Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
42
+ (element) => !element.closest("[hidden], [aria-hidden='true'], details:not([open])")
43
+ );
44
+ }
45
+ function isTopMostDialog(panel) {
46
+ const dialogs = document.querySelectorAll('[role="dialog"][aria-modal="true"]');
47
+ const topMost = dialogs[dialogs.length - 1];
48
+ return !topMost || topMost.contains(panel);
49
+ }
27
50
  var Modal = React__namespace.forwardRef(
28
- ({ isOpen, onClose, children, title, className }, ref) => {
29
- const contentRef = React__namespace.useRef(null);
51
+ ({
52
+ isOpen,
53
+ onClose,
54
+ children,
55
+ title,
56
+ className,
57
+ "aria-label": ariaLabel,
58
+ "aria-labelledby": ariaLabelledBy,
59
+ "aria-describedby": ariaDescribedBy
60
+ }, ref) => {
61
+ const panelRef = React__namespace.useRef(null);
62
+ const generatedTitleId = React__namespace.useId();
63
+ const titleId = title ? `${generatedTitleId}-title` : void 0;
64
+ const labelledBy = ariaLabelledBy ?? titleId;
30
65
  React__namespace.useEffect(() => {
31
66
  if (!isOpen) return;
32
- const handleKeyDown = (e) => {
33
- if (e.key === "Escape") onClose();
67
+ const trigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
68
+ return () => {
69
+ if (trigger && trigger.isConnected) trigger.focus();
70
+ };
71
+ }, [isOpen]);
72
+ React__namespace.useEffect(() => {
73
+ if (!isOpen) return;
74
+ const panel = panelRef.current;
75
+ if (!panel) return;
76
+ const focusable = getFocusableElements(panel);
77
+ (focusable[0] ?? panel).focus();
78
+ }, [isOpen]);
79
+ React__namespace.useEffect(() => {
80
+ if (!isOpen) return;
81
+ const handleKeyDown = (event) => {
82
+ const panel = panelRef.current;
83
+ if (!panel || !isTopMostDialog(panel)) return;
84
+ if (event.key === "Escape") {
85
+ onClose();
86
+ return;
87
+ }
88
+ if (event.key !== "Tab") return;
89
+ const focusable = getFocusableElements(panel);
90
+ const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
91
+ if (focusable.length === 0) {
92
+ event.preventDefault();
93
+ panel.focus();
94
+ return;
95
+ }
96
+ const first = focusable[0];
97
+ const last = focusable[focusable.length - 1];
98
+ if (!active || !panel.contains(active)) {
99
+ event.preventDefault();
100
+ (event.shiftKey ? last : first).focus();
101
+ return;
102
+ }
103
+ if (event.shiftKey && active === first) {
104
+ event.preventDefault();
105
+ last.focus();
106
+ } else if (!event.shiftKey && active === last) {
107
+ event.preventDefault();
108
+ first.focus();
109
+ }
34
110
  };
35
111
  document.addEventListener("keydown", handleKeyDown);
36
112
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -43,13 +119,6 @@ var Modal = React__namespace.forwardRef(
43
119
  document.body.style.overflow = prev;
44
120
  };
45
121
  }, [isOpen]);
46
- React__namespace.useEffect(() => {
47
- if (!isOpen || !contentRef.current) return;
48
- const focusable = contentRef.current.querySelectorAll(
49
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
50
- );
51
- if (focusable.length > 0) focusable[0].focus();
52
- }, [isOpen]);
53
122
  if (!isOpen) return null;
54
123
  return /* @__PURE__ */ jsxRuntime.jsxs(
55
124
  "div",
@@ -58,7 +127,9 @@ var Modal = React__namespace.forwardRef(
58
127
  className: "fixed inset-0 z-50 flex items-center justify-center p-4",
59
128
  role: "dialog",
60
129
  "aria-modal": "true",
61
- "aria-label": title,
130
+ "aria-labelledby": labelledBy,
131
+ "aria-label": labelledBy ? void 0 : ariaLabel,
132
+ "aria-describedby": ariaDescribedBy,
62
133
  children: [
63
134
  /* @__PURE__ */ jsxRuntime.jsx(
64
135
  "div",
@@ -71,14 +142,22 @@ var Modal = React__namespace.forwardRef(
71
142
  /* @__PURE__ */ jsxRuntime.jsxs(
72
143
  "div",
73
144
  {
74
- ref: contentRef,
145
+ ref: panelRef,
146
+ tabIndex: -1,
75
147
  className: chunkADIDI7AJ_js.cn(
76
- "relative bg-white rounded-2xl shadow-xl max-w-md w-full max-h-[90vh] overflow-auto",
148
+ "relative bg-white rounded-2xl shadow-xl max-w-md w-full max-h-[90vh] overflow-auto outline-none",
77
149
  className
78
150
  ),
79
151
  children: [
80
152
  title && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 pt-6 pb-2", children: [
81
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-heading font-semibold text-primary", children: title }),
153
+ /* @__PURE__ */ jsxRuntime.jsx(
154
+ "h2",
155
+ {
156
+ id: titleId,
157
+ className: "text-lg font-heading font-semibold text-primary",
158
+ children: title
159
+ }
160
+ ),
82
161
  /* @__PURE__ */ jsxRuntime.jsx(
83
162
  "button",
84
163
  {
@@ -24,31 +24,61 @@ var inputVariants = cva(
24
24
  }
25
25
  );
26
26
  var Input = React.forwardRef(
27
- ({ className, size, variant, label, error, required, leftIcon, rightIcon, ...props }, ref) => {
27
+ ({
28
+ className,
29
+ size,
30
+ variant,
31
+ label,
32
+ error,
33
+ required,
34
+ leftIcon,
35
+ rightIcon,
36
+ id,
37
+ "aria-describedby": ariaDescribedBy,
38
+ "aria-invalid": ariaInvalid,
39
+ "aria-required": ariaRequired,
40
+ ...props
41
+ }, ref) => {
42
+ const generatedId = React.useId();
43
+ const inputId = id ?? generatedId;
44
+ const errorId = `${inputId}-error`;
28
45
  const resolvedVariant = error ? "error" : variant ?? "default";
46
+ const describedBy = [ariaDescribedBy, error ? errorId : null].filter(Boolean).join(" ") || void 0;
29
47
  return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
30
- label && /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium text-primary mb-1.5", children: [
31
- label,
32
- required && /* @__PURE__ */ jsx("span", { className: "text-secondary", children: " *" })
33
- ] }),
48
+ label && /* @__PURE__ */ jsxs(
49
+ "label",
50
+ {
51
+ htmlFor: inputId,
52
+ className: "block text-sm font-medium text-primary mb-1.5",
53
+ children: [
54
+ label,
55
+ required && /* @__PURE__ */ jsx("span", { className: "text-secondary", "aria-hidden": "true", children: " *" })
56
+ ]
57
+ }
58
+ ),
34
59
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
35
60
  leftIcon && /* @__PURE__ */ jsx("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-muted", children: leftIcon }),
36
61
  /* @__PURE__ */ jsx(
37
62
  "input",
38
63
  {
64
+ ...props,
39
65
  ref,
66
+ id: inputId,
67
+ required,
68
+ "aria-required": ariaRequired ?? (required ? true : void 0),
69
+ "aria-invalid": ariaInvalid ?? (error ? true : void 0),
70
+ "aria-describedby": describedBy,
40
71
  className: cn(
41
72
  inputVariants({ size, variant: resolvedVariant }),
42
73
  leftIcon && "pl-10",
43
74
  rightIcon && "pr-10",
44
75
  className
45
- ),
46
- ...props
76
+ )
47
77
  }
48
78
  ),
49
79
  rightIcon && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-text-muted", children: rightIcon })
50
80
  ] }),
51
- error && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-red-500", children: error })
81
+ error && /* @__PURE__ */ jsx("p", { id: errorId, className: "mt-1 text-sm text-red-500", children: error })
52
82
  ] });
53
83
  }
54
84
  );
@@ -0,0 +1,178 @@
1
+ import { cn } from './chunk-IMKLN273.mjs';
2
+ import * as React from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ var FOCUSABLE_SELECTOR = [
6
+ "a[href]",
7
+ "area[href]",
8
+ "button:not([disabled])",
9
+ "input:not([disabled]):not([type='hidden'])",
10
+ "select:not([disabled])",
11
+ "textarea:not([disabled])",
12
+ "iframe",
13
+ "audio[controls]",
14
+ "video[controls]",
15
+ "[contenteditable]:not([contenteditable='false'])",
16
+ '[tabindex]:not([tabindex="-1"])'
17
+ ].join(", ");
18
+ function getFocusableElements(panel) {
19
+ return Array.from(panel.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
20
+ (element) => !element.closest("[hidden], [aria-hidden='true'], details:not([open])")
21
+ );
22
+ }
23
+ function isTopMostDialog(panel) {
24
+ const dialogs = document.querySelectorAll('[role="dialog"][aria-modal="true"]');
25
+ const topMost = dialogs[dialogs.length - 1];
26
+ return !topMost || topMost.contains(panel);
27
+ }
28
+ var Modal = React.forwardRef(
29
+ ({
30
+ isOpen,
31
+ onClose,
32
+ children,
33
+ title,
34
+ className,
35
+ "aria-label": ariaLabel,
36
+ "aria-labelledby": ariaLabelledBy,
37
+ "aria-describedby": ariaDescribedBy
38
+ }, ref) => {
39
+ const panelRef = React.useRef(null);
40
+ const generatedTitleId = React.useId();
41
+ const titleId = title ? `${generatedTitleId}-title` : void 0;
42
+ const labelledBy = ariaLabelledBy ?? titleId;
43
+ React.useEffect(() => {
44
+ if (!isOpen) return;
45
+ const trigger = document.activeElement instanceof HTMLElement ? document.activeElement : null;
46
+ return () => {
47
+ if (trigger && trigger.isConnected) trigger.focus();
48
+ };
49
+ }, [isOpen]);
50
+ React.useEffect(() => {
51
+ if (!isOpen) return;
52
+ const panel = panelRef.current;
53
+ if (!panel) return;
54
+ const focusable = getFocusableElements(panel);
55
+ (focusable[0] ?? panel).focus();
56
+ }, [isOpen]);
57
+ React.useEffect(() => {
58
+ if (!isOpen) return;
59
+ const handleKeyDown = (event) => {
60
+ const panel = panelRef.current;
61
+ if (!panel || !isTopMostDialog(panel)) return;
62
+ if (event.key === "Escape") {
63
+ onClose();
64
+ return;
65
+ }
66
+ if (event.key !== "Tab") return;
67
+ const focusable = getFocusableElements(panel);
68
+ const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
69
+ if (focusable.length === 0) {
70
+ event.preventDefault();
71
+ panel.focus();
72
+ return;
73
+ }
74
+ const first = focusable[0];
75
+ const last = focusable[focusable.length - 1];
76
+ if (!active || !panel.contains(active)) {
77
+ event.preventDefault();
78
+ (event.shiftKey ? last : first).focus();
79
+ return;
80
+ }
81
+ if (event.shiftKey && active === first) {
82
+ event.preventDefault();
83
+ last.focus();
84
+ } else if (!event.shiftKey && active === last) {
85
+ event.preventDefault();
86
+ first.focus();
87
+ }
88
+ };
89
+ document.addEventListener("keydown", handleKeyDown);
90
+ return () => document.removeEventListener("keydown", handleKeyDown);
91
+ }, [isOpen, onClose]);
92
+ React.useEffect(() => {
93
+ if (!isOpen) return;
94
+ const prev = document.body.style.overflow;
95
+ document.body.style.overflow = "hidden";
96
+ return () => {
97
+ document.body.style.overflow = prev;
98
+ };
99
+ }, [isOpen]);
100
+ if (!isOpen) return null;
101
+ return /* @__PURE__ */ jsxs(
102
+ "div",
103
+ {
104
+ ref,
105
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4",
106
+ role: "dialog",
107
+ "aria-modal": "true",
108
+ "aria-labelledby": labelledBy,
109
+ "aria-label": labelledBy ? void 0 : ariaLabel,
110
+ "aria-describedby": ariaDescribedBy,
111
+ children: [
112
+ /* @__PURE__ */ jsx(
113
+ "div",
114
+ {
115
+ className: "absolute inset-0 bg-black/50",
116
+ onClick: onClose,
117
+ "aria-hidden": "true"
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsxs(
121
+ "div",
122
+ {
123
+ ref: panelRef,
124
+ tabIndex: -1,
125
+ className: cn(
126
+ "relative bg-white rounded-2xl shadow-xl max-w-md w-full max-h-[90vh] overflow-auto outline-none",
127
+ className
128
+ ),
129
+ children: [
130
+ title && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 pt-6 pb-2", children: [
131
+ /* @__PURE__ */ jsx(
132
+ "h2",
133
+ {
134
+ id: titleId,
135
+ className: "text-lg font-heading font-semibold text-primary",
136
+ children: title
137
+ }
138
+ ),
139
+ /* @__PURE__ */ jsx(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: onClose,
144
+ className: "text-text-muted hover:text-primary transition-colors",
145
+ "aria-label": "Fermer",
146
+ children: /* @__PURE__ */ jsxs(
147
+ "svg",
148
+ {
149
+ xmlns: "http://www.w3.org/2000/svg",
150
+ width: "20",
151
+ height: "20",
152
+ viewBox: "0 0 24 24",
153
+ fill: "none",
154
+ stroke: "currentColor",
155
+ strokeWidth: "2",
156
+ strokeLinecap: "round",
157
+ strokeLinejoin: "round",
158
+ children: [
159
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
160
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
161
+ ]
162
+ }
163
+ )
164
+ }
165
+ )
166
+ ] }),
167
+ /* @__PURE__ */ jsx("div", { className: cn(title ? "px-6 pb-6" : "p-6"), children })
168
+ ]
169
+ }
170
+ )
171
+ ]
172
+ }
173
+ );
174
+ }
175
+ );
176
+ Modal.displayName = "Modal";
177
+
178
+ export { Modal };
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkC4RZBOKH_js = require('../../chunk-C4RZBOKH.js');
3
+ var chunkEQPT6VZF_js = require('../../chunk-EQPT6VZF.js');
4
4
  require('../../chunk-ADIDI7AJ.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "Footer", {
9
9
  enumerable: true,
10
- get: function () { return chunkC4RZBOKH_js.Footer; }
10
+ get: function () { return chunkEQPT6VZF_js.Footer; }
11
11
  });
@@ -1,2 +1,2 @@
1
- export { Footer } from '../../chunk-SZ3SV4SJ.mjs';
1
+ export { Footer } from '../../chunk-4DHZ6MIL.mjs';
2
2
  import '../../chunk-IMKLN273.mjs';
@@ -1,15 +1,15 @@
1
1
  'use strict';
2
2
 
3
- var chunk77WKG2D7_js = require('../../chunk-77WKG2D7.js');
3
+ var chunk5UNG2O64_js = require('../../chunk-5UNG2O64.js');
4
4
  require('../../chunk-ADIDI7AJ.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "Input", {
9
9
  enumerable: true,
10
- get: function () { return chunk77WKG2D7_js.Input; }
10
+ get: function () { return chunk5UNG2O64_js.Input; }
11
11
  });
12
12
  Object.defineProperty(exports, "inputVariants", {
13
13
  enumerable: true,
14
- get: function () { return chunk77WKG2D7_js.inputVariants; }
14
+ get: function () { return chunk5UNG2O64_js.inputVariants; }
15
15
  });
@@ -1,2 +1,2 @@
1
- export { Input, inputVariants } from '../../chunk-GRG4USTC.mjs';
1
+ export { Input, inputVariants } from '../../chunk-JWOV5H6G.mjs';
2
2
  import '../../chunk-IMKLN273.mjs';
@@ -6,6 +6,18 @@ interface ModalProps {
6
6
  children: React.ReactNode;
7
7
  title?: string;
8
8
  className?: string;
9
+ /**
10
+ * Nom accessible quand aucun `title` n'est rendu. Ignore si `title` est
11
+ * fourni : le titre visible sert alors de nom via `aria-labelledby`.
12
+ */
13
+ "aria-label"?: string;
14
+ /**
15
+ * Id d'un titre rendu par l'appelant (dialogue sans `title`). Prioritaire
16
+ * sur l'id genere en interne.
17
+ */
18
+ "aria-labelledby"?: string;
19
+ /** Id d'une description du dialogue, relayee telle quelle. */
20
+ "aria-describedby"?: string;
9
21
  }
10
22
  declare const Modal: React.ForwardRefExoticComponent<ModalProps & React.RefAttributes<HTMLDivElement>>;
11
23
 
@@ -6,6 +6,18 @@ interface ModalProps {
6
6
  children: React.ReactNode;
7
7
  title?: string;
8
8
  className?: string;
9
+ /**
10
+ * Nom accessible quand aucun `title` n'est rendu. Ignore si `title` est
11
+ * fourni : le titre visible sert alors de nom via `aria-labelledby`.
12
+ */
13
+ "aria-label"?: string;
14
+ /**
15
+ * Id d'un titre rendu par l'appelant (dialogue sans `title`). Prioritaire
16
+ * sur l'id genere en interne.
17
+ */
18
+ "aria-labelledby"?: string;
19
+ /** Id d'une description du dialogue, relayee telle quelle. */
20
+ "aria-describedby"?: string;
9
21
  }
10
22
  declare const Modal: React.ForwardRefExoticComponent<ModalProps & React.RefAttributes<HTMLDivElement>>;
11
23
 
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkNBDPT3XQ_js = require('../../chunk-NBDPT3XQ.js');
3
+ var chunkHH4X2JYO_js = require('../../chunk-HH4X2JYO.js');
4
4
  require('../../chunk-ADIDI7AJ.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "Modal", {
9
9
  enumerable: true,
10
- get: function () { return chunkNBDPT3XQ_js.Modal; }
10
+ get: function () { return chunkHH4X2JYO_js.Modal; }
11
11
  });
@@ -1,2 +1,2 @@
1
- export { Modal } from '../../chunk-74B2EGKT.mjs';
1
+ export { Modal } from '../../chunk-N5ZNXNCO.mjs';
2
2
  import '../../chunk-IMKLN273.mjs';
package/dist/index.d.mts CHANGED
@@ -164,8 +164,14 @@ interface ReviewSummaryProps extends React.HTMLAttributes<HTMLElement> {
164
164
  sources?: string[];
165
165
  /** Sous-notes détaillées (/10) */
166
166
  subScores?: ReviewSubScore[];
167
- /** Avis mis en avant */
167
+ /** Avis mis en avant (liste COMPLÈTE : la pagination « Voir plus » est gérée en interne) */
168
168
  reviews?: ReviewItem[];
169
+ /** Nombre d'avis affichés initialement (défaut : 8). Le reste est déplié via « Voir plus d'avis ». */
170
+ initialVisibleReviews?: number;
171
+ /** Pas de déploiement à chaque clic sur « Voir plus d'avis » (défaut : 8). */
172
+ reviewsStep?: number;
173
+ /** Libellé du bouton de déploiement progressif (défaut : « Voir plus d'avis »). */
174
+ moreReviewsLabel?: string;
169
175
  /** Callback bouton "Afficher tous les avis" */
170
176
  onSeeAll?: () => void;
171
177
  /** Libellé du bouton (par défaut dérivé du count) */
@@ -177,7 +183,7 @@ interface ReviewSummaryProps extends React.HTMLAttributes<HTMLElement> {
177
183
  * Bloc avis consolidé de la fiche hébergement (échelle /10 unique).
178
184
  * Remplace les blocs avis redondants. N'affiche que des sources réelles.
179
185
  */
180
- declare function ReviewSummary({ score, scoreLabel, count, title, countLabel, sources, subScores, reviews, onSeeAll, seeAllLabel, emptyLabel, className, ...props }: ReviewSummaryProps): react_jsx_runtime.JSX.Element;
186
+ declare function ReviewSummary({ score, scoreLabel, count, title, countLabel, sources, subScores, reviews, initialVisibleReviews, reviewsStep, moreReviewsLabel, onSeeAll, seeAllLabel, emptyLabel, className, ...props }: ReviewSummaryProps): react_jsx_runtime.JSX.Element;
181
187
 
182
188
  interface MobileBookingBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect"> {
183
189
  /** Prix total déjà formaté, ex: "248 €" */
package/dist/index.d.ts CHANGED
@@ -164,8 +164,14 @@ interface ReviewSummaryProps extends React.HTMLAttributes<HTMLElement> {
164
164
  sources?: string[];
165
165
  /** Sous-notes détaillées (/10) */
166
166
  subScores?: ReviewSubScore[];
167
- /** Avis mis en avant */
167
+ /** Avis mis en avant (liste COMPLÈTE : la pagination « Voir plus » est gérée en interne) */
168
168
  reviews?: ReviewItem[];
169
+ /** Nombre d'avis affichés initialement (défaut : 8). Le reste est déplié via « Voir plus d'avis ». */
170
+ initialVisibleReviews?: number;
171
+ /** Pas de déploiement à chaque clic sur « Voir plus d'avis » (défaut : 8). */
172
+ reviewsStep?: number;
173
+ /** Libellé du bouton de déploiement progressif (défaut : « Voir plus d'avis »). */
174
+ moreReviewsLabel?: string;
169
175
  /** Callback bouton "Afficher tous les avis" */
170
176
  onSeeAll?: () => void;
171
177
  /** Libellé du bouton (par défaut dérivé du count) */
@@ -177,7 +183,7 @@ interface ReviewSummaryProps extends React.HTMLAttributes<HTMLElement> {
177
183
  * Bloc avis consolidé de la fiche hébergement (échelle /10 unique).
178
184
  * Remplace les blocs avis redondants. N'affiche que des sources réelles.
179
185
  */
180
- declare function ReviewSummary({ score, scoreLabel, count, title, countLabel, sources, subScores, reviews, onSeeAll, seeAllLabel, emptyLabel, className, ...props }: ReviewSummaryProps): react_jsx_runtime.JSX.Element;
186
+ declare function ReviewSummary({ score, scoreLabel, count, title, countLabel, sources, subScores, reviews, initialVisibleReviews, reviewsStep, moreReviewsLabel, onSeeAll, seeAllLabel, emptyLabel, className, ...props }: ReviewSummaryProps): react_jsx_runtime.JSX.Element;
181
187
 
182
188
  interface MobileBookingBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onSelect"> {
183
189
  /** Prix total déjà formaté, ex: "248 €" */