@gardenfi/garden-book 0.3.1 → 0.3.3
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/dist/BottomSheet/BottomSheet.d.ts +8 -0
- package/dist/BottomSheet/index.js +49 -15
- package/dist/Checkbox/Checkbox.d.ts +1 -0
- package/dist/Checkbox/Checkbox.js +33 -5
- package/dist/CookieConsent/CookieConsent.d.ts +25 -0
- package/dist/CookieConsent/CookieConsent.js +75 -0
- package/dist/CookieConsent/CookieConsentNotice.d.ts +8 -0
- package/dist/CookieConsent/CookieConsentNotice.js +55 -0
- package/dist/CookieConsent/CookieConsentPreferences.d.ts +10 -0
- package/dist/CookieConsent/CookieConsentPreferences.js +75 -0
- package/dist/CookieConsent/index.d.ts +5 -0
- package/dist/CookieConsent/index.js +8 -0
- package/dist/CookieConsent/store.d.ts +19 -0
- package/dist/CookieConsent/store.js +42 -0
- package/dist/CookieConsent/useCookieConsent.d.ts +23 -0
- package/dist/CookieConsent/useCookieConsent.js +31 -0
- package/dist/CookieConsent/utils.d.ts +66 -0
- package/dist/CookieConsent/utils.js +150 -0
- package/dist/DashboardLayout/DashboardSidebar.js +5 -5
- package/dist/DashboardLayout/MobileMenu.js +74 -66
- package/dist/Modal/Modal.d.ts +3 -0
- package/dist/Modal/index.js +21 -21
- package/dist/ResponsiveModal/ResponsiveModal.d.ts +29 -1
- package/dist/ResponsiveModal/index.js +75 -53
- package/dist/index.d.ts +44 -43
- package/dist/index.js +197 -191
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,14 @@ type BottomSheetProps = {
|
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
open: boolean;
|
|
6
6
|
onOpenChange?: (open: boolean) => void;
|
|
7
|
+
/** Classes for the sheet content panel (Drawer.Content). */
|
|
8
|
+
className?: string;
|
|
9
|
+
overlayClassName?: string;
|
|
10
|
+
dismissible?: boolean;
|
|
11
|
+
/** When `false`, the page behind stays interactive */
|
|
12
|
+
modal?: boolean;
|
|
13
|
+
/** When `true`, the page is NOT scroll-locked while open. */
|
|
14
|
+
disableScrollLock?: boolean;
|
|
7
15
|
};
|
|
8
16
|
export declare const BottomSheet: FC<BottomSheetProps>;
|
|
9
17
|
export {};
|
|
@@ -1,17 +1,51 @@
|
|
|
1
|
-
import { jsx as f, jsxs as
|
|
2
|
-
import { Drawer as
|
|
3
|
-
import { ClientOnly as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { jsx as f, jsxs as g } from "react/jsx-runtime";
|
|
2
|
+
import { Drawer as e } from "../node_modules/vaul/dist/index.js";
|
|
3
|
+
import { ClientOnly as m } from "../ClientOnly/index.js";
|
|
4
|
+
import { cn as o } from "../utils/index.js";
|
|
5
|
+
const u = ({
|
|
6
|
+
children: r,
|
|
7
|
+
open: t,
|
|
8
|
+
onOpenChange: l,
|
|
9
|
+
className: i,
|
|
10
|
+
overlayClassName: n,
|
|
11
|
+
dismissible: a,
|
|
12
|
+
modal: s,
|
|
13
|
+
disableScrollLock: c
|
|
14
|
+
}) => /* @__PURE__ */ f(m, { children: /* @__PURE__ */ f(
|
|
15
|
+
e.Root,
|
|
16
|
+
{
|
|
17
|
+
open: t,
|
|
18
|
+
onOpenChange: l,
|
|
19
|
+
repositionInputs: !1,
|
|
20
|
+
dismissible: a,
|
|
21
|
+
modal: s,
|
|
22
|
+
disablePreventScroll: c,
|
|
23
|
+
children: /* @__PURE__ */ g(e.Portal, { children: [
|
|
24
|
+
/* @__PURE__ */ f(
|
|
25
|
+
e.Overlay,
|
|
26
|
+
{
|
|
27
|
+
className: o(
|
|
28
|
+
"gf-fixed gf-inset-0 gf-z-50 gf-h-screen gf-w-screen gf-bg-dark-grey gf-bg-opacity-40 gf-transition-colors gf-duration-500 gf-ease-cubic-in-out",
|
|
29
|
+
n
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
/* @__PURE__ */ g(
|
|
34
|
+
e.Content,
|
|
35
|
+
{
|
|
36
|
+
className: o(
|
|
37
|
+
"gf-fixed gf-bottom-0 gf-left-0 gf-right-0 gf-z-50 gf-mt-24 gf-flex gf-max-h-[85vh] gf-flex-col gf-rounded-t-xl gf-bg-white/50 gf-px-4 gf-py-4 gf-outline-none gf-backdrop-blur-[20px]",
|
|
38
|
+
i
|
|
39
|
+
),
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ f("div", { className: "gf-mx-auto gf-mb-3 gf-h-1 gf-w-[60px] gf-flex-shrink-0 gf-rounded-full gf-bg-white/30" }),
|
|
42
|
+
/* @__PURE__ */ f("div", { className: "gf-scrollbar-hide gf-flex gf-flex-1 gf-flex-col gf-gap-5 gf-overflow-y-auto", children: r })
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
] })
|
|
47
|
+
}
|
|
48
|
+
) });
|
|
15
49
|
export {
|
|
16
|
-
|
|
50
|
+
u as BottomSheet
|
|
17
51
|
};
|
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { Checked as
|
|
3
|
-
import { Unchecked as
|
|
4
|
-
|
|
1
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { Checked as n } from "../Icons/Checked.js";
|
|
3
|
+
import { Unchecked as f } from "../Icons/Unchecked.js";
|
|
4
|
+
import { cn as a } from "../utils/index.js";
|
|
5
|
+
const s = ({
|
|
6
|
+
checked: e,
|
|
7
|
+
color: o,
|
|
8
|
+
disabled: r,
|
|
9
|
+
className: c,
|
|
10
|
+
onClick: m,
|
|
11
|
+
...i
|
|
12
|
+
}) => /* @__PURE__ */ t(
|
|
13
|
+
"div",
|
|
14
|
+
{
|
|
15
|
+
role: "checkbox",
|
|
16
|
+
"aria-checked": e,
|
|
17
|
+
"aria-disabled": r,
|
|
18
|
+
onClick: r ? void 0 : m,
|
|
19
|
+
className: a(
|
|
20
|
+
r ? "gf-cursor-not-allowed" : "gf-cursor-pointer",
|
|
21
|
+
c
|
|
22
|
+
),
|
|
23
|
+
...i,
|
|
24
|
+
children: e ? /* @__PURE__ */ t(
|
|
25
|
+
n,
|
|
26
|
+
{
|
|
27
|
+
color: !r && o ? o : "currentColor",
|
|
28
|
+
className: r ? "gf-text-midGrey" : o ? void 0 : "gf-text-button-primary"
|
|
29
|
+
}
|
|
30
|
+
) : /* @__PURE__ */ t(f, {})
|
|
31
|
+
}
|
|
32
|
+
);
|
|
5
33
|
export {
|
|
6
|
-
|
|
34
|
+
s as CheckBox
|
|
7
35
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { DesktopModalConfig, MobileBottomSheetConfig } from '../ResponsiveModal/ResponsiveModal';
|
|
3
|
+
import { ConsentChoice, CookieCategory, UseCookieConsentOptions } from './utils';
|
|
4
|
+
|
|
5
|
+
export type CookieConsentProps = {
|
|
6
|
+
/** Privacy Policy link shown on the notice screen. */
|
|
7
|
+
privacyPolicyUrl: string;
|
|
8
|
+
/** Categories rendered on the preferences screen (defaults to the three Garden categories). */
|
|
9
|
+
categories?: CookieCategory[];
|
|
10
|
+
/** Initial tick state for the two non-essential categories. */
|
|
11
|
+
defaultChecked?: boolean;
|
|
12
|
+
/** Called after a choice is persisted. */
|
|
13
|
+
onChange?: (choice: ConsentChoice) => void;
|
|
14
|
+
/** Override cookie domain / version / expiry. */
|
|
15
|
+
config?: UseCookieConsentOptions;
|
|
16
|
+
/** Desktop modal overrides — merged over the defaults
|
|
17
|
+
* (docked bottom-left, transparent click-through overlay, no X, no scroll-lock). */
|
|
18
|
+
desktopModal?: DesktopModalConfig;
|
|
19
|
+
/** Mobile bottom sheet overrides — defaults to a standard dismissible sheet
|
|
20
|
+
* (dim backdrop, tap-outside / drag to close). */
|
|
21
|
+
mobileBottomSheet?: MobileBottomSheetConfig;
|
|
22
|
+
};
|
|
23
|
+
/** Orchestrator: owns the consent store + responsive modal, and switches between
|
|
24
|
+
* the two presentational screens. */
|
|
25
|
+
export declare const CookieConsent: FC<CookieConsentProps>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as c } from "react/jsx-runtime";
|
|
2
|
+
import { ResponsiveModal as S } from "../ResponsiveModal/index.js";
|
|
3
|
+
import { useCookieConsent as x } from "./useCookieConsent.js";
|
|
4
|
+
import { CookieConsentNotice as E } from "./CookieConsentNotice.js";
|
|
5
|
+
import { CookieConsentPreferences as N } from "./CookieConsentPreferences.js";
|
|
6
|
+
import { trackConsentEvent as r, DEFAULT_COOKIE_CATEGORIES as b, consentDecision as O } from "./utils.js";
|
|
7
|
+
import { cn as i } from "../utils/index.js";
|
|
8
|
+
const F = ({
|
|
9
|
+
privacyPolicyUrl: o,
|
|
10
|
+
categories: f = b,
|
|
11
|
+
defaultChecked: l = !0,
|
|
12
|
+
onChange: n,
|
|
13
|
+
config: a,
|
|
14
|
+
desktopModal: e,
|
|
15
|
+
mobileBottomSheet: m
|
|
16
|
+
}) => {
|
|
17
|
+
const { isOpen: u, view: _, ready: p, setView: v, close: y, acceptAll: A, save: C } = x(a);
|
|
18
|
+
if (!p) return null;
|
|
19
|
+
const g = {
|
|
20
|
+
preventClose: (e == null ? void 0 : e.preventClose) ?? !0,
|
|
21
|
+
disableScrollLock: (e == null ? void 0 : e.disableScrollLock) ?? !0,
|
|
22
|
+
overlayClassName: i(
|
|
23
|
+
"!gf-items-end !gf-justify-start gf-pl-[40px] !gf-bg-transparent !gf-pointer-events-none",
|
|
24
|
+
e == null ? void 0 : e.overlayClassName
|
|
25
|
+
),
|
|
26
|
+
className: i(
|
|
27
|
+
"!gf-pointer-events-auto !gf-w-[420px] gf-gap-0 gf-px-3 gf-py-4 gf-rounded-t-4 gf-rounded-b-none",
|
|
28
|
+
e == null ? void 0 : e.className
|
|
29
|
+
)
|
|
30
|
+
}, s = () => {
|
|
31
|
+
A(), r("cookie_consent_set", {
|
|
32
|
+
consent_action: "accept_all",
|
|
33
|
+
consent_analytics: !0,
|
|
34
|
+
consent_functional: !0,
|
|
35
|
+
consent_decision: "accept_all"
|
|
36
|
+
}), n == null || n({ analytics: !0, functional: !0 });
|
|
37
|
+
};
|
|
38
|
+
return /* @__PURE__ */ c(
|
|
39
|
+
S,
|
|
40
|
+
{
|
|
41
|
+
open: u,
|
|
42
|
+
onClose: y,
|
|
43
|
+
desktopModal: g,
|
|
44
|
+
mobileBottomSheet: m,
|
|
45
|
+
children: _ === "notice" ? /* @__PURE__ */ c(
|
|
46
|
+
E,
|
|
47
|
+
{
|
|
48
|
+
privacyPolicyUrl: o,
|
|
49
|
+
onChangePreferences: () => {
|
|
50
|
+
r("cookie_prefs_opened"), v("preferences");
|
|
51
|
+
},
|
|
52
|
+
onAcceptAll: s
|
|
53
|
+
}
|
|
54
|
+
) : /* @__PURE__ */ c(
|
|
55
|
+
N,
|
|
56
|
+
{
|
|
57
|
+
categories: f,
|
|
58
|
+
defaultChecked: l,
|
|
59
|
+
onAcceptAll: s,
|
|
60
|
+
onSave: (t) => {
|
|
61
|
+
C(t), r("cookie_consent_set", {
|
|
62
|
+
consent_action: "save",
|
|
63
|
+
consent_analytics: t.analytics,
|
|
64
|
+
consent_functional: t.functional,
|
|
65
|
+
consent_decision: O(t)
|
|
66
|
+
}), n == null || n(t);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
export {
|
|
74
|
+
F as CookieConsent
|
|
75
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsxs as a, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { Typography as r } from "../Typography/index.js";
|
|
3
|
+
import { Button as l } from "../Button/index.js";
|
|
4
|
+
const g = ({
|
|
5
|
+
privacyPolicyUrl: i,
|
|
6
|
+
onChangePreferences: s,
|
|
7
|
+
onAcceptAll: o
|
|
8
|
+
}) => /* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-9", children: [
|
|
9
|
+
/* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-3 gf-px-1", children: [
|
|
10
|
+
/* @__PURE__ */ e(r, { as: "p", size: "h5", weight: "medium", className: "!gf-leading-5", children: "Manage cookies" }),
|
|
11
|
+
/* @__PURE__ */ a(r, { as: "p", size: "h4", children: [
|
|
12
|
+
"We use cookies and analytics to improve Garden. ",
|
|
13
|
+
/* @__PURE__ */ e("br", {}),
|
|
14
|
+
"Learn more in our",
|
|
15
|
+
" ",
|
|
16
|
+
/* @__PURE__ */ e(
|
|
17
|
+
"a",
|
|
18
|
+
{
|
|
19
|
+
href: i,
|
|
20
|
+
target: "_blank",
|
|
21
|
+
rel: "noreferrer",
|
|
22
|
+
className: "gf-font-medium",
|
|
23
|
+
children: "Privacy Policy"
|
|
24
|
+
}
|
|
25
|
+
),
|
|
26
|
+
"."
|
|
27
|
+
] })
|
|
28
|
+
] }),
|
|
29
|
+
/* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-3", children: [
|
|
30
|
+
/* @__PURE__ */ e(
|
|
31
|
+
l,
|
|
32
|
+
{
|
|
33
|
+
variant: "ternary",
|
|
34
|
+
size: "sm",
|
|
35
|
+
colors: { ternary: "rgba(255,255,255,0.5)" },
|
|
36
|
+
className: "gf-w-full",
|
|
37
|
+
onClick: s,
|
|
38
|
+
children: "Change preferences"
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
/* @__PURE__ */ e(
|
|
42
|
+
l,
|
|
43
|
+
{
|
|
44
|
+
variant: "primary",
|
|
45
|
+
size: "sm",
|
|
46
|
+
className: "gf-w-full",
|
|
47
|
+
onClick: o,
|
|
48
|
+
children: "Accept all"
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
] })
|
|
52
|
+
] });
|
|
53
|
+
export {
|
|
54
|
+
g as CookieConsentNotice
|
|
55
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { ConsentChoice, CookieCategory } from './utils';
|
|
3
|
+
|
|
4
|
+
export type CookieConsentPreferencesProps = {
|
|
5
|
+
categories: CookieCategory[];
|
|
6
|
+
defaultChecked: boolean;
|
|
7
|
+
onAcceptAll: () => void;
|
|
8
|
+
onSave: (choice: ConsentChoice) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const CookieConsentPreferences: FC<CookieConsentPreferencesProps>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsxs as a, jsx as l } from "react/jsx-runtime";
|
|
2
|
+
import { useState as p } from "react";
|
|
3
|
+
import { Typography as s } from "../Typography/index.js";
|
|
4
|
+
import { Button as c } from "../Button/index.js";
|
|
5
|
+
import { CheckBox as h } from "../Checkbox/Checkbox.js";
|
|
6
|
+
const v = ({
|
|
7
|
+
categories: n,
|
|
8
|
+
defaultChecked: r,
|
|
9
|
+
onAcceptAll: t,
|
|
10
|
+
onSave: o
|
|
11
|
+
}) => {
|
|
12
|
+
const [f, m] = p({
|
|
13
|
+
analytics: r,
|
|
14
|
+
functional: r
|
|
15
|
+
});
|
|
16
|
+
return /* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-9", children: [
|
|
17
|
+
/* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-3 gf-px-1", children: [
|
|
18
|
+
/* @__PURE__ */ l(s, { as: "p", size: "h5", weight: "medium", className: "!gf-leading-5", children: "Manage cookies" }),
|
|
19
|
+
/* @__PURE__ */ l("div", { className: "gf-flex gf-flex-col gf-gap-4", children: n.map((e) => {
|
|
20
|
+
const i = e.id, d = e.locked ? !0 : f[i];
|
|
21
|
+
return /* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-1", children: [
|
|
22
|
+
/* @__PURE__ */ a("div", { className: "gf-flex gf-items-center gf-justify-between gf-gap-4", children: [
|
|
23
|
+
/* @__PURE__ */ l(s, { as: "p", size: "h4", weight: "medium", children: e.title }),
|
|
24
|
+
/* @__PURE__ */ l(
|
|
25
|
+
h,
|
|
26
|
+
{
|
|
27
|
+
checked: d,
|
|
28
|
+
disabled: e.locked,
|
|
29
|
+
"aria-label": e.title,
|
|
30
|
+
className: "gf-shrink-0 gf-text-dark-grey",
|
|
31
|
+
onClick: () => m((g) => ({ ...g, [i]: !g[i] }))
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
] }),
|
|
35
|
+
/* @__PURE__ */ l(
|
|
36
|
+
s,
|
|
37
|
+
{
|
|
38
|
+
as: "p",
|
|
39
|
+
size: "h4",
|
|
40
|
+
weight: "regular",
|
|
41
|
+
className: "gf-max-w-[340px]",
|
|
42
|
+
children: e.description
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
] }, e.id);
|
|
46
|
+
}) })
|
|
47
|
+
] }),
|
|
48
|
+
/* @__PURE__ */ a("div", { className: "gf-flex gf-flex-col gf-gap-3", children: [
|
|
49
|
+
/* @__PURE__ */ l(
|
|
50
|
+
c,
|
|
51
|
+
{
|
|
52
|
+
variant: "ternary",
|
|
53
|
+
size: "sm",
|
|
54
|
+
colors: { ternary: "rgba(255,255,255,0.5)" },
|
|
55
|
+
className: "gf-w-full",
|
|
56
|
+
onClick: t,
|
|
57
|
+
children: "Accept all"
|
|
58
|
+
}
|
|
59
|
+
),
|
|
60
|
+
/* @__PURE__ */ l(
|
|
61
|
+
c,
|
|
62
|
+
{
|
|
63
|
+
variant: "primary",
|
|
64
|
+
size: "sm",
|
|
65
|
+
className: "gf-w-full",
|
|
66
|
+
onClick: () => o(f),
|
|
67
|
+
children: "Save"
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] })
|
|
71
|
+
] });
|
|
72
|
+
};
|
|
73
|
+
export {
|
|
74
|
+
v as CookieConsentPreferences
|
|
75
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './CookieConsent';
|
|
2
|
+
export { useCookieConsent } from './useCookieConsent';
|
|
3
|
+
export type { UseCookieConsentReturn } from './useCookieConsent';
|
|
4
|
+
export { CONSENT_EVENT } from './utils';
|
|
5
|
+
export type { ConsentChoice, StoredConsent, ConsentView, UseCookieConsentOptions, } from './utils';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConsentView, StoredConsent } from './utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module-level store shared across all `useCookieConsent` instances,
|
|
5
|
+
* so the modal control one source of truth even though they call the
|
|
6
|
+
* hook independently. Subscribed via `useSyncExternalStore`.
|
|
7
|
+
*/
|
|
8
|
+
type State = {
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
view: ConsentView;
|
|
11
|
+
consent: StoredConsent | null;
|
|
12
|
+
ready: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare const setState: (patch: Partial<State>) => void;
|
|
15
|
+
export declare const subscribe: (l: () => void) => () => boolean;
|
|
16
|
+
export declare const getSnapshot: () => State;
|
|
17
|
+
export declare const getServerSnapshot: () => State;
|
|
18
|
+
export declare function boot(): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readRaw as l, isValidConsent as u, applyConsent as n, trackConsentEvent as a, hasTrackingCookies as f, isWithinAutoConsentWindow as d, writeConsent as p } from "./utils.js";
|
|
2
|
+
let s = {
|
|
3
|
+
isOpen: !1,
|
|
4
|
+
view: "notice",
|
|
5
|
+
consent: null,
|
|
6
|
+
ready: !1
|
|
7
|
+
};
|
|
8
|
+
const w = {
|
|
9
|
+
isOpen: !1,
|
|
10
|
+
view: "notice",
|
|
11
|
+
consent: null,
|
|
12
|
+
ready: !1
|
|
13
|
+
}, o = /* @__PURE__ */ new Set(), y = () => o.forEach((e) => e()), t = (e) => {
|
|
14
|
+
s = { ...s, ...e }, y();
|
|
15
|
+
}, h = (e) => (o.add(e), () => o.delete(e)), O = () => s, b = () => w, i = { analytics: !1, functional: !1 };
|
|
16
|
+
let c = !1;
|
|
17
|
+
function v() {
|
|
18
|
+
if (c) return;
|
|
19
|
+
c = !0;
|
|
20
|
+
const e = l();
|
|
21
|
+
if (u(e)) {
|
|
22
|
+
n(e), t({ consent: e, ready: !0, isOpen: !1 });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (e) {
|
|
26
|
+
n(i), t({ consent: null, ready: !0, isOpen: !0, view: "notice" }), a("cookie_banner_shown");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (f() && d()) {
|
|
30
|
+
const r = p({ analytics: !0, functional: !0 });
|
|
31
|
+
n(r), t({ consent: r, ready: !0, isOpen: !1 });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
n(i), t({ consent: null, ready: !0, isOpen: !0, view: "notice" }), a("cookie_banner_shown");
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
v as boot,
|
|
38
|
+
b as getServerSnapshot,
|
|
39
|
+
O as getSnapshot,
|
|
40
|
+
t as setState,
|
|
41
|
+
h as subscribe
|
|
42
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ConsentChoice, ConsentView, StoredConsent, UseCookieConsentOptions } from './utils';
|
|
2
|
+
|
|
3
|
+
export type UseCookieConsentReturn = {
|
|
4
|
+
/** The stored choice, or null if none/expired. */
|
|
5
|
+
consent: StoredConsent | null;
|
|
6
|
+
/** Whether the banner/modal is open. */
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
/** Which screen to show when open. */
|
|
9
|
+
view: ConsentView;
|
|
10
|
+
/** True once boot has run on the client (avoids SSR flash). */
|
|
11
|
+
ready: boolean;
|
|
12
|
+
/** Open the banner at a given screen (default the notice; pass "preferences" for a footer link). */
|
|
13
|
+
open: (view?: ConsentView) => void;
|
|
14
|
+
/** Switch screen without closing (e.g. notice → preferences). */
|
|
15
|
+
setView: (view: ConsentView) => void;
|
|
16
|
+
/** Close without recording a choice (banner reappears next load). */
|
|
17
|
+
close: () => void;
|
|
18
|
+
/** Grant everything and persist. */
|
|
19
|
+
acceptAll: () => void;
|
|
20
|
+
/** Persist a specific choice. */
|
|
21
|
+
save: (choice: ConsentChoice) => void;
|
|
22
|
+
};
|
|
23
|
+
export declare function useCookieConsent(options?: UseCookieConsentOptions): UseCookieConsentReturn;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useSyncExternalStore as a, useEffect as i, useCallback as c } from "react";
|
|
2
|
+
import { subscribe as p, getSnapshot as l, getServerSnapshot as u, boot as f, setState as n } from "./store.js";
|
|
3
|
+
import { configure as m, writeConsent as C, applyConsent as S, clearDeniedCookies as y } from "./utils.js";
|
|
4
|
+
function v(o) {
|
|
5
|
+
const t = a(
|
|
6
|
+
p,
|
|
7
|
+
l,
|
|
8
|
+
u
|
|
9
|
+
);
|
|
10
|
+
i(() => {
|
|
11
|
+
m(o), f();
|
|
12
|
+
}, []);
|
|
13
|
+
const s = c((e) => {
|
|
14
|
+
const r = C(e);
|
|
15
|
+
S(e), y(e), n({ consent: r, isOpen: !1 });
|
|
16
|
+
}, []);
|
|
17
|
+
return {
|
|
18
|
+
consent: t.consent,
|
|
19
|
+
isOpen: t.isOpen,
|
|
20
|
+
view: t.view,
|
|
21
|
+
ready: t.ready,
|
|
22
|
+
open: (e = "notice") => n({ isOpen: !0, view: e }),
|
|
23
|
+
setView: (e) => n({ view: e }),
|
|
24
|
+
close: () => n({ isOpen: !1 }),
|
|
25
|
+
acceptAll: () => s({ analytics: !0, functional: !0 }),
|
|
26
|
+
save: (e) => s(e)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
v as useCookieConsent
|
|
31
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export type CookieCategory = {
|
|
2
|
+
id: "essential" | "analytics" | "functional";
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
/** Always on, no toggle (Essential). */
|
|
6
|
+
locked?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** Default Garden categories for the preferences UI. */
|
|
9
|
+
export declare const DEFAULT_COOKIE_CATEGORIES: CookieCategory[];
|
|
10
|
+
export type ConsentView = "notice" | "preferences";
|
|
11
|
+
export type ConsentChoice = {
|
|
12
|
+
analytics: boolean;
|
|
13
|
+
functional: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type StoredConsent = ConsentChoice & {
|
|
16
|
+
/** Always true — Essential is locked on. Stored explicitly so external readers
|
|
17
|
+
* (proof-of-consent record, audits) see the full picture, even though we never mutate it. */
|
|
18
|
+
essential: true;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
version: number;
|
|
21
|
+
};
|
|
22
|
+
/** Window event the telemetry scripts (loaded separately) listen on to react without a reload. */
|
|
23
|
+
export declare const CONSENT_EVENT = "garden:consentchange";
|
|
24
|
+
type Config = {
|
|
25
|
+
/** Cookie domain — `.garden.finance` shares the choice across www + app. */
|
|
26
|
+
cookieDomain: string;
|
|
27
|
+
/** Bump to force re-consent after a policy/category change. */
|
|
28
|
+
storageVersion: number;
|
|
29
|
+
/** Consent *validity* — re-prompt after this many months (checked via timestamp). */
|
|
30
|
+
expiryMonths: number;
|
|
31
|
+
/** Cookie *lifetime* in days — kept longer than validity (and refreshed each visit)
|
|
32
|
+
* so a stale-but-present cookie can be told apart from an absent one. Browser caps at 400. */
|
|
33
|
+
cookieMaxAgeDays: number;
|
|
34
|
+
};
|
|
35
|
+
export type UseCookieConsentOptions = Partial<Config>;
|
|
36
|
+
export declare const config: Config;
|
|
37
|
+
/**
|
|
38
|
+
* Patch the module config in place. Callers can't reassign the imported `config`
|
|
39
|
+
* binding (ES module imports are read-only), so mutate its properties instead.
|
|
40
|
+
*/
|
|
41
|
+
export declare function configure(options?: UseCookieConsentOptions): void;
|
|
42
|
+
export declare function isWithinAutoConsentWindow(): boolean;
|
|
43
|
+
/** Parse the cookie. Returns the record if present and parseable, else null. */
|
|
44
|
+
export declare function readRaw(): StoredConsent | null;
|
|
45
|
+
/** A record is valid if it matches the current policy version and is within the
|
|
46
|
+
* 13-month validity window. */
|
|
47
|
+
export declare function isValidConsent(data: StoredConsent | null): data is StoredConsent;
|
|
48
|
+
/** Record a new choice (stamps a fresh timestamp + current version). */
|
|
49
|
+
export declare function writeConsent(choice: ConsentChoice): StoredConsent;
|
|
50
|
+
/** True if any pre-existing analytics/functional cookie is present. Used to
|
|
51
|
+
* detect a pre-deploy "existing user" (Essential cookies are excluded). */
|
|
52
|
+
export declare function hasTrackingCookies(): boolean;
|
|
53
|
+
/** Delete the cookies of any category the choice denies. Idempotent (deleting an
|
|
54
|
+
* absent cookie is a no-op), so safe to call on every consent update. Tool-native
|
|
55
|
+
* clears (posthog.reset / Clarity consentv2) happen in the GTM tags. */
|
|
56
|
+
export declare function clearDeniedCookies(choice: ConsentChoice): void;
|
|
57
|
+
export declare function applyConsent(choice: ConsentChoice): void;
|
|
58
|
+
export type ConsentSurface = "app" | "www" | "localhost";
|
|
59
|
+
/** Which site fired the event — so app + www land in one GA4 property and segment by `surface`. */
|
|
60
|
+
export declare function consentSurface(): ConsentSurface;
|
|
61
|
+
/** One-field summary of a choice for easy reporting. */
|
|
62
|
+
export declare function consentDecision(choice: ConsentChoice): "accept_all" | "reject_all" | "partial";
|
|
63
|
+
/** Push a consent-funnel event to the dataLayer. Always tags `surface`. Fires
|
|
64
|
+
* regardless of consent state (GA4 cookieless pings collect it either way). */
|
|
65
|
+
export declare function trackConsentEvent(event: "cookie_banner_shown" | "cookie_prefs_opened" | "cookie_consent_set", props?: Record<string, unknown>): void;
|
|
66
|
+
export {};
|