@el7ven/cookie-kit 0.2.19 → 0.2.21
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/package.json +4 -2
- package/src/core/analytics.js +220 -0
- package/src/core/index.js +112 -5
- package/src/index.js +8 -0
- package/src/vue/CookieConsent.vue +165 -62
- package/src/vue/CookieConsentDebug.vue +168 -0
- package/src/vue/CookieDrawer.vue +274 -104
- package/src/vue/index.js +6 -1
package/src/vue/CookieDrawer.vue
CHANGED
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
<div ref="dialogRef" class="cookie-drawer__content" role="dialog" aria-modal="true" @click.stop>
|
|
18
18
|
<!-- Header -->
|
|
19
19
|
<div class="cookie-drawer__header">
|
|
20
|
-
<
|
|
20
|
+
<div class="cookie-drawer__title">
|
|
21
21
|
{{ isSettingsMode
|
|
22
22
|
? (config.texts?.settings?.title || 'Cookie Settings')
|
|
23
23
|
: (config.mode === 'essential'
|
|
24
24
|
? (config.texts?.essential?.title || 'Essential Cookies')
|
|
25
25
|
: (config.texts?.gdpr?.title || 'Cookie Consent'))
|
|
26
26
|
}}
|
|
27
|
-
</
|
|
28
|
-
|
|
27
|
+
</div>
|
|
28
|
+
<!-- Message -->
|
|
29
29
|
<div v-if="!isSettingsMode && !isEssentialBanner" class="cookie-drawer__message">
|
|
30
|
-
|
|
30
|
+
{{ config.texts?.gdpr?.message || 'We use cookies to enhance your experience.' }}
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
33
|
<button
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
class="cookie-drawer__close"
|
|
37
37
|
aria-label="Close"
|
|
38
38
|
>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
40
|
+
<path d="M6.40013 18.3079L5.69238 17.6001L11.2924 12.0001L5.69238 6.40013L6.40013 5.69238L12.0001 11.2924L17.6001 5.69238L18.3079 6.40013L12.7079 12.0001L18.3079 17.6001L17.6001 18.3079L12.0001 12.7079L6.40013 18.3079Z" fill="currentColor"/>
|
|
41
|
+
</svg>
|
|
42
42
|
</button>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
@@ -46,101 +46,191 @@
|
|
|
46
46
|
<div v-if="!isSettingsMode && isEssentialBanner" class="cookie-drawer__banner">
|
|
47
47
|
<div class="cookie-drawer__essential">
|
|
48
48
|
<div class="cookie-drawer__message cookie-drawer__message--essential">
|
|
49
|
-
{{ config.texts?.essential?.message || '
|
|
50
|
-
</div>
|
|
51
|
-
<div class="cookie-drawer__actions">
|
|
52
|
-
<button @click="$emit('acceptAll')" class="cookie-drawer__button cookie-drawer__button--primary">
|
|
53
|
-
{{ config.texts?.essential?.accept || 'Accept' }}
|
|
54
|
-
</button>
|
|
49
|
+
{{ config.texts?.essential?.message || 'This site uses only essential cookies.' }}
|
|
55
50
|
</div>
|
|
56
51
|
</div>
|
|
57
52
|
</div>
|
|
58
53
|
|
|
59
|
-
<!-- GDPR Banner
|
|
60
|
-
<div v-else class="cookie-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
<div class="cookie-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
54
|
+
<!-- GDPR Banner -->
|
|
55
|
+
<div v-else-if="!isSettingsMode" class="cookie-drawer__banner">
|
|
56
|
+
<div class="cookie-drawer__gdpr">
|
|
57
|
+
<!-- Categories -->
|
|
58
|
+
<div class="cookie-drawer__categories">
|
|
59
|
+
<div
|
|
60
|
+
v-for="category in enabledCategories"
|
|
61
|
+
:key="category.id"
|
|
62
|
+
class="cookie-drawer__category"
|
|
63
|
+
>
|
|
64
|
+
<div class="cookie-drawer__category-header">
|
|
65
|
+
<span class="cookie-drawer__category-name">
|
|
66
|
+
{{ category.label }}
|
|
67
|
+
</span>
|
|
68
|
+
<div class="cookie-drawer__toggle">
|
|
69
|
+
<input
|
|
70
|
+
type="checkbox"
|
|
71
|
+
:id="`drawer-category-${category.id}`"
|
|
72
|
+
:checked="categories[category.id]"
|
|
73
|
+
@change="toggleCategory(category.id)"
|
|
74
|
+
:disabled="category.locked"
|
|
75
|
+
class="cookie-drawer__toggle-input"
|
|
76
|
+
/>
|
|
77
|
+
<label
|
|
78
|
+
:for="`drawer-category-${category.id}`"
|
|
79
|
+
:class="[
|
|
80
|
+
'cookie-drawer__toggle-label',
|
|
81
|
+
{ 'cookie-drawer__toggle-label--disabled': category.locked }
|
|
82
|
+
]"
|
|
83
|
+
></label>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<div v-if="category.descriptionShow" class="cookie-drawer__category-description">
|
|
87
|
+
{{ category.description }}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
76
90
|
</div>
|
|
77
|
-
</div>
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<!-- Tabs -->
|
|
82
|
-
<div v-if="enabledSettingsTabs.length > 1" class="cookie-drawer__tabs">
|
|
92
|
+
<!-- Settings Link -->
|
|
93
|
+
<div class="cookie-drawer__settings-link">
|
|
83
94
|
<button
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
:class="['cookie-drawer__tab', { 'cookie-drawer__tab--active': currentTab === tab }]"
|
|
87
|
-
@click="$emit('selectTab', tab)"
|
|
95
|
+
@click="openSettings"
|
|
96
|
+
class="cookie-drawer__settings-button"
|
|
88
97
|
>
|
|
89
|
-
{{
|
|
98
|
+
{{ config.texts?.buttons?.settings || config.texts?.gdpr?.buttons?.settings || 'Settings' }}
|
|
90
99
|
</button>
|
|
91
100
|
</div>
|
|
92
101
|
|
|
93
|
-
<!--
|
|
94
|
-
<div class="cookie-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
</
|
|
102
|
+
<!-- Capabilities -->
|
|
103
|
+
<div v-if="isV2" class="cookie-drawer__capabilities" aria-live="polite">
|
|
104
|
+
<span v-if="capabilities.gdpr" class="cookie-drawer__capability">GDPR</span>
|
|
105
|
+
<span v-if="capabilities.ccpa" class="cookie-drawer__capability">CCPA</span>
|
|
106
|
+
<span v-if="capabilities.pipeda" class="cookie-drawer__capability">PIPEDA</span>
|
|
107
|
+
<span v-if="capabilities.iabTcf22" class="cookie-drawer__capability">IAB TCF v2.2</span>
|
|
108
|
+
<span v-if="capabilities.googleConsentModeV2" class="cookie-drawer__capability">Google Consent Mode v2</span>
|
|
109
|
+
<span v-if="capabilities.scriptBlocking" class="cookie-drawer__capability">Script Blocking</span>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
<!-- Settings Mode -->
|
|
115
|
+
<div v-else class="cookie-drawer__settings">
|
|
116
|
+
<!-- Tabs -->
|
|
117
|
+
<div class="cookie-drawer__tabs">
|
|
118
|
+
<button
|
|
119
|
+
v-for="tab in availableTabs"
|
|
120
|
+
:key="tab.id"
|
|
121
|
+
@click="selectTab(tab.id)"
|
|
122
|
+
:class="[
|
|
123
|
+
'cookie-drawer__tab',
|
|
124
|
+
{ 'cookie-drawer__tab--active': currentTab === tab.id }
|
|
125
|
+
]"
|
|
126
|
+
>
|
|
127
|
+
{{ tab.label }}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<!-- Tab Content -->
|
|
132
|
+
<div class="cookie-drawer__tab-content">
|
|
133
|
+
<!-- Privacy Tab -->
|
|
134
|
+
<div v-if="currentTab === 'privacy'" class="cookie-drawer__privacy">
|
|
135
|
+
<div class="cookie-drawer__privacy-content">
|
|
136
|
+
<div class="cookie-drawer__text">
|
|
137
|
+
{{ config.texts?.settings?.privacy?.intro || 'We use cookies and similar technologies to ensure the site works properly.' }}
|
|
115
138
|
</div>
|
|
139
|
+
<a :href="policyLinks.privacy || '#'" class="cookie-drawer__link">
|
|
140
|
+
{{ config.texts?.settings?.links?.moreInfo || 'More information' }}
|
|
141
|
+
</a>
|
|
116
142
|
</div>
|
|
117
143
|
</div>
|
|
118
144
|
|
|
119
|
-
<!--
|
|
120
|
-
<div class="cookie-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
<!-- Category Tabs -->
|
|
146
|
+
<div v-else class="cookie-drawer__category">
|
|
147
|
+
<div class="cookie-drawer__category-header">
|
|
148
|
+
<div class="cookie-drawer__category-title">
|
|
149
|
+
{{ currentCategoryData.label }}
|
|
150
|
+
</div>
|
|
151
|
+
<div class="cookie-drawer__toggle">
|
|
152
|
+
<input
|
|
153
|
+
type="checkbox"
|
|
154
|
+
:id="`settings-category-${currentTab}`"
|
|
155
|
+
:checked="categories[currentTab]"
|
|
156
|
+
@change="toggleCategory(currentTab)"
|
|
157
|
+
:disabled="currentCategoryData.locked"
|
|
158
|
+
class="cookie-drawer__toggle-input"
|
|
159
|
+
/>
|
|
160
|
+
<label
|
|
161
|
+
:for="`settings-category-${currentTab}`"
|
|
162
|
+
:class="[
|
|
163
|
+
'cookie-drawer__toggle-label',
|
|
164
|
+
{ 'cookie-drawer__toggle-label--disabled': currentCategoryData.locked }
|
|
165
|
+
]"
|
|
166
|
+
></label>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="cookie-drawer__category-description">
|
|
170
|
+
{{ currentCategoryData.description }}
|
|
171
|
+
</div>
|
|
172
|
+
<a :href="policyLinks.cookies || '#'" class="cookie-drawer__link">
|
|
173
|
+
{{ config.texts?.settings?.links?.cookieDetails || 'Cookie details' }}
|
|
174
|
+
</a>
|
|
124
175
|
</div>
|
|
125
176
|
</div>
|
|
126
177
|
</div>
|
|
178
|
+
|
|
179
|
+
<!-- Actions: GDPR mode -->
|
|
180
|
+
<div v-if="!isEssentialBanner" class="cookie-drawer__actions">
|
|
181
|
+
<button
|
|
182
|
+
@click="handleAcceptSelection"
|
|
183
|
+
class="cookie-drawer__button cookie-drawer__button--outline"
|
|
184
|
+
>
|
|
185
|
+
{{ config.texts?.buttons?.acceptSelection || config.texts?.gdpr?.buttons?.acceptSelection || 'Accept Selection' }}
|
|
186
|
+
</button>
|
|
187
|
+
<button
|
|
188
|
+
@click="handleRejectAll"
|
|
189
|
+
class="cookie-drawer__button cookie-drawer__button--secondary"
|
|
190
|
+
>
|
|
191
|
+
{{ config.texts?.buttons?.rejectAll || config.texts?.gdpr?.buttons?.rejectAll || 'Reject All' }}
|
|
192
|
+
</button>
|
|
193
|
+
<button
|
|
194
|
+
@click="handleAcceptAll"
|
|
195
|
+
class="cookie-drawer__button cookie-drawer__button--primary"
|
|
196
|
+
>
|
|
197
|
+
{{ config.texts?.buttons?.acceptAll || config.texts?.gdpr?.buttons?.acceptAll || 'Accept All' }}
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
<!-- Actions: Essential mode -->
|
|
201
|
+
<div v-else class="cookie-drawer__actions cookie-drawer__actions_essential">
|
|
202
|
+
<button
|
|
203
|
+
@click="handleAcceptAll"
|
|
204
|
+
class="cookie-drawer__button cookie-drawer__button--primary cookie-drawer__button--essential"
|
|
205
|
+
>
|
|
206
|
+
{{ config.texts?.essential?.button || 'Accept' }}
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
127
210
|
</div>
|
|
128
211
|
</div>
|
|
129
212
|
</Transition>
|
|
130
213
|
</template>
|
|
131
214
|
|
|
132
215
|
<script setup>
|
|
133
|
-
import { computed, ref } from 'vue'
|
|
216
|
+
import { computed, ref, watch, onMounted, onUnmounted } from 'vue'
|
|
134
217
|
|
|
135
218
|
const props = defineProps({
|
|
136
|
-
isVisible: Boolean,
|
|
137
|
-
isSettingsMode: Boolean,
|
|
138
|
-
currentTab: String,
|
|
139
|
-
categories: Object,
|
|
140
|
-
config: Object,
|
|
141
|
-
consentVersion: String,
|
|
142
|
-
capabilities: Object,
|
|
143
|
-
isV2: Boolean
|
|
219
|
+
isVisible: { type: Boolean, required: true },
|
|
220
|
+
isSettingsMode: { type: Boolean, default: false },
|
|
221
|
+
currentTab: { type: String, required: true },
|
|
222
|
+
categories: { type: Object, required: true },
|
|
223
|
+
config: { type: Object, required: true },
|
|
224
|
+
consentVersion: { type: String, default: 'v1' },
|
|
225
|
+
capabilities: { type: Object, default: () => ({}) },
|
|
226
|
+
isV2: { type: Boolean, default: false }
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const dialogRef = ref(null)
|
|
230
|
+
const windowWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 1024)
|
|
231
|
+
|
|
232
|
+
defineExpose({
|
|
233
|
+
dialogElement: () => dialogRef.value
|
|
144
234
|
})
|
|
145
235
|
|
|
146
236
|
const emit = defineEmits([
|
|
@@ -153,51 +243,131 @@ const emit = defineEmits([
|
|
|
153
243
|
'rejectAll'
|
|
154
244
|
])
|
|
155
245
|
|
|
156
|
-
|
|
246
|
+
// Computed properties
|
|
247
|
+
const enabledCategories = computed(() => {
|
|
248
|
+
if (!props.config.categories) return []
|
|
249
|
+
return Object.keys(props.config.categories)
|
|
250
|
+
.filter(catId => props.config.categories[catId].enabled)
|
|
251
|
+
.map(catId => props.config.categories[catId])
|
|
252
|
+
})
|
|
157
253
|
|
|
158
|
-
const
|
|
254
|
+
const availableTabs = computed(() => {
|
|
255
|
+
const tabs = [
|
|
256
|
+
{ id: 'privacy', label: props.config.texts?.settings?.tabs?.privacy || 'Privacy' }
|
|
257
|
+
]
|
|
159
258
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
259
|
+
if (props.config.categories) {
|
|
260
|
+
Object.keys(props.config.categories).forEach(categoryId => {
|
|
261
|
+
if (props.config.categories[categoryId].enabled) {
|
|
262
|
+
tabs.push({
|
|
263
|
+
id: categoryId,
|
|
264
|
+
label: props.config.categories[categoryId].label
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
164
269
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}))
|
|
270
|
+
return tabs
|
|
271
|
+
})
|
|
168
272
|
|
|
169
|
-
const
|
|
170
|
-
props.
|
|
171
|
-
)
|
|
273
|
+
const currentCategoryData = computed(() => {
|
|
274
|
+
return props.config.categories?.[props.currentTab] || {}
|
|
275
|
+
})
|
|
172
276
|
|
|
173
|
-
const
|
|
174
|
-
return
|
|
175
|
-
props.categories[id]?.enabled
|
|
176
|
-
)]
|
|
277
|
+
const policyLinks = computed(() => {
|
|
278
|
+
return props.config.policies || {}
|
|
177
279
|
})
|
|
178
280
|
|
|
179
|
-
const
|
|
180
|
-
return props.
|
|
181
|
-
}
|
|
281
|
+
const isEssentialBanner = computed(() => {
|
|
282
|
+
return props.config.mode === 'essential' && !props.isSettingsMode
|
|
283
|
+
})
|
|
182
284
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
285
|
+
// Reactive mobile detection
|
|
286
|
+
const isMobile = computed(() => {
|
|
287
|
+
return windowWidth.value <= 767
|
|
288
|
+
})
|
|
186
289
|
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
|
|
290
|
+
const handleResize = () => {
|
|
291
|
+
if (typeof window !== 'undefined') {
|
|
292
|
+
windowWidth.value = window.innerWidth
|
|
190
293
|
}
|
|
191
294
|
}
|
|
192
295
|
|
|
296
|
+
onMounted(() => {
|
|
297
|
+
if (typeof window !== 'undefined') {
|
|
298
|
+
window.addEventListener('resize', handleResize)
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
onUnmounted(() => {
|
|
303
|
+
if (typeof window !== 'undefined') {
|
|
304
|
+
window.removeEventListener('resize', handleResize)
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// Root classes for essential mode
|
|
309
|
+
const rootClasses = computed(() => {
|
|
310
|
+
return {
|
|
311
|
+
'cookie-drawer--essential-mode': isEssentialBanner.value
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
// Drawer classes and animations
|
|
316
|
+
const drawerClasses = computed(() => {
|
|
317
|
+
return {
|
|
318
|
+
'cookie-drawer--mobile': isMobile.value && !isEssentialBanner.value
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
const drawerAnimation = computed(() => {
|
|
323
|
+
if (isEssentialBanner.value) return 'drawer-slide-up'
|
|
324
|
+
if (isMobile.value) return 'drawer-slide-up'
|
|
325
|
+
return 'drawer-slide-right'
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
// Event handlers
|
|
193
329
|
const handleClose = () => {
|
|
194
330
|
emit('close')
|
|
195
331
|
}
|
|
196
332
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
333
|
+
const handleBackdropClick = () => {
|
|
334
|
+
emit('close')
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const openSettings = () => {
|
|
338
|
+
emit('openSettings')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const selectTab = (tabId) => {
|
|
342
|
+
emit('selectTab', tabId)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const toggleCategory = (categoryId) => {
|
|
346
|
+
emit('toggleCategory', categoryId)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const handleAcceptSelection = () => {
|
|
350
|
+
emit('acceptSelection')
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const handleAcceptAll = () => {
|
|
354
|
+
emit('acceptAll')
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const handleRejectAll = () => {
|
|
358
|
+
emit('rejectAll')
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Watch for drawer visibility to control body overflow
|
|
362
|
+
watch(
|
|
363
|
+
() => props.isVisible,
|
|
364
|
+
(newVal) => {
|
|
365
|
+
if (isEssentialBanner.value) return
|
|
366
|
+
if (typeof document === 'undefined') return
|
|
367
|
+
document.body.style.overflow = newVal ? 'hidden' : ''
|
|
368
|
+
},
|
|
369
|
+
{ immediate: true }
|
|
370
|
+
)
|
|
201
371
|
</script>
|
|
202
372
|
|
|
203
373
|
<style src="@el7ven/cookie-kit/dist/styles"></style>
|
package/src/vue/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { computed, ref } from 'vue'
|
|
2
2
|
import { createCookieKit, DEFAULT_CONFIG } from '../core/index.js'
|
|
3
3
|
import { VERSION, getVersion, getVersionInfo, logVersion } from './version.js'
|
|
4
|
+
import { useCookieConsent } from './composables/useCookieConsent.js'
|
|
5
|
+
import { createAnalyticsManager } from '../core/analytics.js'
|
|
4
6
|
|
|
5
|
-
export { createCookieKit, DEFAULT_CONFIG, VERSION, getVersion, getVersionInfo, logVersion }
|
|
7
|
+
export { createCookieKit, DEFAULT_CONFIG, VERSION, getVersion, getVersionInfo, logVersion, useCookieConsent, createAnalyticsManager }
|
|
8
|
+
export { default as CookieConsent } from './CookieConsent.vue'
|
|
9
|
+
export { default as CookieDrawer } from './CookieDrawer.vue'
|
|
10
|
+
export { default as CookieConsentDebug } from './CookieConsentDebug.vue'
|
|
6
11
|
|
|
7
12
|
export function useCookieKitVue(userConfig = {}) {
|
|
8
13
|
const core = createCookieKit(userConfig)
|