react-lgpd-consent 0.1.8 → 0.1.12

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
@@ -2,7 +2,39 @@
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/react-lgpd-consent?style=for-the-badge&color=blue)](https://www.npmjs.com/package/react-lgpd-consent)
4
4
  [![License](https://img.shields.io/npm/l/react-lgpd-consent?style=for-the-badge)](https://github.com/lucianoedipo/react-lgpd-consent/blob/main/LICENSE)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=for-the-badge&logo=typescript)](https://www.typescriptlang.org/)
5
+ [![TypeScrip```
6
+
7
+ ## 🎛️ Botão Flutuante de Preferências
8
+
9
+ Para facilitar o acesso às configurações após consentimento inicial:
10
+
11
+ ```tsx
12
+ import { FloatingPreferencesButton } from 'react-lgpd-consent'
13
+
14
+ function App() {
15
+ return (
16
+ <ConsentProvider>
17
+ <MeuApp />
18
+ <CookieBanner />
19
+
20
+ {/* Botão flutuante opcional */}
21
+ <FloatingPreferencesButton
22
+ position="bottom-right"
23
+ hideWhenConsented={false}
24
+ tooltip="Configurar Cookies"
25
+ />
26
+ </ConsentProvider>
27
+ )
28
+ }
29
+ ```
30
+
31
+ ### Posições Disponíveis
32
+
33
+ - `bottom-left` | `bottom-right` (padrão)
34
+ - `top-left` | `top-right`
35
+
36
+ ## 🔧 API Completahttps://img.shields.io/badge/TypeScript-Ready-blue?style=for-the-badge&logo=typescript)](https://www.typescriptlang.org/)
37
+
6
38
  [![React](https://img.shields.io/badge/React-18%2B-61DAFB?style=for-the-badge&logo=react)](https://reactjs.org/)
7
39
  [![Material-UI](https://img.shields.io/badge/MUI-Ready-007FFF?style=for-the-badge&logo=mui)](https://mui.com/)
8
40
 
@@ -24,6 +56,7 @@ Solução moderna, acessível e personalizável para gerenciar consentimento de
24
56
  - 🎨 **Sistema de Temas**: Temas customizáveis para integração visual perfeita
25
57
  - ⚡ **Carregamento Condicional**: Scripts só executam após consentimento explícito
26
58
  - 🔌 **Modal Automático**: Modal de preferências incluído automaticamente com lazy loading
59
+ - 🎛️ **Botão Flutuante**: Componente opcional para acesso fácil às preferências
27
60
 
28
61
  ## 🚀 Instalação
29
62
 
@@ -44,7 +77,11 @@ npm install @mui/material js-cookie
44
77
  ## � Exemplo Completo
45
78
 
46
79
  ```tsx
47
- import { ConsentProvider, CookieBanner } from 'react-lgpd-consent'
80
+ import {
81
+ ConsentProvider,
82
+ CookieBanner,
83
+ FloatingPreferencesButton,
84
+ } from 'react-lgpd-consent'
48
85
 
49
86
  function App() {
50
87
  return (
@@ -53,8 +90,11 @@ function App() {
53
90
  <h1>Meu Site</h1>
54
91
  <p>Conteúdo do site...</p>
55
92
 
56
- {/* Banner de cookies */}
93
+ {/* Banner de cookies - Modal incluído automaticamente! */}
57
94
  <CookieBanner policyLinkUrl="/privacy-policy" blocking={true} />
95
+
96
+ {/* Botão flutuante opcional para acesso às preferências */}
97
+ <FloatingPreferencesButton position="bottom-right" />
58
98
  </div>
59
99
  </ConsentProvider>
60
100
  )
@@ -343,12 +383,13 @@ Para controle total, desabilite o modal automático:
343
383
 
344
384
  ### Components
345
385
 
346
- | Componente | Descrição | Props Principais |
347
- | ------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
348
- | `ConsentProvider` | Provider principal do contexto | `initialState`, `texts`, `theme`, `PreferencesModalComponent`, `disableAutomaticModal`, callbacks |
349
- | `CookieBanner` | Banner de consentimento | `policyLinkUrl`, `blocking`, `debug`, pass-through MUI props |
350
- | `PreferencesModal` | Modal de preferências (incluído automaticamente) | `DialogProps` (MUI pass-through) - **Opcional** |
351
- | `ConsentGate` | Renderização condicional por categoria | `category`, `children` |
386
+ | Componente | Descrição | Props Principais |
387
+ | --------------------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------- |
388
+ | `ConsentProvider` | Provider principal do contexto | `initialState`, `texts`, `theme`, `hideBranding`, `PreferencesModalComponent`, callbacks |
389
+ | `CookieBanner` | Banner de consentimento | `policyLinkUrl`, `blocking`, `hideBranding`, `debug`, pass-through MUI props |
390
+ | `PreferencesModal` | Modal de preferências (incluído automaticamente) | `DialogProps`, `hideBranding` - **Opcional** |
391
+ | `FloatingPreferencesButton` | Botão flutuante para abrir preferências | `position`, `hideWhenConsented`, `tooltip`, `icon`, `FabProps` |
392
+ | `ConsentGate` | Renderização condicional por categoria | `category`, `children` |
352
393
 
353
394
  ### Hook `useConsent()`
354
395
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PreferencesModal
3
- } from "./chunk-Y4XEAQXV.js";
3
+ } from "./chunk-V54LZT2Q.js";
4
4
  export {
5
5
  PreferencesModal
6
6
  };
@@ -7,8 +7,8 @@ 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 Typography from "@mui/material/Typography";
11
- import { useState, useEffect as useEffect2 } from "react";
10
+ import Typography2 from "@mui/material/Typography";
11
+ import { useEffect as useEffect2, useState as useState2 } from "react";
12
12
 
13
13
  // src/context/ConsentContext.tsx
14
14
  import * as React from "react";
@@ -20,7 +20,7 @@ var DEFAULT_COOKIE_OPTS = {
20
20
  name: "cookieConsent",
21
21
  maxAgeDays: 365,
22
22
  sameSite: "Lax",
23
- secure: true,
23
+ secure: typeof window !== "undefined" ? window.location.protocol === "https:" : false,
24
24
  path: "/"
25
25
  };
26
26
  function readConsentCookie(name = DEFAULT_COOKIE_OPTS.name) {
@@ -55,12 +55,10 @@ var defaultConsentTheme = createTheme({
55
55
  palette: {
56
56
  primary: {
57
57
  main: "#1976d2",
58
- // Azul institucional
59
58
  contrastText: "#ffffff"
60
59
  },
61
60
  secondary: {
62
61
  main: "#dc004e",
63
- // Rosa/vermelho para ações importantes
64
62
  contrastText: "#ffffff"
65
63
  },
66
64
  background: {
@@ -84,7 +82,6 @@ var defaultConsentTheme = createTheme({
84
82
  button: {
85
83
  fontWeight: 500,
86
84
  textTransform: "none"
87
- // Manter capitalização original
88
85
  }
89
86
  },
90
87
  components: {
@@ -123,7 +120,7 @@ var defaultConsentTheme = createTheme({
123
120
  // src/context/ConsentContext.tsx
124
121
  import { jsx, jsxs } from "react/jsx-runtime";
125
122
  var PreferencesModal = React.lazy(
126
- () => import("./PreferencesModal-4FDYT7VE.js").then((m) => ({
123
+ () => import("./PreferencesModal-IFKCHTF2.js").then((m) => ({
127
124
  default: m.PreferencesModal
128
125
  }))
129
126
  );
@@ -191,6 +188,7 @@ function reducer(state, action) {
191
188
  var StateCtx = React.createContext(null);
192
189
  var ActionsCtx = React.createContext(null);
193
190
  var TextsCtx = React.createContext(DEFAULT_TEXTS);
191
+ var HydrationCtx = React.createContext(false);
194
192
  function ConsentProvider({
195
193
  initialState,
196
194
  texts: textsProp,
@@ -198,6 +196,7 @@ function ConsentProvider({
198
196
  PreferencesModalComponent,
199
197
  preferencesModalProps = {},
200
198
  disableAutomaticModal = false,
199
+ hideBranding = false,
201
200
  onConsentGiven,
202
201
  onPreferencesSaved,
203
202
  cookie: cookieOpts,
@@ -217,14 +216,24 @@ function ConsentProvider({
217
216
  );
218
217
  const boot = React.useMemo(() => {
219
218
  if (initialState) return { ...initialState, isModalOpen: false };
220
- const saved = readConsentCookie(cookie.name);
221
- return saved ?? {
219
+ return {
222
220
  consented: false,
223
221
  preferences: { ...DEFAULT_PREFERENCES },
224
222
  isModalOpen: false
225
223
  };
226
- }, [initialState, cookie.name]);
224
+ }, [initialState]);
227
225
  const [state, dispatch] = React.useReducer(reducer, boot);
226
+ const [isHydrated, setIsHydrated] = React.useState(false);
227
+ React.useEffect(() => {
228
+ if (typeof window !== "undefined" && !initialState) {
229
+ const saved = readConsentCookie(cookie.name);
230
+ if (saved?.consented) {
231
+ console.log("\u{1F680} Immediate hydration: Cookie found", saved);
232
+ dispatch({ type: "HYDRATE", state: saved });
233
+ }
234
+ }
235
+ setIsHydrated(true);
236
+ }, [cookie.name, initialState]);
228
237
  React.useEffect(() => {
229
238
  if (state.consented) writeConsentCookie(state, cookie);
230
239
  }, [state, cookie]);
@@ -266,10 +275,10 @@ function ConsentProvider({
266
275
  resetConsent
267
276
  };
268
277
  }, [state, cookie]);
269
- return /* @__PURE__ */ jsx(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsxs(TextsCtx.Provider, { value: texts, children: [
278
+ return /* @__PURE__ */ jsx(ThemeProvider, { theme: appliedTheme, children: /* @__PURE__ */ jsx(StateCtx.Provider, { value: state, children: /* @__PURE__ */ jsx(ActionsCtx.Provider, { value: api, children: /* @__PURE__ */ jsx(TextsCtx.Provider, { value: texts, children: /* @__PURE__ */ jsxs(HydrationCtx.Provider, { value: isHydrated, children: [
270
279
  children,
271
- !disableAutomaticModal && /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ jsx(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ jsx(PreferencesModal, {}) })
272
- ] }) }) }) });
280
+ !disableAutomaticModal && /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: PreferencesModalComponent ? /* @__PURE__ */ jsx(PreferencesModalComponent, { ...preferencesModalProps }) : /* @__PURE__ */ jsx(PreferencesModal, { hideBranding }) })
281
+ ] }) }) }) }) });
273
282
  }
274
283
  function useConsentStateInternal() {
275
284
  const ctx = React.useContext(StateCtx);
@@ -287,6 +296,9 @@ function useConsentTextsInternal() {
287
296
  const ctx = React.useContext(TextsCtx);
288
297
  return ctx;
289
298
  }
299
+ function useConsentHydrationInternal() {
300
+ return React.useContext(HydrationCtx);
301
+ }
290
302
 
291
303
  // src/hooks/useConsent.ts
292
304
  function useConsent() {
@@ -308,15 +320,78 @@ function useConsent() {
308
320
  function useConsentTexts() {
309
321
  return useConsentTextsInternal();
310
322
  }
323
+ function useConsentHydration() {
324
+ return useConsentHydrationInternal();
325
+ }
311
326
 
312
- // src/components/PreferencesModal.tsx
327
+ // src/components/Branding.tsx
328
+ import Link from "@mui/material/Link";
329
+ import Typography from "@mui/material/Typography";
313
330
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
331
+ var brandingStyles = {
332
+ banner: {
333
+ fontSize: "0.65rem",
334
+ textAlign: "center",
335
+ mt: 1,
336
+ opacity: 0.7,
337
+ fontStyle: "italic"
338
+ },
339
+ modal: {
340
+ fontSize: "0.65rem",
341
+ textAlign: "center",
342
+ px: 3,
343
+ pb: 1,
344
+ opacity: 0.7,
345
+ fontStyle: "italic"
346
+ }
347
+ };
348
+ var linkStyles = {
349
+ textDecoration: "none",
350
+ fontWeight: 500,
351
+ "&:hover": {
352
+ textDecoration: "underline"
353
+ }
354
+ };
355
+ function Branding({ variant, hidden = false }) {
356
+ if (hidden) return null;
357
+ return /* @__PURE__ */ jsxs2(
358
+ Typography,
359
+ {
360
+ variant: "caption",
361
+ sx: (theme) => ({
362
+ ...brandingStyles[variant],
363
+ color: theme.palette.text.secondary
364
+ }),
365
+ children: [
366
+ "fornecido por",
367
+ " ",
368
+ /* @__PURE__ */ jsx2(
369
+ Link,
370
+ {
371
+ href: "https://www.ledipo.eti.br",
372
+ target: "_blank",
373
+ rel: "noopener noreferrer",
374
+ sx: (theme) => ({
375
+ ...linkStyles,
376
+ color: theme.palette.primary.main
377
+ }),
378
+ children: "L\xC9dipO.eti.br"
379
+ }
380
+ )
381
+ ]
382
+ }
383
+ );
384
+ }
385
+
386
+ // src/components/PreferencesModal.tsx
387
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
314
388
  function PreferencesModal2({
315
- DialogProps: DialogProps2
389
+ DialogProps: DialogProps2,
390
+ hideBranding = false
316
391
  }) {
317
392
  const { preferences, setPreferences, closePreferences, isModalOpen } = useConsent();
318
393
  const texts = useConsentTexts();
319
- const [tempPreferences, setTempPreferences] = useState(preferences);
394
+ const [tempPreferences, setTempPreferences] = useState2(preferences);
320
395
  useEffect2(() => {
321
396
  if (isModalOpen) {
322
397
  setTempPreferences(preferences);
@@ -330,7 +405,7 @@ function PreferencesModal2({
330
405
  setTempPreferences(preferences);
331
406
  closePreferences();
332
407
  };
333
- return /* @__PURE__ */ jsxs2(
408
+ return /* @__PURE__ */ jsxs3(
334
409
  Dialog,
335
410
  {
336
411
  "aria-labelledby": "cookie-pref-title",
@@ -338,14 +413,14 @@ function PreferencesModal2({
338
413
  onClose: handleCancel,
339
414
  ...DialogProps2,
340
415
  children: [
341
- /* @__PURE__ */ jsx2(DialogTitle, { id: "cookie-pref-title", children: texts.modalTitle }),
342
- /* @__PURE__ */ jsxs2(DialogContent, { dividers: true, children: [
343
- /* @__PURE__ */ jsx2(Typography, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
344
- /* @__PURE__ */ jsxs2(FormGroup, { children: [
345
- /* @__PURE__ */ jsx2(
416
+ /* @__PURE__ */ jsx3(DialogTitle, { id: "cookie-pref-title", children: texts.modalTitle }),
417
+ /* @__PURE__ */ jsxs3(DialogContent, { dividers: true, children: [
418
+ /* @__PURE__ */ jsx3(Typography2, { variant: "body2", sx: { mb: 2 }, children: texts.modalIntro }),
419
+ /* @__PURE__ */ jsxs3(FormGroup, { children: [
420
+ /* @__PURE__ */ jsx3(
346
421
  FormControlLabel,
347
422
  {
348
- control: /* @__PURE__ */ jsx2(
423
+ control: /* @__PURE__ */ jsx3(
349
424
  Switch,
350
425
  {
351
426
  checked: tempPreferences.analytics,
@@ -358,10 +433,10 @@ function PreferencesModal2({
358
433
  label: "Cookies Anal\xEDticos (medem uso do site)"
359
434
  }
360
435
  ),
361
- /* @__PURE__ */ jsx2(
436
+ /* @__PURE__ */ jsx3(
362
437
  FormControlLabel,
363
438
  {
364
- control: /* @__PURE__ */ jsx2(
439
+ control: /* @__PURE__ */ jsx3(
365
440
  Switch,
366
441
  {
367
442
  checked: tempPreferences.marketing,
@@ -374,18 +449,19 @@ function PreferencesModal2({
374
449
  label: "Cookies de Marketing/Publicidade"
375
450
  }
376
451
  ),
377
- /* @__PURE__ */ jsx2(
452
+ /* @__PURE__ */ jsx3(
378
453
  FormControlLabel,
379
454
  {
380
- control: /* @__PURE__ */ jsx2(Switch, { checked: true, disabled: true }),
455
+ control: /* @__PURE__ */ jsx3(Switch, { checked: true, disabled: true }),
381
456
  label: texts.necessaryAlwaysOn
382
457
  }
383
458
  )
384
459
  ] })
385
460
  ] }),
386
- /* @__PURE__ */ jsxs2(DialogActions, { children: [
387
- /* @__PURE__ */ jsx2(Button, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
388
- /* @__PURE__ */ jsx2(Button, { variant: "contained", onClick: handleSave, children: texts.save })
461
+ !hideBranding && /* @__PURE__ */ jsx3(Branding, { variant: "modal" }),
462
+ /* @__PURE__ */ jsxs3(DialogActions, { children: [
463
+ /* @__PURE__ */ jsx3(Button, { variant: "outlined", onClick: handleCancel, children: "Cancelar" }),
464
+ /* @__PURE__ */ jsx3(Button, { variant: "contained", onClick: handleSave, children: texts.save })
389
465
  ] })
390
466
  ]
391
467
  }
@@ -394,8 +470,10 @@ function PreferencesModal2({
394
470
 
395
471
  export {
396
472
  defaultConsentTheme,
473
+ Branding,
397
474
  PreferencesModal2 as PreferencesModal,
398
475
  ConsentProvider,
399
476
  useConsent,
400
- useConsentTexts
477
+ useConsentTexts,
478
+ useConsentHydration
401
479
  };