react-lgpd-consent 0.4.0 → 0.4.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/CHANGELOG.md +226 -0
- package/QUICKSTART.en.md +34 -0
- package/QUICKSTART.md +562 -1
- package/README.en.md +25 -0
- package/README.md +37 -0
- package/dist/FloatingPreferencesButton-4AGBXNHH.cjs +11 -0
- package/dist/FloatingPreferencesButton-IY7TFD7D.js +2 -0
- package/dist/PreferencesModal-5KNHWW57.js +2 -0
- package/dist/PreferencesModal-YBCWCVUI.cjs +11 -0
- package/dist/chunk-25XEI2DZ.cjs +193 -0
- package/dist/chunk-FJKRAERJ.cjs +119 -0
- package/dist/chunk-N3QOW4SA.js +178 -0
- package/dist/chunk-ORI4PLVG.cjs +1899 -0
- package/dist/chunk-PJFGQMCI.js +92 -0
- package/dist/chunk-RWT2ORFE.js +1840 -0
- package/dist/index.cjs +872 -1560
- package/dist/index.d.cts +1794 -232
- package/dist/index.d.ts +1794 -232
- package/dist/index.js +707 -65
- package/package.json +73 -39
- package/dist/PreferencesModal-D2SEVH3N.js +0 -6
- package/dist/chunk-B5FNONG3.js +0 -1362
package/QUICKSTART.md
CHANGED
|
@@ -62,9 +62,297 @@ export default App
|
|
|
62
62
|
|
|
63
63
|
````
|
|
64
64
|
|
|
65
|
+
## ⚡ Quickstarts: Next.js (App Router) e Vite
|
|
66
|
+
|
|
67
|
+
Os exemplos a seguir integram GTM/GA4 com Consent Mode v2 e garantem que nenhum script de tracking rode antes do consentimento. Eles também mostram como usar `ConsentScriptLoader` e sincronizar os sinais do Consent Mode via `gtag('consent', ...)`.
|
|
68
|
+
|
|
69
|
+
- Exemplos completos: `examples/next-app-router/*`, `examples/vite/*`
|
|
70
|
+
|
|
71
|
+
### Next.js 14/15 — App Router (SSR-safe)
|
|
72
|
+
|
|
73
|
+
1) Criar app Next e instalar deps
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm create next-app@latest my-app --ts --eslint --src-dir --app --no-tailwind --no-experimental-app
|
|
77
|
+
cd my-app
|
|
78
|
+
npm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2) Variáveis públicas no `.env.local`
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
|
|
85
|
+
NEXT_PUBLIC_GTM_ID=GTM-XXXXXXX
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
3) Copiar os arquivos do exemplo e ajustar imports
|
|
89
|
+
|
|
90
|
+
- De `examples/next-app-router/app/layout.tsx` → `app/layout.tsx`
|
|
91
|
+
- De `examples/next-app-router/app/page.tsx` → `app/page.tsx`
|
|
92
|
+
- De `examples/next-app-router/components/ClientConsent.tsx` → `app/components/ClientConsent.tsx`
|
|
93
|
+
|
|
94
|
+
Observação: nos arquivos copiados, troque imports relativos para `import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'`.
|
|
95
|
+
|
|
96
|
+
4) O que esse setup faz
|
|
97
|
+
|
|
98
|
+
- `ClientConsent` é um componente client-only (via `dynamic(..., { ssr: false })` no layout) que:
|
|
99
|
+
- Injeta um stub de `dataLayer/gtag` e define `consent default = denied` para todos os sinais (ad_storage, ad_user_data, ad_personalization, analytics_storage).
|
|
100
|
+
- Sincroniza as mudanças do consentimento com `gtag('consent','update', ...)` mapeando as categorias: `analytics → analytics_storage`, `marketing → ad_*`.
|
|
101
|
+
- Usa `ConsentScriptLoader` para carregar GTM/GA4 somente quando as categorias permitirem. Antes disso, nenhum script de tracking é carregado.
|
|
102
|
+
|
|
103
|
+
5) Rodar
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm run dev
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Validação rápida:
|
|
110
|
+
- Acesse em aba anônima: a rede não carrega `gtm.js`/`gtag/js` até aceitar preferências.
|
|
111
|
+
- Ao aceitar `analytics`, o GA4 é carregado; ao aceitar `marketing`, os sinais `ad_*` são atualizados como granted.
|
|
112
|
+
|
|
113
|
+
### Vite (CSR)
|
|
114
|
+
|
|
115
|
+
1) Criar app Vite e instalar deps
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm create vite@latest my-app -- --template react-ts
|
|
119
|
+
cd my-app
|
|
120
|
+
npm i react-lgpd-consent @mui/material @mui/icons-material @emotion/react @emotion/styled
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2) Variáveis no `.env`
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
VITE_GA_ID=G-XXXXXXXXXX
|
|
127
|
+
VITE_GTM_ID=GTM-XXXXXXX
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
3) Copiar os arquivos do exemplo e ajustar imports
|
|
131
|
+
|
|
132
|
+
- De `examples/vite/index.html` → `index.html` (não adicione scripts do GA/GTM aqui)
|
|
133
|
+
- De `examples/vite/src/main.tsx` → `src/main.tsx`
|
|
134
|
+
- De `examples/vite/src/App.tsx` → `src/App.tsx`
|
|
135
|
+
- De `examples/vite/src/consent/GtagConsent.tsx` → `src/consent/GtagConsent.tsx`
|
|
136
|
+
|
|
137
|
+
Observação: nos arquivos copiados, troque imports relativos para `import { ... } from 'react-lgpd-consent'`.
|
|
138
|
+
|
|
139
|
+
4) Rodar
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm run dev
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Validação rápida:
|
|
146
|
+
- Ao abrir a app (em nova sessão), nenhum script de tracking é baixado até que o usuário consinta.
|
|
147
|
+
- Preferências atualizam `gtag('consent','update', ...)` corretamente por categoria.
|
|
148
|
+
|
|
65
149
|
## 🧩 Categorias customizadas (customCategories)
|
|
66
150
|
Disponível a partir da v0.4.0.
|
|
67
151
|
|
|
152
|
+
## 🍪 Categorias: definição, uso e exemplos
|
|
153
|
+
|
|
154
|
+
Fonte única de verdade
|
|
155
|
+
- Defina as categorias do seu projeto SOMENTE na prop `categories` do `ConsentProvider`.
|
|
156
|
+
- A UI (Banner/Modal), os hooks (`useConsent`, `useCategories`) e as integrações (`ConsentScriptLoader`) leem a mesma definição. Não declare categorias em outros lugares.
|
|
157
|
+
|
|
158
|
+
O que é obrigatório?
|
|
159
|
+
- Apenas a categoria `necessary` é obrigatória (e já é sempre incluída automaticamente).
|
|
160
|
+
- Todas as demais (`analytics`, `marketing`, `functional`, etc.) são opcionais e dependem do seu caso de negócio. Se você não usa analytics/ads/chat, simplesmente não habilite essas categorias.
|
|
161
|
+
|
|
162
|
+
Como “esconder” categorias que não uso?
|
|
163
|
+
- Basta não incluí-las em `enabledCategories` e não declará-las em `customCategories`. A UI não exibirá toggles para categorias ausentes.
|
|
164
|
+
|
|
165
|
+
Exemplo A — Somente necessários (mínimo, comum para apps internos/governo sem tracking)
|
|
166
|
+
```tsx
|
|
167
|
+
import { ConsentProvider } from 'react-lgpd-consent'
|
|
168
|
+
|
|
169
|
+
export default function App() {
|
|
170
|
+
return (
|
|
171
|
+
<ConsentProvider
|
|
172
|
+
categories={{ enabledCategories: [] }}
|
|
173
|
+
texts={{ bannerMessage: 'Usamos apenas cookies necessários para funcionamento.' }}
|
|
174
|
+
>
|
|
175
|
+
<YourApp />
|
|
176
|
+
</ConsentProvider>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Exemplo B — Conjunto completo (site com analytics e marketing)
|
|
182
|
+
```tsx
|
|
183
|
+
import { ConsentProvider } from 'react-lgpd-consent'
|
|
184
|
+
|
|
185
|
+
export default function App() {
|
|
186
|
+
return (
|
|
187
|
+
<ConsentProvider
|
|
188
|
+
categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }}
|
|
189
|
+
>
|
|
190
|
+
<YourApp />
|
|
191
|
+
</ConsentProvider>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Boas práticas
|
|
197
|
+
- Sempre passe `categories` explicitamente. Em DEV, a biblioteca avisa quando `categories` não foi configurado para evitar ambiguidades.
|
|
198
|
+
- Não classifique scripts de analytics/ads como “necessary” — use `ConsentScriptLoader` e categorias adequadas.
|
|
199
|
+
- Em dúvidas, comece com “somente necessários” e evolua quando o negócio exigir outras categorias.
|
|
200
|
+
|
|
201
|
+
### 🔎 Validação de configuração (DEV)
|
|
202
|
+
|
|
203
|
+
Em desenvolvimento, a biblioteca valida a configuração e mostra mensagens amigáveis no console. Nada disso impacta produção (onde só ocorre uma sanitização leve).
|
|
204
|
+
|
|
205
|
+
Avisos comuns e como corrigir:
|
|
206
|
+
- `Prop 'categories' não fornecida...` — defina `categories.enabledCategories` de forma explícita; exemplo mínimo: `categories={{ enabledCategories: [] }}`.
|
|
207
|
+
- `'necessary' é sempre incluída automaticamente` — remova `'necessary'` de `enabledCategories` (ela já é incluída por padrão).
|
|
208
|
+
- `IDs de categoria duplicados detectados` — revise `enabledCategories` e `customCategories` para garantir que não há IDs repetidos.
|
|
209
|
+
- `enabledCategories contém valores inválidos` — verifique se todos os itens são strings não vazias (IDs de categoria).
|
|
210
|
+
- `customCategories: ... — ... deve ser uma string não vazia` — preencha `id`, `name` e `description` das categorias customizadas.
|
|
211
|
+
|
|
212
|
+
Notas:
|
|
213
|
+
- Validação detalhada roda apenas em `NODE_ENV !== 'production'`.
|
|
214
|
+
- Em produção, a lib não carrega o validador; somente remove `'necessary'` se vier por engano, mantendo o comportamento seguro.
|
|
215
|
+
|
|
216
|
+
## 🧱 SSR/Next.js (App Router) — Padrões seguros
|
|
217
|
+
|
|
218
|
+
Objetivo: evitar hydration mismatch, hooks em Server Components e vazamento de scripts.
|
|
219
|
+
|
|
220
|
+
Padrões recomendados
|
|
221
|
+
- Envolva o app com o `ConsentProvider` apenas no cliente.
|
|
222
|
+
- Use `dynamic(() => import('./ClientConsent'), { ssr: false })` no `RootLayout` (Server Component) e mova hooks e efeitos para o componente cliente.
|
|
223
|
+
- Nenhum acesso a `window`/`document` no topo de módulo; use apenas dentro de `useEffect`.
|
|
224
|
+
- Inicialize Consent Mode v2 com `gtag('consent','default', denied)` antes de carregar GTM/GA4; depois, atualize sinais na mudança de preferências.
|
|
225
|
+
|
|
226
|
+
Exemplo de RootLayout (Server) + Client wrapper
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
// app/layout.tsx (Server Component)
|
|
230
|
+
import dynamic from 'next/dynamic'
|
|
231
|
+
|
|
232
|
+
const ClientConsent = dynamic(() => import('./components/ClientConsent'), { ssr: false })
|
|
233
|
+
|
|
234
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
235
|
+
return (
|
|
236
|
+
<html lang="pt-BR">
|
|
237
|
+
<body>
|
|
238
|
+
<ClientConsent>{children}</ClientConsent>
|
|
239
|
+
</body>
|
|
240
|
+
</html>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
// app/components/ClientConsent.tsx (Client Component)
|
|
247
|
+
'use client'
|
|
248
|
+
import React from 'react'
|
|
249
|
+
import { ConsentProvider, ConsentScriptLoader } from 'react-lgpd-consent'
|
|
250
|
+
import { COMMON_INTEGRATIONS } from 'react-lgpd-consent'
|
|
251
|
+
import { useConsent } from 'react-lgpd-consent'
|
|
252
|
+
|
|
253
|
+
function BootstrapConsentMode() {
|
|
254
|
+
React.useEffect(() => {
|
|
255
|
+
const w = window as any
|
|
256
|
+
w.dataLayer = w.dataLayer ?? []
|
|
257
|
+
w.gtag = w.gtag ?? ((...args: any[]) => w.dataLayer.push(args))
|
|
258
|
+
w.gtag('consent', 'default', {
|
|
259
|
+
ad_storage: 'denied',
|
|
260
|
+
ad_user_data: 'denied',
|
|
261
|
+
ad_personalization: 'denied',
|
|
262
|
+
analytics_storage: 'denied',
|
|
263
|
+
})
|
|
264
|
+
}, [])
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function SyncConsentMode() {
|
|
269
|
+
const { consented, preferences } = useConsent()
|
|
270
|
+
React.useEffect(() => {
|
|
271
|
+
if (!consented) return
|
|
272
|
+
const w = window as any
|
|
273
|
+
w.gtag?.('consent', 'update', {
|
|
274
|
+
analytics_storage: preferences.analytics ? 'granted' : 'denied',
|
|
275
|
+
ad_storage: preferences.marketing ? 'granted' : 'denied',
|
|
276
|
+
ad_user_data: preferences.marketing ? 'granted' : 'denied',
|
|
277
|
+
ad_personalization: preferences.marketing ? 'granted' : 'denied',
|
|
278
|
+
})
|
|
279
|
+
}, [consented, preferences])
|
|
280
|
+
return null
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export default function ClientConsent({ children }: { children: React.ReactNode }) {
|
|
284
|
+
const GA = process.env.NEXT_PUBLIC_GA_ID!
|
|
285
|
+
const GTM = process.env.NEXT_PUBLIC_GTM_ID!
|
|
286
|
+
return (
|
|
287
|
+
<ConsentProvider categories={{ enabledCategories: ['analytics', 'marketing', 'functional'] }} blocking>
|
|
288
|
+
<BootstrapConsentMode />
|
|
289
|
+
<SyncConsentMode />
|
|
290
|
+
<ConsentScriptLoader
|
|
291
|
+
integrations={[
|
|
292
|
+
COMMON_INTEGRATIONS.googleAnalytics({ measurementId: GA }),
|
|
293
|
+
COMMON_INTEGRATIONS.googleTagManager({ containerId: GTM }),
|
|
294
|
+
]}
|
|
295
|
+
/>
|
|
296
|
+
{children}
|
|
297
|
+
</ConsentProvider>
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Ordem de provedores e estilos (MUI/Emotion)
|
|
303
|
+
- Preferência de ordem recomendada:
|
|
304
|
+
- `CacheProvider` (Emotion) ou `StyledEngineProvider` com `injectFirst`
|
|
305
|
+
- `ThemeProvider` (MUI)
|
|
306
|
+
- `CssBaseline`
|
|
307
|
+
- `ConsentProvider` (sem criar tema por padrão)
|
|
308
|
+
- Motivo: garante injeção de estilos do MUI antes de CSS da app e evita desalinhamento visual; os componentes da lib herdam o tema quando presente.
|
|
309
|
+
|
|
310
|
+
Z-index e Portals
|
|
311
|
+
- Componentes MUI usam o `zIndex` do tema; modals/portals padrão usam `zIndex.modal = 1300`.
|
|
312
|
+
- O overlay bloqueante do Provider usa `z-index: 1299`; o Modal/Banner usa camadas ≥ 1300.
|
|
313
|
+
- Em caso de conflito com headers fixos, ajuste o `theme.zIndex` (ex.: `appBar: 1200`, `modal: 1300+`) ou os `designTokens` conforme a necessidade.
|
|
314
|
+
|
|
315
|
+
Checklist SSR (evite hydration mismatch)
|
|
316
|
+
- [ ] Hooks somente em Client Components (`'use client'` no topo).
|
|
317
|
+
- [ ] Nada de `window`/`document`/`localStorage` no topo de módulo (apenas em `useEffect`).
|
|
318
|
+
- [ ] `dynamic(..., { ssr: false })` para wrappers que usam hooks e efeitos do consentimento.
|
|
319
|
+
- [ ] GTM/GA4 carregados apenas após consentimento (via `ConsentScriptLoader`).
|
|
320
|
+
- [ ] Sem `<script>` de GTM/GA4 em `head`/`body`; todo carregamento vem do loader.
|
|
321
|
+
|
|
322
|
+
## 🎨 Dica de estilo: Backdrop sensível ao tema
|
|
323
|
+
|
|
324
|
+
No modo bloqueante, o banner usa um backdrop para focar a atenção do usuário. Você pode controlar via design tokens:
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
<ConsentProvider
|
|
328
|
+
categories={{ enabledCategories: ['analytics'] }}
|
|
329
|
+
designTokens={{
|
|
330
|
+
layout: {
|
|
331
|
+
// false: transparente | 'auto': ajusta ao tema | string: cor custom (ex.: '#00000088')
|
|
332
|
+
backdrop: 'auto',
|
|
333
|
+
},
|
|
334
|
+
colors: {
|
|
335
|
+
// Se omitido, usa o palette do tema MUI (background.paper, text.primary)
|
|
336
|
+
// background: '#1e1e1e',
|
|
337
|
+
// text: '#ffffff',
|
|
338
|
+
},
|
|
339
|
+
}}
|
|
340
|
+
>
|
|
341
|
+
<App />
|
|
342
|
+
</ConsentProvider>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Se `colors.background` ou `colors.text` não forem fornecidos, a lib usa automaticamente `theme.palette.background.paper` e `theme.palette.text.primary` do MUI, garantindo compatibilidade com dark mode.
|
|
346
|
+
|
|
347
|
+
## 🧑🏫 Guia do Dev (console)
|
|
348
|
+
|
|
349
|
+
Durante o desenvolvimento, o console exibe um guia com:
|
|
350
|
+
- Avisos quando a configuração padrão é usada; sugestões para explicitar categorias
|
|
351
|
+
- Lista de categorias ativas e quais exigem toggle
|
|
352
|
+
- Detecção de integrações que requerem categorias, com sugestão para habilitá-las
|
|
353
|
+
- Boas práticas LGPD (Brasil) e alertas de UX (categorias demais)
|
|
354
|
+
- Silenciado automaticamente em produção; SSR-safe
|
|
355
|
+
|
|
68
356
|
Adicione categorias específicas do seu projeto (ex.: chat de suporte, players de vídeo, AB testing):
|
|
69
357
|
|
|
70
358
|
```tsx
|
|
@@ -193,7 +481,6 @@ A biblioteca `react-lgpd-consent` não injeta um `ThemeProvider` global por cont
|
|
|
193
481
|
|
|
194
482
|
```tsx
|
|
195
483
|
import { ConsentProvider, createDefaultConsentTheme } from 'react-lgpd-consent'
|
|
196
|
-
|
|
197
484
|
;<ConsentProvider
|
|
198
485
|
theme={createDefaultConsentTheme()}
|
|
199
486
|
categories={{ enabledCategories: ['analytics'] }}
|
|
@@ -275,6 +562,280 @@ function App() {
|
|
|
275
562
|
}
|
|
276
563
|
```
|
|
277
564
|
|
|
565
|
+
### 🍪 Modal Personalizado com Detalhes dos Cookies
|
|
566
|
+
|
|
567
|
+
Para casos mais avançados onde você precisa exibir informações detalhadas sobre cada cookie (nome, finalidade, duração, provedor), use `getCookiesInfoForCategory` junto com `useCategories`:
|
|
568
|
+
|
|
569
|
+
```tsx
|
|
570
|
+
import React from 'react'
|
|
571
|
+
import {
|
|
572
|
+
ConsentProvider,
|
|
573
|
+
useCategories,
|
|
574
|
+
getCookiesInfoForCategory,
|
|
575
|
+
type CustomPreferencesModalProps,
|
|
576
|
+
type CookieDescriptor,
|
|
577
|
+
} from 'react-lgpd-consent'
|
|
578
|
+
|
|
579
|
+
const ModalComDetalhesCookies: React.FC<CustomPreferencesModalProps> = ({
|
|
580
|
+
preferences,
|
|
581
|
+
setPreferences,
|
|
582
|
+
closePreferences,
|
|
583
|
+
isModalOpen,
|
|
584
|
+
texts,
|
|
585
|
+
}) => {
|
|
586
|
+
const { allCategories } = useCategories()
|
|
587
|
+
|
|
588
|
+
if (!isModalOpen) return null
|
|
589
|
+
|
|
590
|
+
// Simula integrações usadas no projeto (normalmente você teria isso em contexto)
|
|
591
|
+
const integracoesUsadas = ['google-analytics', 'google-tag-manager', 'mixpanel']
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<div
|
|
595
|
+
style={{
|
|
596
|
+
position: 'fixed',
|
|
597
|
+
top: 0,
|
|
598
|
+
left: 0,
|
|
599
|
+
width: '100vw',
|
|
600
|
+
height: '100vh',
|
|
601
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
602
|
+
display: 'flex',
|
|
603
|
+
justifyContent: 'center',
|
|
604
|
+
alignItems: 'center',
|
|
605
|
+
zIndex: 2000,
|
|
606
|
+
}}
|
|
607
|
+
>
|
|
608
|
+
<div
|
|
609
|
+
style={{
|
|
610
|
+
backgroundColor: 'white',
|
|
611
|
+
borderRadius: '12px',
|
|
612
|
+
padding: '2rem',
|
|
613
|
+
maxWidth: '800px',
|
|
614
|
+
maxHeight: '80vh',
|
|
615
|
+
overflow: 'auto',
|
|
616
|
+
boxShadow: '0 10px 25px rgba(0, 0, 0, 0.25)',
|
|
617
|
+
}}
|
|
618
|
+
>
|
|
619
|
+
<h2 style={{ marginBottom: '1rem', color: '#333' }}>{texts.modalTitle}</h2>
|
|
620
|
+
<p style={{ marginBottom: '2rem', color: '#666' }}>{texts.modalIntro}</p>
|
|
621
|
+
|
|
622
|
+
{/* Lista de categorias com detalhes dos cookies */}
|
|
623
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
|
|
624
|
+
{allCategories.map((categoria) => {
|
|
625
|
+
const cookiesDetalhados: CookieDescriptor[] = getCookiesInfoForCategory(
|
|
626
|
+
categoria.id as any,
|
|
627
|
+
integracoesUsadas,
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
return (
|
|
631
|
+
<div
|
|
632
|
+
key={categoria.id}
|
|
633
|
+
style={{
|
|
634
|
+
border: '1px solid #e0e0e0',
|
|
635
|
+
borderRadius: '8px',
|
|
636
|
+
padding: '1.5rem',
|
|
637
|
+
backgroundColor: '#fafafa',
|
|
638
|
+
}}
|
|
639
|
+
>
|
|
640
|
+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
|
|
641
|
+
<input
|
|
642
|
+
type="checkbox"
|
|
643
|
+
id={`categoria-${categoria.id}`}
|
|
644
|
+
checked={preferences[categoria.id] || false}
|
|
645
|
+
onChange={(e) =>
|
|
646
|
+
setPreferences({
|
|
647
|
+
...preferences,
|
|
648
|
+
[categoria.id]: e.target.checked,
|
|
649
|
+
})
|
|
650
|
+
}
|
|
651
|
+
disabled={categoria.essential}
|
|
652
|
+
style={{ marginRight: '0.75rem', transform: 'scale(1.2)' }}
|
|
653
|
+
/>
|
|
654
|
+
<label
|
|
655
|
+
htmlFor={`categoria-${categoria.id}`}
|
|
656
|
+
style={{ fontSize: '1.1rem', fontWeight: 'bold', color: '#333' }}
|
|
657
|
+
>
|
|
658
|
+
{categoria.name}
|
|
659
|
+
{categoria.essential && (
|
|
660
|
+
<span style={{ fontSize: '0.8rem', color: '#888', marginLeft: '0.5rem' }}>
|
|
661
|
+
(sempre ativo)
|
|
662
|
+
</span>
|
|
663
|
+
)}
|
|
664
|
+
</label>
|
|
665
|
+
</div>
|
|
666
|
+
|
|
667
|
+
<p style={{ marginBottom: '1rem', color: '#666', fontSize: '0.95rem' }}>
|
|
668
|
+
{categoria.description}
|
|
669
|
+
</p>
|
|
670
|
+
|
|
671
|
+
{/* Lista de cookies desta categoria */}
|
|
672
|
+
{cookiesDetalhados.length > 0 && (
|
|
673
|
+
<details style={{ marginTop: '1rem' }}>
|
|
674
|
+
<summary
|
|
675
|
+
style={{
|
|
676
|
+
cursor: 'pointer',
|
|
677
|
+
fontWeight: '500',
|
|
678
|
+
color: '#4f46e5',
|
|
679
|
+
marginBottom: '0.5rem',
|
|
680
|
+
}}
|
|
681
|
+
>
|
|
682
|
+
Ver cookies desta categoria ({cookiesDetalhados.length})
|
|
683
|
+
</summary>
|
|
684
|
+
<div style={{ marginTop: '0.75rem', paddingLeft: '1rem' }}>
|
|
685
|
+
{cookiesDetalhados.map((cookie, index) => (
|
|
686
|
+
<div
|
|
687
|
+
key={`${cookie.name}-${index}`}
|
|
688
|
+
style={{
|
|
689
|
+
backgroundColor: 'white',
|
|
690
|
+
border: '1px solid #e5e5e5',
|
|
691
|
+
borderRadius: '6px',
|
|
692
|
+
padding: '1rem',
|
|
693
|
+
marginBottom: '0.75rem',
|
|
694
|
+
}}
|
|
695
|
+
>
|
|
696
|
+
<h4
|
|
697
|
+
style={{ margin: '0 0 0.5rem 0', color: '#333', fontSize: '0.95rem' }}
|
|
698
|
+
>
|
|
699
|
+
<code
|
|
700
|
+
style={{
|
|
701
|
+
backgroundColor: '#f3f4f6',
|
|
702
|
+
padding: '2px 6px',
|
|
703
|
+
borderRadius: '4px',
|
|
704
|
+
fontFamily: 'monospace',
|
|
705
|
+
}}
|
|
706
|
+
>
|
|
707
|
+
{cookie.name}
|
|
708
|
+
</code>
|
|
709
|
+
</h4>
|
|
710
|
+
{cookie.purpose && (
|
|
711
|
+
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
|
|
712
|
+
<strong>Finalidade:</strong> {cookie.purpose}
|
|
713
|
+
</p>
|
|
714
|
+
)}
|
|
715
|
+
{cookie.duration && (
|
|
716
|
+
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
|
|
717
|
+
<strong>Duração:</strong> {cookie.duration}
|
|
718
|
+
</p>
|
|
719
|
+
)}
|
|
720
|
+
{cookie.provider && (
|
|
721
|
+
<p style={{ margin: '0.25rem 0', fontSize: '0.9rem', color: '#555' }}>
|
|
722
|
+
<strong>Provedor:</strong> {cookie.provider}
|
|
723
|
+
</p>
|
|
724
|
+
)}
|
|
725
|
+
</div>
|
|
726
|
+
))}
|
|
727
|
+
</div>
|
|
728
|
+
</details>
|
|
729
|
+
)}
|
|
730
|
+
|
|
731
|
+
{/* Fallback para categorias sem cookies catalogados */}
|
|
732
|
+
{cookiesDetalhados.length === 0 &&
|
|
733
|
+
categoria.cookies &&
|
|
734
|
+
categoria.cookies.length > 0 && (
|
|
735
|
+
<div style={{ marginTop: '1rem', fontSize: '0.9rem', color: '#666' }}>
|
|
736
|
+
<strong>Padrões de cookies:</strong>{' '}
|
|
737
|
+
{categoria.cookies.map((pattern, i) => (
|
|
738
|
+
<code
|
|
739
|
+
key={i}
|
|
740
|
+
style={{
|
|
741
|
+
backgroundColor: '#f3f4f6',
|
|
742
|
+
padding: '2px 4px',
|
|
743
|
+
borderRadius: '3px',
|
|
744
|
+
marginRight: '0.5rem',
|
|
745
|
+
fontFamily: 'monospace',
|
|
746
|
+
fontSize: '0.8rem',
|
|
747
|
+
}}
|
|
748
|
+
>
|
|
749
|
+
{pattern}
|
|
750
|
+
</code>
|
|
751
|
+
))}
|
|
752
|
+
</div>
|
|
753
|
+
)}
|
|
754
|
+
</div>
|
|
755
|
+
)
|
|
756
|
+
})}
|
|
757
|
+
</div>
|
|
758
|
+
|
|
759
|
+
{/* Botões de ação */}
|
|
760
|
+
<div
|
|
761
|
+
style={{
|
|
762
|
+
display: 'flex',
|
|
763
|
+
gap: '1rem',
|
|
764
|
+
justifyContent: 'flex-end',
|
|
765
|
+
marginTop: '2rem',
|
|
766
|
+
paddingTop: '1rem',
|
|
767
|
+
borderTop: '1px solid #e0e0e0',
|
|
768
|
+
}}
|
|
769
|
+
>
|
|
770
|
+
<button
|
|
771
|
+
onClick={closePreferences}
|
|
772
|
+
style={{
|
|
773
|
+
padding: '0.75rem 1.5rem',
|
|
774
|
+
backgroundColor: '#6b7280',
|
|
775
|
+
color: 'white',
|
|
776
|
+
border: 'none',
|
|
777
|
+
borderRadius: '6px',
|
|
778
|
+
cursor: 'pointer',
|
|
779
|
+
fontSize: '0.95rem',
|
|
780
|
+
}}
|
|
781
|
+
>
|
|
782
|
+
Cancelar
|
|
783
|
+
</button>
|
|
784
|
+
<button
|
|
785
|
+
onClick={closePreferences}
|
|
786
|
+
style={{
|
|
787
|
+
padding: '0.75rem 1.5rem',
|
|
788
|
+
backgroundColor: '#4f46e5',
|
|
789
|
+
color: 'white',
|
|
790
|
+
border: 'none',
|
|
791
|
+
borderRadius: '6px',
|
|
792
|
+
cursor: 'pointer',
|
|
793
|
+
fontSize: '0.95rem',
|
|
794
|
+
}}
|
|
795
|
+
>
|
|
796
|
+
{texts.save}
|
|
797
|
+
</button>
|
|
798
|
+
</div>
|
|
799
|
+
</div>
|
|
800
|
+
</div>
|
|
801
|
+
)
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function AppComModalAvancado() {
|
|
805
|
+
return (
|
|
806
|
+
<ConsentProvider
|
|
807
|
+
categories={{
|
|
808
|
+
enabledCategories: ['analytics', 'marketing', 'functional'],
|
|
809
|
+
}}
|
|
810
|
+
PreferencesModalComponent={ModalComDetalhesCookies}
|
|
811
|
+
// Especifique as integrações para obter informações detalhadas dos cookies
|
|
812
|
+
scriptIntegrations={[
|
|
813
|
+
{ id: 'google-analytics', config: { measurementId: 'GA_MEASUREMENT_ID' } },
|
|
814
|
+
{ id: 'google-tag-manager', config: { containerId: 'GTM-XXXXXXX' } },
|
|
815
|
+
{ id: 'mixpanel', config: { token: 'MIXPANEL_TOKEN' } },
|
|
816
|
+
]}
|
|
817
|
+
>
|
|
818
|
+
<main>Minha App com Modal Avançado</main>
|
|
819
|
+
</ConsentProvider>
|
|
820
|
+
)
|
|
821
|
+
}
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
#### 🔧 APIs Utilizadas no Exemplo Avançado
|
|
825
|
+
|
|
826
|
+
- **`useCategories()`**: Hook que retorna informações sobre todas as categorias ativas
|
|
827
|
+
- **`getCookiesInfoForCategory(categoryId, integrations)`**: Função que retorna detalhes completos dos cookies
|
|
828
|
+
- **`CookieDescriptor`**: Interface TypeScript com `name`, `purpose`, `duration`, `provider`
|
|
829
|
+
|
|
830
|
+
#### 💡 Principais Funcionalidades
|
|
831
|
+
|
|
832
|
+
1. **Informações Detalhadas**: Cada cookie mostra nome, finalidade, duração e provedor
|
|
833
|
+
2. **Organização por Categoria**: Cookies agrupados logicamente
|
|
834
|
+
3. **Interface Expansível**: Detalhes dos cookies ficam em `<details>` expansível
|
|
835
|
+
4. **Fallback Inteligente**: Mostra padrões básicos quando detalhes não estão disponíveis
|
|
836
|
+
5. **Acessibilidade**: Labels apropriados e navegação por teclado
|
|
837
|
+
6. **Design Responsivo**: Layout que se adapta a diferentes tamanhos de tela
|
|
838
|
+
|
|
278
839
|
## 🎮 Controle Programático
|
|
279
840
|
|
|
280
841
|
### Hook useOpenPreferencesModal (React)
|
package/README.en.md
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
<a href="https://nextjs.org/"><img src="https://img.shields.io/badge/Next.js-Compatible-000000?style=for-the-badge&logo=next.js&logoColor=white" alt="Next.js Compatible"></a>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
+
<div>
|
|
19
|
+
<a href="https://codecov.io/gh/lucianoedipo/react-lgpd-consent"><img src="https://img.shields.io/codecov/c/github/lucianoedipo/react-lgpd-consent?style=for-the-badge&logo=codecov&logoColor=white" alt="Coverage"></a>
|
|
20
|
+
<a href="https://bundlephobia.com/package/react-lgpd-consent"><img src="https://img.shields.io/bundlephobia/minzip/react-lgpd-consent?style=for-the-badge&logo=webpack&logoColor=white" alt="Bundle Size"></a>
|
|
21
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/react-lgpd-consent?style=for-the-badge&logo=node.js&logoColor=white" alt="Node Version"></a>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
18
24
|
<br />
|
|
19
25
|
|
|
20
26
|
<p>
|
|
@@ -64,6 +70,10 @@ export default function App() {
|
|
|
64
70
|
-
|
|
65
71
|
- **[QUICKSTART.en.md](./QUICKSTART.en.md)** (recommended)
|
|
66
72
|
- New in v0.4.0: `customCategories` support — see the “Custom categories (customCategories)” section in the Quickstart.
|
|
73
|
+
- New in v0.4.1: native integrations for Facebook Pixel, Hotjar, Mixpanel, Clarity, Intercom, and Zendesk — see [INTEGRACOES.md](./INTEGRACOES.md).
|
|
74
|
+
- Tip: set `designTokens.layout.backdrop: 'auto'` for a theme-aware blocking banner backdrop.
|
|
75
|
+
- Auto-config of categories: the library detects required categories from integrations and surfaces toggles even if you forgot to enable them (initial value is always rejected). We still recommend explicitly listing them in `categories.enabledCategories` for clarity.
|
|
76
|
+
- Non-blocked Policy/Terms pages: if `policyLinkUrl` and/or `termsLinkUrl` point to the current page, the blocking overlay is not applied — ensuring readability of these pages.
|
|
67
77
|
- **[Docs / API](./API.md)**
|
|
68
78
|
- **[LGPD Compliance](./CONFORMIDADE.md)**
|
|
69
79
|
- **[Integrations](./INTEGRACOES.md)**
|
|
@@ -73,6 +83,21 @@ export default function App() {
|
|
|
73
83
|
- **[⚙️ TypeDoc - API Reference](https://lucianoedipo.github.io/react-lgpd-consent/docs/)**: Automatically generated complete API documentation.
|
|
74
84
|
- **[🏠 Documentation Portal](https://lucianoedipo.github.io/react-lgpd-consent/)**: Home page that navigates between all docs sites.
|
|
75
85
|
|
|
86
|
+
### 🧑🏫 Developer Guidance (dev-only)
|
|
87
|
+
|
|
88
|
+
In development, the library prints a guidance panel in the console to help you configure correctly:
|
|
89
|
+
- Warns when using default categories; suggests making them explicit
|
|
90
|
+
- Lists active categories and which ones require a UI toggle
|
|
91
|
+
- Detects integrations that require categories and suggests enabling them
|
|
92
|
+
- Flags excessive number of categories (UX)
|
|
93
|
+
- Highlights Brazilian LGPD best practices: opt-out by default, clear policy, consent logging, retention
|
|
94
|
+
- Silenced in production; SSR-safe
|
|
95
|
+
|
|
96
|
+
### ⚠️ Breaking Changes v0.4.1
|
|
97
|
+
- **Custom categories support**: `setPreference` and `ScriptIntegration.category` now use `string` instead of `Category`
|
|
98
|
+
- **Minimal impact**: Code using literal strings continues working without changes
|
|
99
|
+
- **Migration guide**: See [CHANGELOG.md](./CHANGELOG.md) for complete details
|
|
100
|
+
|
|
76
101
|
---
|
|
77
102
|
|
|
78
103
|
## 🤝 Contributing
|
package/README.md
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
<a href="https://nextjs.org/"><img src="https://img.shields.io/badge/Next.js-Compatible-000000?style=for-the-badge&logo=next.js&logoColor=white" alt="Next.js Compatible"></a>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
+
<div>
|
|
19
|
+
<a href="https://codecov.io/gh/lucianoedipo/react-lgpd-consent"><img src="https://img.shields.io/codecov/c/github/lucianoedipo/react-lgpd-consent?style=for-the-badge&logo=codecov&logoColor=white" alt="Coverage"></a>
|
|
20
|
+
<a href="https://bundlephobia.com/package/react-lgpd-consent"><img src="https://img.shields.io/bundlephobia/minzip/react-lgpd-consent?style=for-the-badge&logo=webpack&logoColor=white" alt="Bundle Size"></a>
|
|
21
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/react-lgpd-consent?style=for-the-badge&logo=node.js&logoColor=white" alt="Node Version"></a>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
18
24
|
<br />
|
|
19
25
|
|
|
20
26
|
<p>
|
|
@@ -46,6 +52,32 @@ npm install react-lgpd-consent @mui/material @emotion/react @emotion/styled js-c
|
|
|
46
52
|
|
|
47
53
|
---
|
|
48
54
|
|
|
55
|
+
## ✨ Novidades v0.4.1
|
|
56
|
+
|
|
57
|
+
### 🎨 Design Tokens Expandidos
|
|
58
|
+
- **200+ pontos de customização** (cores, tipografia, espaçamento, layout)
|
|
59
|
+
- **Sistema responsivo** com breakpoints e variações
|
|
60
|
+
- **Acessibilidade nativa** com contrast ratios e focus states
|
|
61
|
+
- **Temas light/dark/auto** com transições suaves
|
|
62
|
+
|
|
63
|
+
### 📝 Sistema Avançado de Textos
|
|
64
|
+
- **Templates pré-configurados** para ecommerce, SaaS e governo
|
|
65
|
+
- **Internacionalização completa** (pt, en, es)
|
|
66
|
+
- **Variações de tom** (formal, casual, técnico)
|
|
67
|
+
- **Resolução automática** baseada em contexto
|
|
68
|
+
|
|
69
|
+
### 🔍 Descoberta de Cookies (Experimental)
|
|
70
|
+
- **Detecção automática** de cookies em runtime
|
|
71
|
+
- **Categorização inteligente** usando padrões LGPD
|
|
72
|
+
- **Integração nativa** com sistema de override
|
|
73
|
+
|
|
74
|
+
### ⚠️ Breaking Changes
|
|
75
|
+
- **Suporte a categorias customizadas**: `setPreference` e `ScriptIntegration.category` agora usam `string` ao invés de `Category`
|
|
76
|
+
- **Impacto mínimo**: Código usando strings literais continua funcionando sem alterações
|
|
77
|
+
- **Consulte**: [CHANGELOG.md](./CHANGELOG.md) para guia de migração completo
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
49
81
|
## 📖 Uso Básico
|
|
50
82
|
|
|
51
83
|
Envolva sua aplicação com o `ConsentProvider` (exemplo mínimo):
|
|
@@ -87,7 +119,12 @@ Para mais detalhes sobre customização, hooks e funcionalidades, consulte os se
|
|
|
87
119
|
### 📋 Documentação Principal
|
|
88
120
|
|
|
89
121
|
- **[📚 Guia de Início Rápido (`QUICKSTART.md`)](./QUICKSTART.md)**: Tutorial passo a passo com exemplos práticos, tabela completa de props, debugging e integrações.
|
|
122
|
+
- Seção recomendada: “SSR/Next.js (App Router) — Padrões seguros” com boas práticas de `'use client'`, `dynamic({ ssr: false })` e ordem dos provedores/estilos (MUI/Emotion) para evitar hydration mismatch.
|
|
90
123
|
- Novo na v0.4.0: suporte a `customCategories` — veja a seção “Categorias customizadas (customCategories)” no Quickstart.
|
|
124
|
+
- Novo na v0.4.1: integrações nativas para Facebook Pixel, Hotjar, Mixpanel, Clarity, Intercom e Zendesk — veja o guia [INTEGRACOES.md](./INTEGRACOES.md).
|
|
125
|
+
- Dica: use `designTokens.layout.backdrop: 'auto'` para backdrop sensível ao tema no banner bloqueante.
|
|
126
|
+
- Auto-config de categorias: a biblioteca detecta categorias requeridas pelas integrações e exibe os toggles mesmo se você esquecer de habilitar (valor inicial sempre rejeitado). Recomendamos explicitar em `categories.enabledCategories` para clareza.
|
|
127
|
+
- Páginas de Política/Termos não bloqueadas: se `policyLinkUrl` e/ou `termsLinkUrl` apontarem para a página atual, o overlay bloqueante não é aplicado — garantindo legibilidade destas páginas.
|
|
91
128
|
- **[Guia da API (`API.md`)](./API.md)**: Referência completa de todos os componentes, hooks e tipos.
|
|
92
129
|
- **[Guia de Conformidade (`CONFORMIDADE.md`)](./CONFORMIDADE.md)**: Detalhes sobre as funcionalidades de conformidade com a LGPD.
|
|
93
130
|
- **[Guia de Integrações (`INTEGRACOES.md`)](./INTEGRACOES.md)**: Como usar as integrações nativas e criar as suas.
|