@dododog/ui 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{chunk-SZ3SV4SJ.mjs → chunk-4DHZ6MIL.mjs} +1 -1
- package/dist/chunk-5KOWMIWK.mjs +110 -0
- package/dist/{chunk-77WKG2D7.js → chunk-5UNG2O64.js} +38 -8
- package/dist/{chunk-MCF3EQTV.js → chunk-73BHQJW6.js} +13 -1
- package/dist/{chunk-U3XWNFHH.js → chunk-A3M3XQ35.js} +96 -29
- package/dist/chunk-BKM5VCUS.mjs +569 -0
- package/dist/{chunk-333YD5QI.mjs → chunk-EAATIFYL.mjs} +40 -6
- package/dist/{chunk-C4RZBOKH.js → chunk-EQPT6VZF.js} +1 -1
- package/dist/chunk-FLKEMER2.js +592 -0
- package/dist/{chunk-NBDPT3XQ.js → chunk-HH4X2JYO.js} +94 -15
- package/dist/{chunk-I4S3R6C6.js → chunk-JUB3NGJR.js} +51 -6
- package/dist/{chunk-GRG4USTC.mjs → chunk-JWOV5H6G.mjs} +38 -8
- package/dist/chunk-JWQ2OUEG.js +132 -0
- package/dist/{chunk-3R7PT3JG.js → chunk-LJSMPON7.js} +40 -6
- package/dist/chunk-N5ZNXNCO.mjs +178 -0
- package/dist/{chunk-DMCMWOBJ.mjs → chunk-PBXH4I5N.mjs} +51 -6
- package/dist/{chunk-ERL3WXNY.mjs → chunk-PZZWQ6IG.mjs} +5 -5
- package/dist/{chunk-GBWVIY3C.mjs → chunk-UJEIFKSK.mjs} +96 -29
- package/dist/chunk-WY24TZ4R.mjs +94 -0
- package/dist/chunk-XBNIHUSG.js +116 -0
- package/dist/{chunk-TDGU5Y2P.js → chunk-XOFTU3T7.js} +5 -5
- package/dist/{chunk-NZ7GF6RF.mjs → chunk-YOMOULLR.mjs} +13 -1
- package/dist/components/Accordion/index.js +2 -2
- package/dist/components/Accordion/index.mjs +1 -1
- package/dist/components/Counter/index.d.mts +12 -0
- package/dist/components/Counter/index.d.ts +12 -0
- package/dist/components/Counter/index.js +2 -2
- package/dist/components/Counter/index.mjs +1 -1
- package/dist/components/DateRangePicker/index.d.mts +7 -1
- package/dist/components/DateRangePicker/index.d.ts +7 -1
- package/dist/components/DateRangePicker/index.js +3 -3
- package/dist/components/DateRangePicker/index.mjs +1 -1
- package/dist/components/Drawer/index.d.mts +21 -1
- package/dist/components/Drawer/index.d.ts +21 -1
- package/dist/components/Drawer/index.js +2 -2
- package/dist/components/Drawer/index.mjs +1 -1
- package/dist/components/DualRangeSlider/index.js +2 -2
- package/dist/components/DualRangeSlider/index.mjs +1 -1
- package/dist/components/Footer/index.js +2 -2
- package/dist/components/Footer/index.mjs +1 -1
- package/dist/components/Input/index.js +3 -3
- package/dist/components/Input/index.mjs +1 -1
- package/dist/components/Lightbox/index.d.mts +2 -0
- package/dist/components/Lightbox/index.d.ts +2 -0
- package/dist/components/Lightbox/index.js +2 -2
- package/dist/components/Lightbox/index.mjs +1 -1
- package/dist/components/Modal/index.d.mts +12 -0
- package/dist/components/Modal/index.d.ts +12 -0
- package/dist/components/Modal/index.js +2 -2
- package/dist/components/Modal/index.mjs +1 -1
- package/dist/components/Select/index.d.mts +8 -0
- package/dist/components/Select/index.d.ts +8 -0
- package/dist/components/Select/index.js +3 -3
- package/dist/components/Select/index.mjs +1 -1
- package/dist/components/Switch/index.d.mts +41 -1
- package/dist/components/Switch/index.d.ts +41 -1
- package/dist/components/Switch/index.js +4 -4
- package/dist/components/Switch/index.mjs +1 -1
- package/dist/index.js +45 -45
- package/dist/index.mjs +14 -14
- package/dist/tailwind-preset.d.mts +2 -2
- package/dist/tailwind-preset.d.ts +2 -2
- package/dist/tailwind-preset.js +2 -2
- package/dist/tailwind-preset.mjs +2 -2
- package/package.json +14 -14
- package/dist/chunk-74B2EGKT.mjs +0 -99
- package/dist/chunk-AIA3NHCK.js +0 -373
- package/dist/chunk-FL3GD5FJ.mjs +0 -350
- package/dist/chunk-LRNSVRUN.mjs +0 -55
- package/dist/chunk-T4AT3YCT.js +0 -77
- package/dist/chunk-VWFY24XF.js +0 -89
- package/dist/chunk-ZLF7IL3Y.mjs +0 -67
|
@@ -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 };
|
|
@@ -32,17 +32,47 @@ var switchThumbVariants = cva(
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
var Switch = React.forwardRef(
|
|
35
|
-
({
|
|
36
|
-
|
|
35
|
+
({
|
|
36
|
+
checked = false,
|
|
37
|
+
onCheckedChange,
|
|
38
|
+
disabled = false,
|
|
39
|
+
size,
|
|
40
|
+
label,
|
|
41
|
+
description,
|
|
42
|
+
className,
|
|
43
|
+
name,
|
|
44
|
+
id,
|
|
45
|
+
"aria-label": ariaLabel,
|
|
46
|
+
"aria-labelledby": ariaLabelledBy,
|
|
47
|
+
"aria-describedby": ariaDescribedBy
|
|
48
|
+
}, ref) => {
|
|
49
|
+
const generatedId = React.useId();
|
|
50
|
+
const switchId = id ?? generatedId;
|
|
51
|
+
const labelId = `${switchId}-label`;
|
|
52
|
+
const descriptionId = `${switchId}-description`;
|
|
53
|
+
const labelledBy = [label ? labelId : null, ariaLabelledBy].filter(Boolean).join(" ") || void 0;
|
|
54
|
+
const resolvedAriaLabel = labelledBy ? void 0 : ariaLabel ?? name;
|
|
55
|
+
const describedBy = [description ? descriptionId : null, ariaDescribedBy].filter(Boolean).join(" ") || void 0;
|
|
56
|
+
const hasAccessibleName = Boolean(labelledBy || resolvedAriaLabel);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (hasAccessibleName) return;
|
|
59
|
+
if (typeof process === "undefined") return;
|
|
60
|
+
if (process.env.NODE_ENV === "production") return;
|
|
61
|
+
console.warn(
|
|
62
|
+
"[@dododog/ui] Switch sans nom accessible : passer `label`, `aria-labelledby` ou `aria-label`."
|
|
63
|
+
);
|
|
64
|
+
}, [hasAccessibleName]);
|
|
37
65
|
const track = /* @__PURE__ */ jsxs(
|
|
38
66
|
"button",
|
|
39
67
|
{
|
|
40
68
|
ref,
|
|
41
|
-
id,
|
|
69
|
+
id: switchId,
|
|
42
70
|
type: "button",
|
|
43
71
|
role: "switch",
|
|
44
72
|
"aria-checked": checked,
|
|
45
|
-
"aria-
|
|
73
|
+
"aria-labelledby": labelledBy,
|
|
74
|
+
"aria-label": resolvedAriaLabel,
|
|
75
|
+
"aria-describedby": describedBy,
|
|
46
76
|
disabled,
|
|
47
77
|
onClick: () => onCheckedChange?.(!checked),
|
|
48
78
|
className: cn(
|
|
@@ -69,8 +99,23 @@ var Switch = React.forwardRef(
|
|
|
69
99
|
if (!label && !description) return track;
|
|
70
100
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between gap-3", className), children: [
|
|
71
101
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
72
|
-
label && /* @__PURE__ */ jsx(
|
|
73
|
-
|
|
102
|
+
label && /* @__PURE__ */ jsx(
|
|
103
|
+
"label",
|
|
104
|
+
{
|
|
105
|
+
id: labelId,
|
|
106
|
+
htmlFor: switchId,
|
|
107
|
+
className: "text-sm font-medium text-primary cursor-pointer",
|
|
108
|
+
children: label
|
|
109
|
+
}
|
|
110
|
+
),
|
|
111
|
+
description && /* @__PURE__ */ jsx(
|
|
112
|
+
"p",
|
|
113
|
+
{
|
|
114
|
+
id: descriptionId,
|
|
115
|
+
className: "text-xs text-text-secondary mt-0.5",
|
|
116
|
+
children: description
|
|
117
|
+
}
|
|
118
|
+
)
|
|
74
119
|
] }),
|
|
75
120
|
track
|
|
76
121
|
] });
|
|
@@ -30,12 +30,12 @@ var DualRangeSlider = React.forwardRef(
|
|
|
30
30
|
const labelMin = formatLabel ? formatLabel(valueMin, "min") : String(valueMin);
|
|
31
31
|
const labelMax = formatLabel ? formatLabel(valueMax, "max") : String(valueMax);
|
|
32
32
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", className), children: [
|
|
33
|
-
/* @__PURE__ */ jsxs("div", { className: "relative h-
|
|
34
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-gray-200" }),
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "relative h-6", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 rounded-full bg-gray-200" }),
|
|
35
35
|
/* @__PURE__ */ jsx(
|
|
36
36
|
"div",
|
|
37
37
|
{
|
|
38
|
-
className: "absolute h-
|
|
38
|
+
className: "absolute top-1/2 h-2 -translate-y-1/2 rounded-full bg-secondary",
|
|
39
39
|
style: { left: `${leftPercent}%`, right: `${rightPercent}%` }
|
|
40
40
|
}
|
|
41
41
|
),
|
|
@@ -50,7 +50,7 @@ var DualRangeSlider = React.forwardRef(
|
|
|
50
50
|
onChange: handleMinChange,
|
|
51
51
|
"aria-label": "Minimum",
|
|
52
52
|
className: cn(
|
|
53
|
-
"absolute
|
|
53
|
+
"absolute inset-0 h-6 w-full appearance-none bg-transparent pointer-events-none",
|
|
54
54
|
// Webkit thumb
|
|
55
55
|
"[&::-webkit-slider-thumb]:pointer-events-auto",
|
|
56
56
|
"[&::-webkit-slider-thumb]:appearance-none",
|
|
@@ -82,7 +82,7 @@ var DualRangeSlider = React.forwardRef(
|
|
|
82
82
|
onChange: handleMaxChange,
|
|
83
83
|
"aria-label": "Maximum",
|
|
84
84
|
className: cn(
|
|
85
|
-
"absolute
|
|
85
|
+
"absolute inset-0 h-6 w-full appearance-none bg-transparent pointer-events-none",
|
|
86
86
|
"[&::-webkit-slider-thumb]:pointer-events-auto",
|
|
87
87
|
"[&::-webkit-slider-thumb]:appearance-none",
|
|
88
88
|
"[&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:h-5",
|
|
@@ -2,10 +2,30 @@ import { cn } from './chunk-IMKLN273.mjs';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
|
+
var FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
5
6
|
var Lightbox = React.forwardRef(
|
|
6
|
-
({
|
|
7
|
+
({
|
|
8
|
+
images,
|
|
9
|
+
initialIndex = 0,
|
|
10
|
+
open,
|
|
11
|
+
onClose,
|
|
12
|
+
className,
|
|
13
|
+
"aria-label": ariaLabel = "Visionneuse d'images"
|
|
14
|
+
}, ref) => {
|
|
7
15
|
const [current, setCurrent] = React.useState(initialIndex);
|
|
8
16
|
const touchStartX = React.useRef(0);
|
|
17
|
+
const containerRef = React.useRef(null);
|
|
18
|
+
const setRefs = React.useCallback(
|
|
19
|
+
(node) => {
|
|
20
|
+
containerRef.current = node;
|
|
21
|
+
if (typeof ref === "function") {
|
|
22
|
+
ref(node);
|
|
23
|
+
} else if (ref) {
|
|
24
|
+
ref.current = node;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
[ref]
|
|
28
|
+
);
|
|
9
29
|
React.useEffect(() => {
|
|
10
30
|
if (open) setCurrent(initialIndex);
|
|
11
31
|
}, [open, initialIndex]);
|
|
@@ -15,6 +35,28 @@ var Lightbox = React.forwardRef(
|
|
|
15
35
|
if (e.key === "Escape") onClose();
|
|
16
36
|
if (e.key === "ArrowLeft") setCurrent((c) => (c - 1 + images.length) % images.length);
|
|
17
37
|
if (e.key === "ArrowRight") setCurrent((c) => (c + 1) % images.length);
|
|
38
|
+
if (e.key === "Tab") {
|
|
39
|
+
const node = containerRef.current;
|
|
40
|
+
if (!node) return;
|
|
41
|
+
const items = Array.from(
|
|
42
|
+
node.querySelectorAll(FOCUSABLE_SELECTOR)
|
|
43
|
+
);
|
|
44
|
+
if (items.length === 0) {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const first = items[0];
|
|
49
|
+
const last = items[items.length - 1];
|
|
50
|
+
const active = document.activeElement;
|
|
51
|
+
const inside = active instanceof Node && node.contains(active);
|
|
52
|
+
if (e.shiftKey && (!inside || active === first)) {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
last.focus();
|
|
55
|
+
} else if (!e.shiftKey && (!inside || active === last)) {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
first.focus();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
18
60
|
}
|
|
19
61
|
document.addEventListener("keydown", handleKeyDown);
|
|
20
62
|
document.body.style.overflow = "hidden";
|
|
@@ -23,13 +65,24 @@ var Lightbox = React.forwardRef(
|
|
|
23
65
|
document.body.style.overflow = "";
|
|
24
66
|
};
|
|
25
67
|
}, [open, images.length, onClose]);
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
if (!open) return;
|
|
70
|
+
const previouslyFocused = document.activeElement;
|
|
71
|
+
const node = containerRef.current;
|
|
72
|
+
const target = node?.querySelector(FOCUSABLE_SELECTOR) ?? node ?? null;
|
|
73
|
+
target?.focus?.();
|
|
74
|
+
return () => {
|
|
75
|
+
previouslyFocused?.focus?.();
|
|
76
|
+
};
|
|
77
|
+
}, [open]);
|
|
26
78
|
if (!open || images.length === 0) return null;
|
|
27
79
|
const prev = () => setCurrent((c) => (c - 1 + images.length) % images.length);
|
|
28
80
|
const next = () => setCurrent((c) => (c + 1) % images.length);
|
|
29
81
|
return /* @__PURE__ */ jsxs(
|
|
30
82
|
"div",
|
|
31
83
|
{
|
|
32
|
-
ref,
|
|
84
|
+
ref: setRefs,
|
|
85
|
+
tabIndex: -1,
|
|
33
86
|
className: cn(
|
|
34
87
|
"fixed inset-0 z-50 flex items-center justify-center bg-black/90",
|
|
35
88
|
className
|
|
@@ -37,14 +90,14 @@ var Lightbox = React.forwardRef(
|
|
|
37
90
|
onClick: onClose,
|
|
38
91
|
role: "dialog",
|
|
39
92
|
"aria-modal": "true",
|
|
40
|
-
"aria-label":
|
|
93
|
+
"aria-label": ariaLabel,
|
|
41
94
|
children: [
|
|
42
95
|
/* @__PURE__ */ jsx(
|
|
43
96
|
"button",
|
|
44
97
|
{
|
|
45
98
|
onClick: onClose,
|
|
46
99
|
className: "absolute top-4 right-4 z-10 w-10 h-10 rounded-full bg-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-white/20 transition-colors",
|
|
47
|
-
"aria-label": "
|
|
100
|
+
"aria-label": "Fermer la visionneuse",
|
|
48
101
|
children: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
49
102
|
/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
|
|
50
103
|
/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
|
|
@@ -52,9 +105,12 @@ var Lightbox = React.forwardRef(
|
|
|
52
105
|
}
|
|
53
106
|
),
|
|
54
107
|
/* @__PURE__ */ jsxs("div", { className: "absolute top-4 left-4 z-10 px-3 py-1.5 rounded-full bg-white/10 backdrop-blur-sm text-white text-sm", children: [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
108
|
+
/* @__PURE__ */ jsxs("span", { "aria-hidden": "true", children: [
|
|
109
|
+
current + 1,
|
|
110
|
+
" / ",
|
|
111
|
+
images.length
|
|
112
|
+
] }),
|
|
113
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", children: `Image ${current + 1} sur ${images.length}` })
|
|
58
114
|
] }),
|
|
59
115
|
/* @__PURE__ */ jsx(
|
|
60
116
|
"div",
|
|
@@ -90,7 +146,7 @@ var Lightbox = React.forwardRef(
|
|
|
90
146
|
prev();
|
|
91
147
|
},
|
|
92
148
|
className: "absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 rounded-full bg-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-white/20 transition-colors",
|
|
93
|
-
"aria-label": "
|
|
149
|
+
"aria-label": "Image pr\xE9c\xE9dente",
|
|
94
150
|
children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" }) })
|
|
95
151
|
}
|
|
96
152
|
),
|
|
@@ -102,33 +158,44 @@ var Lightbox = React.forwardRef(
|
|
|
102
158
|
next();
|
|
103
159
|
},
|
|
104
160
|
className: "absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 rounded-full bg-white/10 backdrop-blur-sm flex items-center justify-center text-white hover:bg-white/20 transition-colors",
|
|
105
|
-
"aria-label": "
|
|
161
|
+
"aria-label": "Image suivante",
|
|
106
162
|
children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }) })
|
|
107
163
|
}
|
|
108
164
|
)
|
|
109
165
|
] }),
|
|
110
|
-
images.length > 1 && /* @__PURE__ */ jsx(
|
|
111
|
-
"
|
|
166
|
+
images.length > 1 && /* @__PURE__ */ jsx(
|
|
167
|
+
"div",
|
|
112
168
|
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
"w-16 h-12 rounded-lg overflow-hidden flex-shrink-0 border-2 transition-all",
|
|
119
|
-
i === current ? "border-white opacity-100" : "border-transparent opacity-50 hover:opacity-75"
|
|
120
|
-
),
|
|
121
|
-
children: /* @__PURE__ */ jsx(
|
|
122
|
-
"img",
|
|
169
|
+
role: "group",
|
|
170
|
+
"aria-label": "Vignettes des images",
|
|
171
|
+
className: "absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 max-w-[80vw] overflow-x-auto px-4 py-2",
|
|
172
|
+
children: images.map((img, i) => /* @__PURE__ */ jsx(
|
|
173
|
+
"button",
|
|
123
174
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
175
|
+
onClick: (e) => {
|
|
176
|
+
e.stopPropagation();
|
|
177
|
+
setCurrent(i);
|
|
178
|
+
},
|
|
179
|
+
"aria-label": `Afficher l'image ${i + 1} : ${img.alt}`,
|
|
180
|
+
"aria-current": i === current ? "true" : void 0,
|
|
181
|
+
className: cn(
|
|
182
|
+
"w-16 h-12 rounded-lg overflow-hidden flex-shrink-0 border-2 transition-all",
|
|
183
|
+
i === current ? "border-white opacity-100" : "border-transparent opacity-50 hover:opacity-75"
|
|
184
|
+
),
|
|
185
|
+
children: /* @__PURE__ */ jsx(
|
|
186
|
+
"img",
|
|
187
|
+
{
|
|
188
|
+
src: img.src,
|
|
189
|
+
alt: "",
|
|
190
|
+
"aria-hidden": "true",
|
|
191
|
+
className: "w-full h-full object-cover"
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
i
|
|
196
|
+
))
|
|
197
|
+
}
|
|
198
|
+
)
|
|
132
199
|
]
|
|
133
200
|
}
|
|
134
201
|
);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cn } from './chunk-IMKLN273.mjs';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var Counter = React.forwardRef(
|
|
6
|
+
({
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
min = 0,
|
|
10
|
+
max = 99,
|
|
11
|
+
label,
|
|
12
|
+
subtitle,
|
|
13
|
+
className,
|
|
14
|
+
"aria-label": ariaLabel,
|
|
15
|
+
"aria-labelledby": ariaLabelledBy,
|
|
16
|
+
decrementLabel,
|
|
17
|
+
incrementLabel
|
|
18
|
+
}, ref) => {
|
|
19
|
+
const generatedId = React.useId();
|
|
20
|
+
const labelId = `${generatedId}-label`;
|
|
21
|
+
const canDecrement = value > min;
|
|
22
|
+
const canIncrement = value < max;
|
|
23
|
+
const labelledBy = [label ? labelId : null, ariaLabelledBy].filter(Boolean).join(" ") || void 0;
|
|
24
|
+
const groupName = label ?? ariaLabel;
|
|
25
|
+
const resolvedDecrementLabel = decrementLabel ?? (groupName ? `Diminuer ${groupName}` : "Diminuer");
|
|
26
|
+
const resolvedIncrementLabel = incrementLabel ?? (groupName ? `Augmenter ${groupName}` : "Augmenter");
|
|
27
|
+
return /* @__PURE__ */ jsxs(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
ref,
|
|
31
|
+
role: "group",
|
|
32
|
+
"aria-labelledby": labelledBy,
|
|
33
|
+
"aria-label": labelledBy ? void 0 : ariaLabel,
|
|
34
|
+
className: cn("flex items-center justify-between", className),
|
|
35
|
+
children: [
|
|
36
|
+
(label || subtitle) && /* @__PURE__ */ jsxs("div", { children: [
|
|
37
|
+
label && /* @__PURE__ */ jsx("span", { id: labelId, className: "text-sm font-medium text-primary", children: label }),
|
|
38
|
+
subtitle && /* @__PURE__ */ jsx("span", { className: "block text-xs text-text-secondary", children: subtitle })
|
|
39
|
+
] }),
|
|
40
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
"button",
|
|
43
|
+
{
|
|
44
|
+
type: "button",
|
|
45
|
+
disabled: !canDecrement,
|
|
46
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
47
|
+
onClick: () => canDecrement && onChange?.(value - 1),
|
|
48
|
+
className: cn(
|
|
49
|
+
"w-8 h-8 rounded-full border flex items-center justify-center transition-colors",
|
|
50
|
+
canDecrement ? "border-gray-300 text-primary hover:border-primary hover:bg-primary/5" : "border-gray-200 text-gray-300 cursor-not-allowed"
|
|
51
|
+
),
|
|
52
|
+
"aria-label": resolvedDecrementLabel,
|
|
53
|
+
children: /* @__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", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M5 12h14" }) })
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
/* @__PURE__ */ jsxs(
|
|
57
|
+
"span",
|
|
58
|
+
{
|
|
59
|
+
"aria-live": "polite",
|
|
60
|
+
"aria-atomic": "true",
|
|
61
|
+
className: "w-8 text-center text-sm font-medium text-primary tabular-nums",
|
|
62
|
+
children: [
|
|
63
|
+
groupName && /* @__PURE__ */ jsx("span", { className: "sr-only", children: `${groupName} : ` }),
|
|
64
|
+
value
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"button",
|
|
70
|
+
{
|
|
71
|
+
type: "button",
|
|
72
|
+
disabled: !canIncrement,
|
|
73
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
74
|
+
onClick: () => canIncrement && onChange?.(value + 1),
|
|
75
|
+
className: cn(
|
|
76
|
+
"w-8 h-8 rounded-full border flex items-center justify-center transition-colors",
|
|
77
|
+
canIncrement ? "border-gray-300 text-primary hover:border-primary hover:bg-primary/5" : "border-gray-200 text-gray-300 cursor-not-allowed"
|
|
78
|
+
),
|
|
79
|
+
"aria-label": resolvedIncrementLabel,
|
|
80
|
+
children: /* @__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", "aria-hidden": "true", children: [
|
|
81
|
+
/* @__PURE__ */ jsx("path", { d: "M5 12h14" }),
|
|
82
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5v14" })
|
|
83
|
+
] })
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
] })
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
Counter.displayName = "Counter";
|
|
93
|
+
|
|
94
|
+
export { Counter };
|