@dododog/ui 0.5.0 → 0.6.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.
@@ -0,0 +1,136 @@
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
+ variant = "underline"
16
+ }, ref) => {
17
+ const [activeKey, setActiveKey] = React.useState(items[0]?.key ?? "");
18
+ const [pinned, setPinned] = React.useState(false);
19
+ const [pinBox, setPinBox] = React.useState(null);
20
+ const wrapperRef = React.useRef(null);
21
+ React.useEffect(() => {
22
+ if (items.length === 0) return;
23
+ const ids = items.map((i) => i.key);
24
+ let raf = 0;
25
+ const update = () => {
26
+ raf = 0;
27
+ let current = ids[0];
28
+ let bestTop = -Infinity;
29
+ for (const id of ids) {
30
+ const el = document.getElementById(id);
31
+ if (!el) continue;
32
+ const top = el.getBoundingClientRect().top - offsetTop;
33
+ if (top <= 8 && top > bestTop) {
34
+ bestTop = top;
35
+ current = id;
36
+ }
37
+ }
38
+ setActiveKey((prev) => prev === current ? prev : current);
39
+ if (pinWhileSectionsVisible && wrapperRef.current) {
40
+ const wrap = wrapperRef.current.getBoundingClientRect();
41
+ let lastBottom = -Infinity;
42
+ for (const id of ids) {
43
+ const el = document.getElementById(id);
44
+ if (el) lastBottom = Math.max(lastBottom, el.getBoundingClientRect().bottom);
45
+ }
46
+ const barHeight = wrap.height || 46;
47
+ const shouldPin = wrap.top <= pinTop && lastBottom > pinTop + barHeight;
48
+ setPinned((prev) => prev === shouldPin ? prev : shouldPin);
49
+ setPinBox((prev) => {
50
+ if (!shouldPin) return prev;
51
+ if (prev && prev.left === wrap.left && prev.width === wrap.width && prev.height === barHeight) return prev;
52
+ return { left: wrap.left, width: wrap.width, height: barHeight };
53
+ });
54
+ }
55
+ };
56
+ const onScroll = () => {
57
+ if (raf) return;
58
+ raf = window.requestAnimationFrame(update);
59
+ };
60
+ update();
61
+ window.addEventListener("scroll", onScroll, { passive: true });
62
+ window.addEventListener("resize", onScroll);
63
+ return () => {
64
+ window.removeEventListener("scroll", onScroll);
65
+ window.removeEventListener("resize", onScroll);
66
+ if (raf) window.cancelAnimationFrame(raf);
67
+ };
68
+ }, [items, offsetTop, pinWhileSectionsVisible, pinTop]);
69
+ React.useEffect(() => {
70
+ if (activeKey) onActiveChange?.(activeKey);
71
+ }, [activeKey, onActiveChange]);
72
+ const handleClick = (event, key) => {
73
+ const el = typeof document !== "undefined" ? document.getElementById(key) : null;
74
+ if (!el) return;
75
+ event.preventDefault();
76
+ const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
77
+ const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
78
+ window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
79
+ setActiveKey(key);
80
+ };
81
+ const isPinned = pinWhileSectionsVisible && pinned && pinBox !== null;
82
+ return /* @__PURE__ */ jsx(
83
+ "div",
84
+ {
85
+ ref: wrapperRef,
86
+ className: containerClassName,
87
+ style: isPinned && pinBox ? { minHeight: pinBox.height } : void 0,
88
+ children: /* @__PURE__ */ jsx(
89
+ "nav",
90
+ {
91
+ ref,
92
+ "aria-label": ariaLabel,
93
+ className: cn(
94
+ variant === "pills" ? (
95
+ // Pills : chips posées sur le fond de page, sans carte ni séparateur.
96
+ "flex items-center gap-2 overflow-x-auto"
97
+ ) : "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
98
+ isPinned && "z-40 shadow-sm",
99
+ className
100
+ ),
101
+ style: isPinned && pinBox ? { position: "fixed", top: pinTop, left: pinBox.left, width: pinBox.width } : void 0,
102
+ children: items.map((item) => {
103
+ const isActive = item.key === activeKey;
104
+ return /* @__PURE__ */ jsx(
105
+ "a",
106
+ {
107
+ href: `#${item.key}`,
108
+ "aria-current": isActive ? "location" : void 0,
109
+ onClick: (event) => handleClick(event, item.key),
110
+ className: cn(
111
+ // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
112
+ // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
113
+ // via opacité (transform/opacity, aucun layout shift).
114
+ "flex min-h-[44px] items-center whitespace-nowrap px-4 text-sm outline-none transition-colors duration-200 active:opacity-70 focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-2",
115
+ variant === "pills" ? cn(
116
+ "rounded-full border",
117
+ isActive ? "border-primary bg-primary font-semibold text-white" : "border-gray-200 bg-white font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
118
+ ) : cn(
119
+ "-mb-px border-b-2",
120
+ isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
121
+ )
122
+ ),
123
+ children: item.label
124
+ },
125
+ item.key
126
+ );
127
+ })
128
+ }
129
+ )
130
+ }
131
+ );
132
+ }
133
+ );
134
+ AnchorTabs.displayName = "AnchorTabs";
135
+
136
+ export { AnchorTabs };
@@ -0,0 +1,158 @@
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
+ variant = "underline"
38
+ }, ref) => {
39
+ const [activeKey, setActiveKey] = React__namespace.useState(items[0]?.key ?? "");
40
+ const [pinned, setPinned] = React__namespace.useState(false);
41
+ const [pinBox, setPinBox] = React__namespace.useState(null);
42
+ const wrapperRef = React__namespace.useRef(null);
43
+ React__namespace.useEffect(() => {
44
+ if (items.length === 0) return;
45
+ const ids = items.map((i) => i.key);
46
+ let raf = 0;
47
+ const update = () => {
48
+ raf = 0;
49
+ let current = ids[0];
50
+ let bestTop = -Infinity;
51
+ for (const id of ids) {
52
+ const el = document.getElementById(id);
53
+ if (!el) continue;
54
+ const top = el.getBoundingClientRect().top - offsetTop;
55
+ if (top <= 8 && top > bestTop) {
56
+ bestTop = top;
57
+ current = id;
58
+ }
59
+ }
60
+ setActiveKey((prev) => prev === current ? prev : current);
61
+ if (pinWhileSectionsVisible && wrapperRef.current) {
62
+ const wrap = wrapperRef.current.getBoundingClientRect();
63
+ let lastBottom = -Infinity;
64
+ for (const id of ids) {
65
+ const el = document.getElementById(id);
66
+ if (el) lastBottom = Math.max(lastBottom, el.getBoundingClientRect().bottom);
67
+ }
68
+ const barHeight = wrap.height || 46;
69
+ const shouldPin = wrap.top <= pinTop && lastBottom > pinTop + barHeight;
70
+ setPinned((prev) => prev === shouldPin ? prev : shouldPin);
71
+ setPinBox((prev) => {
72
+ if (!shouldPin) return prev;
73
+ if (prev && prev.left === wrap.left && prev.width === wrap.width && prev.height === barHeight) return prev;
74
+ return { left: wrap.left, width: wrap.width, height: barHeight };
75
+ });
76
+ }
77
+ };
78
+ const onScroll = () => {
79
+ if (raf) return;
80
+ raf = window.requestAnimationFrame(update);
81
+ };
82
+ update();
83
+ window.addEventListener("scroll", onScroll, { passive: true });
84
+ window.addEventListener("resize", onScroll);
85
+ return () => {
86
+ window.removeEventListener("scroll", onScroll);
87
+ window.removeEventListener("resize", onScroll);
88
+ if (raf) window.cancelAnimationFrame(raf);
89
+ };
90
+ }, [items, offsetTop, pinWhileSectionsVisible, pinTop]);
91
+ React__namespace.useEffect(() => {
92
+ if (activeKey) onActiveChange?.(activeKey);
93
+ }, [activeKey, onActiveChange]);
94
+ const handleClick = (event, key) => {
95
+ const el = typeof document !== "undefined" ? document.getElementById(key) : null;
96
+ if (!el) return;
97
+ event.preventDefault();
98
+ const y = el.getBoundingClientRect().top + window.scrollY - offsetTop;
99
+ const prefersReducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
100
+ window.scrollTo({ top: y, behavior: prefersReducedMotion ? "auto" : "smooth" });
101
+ setActiveKey(key);
102
+ };
103
+ const isPinned = pinWhileSectionsVisible && pinned && pinBox !== null;
104
+ return /* @__PURE__ */ jsxRuntime.jsx(
105
+ "div",
106
+ {
107
+ ref: wrapperRef,
108
+ className: containerClassName,
109
+ style: isPinned && pinBox ? { minHeight: pinBox.height } : void 0,
110
+ children: /* @__PURE__ */ jsxRuntime.jsx(
111
+ "nav",
112
+ {
113
+ ref,
114
+ "aria-label": ariaLabel,
115
+ className: chunkADIDI7AJ_js.cn(
116
+ variant === "pills" ? (
117
+ // Pills : chips posées sur le fond de page, sans carte ni séparateur.
118
+ "flex items-center gap-2 overflow-x-auto"
119
+ ) : "flex items-stretch gap-1 overflow-x-auto border-b border-sand-dark bg-white",
120
+ isPinned && "z-40 shadow-sm",
121
+ className
122
+ ),
123
+ style: isPinned && pinBox ? { position: "fixed", top: pinTop, left: pinBox.left, width: pinBox.width } : void 0,
124
+ children: items.map((item) => {
125
+ const isActive = item.key === activeKey;
126
+ return /* @__PURE__ */ jsxRuntime.jsx(
127
+ "a",
128
+ {
129
+ href: `#${item.key}`,
130
+ "aria-current": isActive ? "location" : void 0,
131
+ onClick: (event) => handleClick(event, item.key),
132
+ className: chunkADIDI7AJ_js.cn(
133
+ // Cible tactile >= 44px (min-h-[44px] + flex centré) ; transition
134
+ // limitée aux couleurs (150-300ms, sans reflow) ; press feedback
135
+ // via opacité (transform/opacity, aucun layout shift).
136
+ "flex min-h-[44px] items-center whitespace-nowrap px-4 text-sm outline-none transition-colors duration-200 active:opacity-70 focus-visible:ring-2 focus-visible:ring-secondary focus-visible:ring-offset-2",
137
+ variant === "pills" ? chunkADIDI7AJ_js.cn(
138
+ "rounded-full border",
139
+ isActive ? "border-primary bg-primary font-semibold text-white" : "border-gray-200 bg-white font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
140
+ ) : chunkADIDI7AJ_js.cn(
141
+ "-mb-px border-b-2",
142
+ isActive ? "border-secondary font-semibold text-primary" : "border-transparent font-medium text-text-secondary hover:border-gray-300 hover:text-primary"
143
+ )
144
+ ),
145
+ children: item.label
146
+ },
147
+ item.key
148
+ );
149
+ })
150
+ }
151
+ )
152
+ }
153
+ );
154
+ }
155
+ );
156
+ AnchorTabs.displayName = "AnchorTabs";
157
+
158
+ exports.AnchorTabs = AnchorTabs;
@@ -23,6 +23,24 @@ 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;
37
+ /**
38
+ * Présentation de la barre :
39
+ * - `underline` (défaut) : rangée d'onglets sur fond blanc, actif souligné ;
40
+ * - `pills` : chips arrondies posées sur le fond de page (pattern
41
+ * FilterPill) — actif rempli `primary`, inactifs en pill blanche bordée.
42
+ */
43
+ variant?: "underline" | "pills";
26
44
  }
