react-lgpd-consent 0.2.4 → 0.3.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.
@@ -1,5 +1,5 @@
1
1
  // src/components/PreferencesModal.tsx
2
- import Button from "@mui/material/Button";
2
+ import Button2 from "@mui/material/Button";
3
3
  import Dialog from "@mui/material/Dialog";
4
4
  import DialogActions from "@mui/material/DialogActions";
5
5
  import DialogContent from "@mui/material/DialogContent";
@@ -7,7 +7,7 @@ import DialogTitle from "@mui/material/DialogTitle";
7
7
  import FormControlLabel from "@mui/material/FormControlLabel";
8
8
  import FormGroup from "@mui/material/FormGroup";
9
9
  import Switch from "@mui/material/Switch";
10
- import Typography2 from "@mui/material/Typography";
10
+ import Typography3 from "@mui/material/Typography";
11
11
  import { useEffect as useEffect4, useState as useState2 } from "react";
12
12
 
13
13
  // src/context/CategoriesContext.tsx
@@ -16,9 +16,8 @@ import * as React2 from "react";
16
16
  // src/utils/developerGuidance.ts
17
17
  import * as React from "react";
18
18
  var DEFAULT_PROJECT_CATEGORIES = {
19
- enabledCategories: ["analytics"],
19
+ enabledCategories: ["analytics"]
20
20
  // Só analytics além de necessary
21
- customCategories: []
22
21
  };
23
22
  function analyzeDeveloperConfiguration(config) {
24
23
  const guidance = {
@@ -77,17 +76,6 @@ function analyzeDeveloperConfiguration(config) {
77
76
  });
78
77
  }
79
78
  });
80
- const customCategories = finalConfig.customCategories || [];
81
- customCategories.forEach((category) => {
82
- guidance.activeCategoriesInfo.push({
83
- id: category.id,
84
- name: category.name,
85
- description: category.description,
86
- essential: category.essential === true,
87
- uiRequired: category.essential !== true
88
- // Apenas não-essenciais precisam toggle
89
- });
90
- });
91
79
  const totalToggleable = guidance.activeCategoriesInfo.filter(
92
80
  (c) => c.uiRequired
93
81
  ).length;
@@ -102,73 +90,62 @@ function analyzeDeveloperConfiguration(config) {
102
90
  'prejudicar experi\xEAncia do usu\xE1rio. Considere agrupar categorias similares.`
103
91
  );
104
92
  }
105
- const poorDescriptions = customCategories.filter(
106
- (c) => !c.description || c.description.length < 10
107
- );
108
- if (poorDescriptions.length > 0) {
109
- guidance.warnings.push(
110
- `Categorias customizadas com descri\xE7\xF5es inadequadas: ${poorDescriptions.map((c) => c.id).join(", ")}. Descri\xE7\xF5es claras s\xE3o obrigat\xF3rias para compliance LGPD.`
111
- );
112
- }
113
93
  return guidance;
114
94
  }
