@peax/consent-banner-react 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -213,3 +213,5 @@ Keyboard order follows visual order where `reading-flow` is supported; otherwise
213
213
  Run `yarn workspace @peax/consent-banner-react dev` from the repository. The demo supports `?variant=stacked`, `?locale=de`, and `?showCustomizePreferences=false`. Theme edits update live from source.
214
214
 
215
215
  See [Banner verification](https://github.com/peaxco/consent/blob/main/docs/engineering/banner-verification.md) for browser setup and snapshot checks, and [Contributing](https://github.com/peaxco/consent/blob/main/CONTRIBUTING.md) for repository commands.
216
+
217
+ The adapter shares pure presentation contracts and UI state helpers with `@peax/consent-banner-shared`. Existing React exports and callback behavior remain unchanged. See [Vue integration](../../docs/vue.md) for the equivalent Vue package.
package/dist/index.d.ts CHANGED
@@ -1,72 +1,21 @@
1
- import { BannerMessages } from '@peax/consent-banner-locales';
2
- import { BuiltInPreferencesSectionId } from '@peax/consent-banner-locales';
1
+ import { BannerMsg as BannerMsg_2 } from '@peax/consent-banner-shared';
2
+ import { BannerOptions } from '@peax/consent-banner-shared';
3
3
  import { NamedExoticComponent } from 'react';
4
+ import { PreferencesSection } from '@peax/consent-banner-shared';
5
+ import { PreferencesSectionId } from '@peax/consent-banner-shared';
4
6
 
5
7
  export declare const Banner: NamedExoticComponent<BannerProps>;
6
8
 
7
- export declare type BannerMsg = PreferencesNavigationMsg | {
8
- type: 'on_accept_all' | 'on_reject_all';
9
- } | {
10
- type: 'on_apply_preferences';
11
- preferences: Record<string, boolean>;
12
- } | {
13
- type: 'on_change_preference';
14
- sectionId: PreferencesSectionId;
15
- checked: boolean;
16
- };
9
+ export declare type BannerMsg = BannerMsg_2;
17
10
 
18
- export declare type BannerProps = Placement & FloatingButtonProps & {
19
- isOpen?: boolean;
20
- appearanceDelay?: number;
21
- hasAcceptRejectEqualWeight?: boolean;
22
- showCustomizePreferences?: boolean;
23
- variant?: 'compact' | 'stacked';
24
- locale?: string;
25
- cookiePolicyUrl?: string;
26
- bannerContent?: Partial<BannerMessages>;
27
- preferencesSections?: readonly PreferencesSectionId[];
28
- preferencesContent?: Readonly<Partial<Record<PreferencesSectionId, PreferencesSection>>>;
11
+ export declare type BannerProps = BannerOptions & {
29
12
  className?: string;
30
13
  style?: React.CSSProperties;
31
- theme?: 'light' | 'dark' | 'auto';
32
14
  onMsg?: (msg: BannerMsg) => void;
33
15
  };
34
16
 
35
- declare type FloatingButtonPosition = 'left' | 'right';
36
-
37
- declare type FloatingButtonProps = {
38
- showFloatingButton?: false;
39
- floatingButtonPosition?: never;
40
- } | {
41
- showFloatingButton: true;
42
- floatingButtonPosition?: FloatingButtonPosition;
43
- };
44
-
45
- declare type Placement = {
46
- position?: 'right';
47
- slideFrom?: 'bottom' | 'right';
48
- } | {
49
- position: 'left';
50
- slideFrom?: 'bottom' | 'left';
51
- } | {
52
- position: 'center';
53
- slideFrom?: 'bottom';
54
- };
55
-
56
- declare type PreferencesNavigationMsg = {
57
- type: 'on_open_preferences' | 'on_close_preferences';
58
- source: Source;
59
- };
60
-
61
- export declare type PreferencesSection = Readonly<{
62
- title?: string;
63
- text?: string;
64
- checked?: boolean;
65
- disabled?: boolean;
66
- }>;
67
-
68
- export declare type PreferencesSectionId = BuiltInPreferencesSectionId | (string & Record<never, never>);
17
+ export { PreferencesSection }
69
18
 
70
- declare type Source = 'banner' | 'floating-button';
19
+ export { PreferencesSectionId }
71
20
 
72
21
  export { }
package/dist/index.js CHANGED
@@ -1,38 +1,43 @@
1
1
  import e from "clsx";
2
2
  import { getMessages as t, getPreferencesMessages as n, parseMessage as r, resolveLocale as i } from "@peax/consent-banner-locales";
3
- import { forwardRef as a, memo as o, useCallback as s, useEffect as c, useId as l, useMemo as u, useRef as d, useState as f } from "react";
4
- import { AnimatePresence as p, LazyMotion as m, domAnimation as h, useIsPresent as g, usePresenceData as _, useReducedMotion as v } from "framer-motion";
5
- import * as y from "framer-motion/m";
6
- import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
7
- import { Button as C } from "@base-ui/react/button";
8
- import { ScrollArea as w } from "@base-ui/react/scroll-area";
9
- import { Switch as T } from "@base-ui/react/switch";
3
+ import { defaultPreferencesSections as a, entranceDuration as o, entranceEase as s, mergeBannerContent as c, normalizeAppearanceDelay as l, reconcileDraft as u, resolveFloatingPosition as d, resolveSections as f, resolveSlideFrom as p, safeHref as m, safeHref as h, transitionPreferencesView as g } from "@peax/consent-banner-shared";
4
+ import { forwardRef as _, memo as v, useCallback as y, useEffect as b, useId as x, useMemo as S, useRef as C, useState as w } from "react";
5
+ import { AnimatePresence as T, LazyMotion as E, domAnimation as D, useIsPresent as O, usePresenceData as k, useReducedMotion as A } from "framer-motion";
6
+ import * as j from "framer-motion/m";
7
+ import { Fragment as M, jsx as N, jsxs as P } from "react/jsx-runtime";
8
+ import { Button as F } from "@base-ui/react/button";
9
+ import { ScrollArea as I } from "@base-ui/react/scroll-area";
10
+ import { Switch as L } from "@base-ui/react/switch";
10
11
  //#region src/hooks/usePreferencesView.ts
11
- function E({ isOpen: e, showFloatingButton: t, showCustomizePreferences: n, floatingPosition: r, onMsg: i }) {
12
- let [a, o] = f(null), [l, u] = f(!1), p = d(null);
13
- return c(() => {
14
- e || o(null), (e || !t) && (p.current = null);
12
+ function R({ isOpen: e, showFloatingButton: t, showCustomizePreferences: n, floatingPosition: r, onMsg: i }) {
13
+ let [a, o] = w(null), [s, c] = w(!1), l = C(null);
14
+ return b(() => {
15
+ o((t) => g(t, {
16
+ type: "sync",
17
+ isOpen: e
18
+ })), (e || !t) && (l.current = null);
15
19
  }, [e, t]), {
16
20
  view: a,
17
- hasPanel: l,
18
- setHasPanel: u,
19
- triggerRef: s((e) => {
21
+ hasPanel: s,
22
+ setHasPanel: c,
23
+ triggerRef: y((e) => {
20
24
  if (!e) return;
21
- let t = p.current;
22
- p.current = null, t && (document.activeElement === t || document.activeElement === document.body) && e.focus();
25
+ let t = l.current;
26
+ l.current = null, t && (document.activeElement === t || document.activeElement === document.body) && e.focus();
23
27
  }, []),
24
- onExitFocus: s(() => (t && (p.current = document.activeElement), t), [t]),
28
+ onExitFocus: y(() => (t && (l.current = document.activeElement), t), [t]),
25
29
  openPreferences: (e) => {
26
- o(e === "banner" ? { source: e } : {
30
+ o((t) => g(t, {
31
+ type: "open",
27
32
  source: e,
28
33
  position: r
29
- }), i?.({
34
+ })), i?.({
30
35
  type: "on_open_preferences",
31
36
  source: e
32
37
  });
33
38
  },
34
39
  closePreferences: () => {
35
- a && (a.source === "banner" && (p.current = n ? document.activeElement : null, o(null)), i?.({
40
+ a && (a.source === "banner" && (l.current = n ? document.activeElement : null, o((e) => g(e, { type: "close" }))), i?.({
36
41
  type: "on_close_preferences",
37
42
  source: a.source
38
43
  }));
@@ -41,72 +46,67 @@ function E({ isOpen: e, showFloatingButton: t, showCustomizePreferences: n, floa
41
46
  }
42
47
  //#endregion
43
48
  //#region src/components/Container/ContainerWrapper.tsx
44
- var D = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
45
- let a = g(), o = _(), s = v(), l = d(null), u = d(null);
46
- c(() => (r?.(!0), () => r?.(!1)), [r]), c(() => {
47
- let e = l.current;
49
+ var z = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
50
+ let a = O(), c = k(), l = A(), u = C(null), d = C(null);
51
+ b(() => (r?.(!0), () => r?.(!1)), [r]), b(() => {
52
+ let e = u.current;
48
53
  if (e) {
49
54
  if (a) {
50
55
  e.removeAttribute("inert");
51
56
  let t = document.activeElement;
52
- t instanceof HTMLElement && !e.contains(t) && (u.current = t);
57
+ t instanceof HTMLElement && !e.contains(t) && (d.current = t);
53
58
  return;
54
59
  }
55
- if (e.contains(document.activeElement) && !o?.()) {
56
- let e = u.current;
60
+ if (e.contains(document.activeElement) && !c?.()) {
61
+ let e = d.current;
57
62
  e?.isConnected && e.focus({ preventScroll: !0 });
58
63
  }
59
64
  e.setAttribute("inert", "");
60
65
  }
61
- }, [a, o]);
62
- let f = n === t ? n : "bottom", p = { transform: s ? "none" : f === "bottom" ? "translateY(100%)" : `translateX(${f === "left" ? "-100%" : "100%"})` };
63
- return /* @__PURE__ */ x(y.div, {
64
- ref: l,
66
+ }, [a, c]);
67
+ let f = p(t, n), m = { transform: l ? "none" : f === "bottom" ? "translateY(100%)" : `translateX(${f === "left" ? "-100%" : "100%"})` };
68
+ return /* @__PURE__ */ N(j.div, {
69
+ ref: u,
65
70
  exit: { transform: "none" },
66
- initial: p,
71
+ initial: m,
67
72
  animate: { transform: "none" },
68
73
  "aria-hidden": !a || void 0,
69
74
  "data-state": a ? "open" : "closing",
70
75
  className: e("pxc-wrapper", `pxc-wrapper--${t}`),
71
76
  transition: {
72
- duration: a && !s ? .36 : 0,
73
- ease: [
74
- .16,
75
- 1,
76
- .3,
77
- 1
78
- ]
77
+ duration: a && !l ? o : 0,
78
+ ease: [...s]
79
79
  },
80
80
  children: i
81
81
  });
82
- }, O = a(function({ isOpen: t = !0, appearanceDelay: n = 0, position: r = "right", slideFrom: i = "bottom", onPresenceChange: a, onExitFocus: o, className: s, children: l, actions: u, ...d }, g) {
83
- let _ = Number.isFinite(n) ? Math.max(0, n) : 0, [v, y] = f(_ === 0);
84
- return c(() => {
85
- if (v || !t) return;
86
- let e = window.setTimeout(() => y(!0), _);
82
+ }, B = _(function({ isOpen: t = !0, appearanceDelay: n = 0, position: r = "right", slideFrom: i = "bottom", onPresenceChange: a, onExitFocus: o, className: s, children: c, actions: u, ...d }, f) {
83
+ let p = l(n), [m, h] = w(p === 0);
84
+ return b(() => {
85
+ if (m || !t) return;
86
+ let e = window.setTimeout(() => h(!0), p);
87
87
  return () => window.clearTimeout(e);
88
88
  }, [
89
- _,
89
+ p,
90
90
  t,
91
- v
92
- ]), /* @__PURE__ */ x(m, {
93
- features: h,
91
+ m
92
+ ]), /* @__PURE__ */ N(E, {
93
+ features: D,
94
94
  strict: !0,
95
- children: /* @__PURE__ */ x(p, {
95
+ children: /* @__PURE__ */ N(T, {
96
96
  custom: o,
97
- children: t && v && /* @__PURE__ */ x(D, {
97
+ children: t && m && /* @__PURE__ */ N(z, {
98
98
  position: r,
99
99
  slideFrom: i,
100
100
  onPresenceChange: a,
101
- children: /* @__PURE__ */ x("div", {
102
- ref: g,
101
+ children: /* @__PURE__ */ N("div", {
102
+ ref: f,
103
103
  className: e("pxc-container", u != null && "pxc-container--with-actions", s),
104
104
  ...d,
105
- children: u == null ? l : /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x("div", {
105
+ children: u == null ? c : /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ N("div", {
106
106
  className: "pxc-container__content",
107
107
  "data-container-content": !0,
108
- children: l
109
- }), /* @__PURE__ */ x("div", {
108
+ children: c
109
+ }), /* @__PURE__ */ N("div", {
110
110
  className: "pxc-container__actions",
111
111
  "data-container-actions": !0,
112
112
  children: u
@@ -115,7 +115,7 @@ var D = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
115
115
  }, "container")
116
116
  })
117
117
  });
118
- }), k = () => /* @__PURE__ */ S("svg", {
118
+ }), V = () => /* @__PURE__ */ P("svg", {
119
119
  width: "24",
120
120
  height: "24",
121
121
  viewBox: "0 0 24 24",
@@ -128,14 +128,14 @@ var D = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
128
128
  "aria-hidden": "true",
129
129
  focusable: "false",
130
130
  children: [
131
- /* @__PURE__ */ x("path", { d: "M11 17H11.01" }),
132
- /* @__PURE__ */ x("path", { d: "M12.025 2.61499C12.054 2.29199 11.82 1.98399 11.496 1.99999C9.02138 2.12561 6.68155 3.16546 4.92997 4.91799C3.0533 6.79479 1.99905 9.34023 1.99915 11.9943C1.99924 14.6484 3.05367 17.1938 4.93047 19.0705C6.80728 20.9472 9.35272 22.0014 12.0068 22.0013C14.6609 22.0012 17.2063 20.9468 19.083 19.07C20.8344 17.3179 21.8732 14.9782 21.998 12.504C22.014 12.181 21.707 11.947 21.384 11.977C20.8082 12.0311 20.2276 11.9597 19.6821 11.7677C19.1366 11.5757 18.6392 11.2678 18.2242 10.865C17.8092 10.4623 17.4864 9.97436 17.2782 9.43485C17.07 8.89535 16.9812 8.31711 17.018 7.73999C17.0244 7.63881 17.0093 7.53742 16.9734 7.44257C16.9376 7.34773 16.882 7.26161 16.8103 7.18997C16.7385 7.11833 16.6523 7.06281 16.5574 7.02712C16.4626 6.99143 16.3611 6.97638 16.26 6.98299C15.6828 7.01952 15.1046 6.93048 14.5652 6.72199C14.0258 6.51351 13.538 6.19054 13.1355 5.77535C12.7329 5.36015 12.4252 4.86261 12.2335 4.31701C12.0417 3.77141 11.9706 3.19073 12.025 2.61499Z" }),
133
- /* @__PURE__ */ x("path", { d: "M12 12H12.01" }),
134
- /* @__PURE__ */ x("path", { d: "M16 16H16.01" }),
135
- /* @__PURE__ */ x("path", { d: "M7 14H7.01" }),
136
- /* @__PURE__ */ x("path", { d: "M9 8H9.01" })
131
+ /* @__PURE__ */ N("path", { d: "M11 17H11.01" }),
132
+ /* @__PURE__ */ N("path", { d: "M12.025 2.61499C12.054 2.29199 11.82 1.98399 11.496 1.99999C9.02138 2.12561 6.68155 3.16546 4.92997 4.91799C3.0533 6.79479 1.99905 9.34023 1.99915 11.9943C1.99924 14.6484 3.05367 17.1938 4.93047 19.0705C6.80728 20.9472 9.35272 22.0014 12.0068 22.0013C14.6609 22.0012 17.2063 20.9468 19.083 19.07C20.8344 17.3179 21.8732 14.9782 21.998 12.504C22.014 12.181 21.707 11.947 21.384 11.977C20.8082 12.0311 20.2276 11.9597 19.6821 11.7677C19.1366 11.5757 18.6392 11.2678 18.2242 10.865C17.8092 10.4623 17.4864 9.97436 17.2782 9.43485C17.07 8.89535 16.9812 8.31711 17.018 7.73999C17.0244 7.63881 17.0093 7.53742 16.9734 7.44257C16.9376 7.34773 16.882 7.26161 16.8103 7.18997C16.7385 7.11833 16.6523 7.06281 16.5574 7.02712C16.4626 6.99143 16.3611 6.97638 16.26 6.98299C15.6828 7.01952 15.1046 6.93048 14.5652 6.72199C14.0258 6.51351 13.538 6.19054 13.1355 5.77535C12.7329 5.36015 12.4252 4.86261 12.2335 4.31701C12.0417 3.77141 11.9706 3.19073 12.025 2.61499Z" }),
133
+ /* @__PURE__ */ N("path", { d: "M12 12H12.01" }),
134
+ /* @__PURE__ */ N("path", { d: "M16 16H16.01" }),
135
+ /* @__PURE__ */ N("path", { d: "M7 14H7.01" }),
136
+ /* @__PURE__ */ N("path", { d: "M9 8H9.01" })
137
137
  ]
138
- }), A = () => /* @__PURE__ */ S("svg", {
138
+ }), H = () => /* @__PURE__ */ P("svg", {
139
139
  xmlns: "http://www.w3.org/2000/svg",
140
140
  width: "24",
141
141
  height: "24",
@@ -148,40 +148,40 @@ var D = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
148
148
  "aria-hidden": "true",
149
149
  focusable: "false",
150
150
  children: [
151
- /* @__PURE__ */ x("path", { d: "M14 17H5" }),
152
- /* @__PURE__ */ x("path", { d: "M19 7h-9" }),
153
- /* @__PURE__ */ x("circle", {
151
+ /* @__PURE__ */ N("path", { d: "M14 17H5" }),
152
+ /* @__PURE__ */ N("path", { d: "M19 7h-9" }),
153
+ /* @__PURE__ */ N("circle", {
154
154
  cx: "17",
155
155
  cy: "17",
156
156
  r: "3"
157
157
  }),
158
- /* @__PURE__ */ x("circle", {
158
+ /* @__PURE__ */ N("circle", {
159
159
  cx: "7",
160
160
  cy: "7",
161
161
  r: "3"
162
162
  })
163
163
  ]
164
- }), j = a(function({ variant: t, className: n, children: r, ...i }, a) {
165
- return /* @__PURE__ */ x(C, {
164
+ }), U = _(function({ variant: t, className: n, children: r, ...i }, a) {
165
+ return /* @__PURE__ */ N(F, {
166
166
  ref: a,
167
167
  className: (r) => e("pxc-button", `pxc-button--${t}`, typeof n == "function" ? n(r) : n),
168
168
  ...i,
169
169
  focusableWhenDisabled: !1,
170
170
  children: r
171
171
  });
172
- }), M = a(function({ label: e, position: t, onMsg: n }, r) {
173
- return /* @__PURE__ */ x(j, {
172
+ }), W = _(function({ label: e, position: t, onMsg: n }, r) {
173
+ return /* @__PURE__ */ N(U, {
174
174
  ref: r,
175
175
  variant: "iconed",
176
176
  className: `pxc-floating-button pxc-floating-button--${t}`,
177
177
  "aria-label": e,
178
178
  onClick: () => n({ type: "on_open_preferences" }),
179
- children: /* @__PURE__ */ x(k, {})
179
+ children: /* @__PURE__ */ N(V, {})
180
180
  });
181
- }), N = () => /* @__PURE__ */ x("div", {
181
+ }), G = () => /* @__PURE__ */ N("div", {
182
182
  className: "pxc-lead-icon",
183
- children: /* @__PURE__ */ x(k, {})
184
- }), P = () => /* @__PURE__ */ x("svg", {
183
+ children: /* @__PURE__ */ N(V, {})
184
+ }), K = () => /* @__PURE__ */ N("svg", {
185
185
  viewBox: "0 0 24 24",
186
186
  fill: "none",
187
187
  stroke: "currentColor",
@@ -189,43 +189,36 @@ var D = ({ position: t, slideFrom: n, onPresenceChange: r, children: i }) => {
189
189
  strokeLinecap: "round",
190
190
  "aria-hidden": "true",
191
191
  focusable: "false",
192
- children: /* @__PURE__ */ x("path", { d: "m6 6 12 12M6 18 18 6" })
192
+ children: /* @__PURE__ */ N("path", { d: "m6 6 12 12M6 18 18 6" })
193
193
  });
194
194
  //#endregion
195
195
  //#region src/utils/notReachable.ts
196
- function F(e, t = { throwError: !0 }) {
196
+ function q(e, t = { throwError: !0 }) {
197
197
  let n = `Should not be reached ${e}`;
198
198
  if (t.throwError) throw Error(n);
199
199
  }
200
200
  //#endregion
201
201
  //#region src/components/Text/RichText.tsx
202
- var I = (e) => {
203
- if (e?.trim()) try {
204
- let t = new URL(e, "https://consent.invalid");
205
- return t.protocol === "http:" || t.protocol === "https:" ? e : void 0;
206
- } catch {
207
- return;
208
- }
209
- }, L = ({ message: e, links: t }) => u(() => r(e, Object.keys(t)), [e, t]).map((e) => {
202
+ var J = ({ message: e, links: t }) => S(() => r(e, Object.keys(t)), [e, t]).map((e) => {
210
203
  if (e.type === "text") return e.value;
211
- let n = I(t[e.name]);
212
- return n && e.value.trim() ? /* @__PURE__ */ x("a", {
204
+ let n = m(t[e.name]);
205
+ return n && e.value.trim() ? /* @__PURE__ */ N("a", {
213
206
  className: "pxc-link",
214
207
  href: n,
215
208
  children: e.value
216
209
  }, e.offset) : e.value;
217
- }), R = ({ section: e, checked: t, links: n, onMsg: r }) => {
218
- let i = l();
219
- return /* @__PURE__ */ S("section", {
210
+ }), Y = ({ section: e, checked: t, links: n, onMsg: r }) => {
211
+ let i = x();
212
+ return /* @__PURE__ */ P("section", {
220
213
  className: "pxc-preferences__section",
221
214
  "aria-labelledby": `${i}-title`,
222
- children: [/* @__PURE__ */ S("div", {
215
+ children: [/* @__PURE__ */ P("div", {
223
216
  className: "pxc-preferences__section-heading",
224
- children: [/* @__PURE__ */ x("h4", {
217
+ children: [/* @__PURE__ */ N("h4", {
225
218
  id: `${i}-title`,
226
219
  className: "pxc-text--h4",
227
220
  children: e.title
228
- }), /* @__PURE__ */ x(T.Root, {
221
+ }), /* @__PURE__ */ N(L.Root, {
229
222
  className: "pxc-switch",
230
223
  checked: t,
231
224
  disabled: e.disabled ?? !1,
@@ -236,231 +229,208 @@ var I = (e) => {
236
229
  sectionId: e.id,
237
230
  checked: t
238
231
  }),
239
- children: /* @__PURE__ */ x(T.Thumb, { className: "pxc-switch__thumb" })
232
+ children: /* @__PURE__ */ N(L.Thumb, { className: "pxc-switch__thumb" })
240
233
  })]
241
- }), e.text && /* @__PURE__ */ x("p", {
234
+ }), e.text && /* @__PURE__ */ N("p", {
242
235
  id: `${i}-text`,
243
236
  className: "pxc-text--paragraph pxc-preferences__description",
244
- children: /* @__PURE__ */ x(L, {
237
+ children: /* @__PURE__ */ N(J, {
245
238
  message: e.text,
246
239
  links: n
247
240
  })
248
241
  })]
249
242
  });
250
- }, z = ({ sections: e, content: t, links: n, onMsg: r }) => {
251
- let i = l(), a = d(null), [o, s] = f(() => new Map(e.map((e) => [e.id, e.checked]))), u = new Map(e.map((e) => [e.id, e.disabled ? e.checked : o.get(e.id) ?? e.checked]));
252
- (o.size !== u.size || [...u].some(([e, t]) => o.get(e) !== t)) && s(u), c(() => {
243
+ }, X = ({ sections: e, content: t, links: n, onMsg: r }) => {
244
+ let i = x(), a = C(null), [o, s] = w(() => u(e)), c = u(e, o);
245
+ (o.size !== c.size || [...c].some(([e, t]) => o.get(e) !== t)) && s(c), b(() => {
253
246
  a.current?.focus({ preventScroll: !0 });
254
247
  }, []);
255
- let p = I(n.cookiePolicy);
256
- return /* @__PURE__ */ S("section", {
248
+ let l = h(n.cookiePolicy);
249
+ return /* @__PURE__ */ P("section", {
257
250
  className: "pxc-preferences",
258
251
  "aria-labelledby": i,
259
252
  onKeyDown: (e) => {
260
253
  e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), r({ type: "on_close_preferences" }));
261
254
  },
262
255
  children: [
263
- /* @__PURE__ */ S("header", {
256
+ /* @__PURE__ */ P("header", {
264
257
  className: "pxc-preferences__header",
265
- children: [/* @__PURE__ */ x("h3", {
258
+ children: [/* @__PURE__ */ N("h3", {
266
259
  id: i,
267
260
  ref: a,
268
261
  tabIndex: -1,
269
262
  className: "pxc-text--h3",
270
263
  children: t.preferencesTitle
271
- }), /* @__PURE__ */ x(j, {
264
+ }), /* @__PURE__ */ N(U, {
272
265
  variant: "iconed",
273
266
  "aria-label": t.closePreferencesLabel,
274
267
  onClick: () => r({ type: "on_close_preferences" }),
275
- children: /* @__PURE__ */ x(P, {})
268
+ children: /* @__PURE__ */ N(K, {})
276
269
  })]
277
270
  }),
278
- /* @__PURE__ */ S(w.Root, {
271
+ /* @__PURE__ */ P(I.Root, {
279
272
  className: "pxc-preferences__scroll-area",
280
- children: [/* @__PURE__ */ x(w.Viewport, {
273
+ children: [/* @__PURE__ */ N(I.Viewport, {
281
274
  className: "pxc-preferences__body",
282
275
  role: "group",
283
276
  "aria-labelledby": i,
284
- children: /* @__PURE__ */ S(w.Content, {
277
+ children: /* @__PURE__ */ P(I.Content, {
285
278
  className: "pxc-preferences__content",
286
- children: [t.preferencesText && /* @__PURE__ */ x("p", {
279
+ children: [t.preferencesText && /* @__PURE__ */ N("p", {
287
280
  className: "pxc-text--paragraph pxc-preferences__introduction",
288
- children: /* @__PURE__ */ x(L, {
281
+ children: /* @__PURE__ */ N(J, {
289
282
  message: t.preferencesText,
290
283
  links: n
291
284
  })
292
- }), e.map((e) => /* @__PURE__ */ x(R, {
285
+ }), e.map((e) => /* @__PURE__ */ N(Y, {
293
286
  section: e,
294
- checked: u.get(e.id) ?? e.checked,
287
+ checked: c.get(e.id) ?? e.checked,
295
288
  links: n,
296
289
  onMsg: (e) => {
297
290
  switch (e.type) {
298
291
  case "on_change_preference":
299
292
  s((t) => new Map(t).set(e.sectionId, e.checked)), r(e);
300
293
  break;
301
- default: F(e.type);
294
+ default: q(e.type);
302
295
  }
303
296
  }
304
297
  }, e.id))]
305
298
  })
306
- }), /* @__PURE__ */ x(w.Scrollbar, {
299
+ }), /* @__PURE__ */ N(I.Scrollbar, {
307
300
  className: "pxc-preferences__scrollbar",
308
- children: /* @__PURE__ */ x(w.Thumb, {
301
+ children: /* @__PURE__ */ N(I.Thumb, {
309
302
  className: "pxc-preferences__scroll-thumb",
310
- children: /* @__PURE__ */ x("span", { className: "pxc-preferences__scroll-indicator" })
303
+ children: /* @__PURE__ */ N("span", { className: "pxc-preferences__scroll-indicator" })
311
304
  })
312
305
  })]
313
306
  }),
314
- /* @__PURE__ */ S("footer", {
307
+ /* @__PURE__ */ P("footer", {
315
308
  className: "pxc-preferences__footer",
316
- children: [/* @__PURE__ */ x(j, {
309
+ children: [/* @__PURE__ */ N(U, {
317
310
  variant: "primary",
318
311
  onClick: () => r({
319
312
  type: "on_apply_preferences",
320
- preferences: Object.fromEntries(u)
313
+ preferences: Object.fromEntries(c)
321
314
  }),
322
315
  children: t.applyPreferencesLabel
323
- }), p ? /* @__PURE__ */ x("a", {
316
+ }), l ? /* @__PURE__ */ N("a", {
324
317
  className: "pxc-link pxc-preferences__policy",
325
- href: p,
318
+ href: l,
326
319
  children: t.cookiePolicyLabel
327
- }) : /* @__PURE__ */ x("span", {
320
+ }) : /* @__PURE__ */ N("span", {
328
321
  className: "pxc-preferences__policy",
329
322
  children: t.cookiePolicyLabel
330
323
  })]
331
324
  })
332
325
  ]
333
326
  });
334
- }, B = [
335
- "essential",
336
- "analytics",
337
- "marketing"
338
- ];
339
- function V(e, t, n) {
340
- let r = new Map(Object.entries(n));
341
- return [...new Set(e.filter((e) => e.trim()))].map((e) => {
342
- let n = r.get(e), i = t && Object.hasOwn(t, e) ? t[e] : void 0;
343
- return {
344
- id: e,
345
- title: i?.title?.trim() ? i.title : n?.title ?? e,
346
- text: i?.text ?? n?.text ?? "",
347
- checked: i?.checked ?? e === "essential",
348
- disabled: i?.disabled ?? e === "essential"
349
- };
350
- });
351
- }
352
- //#endregion
353
- //#region src/components/Text/Text.tsx
354
- var H = ({ variant: e, tag: t, children: n }) => {
327
+ }, Z = ({ variant: e, tag: t, children: n }) => {
355
328
  switch (e) {
356
- case "label": return /* @__PURE__ */ x(t, {
329
+ case "label": return /* @__PURE__ */ N(t, {
357
330
  className: "pxc-text--label",
358
331
  children: n
359
332
  });
360
- case "heading": return /* @__PURE__ */ x(t, {
333
+ case "heading": return /* @__PURE__ */ N(t, {
361
334
  className: `pxc-text--${t}`,
362
335
  children: n
363
336
  });
364
- case "paragraph": return /* @__PURE__ */ x("p", {
337
+ case "paragraph": return /* @__PURE__ */ N("p", {
365
338
  className: "pxc-text--paragraph",
366
339
  children: n
367
340
  });
368
- default: return F(e);
341
+ default: return q(e);
369
342
  }
370
- }, U = ({ variant: e, content: t, equalWeight: n, showCustomizePreferences: r, preferencesRef: i, onMsg: a }) => /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ S("div", {
343
+ }, Q = ({ variant: e, content: t, equalWeight: n, showCustomizePreferences: r, preferencesRef: i, onMsg: a }) => /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ P("div", {
371
344
  className: "pxc-banner__choices",
372
- children: [/* @__PURE__ */ x(j, {
345
+ children: [/* @__PURE__ */ N(U, {
373
346
  variant: n ? "secondary" : "primary",
374
347
  onClick: () => a({ type: "on_accept_all" }),
375
348
  children: t.acceptAllLabel
376
- }), /* @__PURE__ */ x(j, {
349
+ }), /* @__PURE__ */ N(U, {
377
350
  variant: "secondary",
378
351
  onClick: () => a({ type: "on_reject_all" }),
379
352
  children: t.rejectAllLabel
380
353
  })]
381
- }), r && /* @__PURE__ */ x("div", {
354
+ }), r && /* @__PURE__ */ N("div", {
382
355
  className: "pxc-banner__preferences",
383
- children: /* @__PURE__ */ x(j, {
356
+ children: /* @__PURE__ */ N(U, {
384
357
  ref: i,
385
358
  variant: "inline",
386
359
  className: e === "compact" ? "pxc-button--iconed" : void 0,
387
360
  "aria-label": t.customizePreferencesLabel,
388
361
  onClick: () => a({ type: "on_open_preferences" }),
389
- children: e === "compact" ? /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x(A, {}), /* @__PURE__ */ x("span", { children: t.customizePreferencesLabel })] }) : t.customizePreferencesLabel
362
+ children: e === "compact" ? /* @__PURE__ */ P(M, { children: [/* @__PURE__ */ N(H, {}), /* @__PURE__ */ N("span", { children: t.customizePreferencesLabel })] }) : t.customizePreferencesLabel
390
363
  })
391
- })] }), W = o(function({ bannerContent: r, cookiePolicyUrl: a, isOpen: o = !1, appearanceDelay: s = 0, preferencesSections: c = B, preferencesContent: l, hasAcceptRejectEqualWeight: d = !1, showCustomizePreferences: f = !0, showFloatingButton: p = !0, floatingButtonPosition: m, variant: h = "compact", theme: g = "auto", locale: _ = "en", onMsg: v, className: y, style: C, ...w }) {
392
- let T = i(_), D = t(T), k = Object.fromEntries(Object.entries(r ?? {}).filter(([, e]) => e != null)), A = {
393
- ...D,
394
- ...k
395
- }, j = u(() => ({ cookiePolicy: a }), [a]), P = u(() => V(c, l, n(T)), [
396
- c,
397
- l,
398
- T
399
- ]), F = m ?? (w.position === "left" ? "left" : "right"), I = E({
400
- isOpen: o,
401
- showFloatingButton: p,
402
- showCustomizePreferences: f,
403
- floatingPosition: F,
404
- onMsg: v
405
- }), R = I.view !== null, W = I.view?.source === "floating-button" ? {
406
- ...w,
407
- position: I.view.position,
364
+ })] }), $ = v(function({ bannerContent: r, cookiePolicyUrl: o, isOpen: s = !1, appearanceDelay: l = 0, preferencesSections: u = a, preferencesContent: p, hasAcceptRejectEqualWeight: m = !1, showCustomizePreferences: h = !0, showFloatingButton: g = !0, floatingButtonPosition: _, variant: v = "compact", theme: y = "auto", locale: b = "en", onMsg: x, className: C, style: w, ...T }) {
365
+ let E = i(b), D = t(E), O = c(D, r), k = S(() => ({ cookiePolicy: o }), [o]), A = S(() => f(u, p, n(E)), [
366
+ u,
367
+ p,
368
+ E
369
+ ]), j = d(T.position, _), F = R({
370
+ isOpen: s,
371
+ showFloatingButton: g,
372
+ showCustomizePreferences: h,
373
+ floatingPosition: j,
374
+ onMsg: x
375
+ }), I = F.view !== null, L = F.view?.source === "floating-button" ? {
376
+ ...T,
377
+ position: F.view.position,
408
378
  slideFrom: "bottom"
409
- } : w, G = (e) => {
410
- if (e.type === "on_open_preferences") return I.openPreferences("banner");
411
- if (e.type === "on_close_preferences") return I.closePreferences();
412
- v?.(e);
379
+ } : T, z = (e) => {
380
+ if (e.type === "on_open_preferences") return F.openPreferences("banner");
381
+ if (e.type === "on_close_preferences") return F.closePreferences();
382
+ x?.(e);
413
383
  };
414
- return /* @__PURE__ */ S("div", {
415
- "data-theme": g,
416
- "data-banner-variant": h,
417
- className: e("pxc", y),
384
+ return /* @__PURE__ */ P("div", {
385
+ "data-theme": y,
386
+ "data-banner-variant": v,
387
+ className: e("pxc", C),
418
388
  style: {
419
389
  fontFamily: "var(--pxc-font-family)",
420
- ...C
390
+ ...w
421
391
  },
422
- lang: T,
423
- children: [/* @__PURE__ */ x(O, {
424
- ...W,
425
- isOpen: o,
426
- onExitFocus: I.onExitFocus,
427
- onPresenceChange: I.setHasPanel,
428
- appearanceDelay: s,
429
- className: R ? "pxc-container--preferences" : void 0,
430
- actions: R ? null : /* @__PURE__ */ x(U, {
431
- variant: h,
432
- content: A,
433
- equalWeight: d,
434
- showCustomizePreferences: f,
435
- preferencesRef: I.triggerRef,
436
- onMsg: G
392
+ lang: E,
393
+ children: [/* @__PURE__ */ N(B, {
394
+ ...L,
395
+ isOpen: s,
396
+ onExitFocus: F.onExitFocus,
397
+ onPresenceChange: F.setHasPanel,
398
+ appearanceDelay: l,
399
+ className: I ? "pxc-container--preferences" : void 0,
400
+ actions: I ? null : /* @__PURE__ */ N(Q, {
401
+ variant: v,
402
+ content: O,
403
+ equalWeight: m,
404
+ showCustomizePreferences: h,
405
+ preferencesRef: F.triggerRef,
406
+ onMsg: z
437
407
  }),
438
- children: R ? /* @__PURE__ */ x(z, {
439
- links: j,
440
- content: A,
441
- sections: P,
442
- onMsg: G
443
- }) : /* @__PURE__ */ S(b, { children: [h === "stacked" ? /* @__PURE__ */ S("div", {
408
+ children: I ? /* @__PURE__ */ N(X, {
409
+ links: k,
410
+ content: O,
411
+ sections: A,
412
+ onMsg: z
413
+ }) : /* @__PURE__ */ P(M, { children: [v === "stacked" ? /* @__PURE__ */ P("div", {
444
414
  className: "pxc-banner__heading",
445
- children: [/* @__PURE__ */ x(N, {}), /* @__PURE__ */ x(H, {
415
+ children: [/* @__PURE__ */ N(G, {}), /* @__PURE__ */ N(Z, {
446
416
  variant: "heading",
447
417
  tag: "h3",
448
- children: A.title
418
+ children: O.title
449
419
  })]
450
- }) : /* @__PURE__ */ x(N, {}), /* @__PURE__ */ x(H, {
420
+ }) : /* @__PURE__ */ N(G, {}), /* @__PURE__ */ N(Z, {
451
421
  variant: "paragraph",
452
- children: /* @__PURE__ */ x(L, {
453
- message: A.collapsedText,
454
- links: j
422
+ children: /* @__PURE__ */ N(J, {
423
+ message: O.collapsedText,
424
+ links: k
455
425
  })
456
426
  })] })
457
- }), p && !o && !I.hasPanel && /* @__PURE__ */ x(M, {
458
- ref: I.triggerRef,
459
- position: F,
460
- label: A.customizePreferencesLabel.trim() || D.customizePreferencesLabel,
461
- onMsg: () => I.openPreferences("floating-button")
427
+ }), g && !s && !F.hasPanel && /* @__PURE__ */ N(W, {
428
+ ref: F.triggerRef,
429
+ position: j,
430
+ label: O.customizePreferencesLabel.trim() || D.customizePreferencesLabel,
431
+ onMsg: () => F.openPreferences("floating-button")
462
432
  })]
463
433
  });
464
434
  });
465
435
  //#endregion
466
- export { W as Banner };
436
+ export { $ as Banner };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peax/consent-banner-react",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Accessible React bindings and components for Peax Consent.",
5
5
  "keywords": [
6
6
  "consent-management",
@@ -45,7 +45,8 @@
45
45
  "build": "vite build",
46
46
  "dev": "vite src/demo --config vite.config.ts",
47
47
  "test": "vitest run",
48
- "test:browser": "vitest run --config vitest.browser.config.ts",
48
+ "test:browser": "node ../../scripts/test-browser.mjs react",
49
+ "test:browser:native": "vitest run --config vitest.browser.config.ts --testTimeout 30000",
49
50
  "typecheck": "tsc --noEmit"
50
51
  },
51
52
  "peerDependencies": {
@@ -54,8 +55,9 @@
54
55
  },
55
56
  "dependencies": {
56
57
  "@base-ui/react": "^1.7.0",
57
- "@peax/consent-banner-locales": "^0.2.0",
58
- "@peax/consent-banner-theme": "^0.2.0",
58
+ "@peax/consent-banner-locales": "^0.4.0",
59
+ "@peax/consent-banner-shared": "^0.4.0",
60
+ "@peax/consent-banner-theme": "^0.4.0",
59
61
  "clsx": "^2.1.1",
60
62
  "framer-motion": "^13.1.1"
61
63
  },