27
45
  /**
28
46
  * Barre d'onglets d'ancrage (desktop) avec scroll-spy.
@@ -23,6 +23,24 @@ 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;
37
+ /**
38
+ * Présentation de la barre :
39
+ * - `underline` (défaut) : rangée d'onglets sur fond blanc, actif souligné ;
40
+ * - `pills` : chips arrondies posées sur le fond de page (pattern
41
+ * FilterPill) — actif rempli `primary`, inactifs en pill blanche bordée.
42
+ */
43
+ variant?: "underline" | "pills";
26
44
  }
27
45
  /**
28
46
  * 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 chunkWXEZA6NE_js = require('../../chunk-WXEZA6NE.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 chunkWXEZA6NE_js.AnchorTabs; }
11
11
  });
@@ -1,2 +1,2 @@
1
- export { AnchorTabs } from '../../chunk-PU4CWOWU.mjs';
1
+ export { AnchorTabs } from '../../chunk-M2H6QUAL.mjs';
2
2
  import '../../chunk-IMKLN273.mjs';
package/dist/index.js CHANGED
@@ -31,15 +31,15 @@ var chunkPRDIS2VR_js = require('./chunk-PRDIS2VR.js');
31
31
  var chunkIRKM5UF4_js = require('./chunk-IRKM5UF4.js');
32
32
  var chunkODUY7NXD_js = require('./chunk-ODUY7NXD.js');
33
33
  var chunkYN4HPIRC_js = require('./chunk-YN4HPIRC.js');
34
- var chunkMKOKWME3_js = require('./chunk-MKOKWME3.js');
35
34
  var chunkDLOHAW74_js = require('./chunk-DLOHAW74.js');
36
35
  var chunk77WKG2D7_js = require('./chunk-77WKG2D7.js');
36
+ var chunkMKOKWME3_js = require('./chunk-MKOKWME3.js');
37
37
  var chunkU3XWNFHH_js = require('./chunk-U3XWNFHH.js');
38
38
  var chunkW7DZZS6L_js = require('./chunk-W7DZZS6L.js');
39
- var chunkTDGU5Y2P_js = require('./chunk-TDGU5Y2P.js');
40
39
  var chunkIEHX4DU6_js = require('./chunk-IEHX4DU6.js');
41
- var chunkS5TLUDNM_js = require('./chunk-S5TLUDNM.js');
40
+ var chunkTDGU5Y2P_js = require('./chunk-TDGU5Y2P.js');
42
41
  var chunk7W3TFPIF_js = require('./chunk-7W3TFPIF.js');
42
+ var chunkS5TLUDNM_js = require('./chunk-S5TLUDNM.js');
43
43
  var chunkN2Q4Z2FU_js = require('./chunk-N2Q4Z2FU.js');
44
44
  var chunkS732LTPT_js = require('./chunk-S732LTPT.js');
45
45
  var chunkC4RZBOKH_js = require('./chunk-C4RZBOKH.js');
@@ -52,18 +52,18 @@ var chunkE24VNM6S_js = require('./chunk-E24VNM6S.js');
52
52
  var chunkXXC2YD3D_js = require('./chunk-XXC2YD3D.js');
53
53
  var chunkVWFY24XF_js = require('./chunk-VWFY24XF.js');
54
54
  var chunkT5FLQQP6_js = require('./chunk-T5FLQQP6.js');
55
- var chunk5A3MVRZJ_js = require('./chunk-5A3MVRZJ.js');
56
- var chunkZ5S7LHMY_js = require('./chunk-Z5S7LHMY.js');
57
- var chunk6EG6EAHW_js = require('./chunk-6EG6EAHW.js');
55
+ var chunkEY4ZIR3P_js = require('./chunk-EY4ZIR3P.js');
58
56
  var chunkPND5YKFN_js = require('./chunk-PND5YKFN.js');
59
- var chunkH3JNRTAI_js = require('./chunk-H3JNRTAI.js');
57
+ var chunk6EG6EAHW_js = require('./chunk-6EG6EAHW.js');
58
+ var chunkZ5S7LHMY_js = require('./chunk-Z5S7LHMY.js');
60
59
  var chunkYZTVHCLK_js = require('./chunk-YZTVHCLK.js');
60
+ var chunkH3JNRTAI_js = require('./chunk-H3JNRTAI.js');
61
61
  var chunkZAUYE2EI_js = require('./chunk-ZAUYE2EI.js');
62
62
  var chunkMY6BYO5F_js = require('./chunk-MY6BYO5F.js');
63
- var chunkWBRVUWGC_js = require('./chunk-WBRVUWGC.js');
64
63
  var chunkMCF3EQTV_js = require('./chunk-MCF3EQTV.js');
64
+ var chunkWBRVUWGC_js = require('./chunk-WBRVUWGC.js');
65
+ var chunkWXEZA6NE_js = require('./chunk-WXEZA6NE.js');
65
66
  var chunkNHB2TI2B_js = require('./chunk-NHB2TI2B.js');
66
- var chunkEY4ZIR3P_js = require('./chunk-EY4ZIR3P.js');
67
67
  var chunkADIDI7AJ_js = require('./chunk-ADIDI7AJ.js');
68
68
  var React = require('react');
69
69
  var classVarianceAuthority = require('class-variance-authority');
@@ -672,10 +672,6 @@ Object.defineProperty(exports, "iconBadgeVariants", {
672
672
  enumerable: true,
673
673
  get: function () { return chunkYN4HPIRC_js.iconBadgeVariants; }
674
674
  });
675
- Object.defineProperty(exports, "ImageGallery", {
676
- enumerable: true,
677
- get: function () { return chunkMKOKWME3_js.ImageGallery; }
678
- });
679
675
  Object.defineProperty(exports, "ImageWithFallback", {
680
676
  enumerable: true,
681
677
  get: function () { return chunkDLOHAW74_js.ImageWithFallback; }
@@ -688,6 +684,10 @@ Object.defineProperty(exports, "inputVariants", {
688
684
  enumerable: true,
689
685
  get: function () { return chunk77WKG2D7_js.inputVariants; }
690
686
  });
687
+ Object.defineProperty(exports, "ImageGallery", {
688
+ enumerable: true,
689
+ get: function () { return chunkMKOKWME3_js.ImageGallery; }
690
+ });
691
691
  Object.defineProperty(exports, "Lightbox", {
692
692
  enumerable: true,
693
693
  get: function () { return chunkU3XWNFHH_js.Lightbox; }
@@ -696,22 +696,22 @@ Object.defineProperty(exports, "LinkCard", {
696
696
  enumerable: true,
697
697
  get: function () { return chunkW7DZZS6L_js.LinkCard; }
698
698
  });
699
- Object.defineProperty(exports, "DualRangeSlider", {
700
- enumerable: true,
701
- get: function () { return chunkTDGU5Y2P_js.DualRangeSlider; }
702
- });
703
699
  Object.defineProperty(exports, "EmptyState", {
704
700
  enumerable: true,
705
701
  get: function () { return chunkIEHX4DU6_js.EmptyState; }
706
702
  });
707
- Object.defineProperty(exports, "ErrorBoundary", {
703
+ Object.defineProperty(exports, "DualRangeSlider", {
708
704
  enumerable: true,
709
- get: function () { return chunkS5TLUDNM_js.ErrorBoundary; }
705
+ get: function () { return chunkTDGU5Y2P_js.DualRangeSlider; }
710
706
  });
711
707
  Object.defineProperty(exports, "FilterAccordion", {
712
708
  enumerable: true,
713
709
  get: function () { return chunk7W3TFPIF_js.FilterAccordion; }
714
710
  });
711
+ Object.defineProperty(exports, "ErrorBoundary", {
712
+ enumerable: true,
713
+ get: function () { return chunkS5TLUDNM_js.ErrorBoundary; }
714
+ });
715
715
  Object.defineProperty(exports, "FilterPill", {
716
716
  enumerable: true,
717
717
  get: function () { return chunkN2Q4Z2FU_js.FilterPill; }
@@ -764,17 +764,13 @@ Object.defineProperty(exports, "DropdownMenu", {
764
764
  enumerable: true,
765
765
  get: function () { return chunkT5FLQQP6_js.DropdownMenu; }
766
766
  });
767
- Object.defineProperty(exports, "AnchorTabs", {
768
- enumerable: true,
769
- get: function () { return chunk5A3MVRZJ_js.AnchorTabs; }
770
- });
771
- Object.defineProperty(exports, "Badge", {
767
+ Object.defineProperty(exports, "Avatar", {
772
768
  enumerable: true,
773
- get: function () { return chunkZ5S7LHMY_js.Badge; }
769
+ get: function () { return chunkEY4ZIR3P_js.Avatar; }
774
770
  });
775
- Object.defineProperty(exports, "badgeVariants", {
771
+ Object.defineProperty(exports, "BottomNavBar", {
776
772
  enumerable: true,
777
- get: function () { return chunkZ5S7LHMY_js.badgeVariants; }
773
+ get: function () { return chunkPND5YKFN_js.BottomNavBar; }
778
774
  });
779
775
  Object.defineProperty(exports, "Banner", {
780
776
  enumerable: true,
@@ -784,17 +780,13 @@ Object.defineProperty(exports, "bannerVariants", {
784
780
  enumerable: true,
785
781
  get: function () { return chunk6EG6EAHW_js.bannerVariants; }
786
782
  });
787
- Object.defineProperty(exports, "BottomNavBar", {
788
- enumerable: true,
789
- get: function () { return chunkPND5YKFN_js.BottomNavBar; }
790
- });
791
- Object.defineProperty(exports, "Breadcrumb", {
783
+ Object.defineProperty(exports, "Badge", {
792
784
  enumerable: true,
793
- get: function () { return chunkH3JNRTAI_js.Breadcrumb; }
785
+ get: function () { return chunkZ5S7LHMY_js.Badge; }
794
786
  });
795
- Object.defineProperty(exports, "breadcrumbVariants", {
787
+ Object.defineProperty(exports, "badgeVariants", {
796
788
  enumerable: true,
797
- get: function () { return chunkH3JNRTAI_js.breadcrumbVariants; }
789
+ get: function () { return chunkZ5S7LHMY_js.badgeVariants; }
798
790
  });
799
791
  Object.defineProperty(exports, "Button", {
800
792
  enumerable: true,
@@ -804,6 +796,14 @@ Object.defineProperty(exports, "buttonVariants", {
804
796
  enumerable: true,
805
797
  get: function () { return chunkYZTVHCLK_js.buttonVariants; }
806
798
  });
799
+ Object.defineProperty(exports, "Breadcrumb", {
800
+ enumerable: true,
801
+ get: function () { return chunkH3JNRTAI_js.Breadcrumb; }
802
+ });
803
+ Object.defineProperty(exports, "breadcrumbVariants", {
804
+ enumerable: true,
805
+ get: function () { return chunkH3JNRTAI_js.breadcrumbVariants; }
806
+ });
807
807
  Object.defineProperty(exports, "Card", {
808
808
  enumerable: true,
809
809
  get: function () { return chunkZAUYE2EI_js.Card; }
@@ -836,13 +836,17 @@ Object.defineProperty(exports, "CardList", {
836
836
  enumerable: true,
837
837
  get: function () { return chunkMY6BYO5F_js.CardList; }
838
838
  });
839
+ Object.defineProperty(exports, "Accordion", {
840
+ enumerable: true,
841
+ get: function () { return chunkMCF3EQTV_js.Accordion; }
842
+ });
839
843
  Object.defineProperty(exports, "Alert", {
840
844
  enumerable: true,
841
845
  get: function () { return chunkWBRVUWGC_js.Alert; }
842
846
  });
843
- Object.defineProperty(exports, "Accordion", {
847
+ Object.defineProperty(exports, "AnchorTabs", {
844
848
  enumerable: true,
845
- get: function () { return chunkMCF3EQTV_js.Accordion; }
849
+ get: function () { return chunkWXEZA6NE_js.AnchorTabs; }
846
850
  });
847
851
  Object.defineProperty(exports, "Autocomplete", {
848
852
  enumerable: true,
@@ -852,10 +856,6 @@ Object.defineProperty(exports, "autocompleteInputVariants", {
852
856
  enumerable: true,
853
857
  get: function () { return chunkNHB2TI2B_js.autocompleteInputVariants; }
854
858
  });
855
- Object.defineProperty(exports, "Avatar", {
856
- enumerable: true,
857
- get: function () { return chunkEY4ZIR3P_js.Avatar; }
858
- });
859
859
  Object.defineProperty(exports, "cn", {
860
860
  enumerable: true,
861
861
  get: function () { return chunkADIDI7AJ_js.cn; }
package/dist/index.mjs CHANGED
@@ -29,15 +29,15 @@ export { PromoBanner, promoBannerVariants } from './chunk-FNDR3WOZ.mjs';
29
29
  export { Header } from './chunk-PPDKQ3FF.mjs';
30
30
  export { HotelCard } from './chunk-PXHZ4CXG.mjs';
31
31
  export { IconBadge, iconBadgeVariants } from './chunk-I4WCNTNP.mjs';
32
- export { ImageGallery } from './chunk-QU6ZRLKO.mjs';
33
32
  export { ImageWithFallback } from './chunk-BUXVK2HE.mjs';
34
33
  export { Input, inputVariants } from './chunk-GRG4USTC.mjs';
34
+ export { ImageGallery } from './chunk-QU6ZRLKO.mjs';
35
35
  export { Lightbox } from './chunk-GBWVIY3C.mjs';
36
36
  export { LinkCard } from './chunk-UYDZKAGZ.mjs';
37
- export { DualRangeSlider } from './chunk-ERL3WXNY.mjs';
38
37
  export { EmptyState } from './chunk-OCUHCDAQ.mjs';
39
- export { ErrorBoundary } from './chunk-5257MWFI.mjs';
38
+ export { DualRangeSlider } from './chunk-ERL3WXNY.mjs';
40
39
  export { FilterAccordion } from './chunk-VQVRKRSM.mjs';
40
+ export { ErrorBoundary } from './chunk-5257MWFI.mjs';
41
41
  export { FilterPill } from './chunk-TNWMTKNR.mjs';
42
42
  export { FloatingDock } from './chunk-PBDPZTHK.mjs';
43
43
  export { Footer } from './chunk-SZ3SV4SJ.mjs';
@@ -50,18 +50,18 @@ export { DetailList } from './chunk-N6THLJIG.mjs';
50
50
  export { Divider } from './chunk-E4B6LXK7.mjs';
51
51
  export { Drawer } from './chunk-ZLF7IL3Y.mjs';
52
52
  export { DropdownMenu } from './chunk-Q7BKR6O7.mjs';
53
- export { AnchorTabs } from './chunk-PU4CWOWU.mjs';
54
- export { Badge, badgeVariants } from './chunk-XZU2SISM.mjs';
55
- export { Banner, bannerVariants } from './chunk-LFIZX2S6.mjs';
53
+ export { Avatar } from './chunk-2POGTS27.mjs';
56
54
  export { BottomNavBar } from './chunk-UQRQZLMQ.mjs';
57
- export { Breadcrumb, breadcrumbVariants } from './chunk-UKCH6RYL.mjs';
55
+ export { Banner, bannerVariants } from './chunk-LFIZX2S6.mjs';
56
+ export { Badge, badgeVariants } from './chunk-XZU2SISM.mjs';
58
57
  export { Button, buttonVariants } from './chunk-4U5MNA3B.mjs';
58
+ export { Breadcrumb, breadcrumbVariants } from './chunk-UKCH6RYL.mjs';
59
59
  export { Card, CardContent, CardDescription, CardImage, CardTitle, cardImageVariants, cardVariants } from './chunk-V5J2XLPD.mjs';
60
60
  export { CardList } from './chunk-RJWHPHHX.mjs';
61
- export { Alert } from './chunk-BQWVWK74.mjs';
62
61
  export { Accordion } from './chunk-NZ7GF6RF.mjs';
62
+ export { Alert } from './chunk-BQWVWK74.mjs';
63
+ export { AnchorTabs } from './chunk-M2H6QUAL.mjs';
63
64
  export { Autocomplete, autocompleteInputVariants } from './chunk-B47HQHX3.mjs';
64
- export { Avatar } from './chunk-2POGTS27.mjs';
65
65
  import { cn } from './chunk-IMKLN273.mjs';
66
66
  export { cn } from './chunk-IMKLN273.mjs';
67
67
  import * as React from 'react';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dododog/ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
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 };