115
95
  function logDeveloperGuidance(guidance, disableGuidanceProp) {
116
- try {
117
- const isProduction = (
118
- // 1. NODE_ENV de bundlers (Vite, webpack, etc.)
119
- typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV === "production" || // 2. Vite/bundler env vars (apenas em ESM)
120
- typeof globalThis !== "undefined" && typeof globalThis.import !== "undefined" && globalThis.import.meta?.env?.PROD === true || // 3. Flag customizada para desabilitar logs
121
- typeof globalThis !== "undefined" && globalThis.__LGPD_PRODUCTION__ || // 4. Flag de desenvolvimento desabilitada via window global (legado)
122
- typeof window !== "undefined" && window.__LGPD_DISABLE_GUIDANCE__ || // 5. Flag de desenvolvimento desabilitada via prop do ConsentProvider (preferencial)
123
- disableGuidanceProp
124
- );
125
- if (isProduction) return;
126
- const PREFIX = "[\u{1F36A} LGPD-CONSENT]";
127
- if (guidance.warnings.length > 0) {
128
- console.group(`${PREFIX} \u26A0\uFE0F Avisos de Configura\xE7\xE3o`);
129
- guidance.warnings.forEach(
130
- (warning) => console.warn(`${PREFIX} ${warning}`)
131
- );
132
- console.groupEnd();
133
- }
134
- if (guidance.suggestions.length > 0) {
135
- console.group(`${PREFIX} \u{1F4A1} Sugest\xF5es`);
136
- guidance.suggestions.forEach(
137
- (suggestion) => console.info(`${PREFIX} ${suggestion}`)
138
- );
139
- console.groupEnd();
140
- }
141
- if (guidance.usingDefaults) {
142
- console.info(
143
- `${PREFIX} \u{1F4CB} Usando configura\xE7\xE3o padr\xE3o. Para personalizar, use a prop "categories" no ConsentProvider.`
144
- );
145
- }
146
- console.group(`${PREFIX} \u{1F527} Categorias Ativas (para UI customizada)`);
147
- console.table(
148
- guidance.activeCategoriesInfo.map((cat) => ({
149
- ID: cat.id,
150
- Nome: cat.name,
151
- "Toggle UI?": cat.uiRequired ? "\u2705 SIM" : "\u274C N\xC3O (sempre ativo)",
152
- "Essencial?": cat.essential ? "\u{1F512} SIM" : "\u2699\uFE0F N\xC3O"
153
- }))
154
- );
155
- console.info(
156
- `${PREFIX} \u2139\uFE0F Use estes dados para criar componentes customizados adequados.`
96
+ if (disableGuidanceProp) {
97
+ return;
98
+ }
99
+ const isProduction = (
100
+ // 1. NODE_ENV de bundlers (Vite, webpack, etc.)
101
+ typeof globalThis.process !== "undefined" && globalThis.process.env?.NODE_ENV === "production" || // 2. Flag customizada para desabilitar logs
102
+ typeof globalThis !== "undefined" && globalThis.__LGPD_PRODUCTION__ === true || // 3. Flag de desenvolvimento desabilitada via window global (legado)
103
+ typeof window !== "undefined" && window.__LGPD_DISABLE_GUIDANCE__ === true
104
+ );
105
+ if (isProduction) return;
106
+ const PREFIX = "[\u{1F36A} LGPD-CONSENT]";
107
+ if (guidance.warnings.length > 0) {
108
+ console.group(`${PREFIX} \u26A0\uFE0F Avisos de Configura\xE7\xE3o`);
109
+ guidance.warnings.forEach((warning) => console.warn(`${PREFIX} ${warning}`));
110
+ console.groupEnd();
111
+ }
112
+ if (guidance.suggestions.length > 0) {
113
+ console.group(`${PREFIX} \u{1F4A1} Sugest\xF5es`);
114
+ guidance.suggestions.forEach(
115
+ (suggestion) => console.info(`${PREFIX} ${suggestion}`)
157
116
  );
158
117
  console.groupEnd();
159
- } catch (error) {
160
- if (typeof console !== "undefined" && console.warn) {
161
- console.warn(
162
- "[\u{1F36A} LGPD-CONSENT] Sistema de orienta\xE7\xF5es encontrou erro:",
163
- error
164
- );
165
- }
166
118
  }
119
+ if (guidance.usingDefaults) {
120
+ console.warn(
121
+ // Changed from console.info to console.warn
122
+ `${PREFIX} \u{1F4CB} Usando configura\xE7\xE3o padr\xE3o. Para personalizar, use a prop "categories" no ConsentProvider.`
123
+ );
124
+ }
125
+ console.group(`${PREFIX} \u{1F527} Categorias Ativas (para UI customizada)`);
126
+ console.table(
127
+ guidance.activeCategoriesInfo.map((cat) => ({
128
+ ID: cat.id,
129
+ Nome: cat.name,
130
+ "Toggle UI?": cat.uiRequired ? "\u2705 SIM" : "\u274C N\xC3O (sempre ativo)",
131
+ "Essencial?": cat.essential ? "\u{1F512} SIM" : "\u2699\uFE0F N\xC3O"
132
+ }))
133
+ );
134
+ console.info(
135
+ `${PREFIX} \u2139\uFE0F Use estes dados para criar componentes customizados adequados.`
136
+ );
137
+ console.groupEnd();
167
138
  }
168
139
  function useDeveloperGuidance(config, disableGuidanceProp) {
169
140
  const guidance = analyzeDeveloperConfiguration(config);
170
- const stringifiedConfig = React.useMemo(() => JSON.stringify(config), [config]);
141
+ const stringifiedConfig = React.useMemo(
142
+ () => JSON.stringify(config),
143
+ [config]
144
+ );
171
145
  React.useEffect(() => {
146
+ if (disableGuidanceProp === true) {
147
+ return;
148
+ }
172
149
  logDeveloperGuidance(guidance, disableGuidanceProp);
173
150
  }, [guidance, stringifiedConfig, disableGuidanceProp]);
174
151
  return guidance;
@@ -179,27 +156,15 @@ import { jsx } from "react/jsx-runtime";
179
156
  var CategoriesContext = React2.createContext(
180
157
  null
181
158
  );
182
- var CategoriesCtx = React2.createContext([]);
183
159
  function CategoriesProvider({
184
160
  children,
185
- categories,
186
- // LEGACY: prop antiga (apenas customCategories)
187
- config
161
+ config,
188
162
  // NOVO: configuração completa
163
+ disableDeveloperGuidance
189
164
  }) {
190
165
  const contextValue = React2.useMemo(() => {
191
- let finalConfig;
192
- if (categories && !config) {
193
- finalConfig = {
194
- enabledCategories: DEFAULT_PROJECT_CATEGORIES.enabledCategories,
195
- customCategories: categories
196
- };
197
- } else {
198
- finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
199
- }
200
- const guidance = analyzeDeveloperConfiguration(
201
- config || (categories ? { customCategories: categories } : void 0)
202
- );
166
+ const finalConfig = config || DEFAULT_PROJECT_CATEGORIES;
167
+ const guidance = analyzeDeveloperConfiguration(config);
203
168
  const toggleableCategories = guidance.activeCategoriesInfo.filter(
204
169
  (cat) => cat.uiRequired
205
170
  );
@@ -207,14 +172,13 @@ function CategoriesProvider({
207
172
  config: finalConfig,
208
173
  guidance,
209
174
  toggleableCategories,
210
- allCategories: guidance.activeCategoriesInfo,
211
- legacyCategories: categories || []
175
+ allCategories: guidance.activeCategoriesInfo
212
176
  };
213
- }, [config, categories]);
177
+ }, [config]);
214
178
  React2.useEffect(() => {
215
- logDeveloperGuidance(contextValue.guidance);
216
- }, [contextValue.guidance]);
217
- return /* @__PURE__ */ jsx(CategoriesContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(CategoriesCtx.Provider, { value: contextValue.legacyCategories, children }) });
179
+ logDeveloperGuidance(contextValue.guidance, disableDeveloperGuidance);
180
+ }, [contextValue.guidance, disableDeveloperGuidance]);
181
+ return /* @__PURE__ */ jsx(CategoriesContext.Provider, { value: contextValue, children });
218
182
  }
219
183
  function useCategories() {
220
184
  const context = React2.useContext(CategoriesContext);
@@ -236,12 +200,9 @@ function useCategoryStatus(categoryId) {
236
200
  description: category?.description
237
201
  };
238
202
  }
239
- function useCustomCategories() {
240
- return React2.useContext(CategoriesCtx);
241
- }
242
203
 
243
204
  // src/context/ConsentContext.tsx
244
- import * as React3 from "react";
205
+ import * as React4 from "react";
245
206
  import { ThemeProvider } from "@mui/material/styles";
246
207
 
247
208
  // src/utils/cookieUtils.ts
@@ -293,7 +254,7 @@ function migrateLegacyCookie(legacyData) {
293
254
  return null;
294
255
  }
295
256
  }
296
- function writeConsentCookie(state, source = "banner", opts) {
257
+ function writeConsentCookie(state, config, opts, source = "banner") {
297
258
  if (typeof document === "undefined") return;
298
259
  const now = (/* @__PURE__ */ new Date()).toISOString();
299
260
  const o = { ...DEFAULT_COOKIE_OPTS, ...opts };
@@ -304,7 +265,8 @@ function writeConsentCookie(state, source = "banner", opts) {
304
265
  consentDate: state.consentDate || now,
305
266
  // Preservar data original ou usar atual
306
267
  lastUpdate: now,
307
- source
268
+ source,
269
+ projectConfig: config
308
270
  // isModalOpen NÃO é persistido (campo de UI apenas)
309
271
  };
310
272
  Cookies.set(o.name, JSON.stringify(cookieData), {
@@ -320,6 +282,34 @@ function removeConsentCookie(opts) {
320
282
  Cookies.remove(o.name, { path: o.path });
321
283
  }
322
284
 
285
+ // src/utils/categoryUtils.ts
286
+ function createProjectPreferences(config, defaultValue = false) {
287
+ const preferences = {
288
+ necessary: true
289
+ // Sempre presente e true (essencial)
290
+ };
291
+ const enabledCategories = config?.enabledCategories || [];
292
+ enabledCategories.forEach((category) => {
293
+ if (category !== "necessary") {
294
+ preferences[category] = defaultValue;
295
+ }
296
+ });
297
+ return preferences;
298
+ }
299
+ function validateProjectPreferences(preferences, config) {
300
+ const validPreferences = {
301
+ necessary: true
302
+ // Sempre válida
303
+ };
304
+ const enabledCategories = config?.enabledCategories || [];
305
+ enabledCategories.forEach((category) => {
306
+ if (category !== "necessary" && preferences[category] !== void 0) {
307
+ validPreferences[category] = preferences[category];
308
+ }
309
+ });
310
+ return validPreferences;
311
+ }
312
+
323
313
  // src/utils/theme.ts
324
314
  import { createTheme } from "@mui/material/styles";
325
315
  var defaultConsentTheme = createTheme({
@@ -388,27 +378,285 @@ var defaultConsentTheme = createTheme({
388
378
  }
389
379
  });
390
380
 
381
+ // src/context/DesignContext.tsx
382
+ import * as React3 from "react";
383
+ import { jsx as jsx2 } from "react/jsx-runtime";
384
+ var DesignContext = React3.createContext(void 0);
385
+ function DesignProvider({
386
+ tokens,
387
+ children
388
+ }) {
389
+ return /* @__PURE__ */ jsx2(DesignContext.Provider, { value: tokens, children });
390
+ }
391
+ function useDesignTokens() {
392
+ return React3.useContext(DesignContext);
393
+ }
394
+
395
+ // src/components/CookieBanner.tsx
396
+ import Button from "@mui/material/Button";
397
+ import Box from "@mui/material/Box";
398
+ import Paper from "@mui/material/Paper";
399
+ import Snackbar from "@mui/material/Snackbar";
400
+ import Stack from "@mui/material/Stack";
401
+ import Typography2 from "@mui/material/Typography";
402
+ import Link2 from "@mui/material/Link";
403
+
404
+ // src/components/Branding.tsx
405
+ import Link from "@mui/material/Link";
406
+ import Typography from "@mui/material/Typography";
407
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
408
+ var brandingStyles = {
409
+ banner: {
410
+ fontSize: "0.65rem",
411
+ textAlign: "right",
412
+ mt: 1,
413
+ opacity: 0.7,
414
+ fontStyle: "italic",
415
+ width: "100%"
416
+ },
417
+ modal: {
418
+ fontSize: "0.65rem",
419
+ textAlign: "right",
420
+ px: 3,
421
+ pb: 1,
422
+ opacity: 0.7,
423
+ fontStyle: "italic",
424
+ width: "100%"
425
+ }
426
+ };
427
+ var linkStyles = {
428
+ textDecoration: "none",
429
+ fontWeight: 500,
430
+ "&:hover": {
431
+ textDecoration: "underline"
432
+ }
433
+ };
434
+ function Branding({ variant, hidden = false }) {
435
+ if (hidden) return null;
436
+ return /* @__PURE__ */ jsxs(
437
+ Typography,
438
+ {
439
+ variant: "caption",
440
+ sx: (theme) => ({
441
+ ...brandingStyles[variant],
442
+ color: theme.palette.text.secondary
443
+ }),
444
+ children: [
445
+ "fornecido por",
446
+ " ",
447
+ /* @__PURE__ */ jsx3(
448
+ Link,
449
+ {
450
+ href: "https://www.ledipo.eti.br",
451
+ target: "_blank",
452
+ rel: "noopener noreferrer",
453
+ sx: (theme) => ({
454
+ ...linkStyles,
455
+ color: theme.palette.primary.main
456
+ }),
457
+ children: "L\xC9dipO.eti.br"
458
+ }
459
+ )
460
+ ]
461
+ }
462
+ );
463
+ }
464
+
465
+ // src/components/CookieBanner.tsx
466
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
467
+ function CookieBanner({
468
+ policyLinkUrl,
469
+ debug,
470
+ blocking = true,
471
+ hideBranding = false,
472
+ SnackbarProps,
473
+ PaperProps
474
+ }) {
475
+ const { consented, acceptAll, rejectAll, openPreferences } = useConsent();
476
+ const texts = useConsentTexts();
477
+ const isHydrated = useConsentHydration();
478
+ const designTokens = useDesignTokens();
479
+ const open = debug ? true : isHydrated && !consented;
480
+ if (!open) return null;
481
+ const bannerStyle = {
482
+ p: designTokens?.spacing?.padding?.banner ?? 2,
483
+ maxWidth: 720,
484
+ mx: "auto",
485
+ backgroundColor: designTokens?.colors?.background,
486
+ color: designTokens?.colors?.text,
487
+ borderRadius: designTokens?.spacing?.borderRadius?.banner,
488
+ fontFamily: designTokens?.typography?.fontFamily
489
+ };
490
+ const bannerContent = /* @__PURE__ */ jsx4(Paper, { elevation: 3, sx: bannerStyle, ...PaperProps, children: /* @__PURE__ */ jsxs2(Stack, { spacing: 1, children: [
491
+ /* @__PURE__ */ jsxs2(
492
+ Typography2,
493
+ {
494
+ variant: "body2",
495
+ sx: { fontSize: designTokens?.typography?.fontSize?.banner },
496
+ children: [
497
+ texts.bannerMessage,
498
+ " ",
499
+ policyLinkUrl && /* @__PURE__ */ jsx4(
500
+ Link2,
501
+ {
502
+ href: policyLinkUrl,
503
+ underline: "hover",
504
+ target: "_blank",
505
+ rel: "noopener noreferrer",
506
+ sx: { color: designTokens?.colors?.primary },
507
+ children: texts.policyLink ?? "Saiba mais"
508
+ }
509
+ )
510
+ ]
511
+ }
512
+ ),
513
+ /* @__PURE__ */ jsxs2(
514
+ Stack,
515
+ {
516
+ direction: { xs: "column", sm: "row" },
517
+ spacing: 1,
518
+ justifyContent: "flex-end",
519
+ children: [
520
+ /* @__PURE__ */ jsx4(
521
+ Button,
522
+ {
523
+ variant: "outlined",
524
+ onClick: rejectAll,
525
+ sx: { color: designTokens?.colors?.secondary },
526
+ children: texts.declineAll
527
+ }
528
+ ),
529
+ /* @__PURE__ */ jsx4(
530
+ Button,
531
+ {
532
+ variant: "contained",
533
+ onClick: acceptAll,
534
+ sx: { backgroundColor: designTokens?.colors?.primary },
535
+ children: texts.acceptAll
536
+ }
537
+ ),
538
+ /* @__PURE__ */ jsx4(
539
+ Button,
540
+ {
541
+ variant: "text",
542
+ onClick: openPreferences,
543
+ sx: { color: designTokens?.colors?.text },
544
+ children: texts.preferences
545
+ }
546
+ )
547
+ ]
548
+ }
549
+ ),
550
+ !hideBranding && /* @__PURE__ */ jsx4(Branding, { variant: "banner" })
551
+ ] }) });
552
+ const positionStyle = {
553
+ position: "fixed",
554
+ zIndex: 1300,
555
+ ...designTokens?.layout?.position === "top" ? { top: 0 } : { bottom: 0 },
556
+ left: 0,
557
+ right: 0,
558
+ width: designTokens?.layout?.width?.desktop ?? "100%",
559
+ p: 2
560
+ };
561
+ if (blocking) {
562
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
563
+ /* @__PURE__ */ jsx4(
564
+ Box,
565
+ {
566
+ sx: {
567
+ position: "fixed",
568
+ top: 0,
569
+ left: 0,
570
+ right: 0,
571
+ bottom: 0,
572
+ backgroundColor: designTokens?.layout?.backdrop ? "rgba(0, 0, 0, 0.5)" : "transparent",
573
+ zIndex: 1299
574
+ }
575
+ }
576
+ ),
577
+ /* @__PURE__ */ jsx4(Box, { sx: positionStyle, children: bannerContent })
578
+ ] });
579
+ }
580
+ return /* @__PURE__ */ jsx4(
581
+ Snackbar,
582
+ {
583
+ open,
584
+ anchorOrigin: {
585
+ vertical: designTokens?.layout?.position === "top" ? "top" : "bottom",
586
+ horizontal: "center"
587
+ },
588
+ ...SnackbarProps,
589
+ children: bannerContent
590
+ }
591
+ );
592
+ }
593
+
594
+ // src/components/FloatingPreferencesButton.tsx
595
+ import CookieOutlined from "@mui/icons-material/CookieOutlined";
596
+ import Fab from "@mui/material/Fab";
597
+ import Tooltip from "@mui/material/Tooltip";
598
+ import { useTheme } from "@mui/material/styles";
599
+ import { jsx as jsx5 } from "react/jsx-runtime";
600
+ function FloatingPreferencesButton({
601
+ position = "bottom-right",
602
+ offset = 24,
603
+ icon = /* @__PURE__ */ jsx5(CookieOutlined, {}),
604
+ tooltip,
605
+ FabProps,
606
+ hideWhenConsented = false
607
+ }) {
608
+ const { openPreferences, consented } = useConsent();
609
+ const theme = useTheme();
610
+ if (hideWhenConsented && consented) {
611
+ return null;
612
+ }
613
+ const tooltipText = tooltip ?? "Gerenciar Prefer\xEAncias de Cookies";
614
+ const getPosition = () => {
615
+ const styles = {
616
+ position: "fixed",
617
+ zIndex: 1200
618
+ };
619
+ switch (position) {
620
+ case "bottom-left":
621
+ return { ...styles, bottom: offset, left: offset };
622
+ case "bottom-right":
623
+ return { ...styles, bottom: offset, right: offset };
624
+ case "top-left":
625
+ return { ...styles, top: offset, left: offset };
626
+ case "top-right":
627
+ return { ...styles, top: offset, right: offset };
628
+ default:
629
+ return { ...styles, bottom: offset, right: offset };
630
+ }
631
+ };
632
+ return /* @__PURE__ */ jsx5(Tooltip, { title: tooltipText, placement: "top", children: /* @__PURE__ */ jsx5(
633
+ Fab,
634
+ {
635
+ size: "medium",
636
+ color: "primary",
637
+ onClick: openPreferences,
638
+ sx: {
639
+ ...getPosition(),
640
+ backgroundColor: theme.palette.primary.main,
641
+ "&:hover": {
642
+ backgroundColor: theme.palette.primary.dark
643
+ }
644
+ },
645
+ "aria-label": tooltipText,
646
+ ...FabProps,
647
+ children: icon
648
+ }
649
+ ) });
650
+ }
651
+
391
652
  // src/context/ConsentContext.tsx
392
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
393
- var PreferencesModal = React3.lazy(
394
- () => import("./PreferencesModal-SOVV24DU.js").then((m) => ({
653
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
654
+ var PreferencesModal = React4.lazy(
655
+ () => import("./PreferencesModal-XCTM6WJN.js").then((m) => ({
395
656
  default: m.PreferencesModal
396
657
  }))
397
658
  );
398
- var DEFAULT_PREFERENCES = {
399
- necessary: true
400
- // Sempre ativo (essencial)
401
- };
402
- function createInitialPreferences(customCategories) {
403
- const prefs = { ...DEFAULT_PREFERENCES };
404
- if (customCategories) {
405
- customCategories.forEach((category) => {
406
- prefs[category.id] = category.essential === true;
407
- });
408
- }
409
- return prefs;
410
- }
411
- function createFullConsentState(consented, preferences, source, isModalOpen = false, existingState) {
659
+ function createFullConsentState(consented, preferences, source, projectConfig, isModalOpen = false, existingState) {
412
660
  const now = (/* @__PURE__ */ new Date()).toISOString();
413
661
  return {
414
662
  version: "1.0",
@@ -417,6 +665,7 @@ function createFullConsentState(consented, preferences, source, isModalOpen = fa
417
665
  consentDate: existingState?.consentDate || now,
418
666
  lastUpdate: now,
419
667
  source,
668
+ projectConfig,
420
669
  isModalOpen
421
670
  };
422
671
  }
@@ -452,22 +701,26 @@ var DEFAULT_TEXTS = {
452
701
  function reducer(state, action) {
453
702
  switch (action.type) {
454
703
  case "ACCEPT_ALL": {
455
- const prefs = createInitialPreferences(action.customCategories);
456
- Object.keys(prefs).forEach((key) => {
457
- prefs[key] = true;
458
- });
459
- return createFullConsentState(true, prefs, "banner", false, state);
704
+ const prefs = createProjectPreferences(action.config, true);
705
+ return createFullConsentState(
706
+ true,
707
+ prefs,
708
+ "banner",
709
+ action.config,
710
+ false,
711
+ state
712
+ );
460
713
  }
461
714
  case "REJECT_ALL": {
462
- const prefs = createInitialPreferences(action.customCategories);
463
- if (action.customCategories) {
464
- action.customCategories.forEach((category) => {
465
- if (category.essential) {
466
- prefs[category.id] = true;
467
- }
468
- });
469
- }
470
- return createFullConsentState(true, prefs, "banner", false, state);
715
+ const prefs = createProjectPreferences(action.config, false);
716
+ return createFullConsentState(
717
+ true,
718
+ prefs,
719
+ "banner",
720
+ action.config,
721
+ false,
722
+ state
723
+ );
471
724
  }
472
725
  case "SET_CATEGORY":
473
726
  return {
@@ -483,6 +736,7 @@ function reducer(state, action) {
483
736
  true,
484
737
  action.preferences,
485
738
  "modal",
739
+ action.config,
486
740
  false,
487
741
  state
488
742
  );
@@ -493,121 +747,133 @@ function reducer(state, action) {
493
747
  true,
494
748
  state.preferences,
495
749
  "modal",
750
+ action.config,
496
751
  false,
497
752
  state
498
753
  );
499
754
  case "RESET": {
500
755
  return createFullConsentState(
501
756
  false,
502
- createInitialPreferences(action.customCategories),
757
+ createProjectPreferences(action.config),
503
758
  "programmatic",
759
+ action.config,
504
760
  false
505
761
  );
506
762
  }
507
- case "HYDRATE":
508
- return { ...action.state, isModalOpen: false };
509
- // Nunca hidratar com modal aberto
763
+ case "HYDRATE": {
764
+ const validatedPreferences = validateProjectPreferences(
765
+ action.state.preferences,
766
+ action.config
767
+ );
768
+ return {
769
+ ...action.state,
770
+ preferences: validatedPreferences,
771
+ isModalOpen: false
772
+ };
773
+ }
510
774
  default:
511
775
  return state;
512
776
  }
513
777
  }
514
- var StateCtx = React3.createContext(null);
515
- var ActionsCtx = React3.createContext(null);
516
- var TextsCtx = React3.createContext(DEFAULT_TEXTS);
517
- var HydrationCtx = React3.createContext(false);
778
+ var StateCtx = React4.createContext(null);
779
+ var ActionsCtx = React4.createContext(null);
780
+ var TextsCtx = React4.createContext(DEFAULT_TEXTS);
781
+ var HydrationCtx = React4.createContext(false);
518
782
  function ConsentProvider({
519
783
  initialState,
520
784
  categories,
521
- // NOVO: configuração completa de categorias
785
+ // Nova prop para configuração de categorias
522
786
  texts: textsProp,
523
787
  theme,
524
- customCategories,
525
- // LEGACY: compatibilidade
788
+ designTokens,
526
789
  scriptIntegrations,
527
790
  // eslint-disable-line no-unused-vars
528
791
  PreferencesModalComponent,
529
792
  preferencesModalProps = {},
530
- disableAutomaticModal = false,
793
+ CookieBannerComponent,
794
+ cookieBannerProps = {},
795
+ FloatingPreferencesButtonComponent,
796
+ floatingPreferencesButtonProps = {},
797
+ disableFloatingPreferencesButton = false,
531
798
  hideBranding = false,
532
799
  onConsentGiven,
533
800
  onPreferencesSaved,
534
801
  cookie: cookieOpts,
535
802
  disableDeveloperGuidance,
536
- // NOVO: desabilita avisos de dev
537
803
  children
538
804
  }) {
539
- const texts = React3.useMemo(
805
+ const texts = React4.useMemo(
540
806
  () => ({ ...DEFAULT_TEXTS, ...textsProp ?? {} }),
541
807
  [textsProp]
542
808
  );
543
- const cookie = React3.useMemo(
809
+ const cookie = React4.useMemo(
544
810
  () => ({ ...DEFAULT_COOKIE_OPTS, ...cookieOpts ?? {} }),
545
811
  [cookieOpts]
546
812
  );
547
- const appliedTheme = React3.useMemo(
813
+ const appliedTheme = React4.useMemo(
548
814
  () => theme || defaultConsentTheme,
549
815
  [theme]
550
816
  );
551
- const finalCategoriesConfig = React3.useMemo(() => {
817
+ const finalCategoriesConfig = React4.useMemo(() => {
552
818
  if (categories) return categories;
553
- if (customCategories) {
554
- return {
555
- enabledCategories: ["analytics"],
556
- // padrão quando usando API antiga
557
- customCategories
558
- };
559
- }
560
- return void 0;
561
- }, [categories, customCategories]);
819
+ return DEFAULT_PROJECT_CATEGORIES;
820
+ }, [categories]);
562
821
  useDeveloperGuidance(finalCategoriesConfig, disableDeveloperGuidance);
563
- const boot = React3.useMemo(() => {
822
+ const boot = React4.useMemo(() => {
564
823
  if (initialState) return { ...initialState, isModalOpen: false };
565
824
  return createFullConsentState(
566
825
  false,
567
- createInitialPreferences(customCategories),
826
+ createProjectPreferences(finalCategoriesConfig),
568
827
  "banner",
828
+ finalCategoriesConfig,
569
829
  false
570
830
  );
571
- }, [initialState, customCategories]);
572
- const [state, dispatch] = React3.useReducer(reducer, boot);
573
- const [isHydrated, setIsHydrated] = React3.useState(false);
574
- React3.useEffect(() => {
831
+ }, [initialState, finalCategoriesConfig]);
832
+ const [state, dispatch] = React4.useReducer(reducer, boot);
833
+ const [isHydrated, setIsHydrated] = React4.useState(false);
834
+ React4.useEffect(() => {
575
835
  if (!initialState) {
576
836
  const saved = readConsentCookie(cookie.name);
577
837
  if (saved?.consented) {
578
- console.log("\u{1F680} Immediate hydration: Cookie found", saved);
579
- dispatch({ type: "HYDRATE", state: saved });
838
+ dispatch({
839
+ type: "HYDRATE",
840
+ state: saved,
841
+ config: finalCategoriesConfig
842
+ });
580
843
  }
581
844
  }
582
845
  setIsHydrated(true);
583
- }, [cookie.name, initialState]);
584
- React3.useEffect(() => {
585
- if (state.consented) writeConsentCookie(state, state.source, cookie);
586
- }, [state, cookie]);
587
- const prevConsented = React3.useRef(state.consented);
588
- React3.useEffect(() => {
846
+ }, [cookie.name, initialState, finalCategoriesConfig]);
847
+ React4.useEffect(() => {
848
+ if (state.consented)
849
+ writeConsentCookie(state, finalCategoriesConfig, cookie);
850
+ }, [state, cookie, finalCategoriesConfig]);
851
+ const prevConsented = React4.useRef(state.consented);
852
+ React4.useEffect(() => {
589
853
  if (!prevConsented.current && state.consented && onConsentGiven) {
590
854
  setTimeout(() => onConsentGiven(state), 150);
591
855
  }
592
856
  prevConsented.current = state.consented;
593
857
  }, [state, onConsentGiven]);
594
- const prevPrefs = React3.useRef(state.preferences);
595
- React3.useEffect(() => {
596
- if (state.consented && onPreferencesSaved && prevPrefs.current !== state.preferences) {
597
- setTimeout(() => onPreferencesSaved(state.preferences), 150);
598
- prevPrefs.current = state.preferences;
599
- }
600
- }, [state, onPreferencesSaved]);
601
- const api = React3.useMemo(() => {
602
- const acceptAll = () => dispatch({ type: "ACCEPT_ALL", customCategories });
603
- const rejectAll = () => dispatch({ type: "REJECT_ALL", customCategories });
858
+ const api = React4.useMemo(() => {
859
+ const acceptAll = () => dispatch({ type: "ACCEPT_ALL", config: finalCategoriesConfig });
860
+ const rejectAll = () => dispatch({ type: "REJECT_ALL", config: finalCategoriesConfig });
604
861
  const setPreference = (category, value) => dispatch({ type: "SET_CATEGORY", category, value });
605
- const setPreferences = (preferences) => dispatch({ type: "SET_PREFERENCES", preferences });
862
+ const setPreferences = (preferences) => {
863
+ dispatch({
864
+ type: "SET_PREFERENCES",
865
+ preferences,
866
+ config: finalCategoriesConfig
867
+ });
868
+ if (onPreferencesSaved) {
869
+ setTimeout(() => onPreferencesSaved(preferences), 150);
870
+ }
871
+ };
606
872
  const openPreferences = () => dispatch({ type: "OPEN_MODAL" });
607
- const closePreferences = () => dispatch({ type: "CLOSE_MODAL" });
873
+ const closePreferences = () => dispatch({ type: "CLOSE_MODAL", config: finalCategoriesConfig });
608
874
  const resetConsent = () => {
609
875
  removeConsentCookie(cookie);
610
- dispatch({ type: "RESET", customCategories });
876
+ dispatch({ type: "RESET", config: finalCategoriesConfig });
611
877
  };
612
878
  return {
613
879
  consented: !!state.consented,
@@ -621,37 +887,66 @@ function ConsentProvider({
621
887
  closePreferences,
622
888
  resetConsent
623
889
  };
624
- }, [state, cookie, customCategories]);
625
- return /* @__PURE__ */ jsx2(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx2(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx2(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx2(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsx2(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsxs(
890
+ }, [state, cookie, finalCategoriesConfig, onPreferencesSaved]);
891
+ return /* @__PURE__ */ jsx6(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx6(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx6(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx6(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsx6(HydrationCtx.Provider, { value: isHydrated, children: /* @__PURE__ */ jsx6(DesignProvider, { tokens: designTokens, children: /* @__PURE__ */ jsxs3(
626
892
  CategoriesProvider,
627
893
  {
628
894
  config: finalCategoriesConfig,
629
- categories: customCategories,
895
+ disableDeveloperGuidance,
630
896
  children: [
631
897
  children,
632
- !disableAutomaticModal && /* @__PURE__ */ jsx2(React3.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ jsx2(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ jsx2(PreferencesModal, { hideBranding }) })
898
+ /* @__PURE__ */ jsx6(React4.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ jsx6(
899
+ PreferencesModalComponent,
900
+ {
901
+ preferences: api.preferences,
902
+ setPreferences: api.setPreferences,
903
+ closePreferences: api.closePreferences,
904
+ isModalOpen: api.isModalOpen,
905
+ texts,
906
+ ...preferencesModalProps
907
+ }
908
+ ) : /* @__PURE__ */ jsx6(PreferencesModal, { hideBranding }) }),
909
+ !state.consented && isHydrated && (CookieBannerComponent ? /* @__PURE__ */ jsx6(
910
+ CookieBannerComponent,
911
+ {
912
+ consented: api.consented,
913
+ acceptAll: api.acceptAll,
914
+ rejectAll: api.rejectAll,
915
+ openPreferences: api.openPreferences,
916
+ texts,
917
+ ...cookieBannerProps
918
+ }
919
+ ) : /* @__PURE__ */ jsx6(CookieBanner, {})),
920
+ state.consented && !disableFloatingPreferencesButton && (FloatingPreferencesButtonComponent ? /* @__PURE__ */ jsx6(
921
+ FloatingPreferencesButtonComponent,
922
+ {
923
+ openPreferences: api.openPreferences,
924
+ consented: api.consented,
925
+ ...floatingPreferencesButtonProps
926
+ }
927
+ ) : /* @__PURE__ */ jsx6(FloatingPreferencesButton, {}))
633
928
  ]
634
929
  }
635
- ) }) }) }) }) });
930
+ ) }) }) }) }) }) });
636
931
  }
637
932
  function useConsentStateInternal() {
638
- const ctx = React3.useContext(StateCtx);
933
+ const ctx = React4.useContext(StateCtx);
639
934
  if (!ctx)
640
935
  throw new Error("useConsentState must be used within ConsentProvider");
641
936
  return ctx;
642
937
  }
643
938
  function useConsentActionsInternal() {
644
- const ctx = React3.useContext(ActionsCtx);
939
+ const ctx = React4.useContext(ActionsCtx);
645
940
  if (!ctx)
646
941
  throw new Error("useConsentActions must be used within ConsentProvider");
647
942
  return ctx;
648
943
  }
649
944
  function useConsentTextsInternal() {
650
- const ctx = React3.useContext(TextsCtx);
945
+ const ctx = React4.useContext(TextsCtx);
651
946
  return ctx;
652
947
  }
653
948
  function useConsentHydrationInternal() {
654
- return React3.useContext(HydrationCtx);
949
+ return React4.useContext(HydrationCtx);
655
950
  }
656
951
 
657
952
  // src/hooks/useConsent.ts
@@ -678,67 +973,8 @@ function useConsentHydration() {
678
973
  return useConsentHydrationInternal();
679
974
  }
680
975
 
681
- // src/components/Branding.tsx
682
- import Link from "@mui/material/Link";
683
- import Typography from "@mui/material/Typography";
684
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
685
- var brandingStyles = {
686
- banner: {
687
- fontSize: "0.65rem",
688
- textAlign: "center",
689
- mt: 1,
690
- opacity: 0.7,
691
- fontStyle: "italic"
692
- },
693
- modal: {
694
- fontSize: "0.65rem",
695
- textAlign: "center",
696
- px: 3,
697
- pb: 1,
698
- opacity: 0.7,
699
- fontStyle: "italic"
700
- }
701
- };
702
- var linkStyles = {
703
- textDecoration: "none",
704
- fontWeight: 500,
705
- "&:hover": {
706
- textDecoration: "underline"
707
- }
708
- };
709
- function Branding({ variant, hidden = false }) {
710
- if (hidden) return null;
711
- return /* @__PURE__ */ jsxs2(
712
- Typography,
713
- {
714
- variant: "caption",
715
- sx: (theme) => ({
716
- ...brandingStyles[variant],
717
- color: theme.palette.text.secondary
718
- }),
719
- children: [
720
- "fornecido por",
721
- " ",
722
- /* @__PURE__ */ jsx3(
723
- Link,
724
- {
725
- href: "https://www.ledipo.eti.br",
726
- target: "_blank",
727
- rel: "noopener noreferrer",
728
- sx: (theme) => ({
729
- ...linkStyles,
730
- color: theme.palette.primary.main
731
- }),
732
- children: "L\xC9dipO.eti.br"
733
- }
734
- )
735
- ]
736
- }
737
- );
738
- }
739
-
740
976
  // src/components/PreferencesModal.tsx
741
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
977
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
742
978
  function PreferencesModal2({
743
979
  DialogProps: DialogProps2,
744
980
  hideBranding = false
@@ -772,7 +1008,7 @@ function PreferencesModal2({
772
1008
  setTempPreferences(preferences);
773
1009
  closePreferences();
774
1010
  };
775
- return /* @__PURE__ */ jsxs3(
1011
+ return /* @__PURE__ */ jsxs4(
776
1012
  Dialog,
777
1013
  {
778
1014
  "aria-labelledby": "cookie-pref-title",
@@ -780,14 +1016,14 @@ function PreferencesModal2({
780
1016
  onClose: handleCancel,
781
1017
  ...DialogProps2,
782
1018
  children: [
783
- /* @__PURE__ */ jsx4(DialogTitle, { id: "cookie-pref-title", children: texts.modalTitle }),
784
- /* @__PURE__ */ jsxs3(DialogContent, { dividers: true, children: [
785
- /* @__PURE__ */ jsx4(Typography2, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
786
- /* @__PURE__ */ jsxs3(FormGroup, { children: [
787
- toggleableCategories.map((category) => /* @__PURE__ */ jsx4(
1019
+ /* @__PURE__ */ jsx7(DialogTitle, { id: "cookie-pref-title", children: texts.modalTitle }),
1020
+ /* @__PURE__ */ jsxs4(DialogContent, { dividers: true, children: [
1021
+ /* @__PURE__ */ jsx7(Typography3, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
1022
+ /* @__PURE__ */ jsxs4(FormGroup, { children: [
1023
+ toggleableCategories.map((category) => /* @__PURE__ */ jsx7(
788
1024
  FormControlLabel,
789
1025
  {
790
- control: /* @__PURE__ */ jsx4(
1026
+ control: /* @__PURE__ */ jsx7(
791
1027
  Switch,
792
1028
  {
793
1029
  checked: tempPreferences[category.id] ?? false,
@@ -801,36 +1037,34 @@ function PreferencesModal2({
801
1037
  },
802
1038
  category.id
803
1039
  )),
804
- /* @__PURE__ */ jsx4(
1040
+ /* @__PURE__ */ jsx7(
805
1041
  FormControlLabel,
806
1042
  {
807
- control: /* @__PURE__ */ jsx4(Switch, { checked: true, disabled: true }),
1043
+ control: /* @__PURE__ */ jsx7(Switch, { checked: true, disabled: true }),
808
1044
  label: texts.necessaryAlwaysOn
809
1045
  }
810
1046
  )
811
1047
  ] })
812
1048
  ] }),
813
- !hideBranding && /* @__PURE__ */ jsx4(Branding, { variant: "modal" }),
814
- /* @__PURE__ */ jsxs3(DialogActions, { children: [
815
- /* @__PURE__ */ jsx4(Button, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
816
- /* @__PURE__ */ jsx4(Button, { variant: "contained", onClick: handleSave, children: texts.save })
817
- ] })
1049
+ /* @__PURE__ */ jsxs4(DialogActions, { children: [
1050
+ /* @__PURE__ */ jsx7(Button2, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
1051
+ /* @__PURE__ */ jsx7(Button2, { variant: "contained", onClick: handleSave, children: texts.save })
1052
+ ] }),
1053
+ !hideBranding && /* @__PURE__ */ jsx7(Branding, { variant: "modal" })
818
1054
  ]
819
1055
  }
820
1056
  );
821
1057
  }
822
1058
 
823
1059
  export {
824
- defaultConsentTheme,
825
1060
  DEFAULT_PROJECT_CATEGORIES,
826
1061
  analyzeDeveloperConfiguration,
827
1062
  useCategories,
828
1063
  useCategoryStatus,
829
- useCustomCategories,
830
- Branding,
831
- PreferencesModal2 as PreferencesModal,
1064
+ defaultConsentTheme,
832
1065
  ConsentProvider,
833
1066
  useConsent,
834
1067
  useConsentTexts,
835
- useConsentHydration
1068
+ useConsentHydration,
1069
+ PreferencesModal2 as PreferencesModal
836
1070
  };