@el7ven/cookie-kit 0.2.15
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 +201 -0
- package/dist/styles/index.css +744 -0
- package/package.json +64 -0
- package/src/core/index.js +232 -0
- package/src/core/index.test.js +90 -0
- package/src/core/version.js +29 -0
- package/src/index.js +67 -0
- package/src/js/CookieConsent.js +618 -0
- package/src/js/components/index.js +1 -0
- package/src/js/index.js +53 -0
- package/src/js/index.test.js +42 -0
- package/src/js/version.js +29 -0
- package/src/react/CookieConsent.tsx +152 -0
- package/src/react/CookieDrawer.tsx +233 -0
- package/src/react/components/index.js +2 -0
- package/src/react/index.js +89 -0
- package/src/react/index.test.js +62 -0
- package/src/react/types.ts +47 -0
- package/src/react/version.js +29 -0
- package/src/vue/CookieConsent.vue +180 -0
- package/src/vue/CookieDrawer.vue +203 -0
- package/src/vue/components/index.js +2 -0
- package/src/vue/composables/useCookieConsent.js +182 -0
- package/src/vue/index.js +58 -0
- package/src/vue/index.test.js +40 -0
- package/src/vue/version.js +29 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cookie-consent" :data-cookie-kit-theme="theme" :style="themeVarsStyle">
|
|
3
|
+
<!-- Cookie Drawer (Unified Component) -->
|
|
4
|
+
<CookieDrawer
|
|
5
|
+
ref="drawerRef"
|
|
6
|
+
:is-visible="isVisible"
|
|
7
|
+
:is-settings-mode="isSettingsMode"
|
|
8
|
+
:current-tab="currentTab"
|
|
9
|
+
:categories="categories"
|
|
10
|
+
:config="config"
|
|
11
|
+
:consent-version="consentVersion"
|
|
12
|
+
:capabilities="capabilities"
|
|
13
|
+
:is-v2="isV2"
|
|
14
|
+
@close="handleClose"
|
|
15
|
+
@open-settings="handleOpenSettings"
|
|
16
|
+
@select-tab="handleSelectTab"
|
|
17
|
+
@toggle-category="handleToggleCategory"
|
|
18
|
+
@accept-selection="handleAcceptSelection"
|
|
19
|
+
@accept-all="handleAcceptAll"
|
|
20
|
+
@reject-all="handleRejectAll"
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
|
|
27
|
+
import { useCookieKit } from './index.js'
|
|
28
|
+
import CookieDrawer from './CookieDrawer.vue'
|
|
29
|
+
|
|
30
|
+
// Props for customization
|
|
31
|
+
const props = defineProps({
|
|
32
|
+
config: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: () => ({
|
|
35
|
+
categories: {
|
|
36
|
+
necessary: { required: true, enabled: true, name: 'Essential Cookies' },
|
|
37
|
+
analytics: { required: false, enabled: true, name: 'Analytics Cookies' },
|
|
38
|
+
marketing: { required: false, enabled: false, name: 'Marketing Cookies' }
|
|
39
|
+
},
|
|
40
|
+
consentExpireDays: 365,
|
|
41
|
+
version: '0.2.0',
|
|
42
|
+
theme: 'light'
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Use the new composable
|
|
48
|
+
const {
|
|
49
|
+
consent,
|
|
50
|
+
acceptAll,
|
|
51
|
+
rejectAll,
|
|
52
|
+
acceptCategory,
|
|
53
|
+
hasConsent,
|
|
54
|
+
hasCategoryConsent
|
|
55
|
+
} = useCookieKit(props.config)
|
|
56
|
+
|
|
57
|
+
// Local state
|
|
58
|
+
const isVisible = computed(() => !consent.value?.hasConsented)
|
|
59
|
+
const isSettingsMode = ref(false)
|
|
60
|
+
const currentTab = ref('privacy')
|
|
61
|
+
const categories = ref(props.config.categories)
|
|
62
|
+
const consentVersion = computed(() => props.config.version || '0.2.0')
|
|
63
|
+
const capabilities = computed(() => ({}))
|
|
64
|
+
const isV2 = computed(() => true)
|
|
65
|
+
const theme = computed(() => props.config.theme || 'light')
|
|
66
|
+
const themeVarsStyle = computed(() => {
|
|
67
|
+
const vars = props.config.themeVars || {}
|
|
68
|
+
return Object.fromEntries(
|
|
69
|
+
Object.entries(vars).map(([key, value]) => [key.startsWith('--') ? key : `--${key}`, value])
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
const drawerRef = ref(null)
|
|
73
|
+
|
|
74
|
+
const enabledSettingsTabs = computed(() => {
|
|
75
|
+
return Object.keys(props.config.categories || {}).filter(categoryId => {
|
|
76
|
+
return props.config.categories[categoryId]?.enabled
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const openSettings = () => {
|
|
81
|
+
isSettingsMode.value = true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const closeSettings = () => {
|
|
85
|
+
if (consent.value?.hasConsented) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
isSettingsMode.value = false
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const acceptSelection = () => {
|
|
92
|
+
// Save current category states
|
|
93
|
+
Object.keys(categories.value).forEach(id => {
|
|
94
|
+
const isEnabled = categories.value[id]?.enabled
|
|
95
|
+
if (isEnabled !== undefined) {
|
|
96
|
+
acceptCategory(id, isEnabled)
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const selectTab = (tabId) => {
|
|
102
|
+
currentTab.value = tabId
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const getDrawerDialogElement = () => {
|
|
106
|
+
const exposed = drawerRef.value?.dialogElement
|
|
107
|
+
const result = typeof exposed === 'function' ? exposed() : exposed?.value || exposed || null
|
|
108
|
+
return result
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Focus trap logic (simplified)
|
|
112
|
+
watch(isVisible, async (visible) => {
|
|
113
|
+
if (visible) {
|
|
114
|
+
await nextTick()
|
|
115
|
+
await new Promise(resolve => setTimeout(resolve, 50))
|
|
116
|
+
const dialogElement = getDrawerDialogElement()
|
|
117
|
+
if (dialogElement && props.config.debug) {
|
|
118
|
+
console.log('[CookieConsent] Focus trap activated')
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Event handlers
|
|
124
|
+
const handleAcceptAll = () => {
|
|
125
|
+
acceptAll()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const handleRejectAll = () => {
|
|
129
|
+
rejectAll()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const handleAcceptSelection = () => {
|
|
133
|
+
acceptSelection()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const handleOpenSettings = () => {
|
|
137
|
+
openSettings()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const handleSelectTab = (tabId) => {
|
|
141
|
+
selectTab(tabId)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const handleToggleCategory = (categoryId) => {
|
|
145
|
+
const current = categories.value[categoryId]
|
|
146
|
+
if (current) {
|
|
147
|
+
categories.value[categoryId] = { ...current, enabled: !current.enabled }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const handleClose = () => {
|
|
152
|
+
if (isSettingsMode.value) {
|
|
153
|
+
closeSettings()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Cleanup on unmount
|
|
158
|
+
onUnmounted(() => {
|
|
159
|
+
// Add cleanup if needed
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// Expose methods for external use
|
|
163
|
+
defineExpose({
|
|
164
|
+
acceptAll: handleAcceptAll,
|
|
165
|
+
rejectAll: handleRejectAll,
|
|
166
|
+
resetConsent: () => {
|
|
167
|
+
// Reset logic if needed
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<style lang="scss" scoped>
|
|
173
|
+
.cookie-consent {
|
|
174
|
+
position: relative;
|
|
175
|
+
|
|
176
|
+
&--has-modal {
|
|
177
|
+
min-height: 100vh;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
</style>
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Backdrop with opacity animation -->
|
|
3
|
+
<Transition name="backdrop-fade" appear>
|
|
4
|
+
<div
|
|
5
|
+
v-if="isVisible && !isEssentialBanner"
|
|
6
|
+
class="cookie-drawer__backdrop"
|
|
7
|
+
@click="handleBackdropClick"
|
|
8
|
+
></div>
|
|
9
|
+
</Transition>
|
|
10
|
+
|
|
11
|
+
<!-- Drawer with slide animation -->
|
|
12
|
+
<Transition :name="drawerAnimation" appear>
|
|
13
|
+
<div
|
|
14
|
+
v-if="isVisible"
|
|
15
|
+
:class="['cookie-drawer__content-wrapper', drawerClasses, rootClasses]"
|
|
16
|
+
>
|
|
17
|
+
<div ref="dialogRef" class="cookie-drawer__content" role="dialog" aria-modal="true" @click.stop>
|
|
18
|
+
<!-- Header -->
|
|
19
|
+
<div class="cookie-drawer__header">
|
|
20
|
+
<h3 class="cookie-drawer__title">
|
|
21
|
+
{{ isSettingsMode
|
|
22
|
+
? (config.texts?.settings?.title || 'Cookie Settings')
|
|
23
|
+
: (config.mode === 'essential'
|
|
24
|
+
? (config.texts?.essential?.title || 'Essential Cookies')
|
|
25
|
+
: (config.texts?.gdpr?.title || 'Cookie Consent'))
|
|
26
|
+
}}
|
|
27
|
+
</h3>
|
|
28
|
+
<!-- Message -->
|
|
29
|
+
<div v-if="!isSettingsMode && !isEssentialBanner" class="cookie-drawer__message">
|
|
30
|
+
{{ config.texts?.gdpr?.message || 'We use cookies to enhance your experience. By continuing to visit this site you agree to our use of cookies.' }}
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<button
|
|
34
|
+
v-if="isSettingsMode"
|
|
35
|
+
@click="handleClose"
|
|
36
|
+
class="cookie-drawer__close"
|
|
37
|
+
aria-label="Close"
|
|
38
|
+
>
|
|
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="#0D0B3D"/>
|
|
41
|
+
</svg>
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- Essential Banner -->
|
|
46
|
+
<div v-if="!isSettingsMode && isEssentialBanner" class="cookie-drawer__banner">
|
|
47
|
+
<div class="cookie-drawer__essential">
|
|
48
|
+
<div class="cookie-drawer__message cookie-drawer__message--essential">
|
|
49
|
+
{{ config.texts?.essential?.message || 'We use essential cookies to make our site work. By using our site, you accept our use of essential cookies.' }}
|
|
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>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- GDPR Banner/Settings -->
|
|
60
|
+
<div v-else class="cookie-drawer__body">
|
|
61
|
+
<!-- Banner Mode -->
|
|
62
|
+
<div v-if="!isSettingsMode" class="cookie-drawer__banner">
|
|
63
|
+
<div class="cookie-drawer__message">
|
|
64
|
+
{{ config.texts?.gdpr?.message || 'We use cookies to enhance your experience. By continuing to visit this site you agree to our use of cookies.' }}
|
|
65
|
+
</div>
|
|
66
|
+
<div class="cookie-drawer__actions">
|
|
67
|
+
<button @click="$emit('rejectAll')" class="cookie-drawer__button cookie-drawer__button--secondary">
|
|
68
|
+
{{ config.texts?.gdpr?.reject || 'Reject All' }}
|
|
69
|
+
</button>
|
|
70
|
+
<button @click="$emit('acceptAll')" class="cookie-drawer__button cookie-drawer__button--primary">
|
|
71
|
+
{{ config.texts?.gdpr?.accept || 'Accept All' }}
|
|
72
|
+
</button>
|
|
73
|
+
<button @click="$emit('openSettings')" class="cookie-drawer__button cookie-drawer__button--link">
|
|
74
|
+
{{ config.texts?.gdpr?.settings || 'Customize' }}
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Settings Mode -->
|
|
80
|
+
<div v-else class="cookie-drawer__settings">
|
|
81
|
+
<!-- Tabs -->
|
|
82
|
+
<div v-if="enabledSettingsTabs.length > 1" class="cookie-drawer__tabs">
|
|
83
|
+
<button
|
|
84
|
+
v-for="tab in enabledSettingsTabs"
|
|
85
|
+
:key="tab"
|
|
86
|
+
:class="['cookie-drawer__tab', { 'cookie-drawer__tab--active': currentTab === tab }]"
|
|
87
|
+
@click="$emit('selectTab', tab)"
|
|
88
|
+
>
|
|
89
|
+
{{ getCategoryTitle(tab) }}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<!-- Tab Content -->
|
|
94
|
+
<div class="cookie-drawer__tab-content">
|
|
95
|
+
<div v-if="currentTab === 'privacy'" class="cookie-drawer__privacy">
|
|
96
|
+
<h4>{{ config.texts?.settings?.privacy?.title || 'Privacy Policy' }}</h4>
|
|
97
|
+
<p>{{ config.texts?.settings?.privacy?.description || 'Learn about how we use cookies and protect your privacy.' }}</p>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div v-else class="cookie-drawer__category">
|
|
101
|
+
<div class="cookie-drawer__category-header">
|
|
102
|
+
<h4>{{ getCategoryTitle(currentTab) }}</h4>
|
|
103
|
+
<label class="cookie-drawer__toggle">
|
|
104
|
+
<input
|
|
105
|
+
type="checkbox"
|
|
106
|
+
:checked="categories[currentTab]?.enabled"
|
|
107
|
+
:disabled="categories[currentTab]?.required"
|
|
108
|
+
@change="$emit('toggleCategory', currentTab)"
|
|
109
|
+
/>
|
|
110
|
+
<span class="cookie-drawer__toggle-slider"></span>
|
|
111
|
+
</label>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="cookie-drawer__category-description">
|
|
114
|
+
{{ getCategoryDescription(currentTab) }}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- Settings Actions -->
|
|
120
|
+
<div class="cookie-drawer__settings-actions">
|
|
121
|
+
<button @click="$emit('acceptSelection')" class="cookie-drawer__button cookie-drawer__button--primary">
|
|
122
|
+
{{ config.texts?.settings?.save || 'Save Preferences' }}
|
|
123
|
+
</button>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</Transition>
|
|
130
|
+
</template>
|
|
131
|
+
|
|
132
|
+
<script setup>
|
|
133
|
+
import { computed, ref } from 'vue'
|
|
134
|
+
|
|
135
|
+
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
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const emit = defineEmits([
|
|
147
|
+
'close',
|
|
148
|
+
'openSettings',
|
|
149
|
+
'selectTab',
|
|
150
|
+
'toggleCategory',
|
|
151
|
+
'acceptSelection',
|
|
152
|
+
'acceptAll',
|
|
153
|
+
'rejectAll'
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
const dialogRef = ref(null)
|
|
157
|
+
|
|
158
|
+
const isEssentialBanner = computed(() => props.config.mode === 'essential')
|
|
159
|
+
|
|
160
|
+
const drawerClasses = computed(() => ({
|
|
161
|
+
'cookie-drawer__content-wrapper--settings': props.isSettingsMode,
|
|
162
|
+
'cookie-drawer__content-wrapper--banner': !props.isSettingsMode
|
|
163
|
+
}))
|
|
164
|
+
|
|
165
|
+
const rootClasses = computed(() => ({
|
|
166
|
+
'cookie-drawer__content-wrapper--bottom': true
|
|
167
|
+
}))
|
|
168
|
+
|
|
169
|
+
const drawerAnimation = computed(() =>
|
|
170
|
+
props.isSettingsMode ? 'drawer-slide-up' : 'banner-slide-up'
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
const enabledSettingsTabs = computed(() => {
|
|
174
|
+
return ['privacy', ...Object.keys(props.categories || {}).filter(id =>
|
|
175
|
+
props.categories[id]?.enabled
|
|
176
|
+
)]
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const getCategoryTitle = (categoryId) => {
|
|
180
|
+
return props.categories[categoryId]?.name || categoryId
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const getCategoryDescription = (categoryId) => {
|
|
184
|
+
return props.categories[categoryId]?.description || `Description for ${categoryId} cookies.`
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const handleBackdropClick = () => {
|
|
188
|
+
if (!props.isSettingsMode) {
|
|
189
|
+
emit('close')
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const handleClose = () => {
|
|
194
|
+
emit('close')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Expose dialog element for focus trap
|
|
198
|
+
defineExpose({
|
|
199
|
+
dialogElement: () => dialogRef.value
|
|
200
|
+
})
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<style src="@el7ven/cookie-consent/dist/styles"></style>
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
2
|
+
|
|
3
|
+
export function useCookieConsent(config = {}) {
|
|
4
|
+
const isVisible = ref(false)
|
|
5
|
+
const isSettingsMode = ref(false)
|
|
6
|
+
const currentTab = ref('categories')
|
|
7
|
+
const consentVersion = ref('1.0.0')
|
|
8
|
+
|
|
9
|
+
const categories = ref({
|
|
10
|
+
necessary: {
|
|
11
|
+
required: true,
|
|
12
|
+
enabled: true,
|
|
13
|
+
name: 'Essential Cookies',
|
|
14
|
+
description: 'These cookies are necessary for the website to function and cannot be switched off.'
|
|
15
|
+
},
|
|
16
|
+
analytics: {
|
|
17
|
+
required: false,
|
|
18
|
+
enabled: false,
|
|
19
|
+
name: 'Analytics Cookies',
|
|
20
|
+
description: 'These cookies help us understand how visitors interact with our website.'
|
|
21
|
+
},
|
|
22
|
+
marketing: {
|
|
23
|
+
required: false,
|
|
24
|
+
enabled: false,
|
|
25
|
+
name: 'Marketing Cookies',
|
|
26
|
+
description: 'These cookies are used to track visitors across websites to show relevant ads.'
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const capabilities = ref({
|
|
31
|
+
hasConsented: false,
|
|
32
|
+
canShowSettings: true,
|
|
33
|
+
canAcceptAll: true,
|
|
34
|
+
canRejectAll: true,
|
|
35
|
+
canCustomize: true
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const isV2 = ref(true)
|
|
39
|
+
|
|
40
|
+
const hasConsent = computed(() => {
|
|
41
|
+
return localStorage.getItem('cookie-consent') !== null
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const consentData = computed(() => {
|
|
45
|
+
const stored = localStorage.getItem('cookie-consent')
|
|
46
|
+
return stored ? JSON.parse(stored) : null
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const showConsent = () => {
|
|
50
|
+
isVisible.value = true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const hideConsent = () => {
|
|
54
|
+
isVisible.value = false
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const openSettings = () => {
|
|
58
|
+
isSettingsMode.value = true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const closeSettings = () => {
|
|
62
|
+
isSettingsMode.value = false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const selectTab = (tab) => {
|
|
66
|
+
currentTab.value = tab
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const toggleCategory = (categoryName, enabled) => {
|
|
70
|
+
if (categories.value[categoryName] && !categories.value[categoryName].required) {
|
|
71
|
+
categories.value[categoryName].enabled = enabled
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const acceptSelection = () => {
|
|
76
|
+
const consent = {
|
|
77
|
+
version: consentVersion.value,
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
categories: Object.keys(categories.value).reduce((acc, key) => {
|
|
80
|
+
acc[key] = categories.value[key].enabled
|
|
81
|
+
return acc
|
|
82
|
+
}, {})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
localStorage.setItem('cookie-consent', JSON.stringify(consent))
|
|
86
|
+
capabilities.value.hasConsented = true
|
|
87
|
+
hideConsent()
|
|
88
|
+
closeSettings()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const acceptAll = () => {
|
|
92
|
+
Object.keys(categories.value).forEach(key => {
|
|
93
|
+
categories.value[key].enabled = true
|
|
94
|
+
})
|
|
95
|
+
acceptSelection()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const rejectAll = () => {
|
|
99
|
+
Object.keys(categories.value).forEach(key => {
|
|
100
|
+
if (!categories.value[key].required) {
|
|
101
|
+
categories.value[key].enabled = false
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
acceptSelection()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const resetConsent = () => {
|
|
108
|
+
localStorage.removeItem('cookie-consent')
|
|
109
|
+
capabilities.value.hasConsented = false
|
|
110
|
+
Object.keys(categories.value).forEach(key => {
|
|
111
|
+
if (!categories.value[key].required) {
|
|
112
|
+
categories.value[key].enabled = false
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const handleClose = () => {
|
|
118
|
+
hideConsent()
|
|
119
|
+
closeSettings()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleOpenSettings = () => {
|
|
123
|
+
openSettings()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const handleSelectTab = (tab) => {
|
|
127
|
+
selectTab(tab)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const handleToggleCategory = (categoryName, enabled) => {
|
|
131
|
+
toggleCategory(categoryName, enabled)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const handleAcceptSelection = () => {
|
|
135
|
+
acceptSelection()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const handleAcceptAll = () => {
|
|
139
|
+
acceptAll()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const handleRejectAll = () => {
|
|
143
|
+
rejectAll()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
onMounted(() => {
|
|
147
|
+
if (!hasConsent.value) {
|
|
148
|
+
showConsent()
|
|
149
|
+
} else {
|
|
150
|
+
capabilities.value.hasConsented = true
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
isVisible,
|
|
156
|
+
isSettingsMode,
|
|
157
|
+
currentTab,
|
|
158
|
+
categories,
|
|
159
|
+
capabilities,
|
|
160
|
+
isV2,
|
|
161
|
+
consentVersion,
|
|
162
|
+
hasConsent,
|
|
163
|
+
consentData,
|
|
164
|
+
showConsent,
|
|
165
|
+
hideConsent,
|
|
166
|
+
openSettings,
|
|
167
|
+
closeSettings,
|
|
168
|
+
selectTab,
|
|
169
|
+
toggleCategory,
|
|
170
|
+
acceptSelection,
|
|
171
|
+
acceptAll,
|
|
172
|
+
rejectAll,
|
|
173
|
+
resetConsent,
|
|
174
|
+
handleClose,
|
|
175
|
+
handleOpenSettings,
|
|
176
|
+
handleSelectTab,
|
|
177
|
+
handleToggleCategory,
|
|
178
|
+
handleAcceptSelection,
|
|
179
|
+
handleAcceptAll,
|
|
180
|
+
handleRejectAll
|
|
181
|
+
}
|
|
182
|
+
}
|
package/src/vue/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
|
+
import { createCookieKit, DEFAULT_CONFIG } from '../core/index.js'
|
|
3
|
+
import { VERSION, getVersion, getVersionInfo, logVersion } from './version.js'
|
|
4
|
+
|
|
5
|
+
export { createCookieKit, DEFAULT_CONFIG, VERSION, getVersion, getVersionInfo, logVersion }
|
|
6
|
+
|
|
7
|
+
export function useCookieKitVue(userConfig = {}) {
|
|
8
|
+
const core = createCookieKit(userConfig)
|
|
9
|
+
|
|
10
|
+
const consent = ref(core.getConsent())
|
|
11
|
+
const categories = ref(consent.value?.categories || core.getDefaultCategoriesState())
|
|
12
|
+
|
|
13
|
+
core.on('consentChanged', ({ consent: nextConsent, categories: nextCategories }) => {
|
|
14
|
+
consent.value = nextConsent
|
|
15
|
+
categories.value = { ...nextCategories }
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const hasConsented = computed(() => consent.value?.hasConsented === true)
|
|
19
|
+
|
|
20
|
+
const acceptAll = async () => {
|
|
21
|
+
const next = await core.acceptAll('banner')
|
|
22
|
+
consent.value = next
|
|
23
|
+
categories.value = { ...next.categories }
|
|
24
|
+
return next
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rejectAll = async () => {
|
|
28
|
+
const next = await core.rejectAll('banner')
|
|
29
|
+
consent.value = next
|
|
30
|
+
categories.value = { ...next.categories }
|
|
31
|
+
return next
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const acceptSelected = async (selectedIds) => {
|
|
35
|
+
const next = await core.acceptSelected(selectedIds, 'settings')
|
|
36
|
+
consent.value = next
|
|
37
|
+
categories.value = { ...next.categories }
|
|
38
|
+
return next
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const resetConsent = () => {
|
|
42
|
+
core.clearConsent()
|
|
43
|
+
consent.value = null
|
|
44
|
+
categories.value = core.getDefaultCategoriesState()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
core,
|
|
49
|
+
consent,
|
|
50
|
+
categories,
|
|
51
|
+
hasConsented,
|
|
52
|
+
acceptAll,
|
|
53
|
+
rejectAll,
|
|
54
|
+
acceptSelected,
|
|
55
|
+
resetConsent,
|
|
56
|
+
hasCategoryConsent: category => core.hasCategoryConsent(category)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import test from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { useCookieKitVue } from './index.js'
|
|
5
|
+
|
|
6
|
+
function createMemoryStorageAdapter() {
|
|
7
|
+
const memory = new Map()
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
getItem: key => memory.get(key) ?? null,
|
|
11
|
+
setItem: (key, value) => memory.set(key, value),
|
|
12
|
+
removeItem: key => memory.delete(key),
|
|
13
|
+
defaultKey: 'cookie_consent'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('vue adapter: composable state updates after actions', async () => {
|
|
18
|
+
const kit = useCookieKitVue({
|
|
19
|
+
storageAdapter: createMemoryStorageAdapter(),
|
|
20
|
+
categories: {
|
|
21
|
+
necessary: { required: true, enabled: true },
|
|
22
|
+
analytics: { required: false, enabled: true }
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
assert.equal(kit.hasConsented.value, false)
|
|
27
|
+
|
|
28
|
+
await kit.acceptAll()
|
|
29
|
+
|
|
30
|
+
assert.equal(kit.hasConsented.value, true)
|
|
31
|
+
assert.equal(kit.consent.value?.categories?.necessary, true)
|
|
32
|
+
assert.equal(kit.consent.value?.categories?.analytics, true)
|
|
33
|
+
|
|
34
|
+
kit.resetConsent()
|
|
35
|
+
|
|
36
|
+
assert.equal(kit.hasConsented.value, false)
|
|
37
|
+
assert.equal(kit.consent.value, null)
|
|
38
|
+
assert.equal(kit.categories.value.necessary, true)
|
|
39
|
+
assert.equal(kit.categories.value.analytics, false)
|
|
40
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version metadata for package runtime.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const VERSION = '1.0.0-beta.0'
|
|
6
|
+
export const PACKAGE_NAME = '@el7ven/cookie-consent/vue'
|
|
7
|
+
|
|
8
|
+
// Vue adapter version metadata.
|
|
9
|
+
export const VERSION_INFO = {
|
|
10
|
+
version: VERSION,
|
|
11
|
+
name: PACKAGE_NAME,
|
|
12
|
+
buildDate: new Date().toISOString(),
|
|
13
|
+
environment: 'production'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Return package version.
|
|
17
|
+
export function getVersion() {
|
|
18
|
+
return VERSION
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Return full version metadata.
|
|
22
|
+
export function getVersionInfo() {
|
|
23
|
+
return VERSION_INFO
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Print runtime version in debug-friendly format.
|
|
27
|
+
export function logVersion() {
|
|
28
|
+
console.log(`💚 ${PACKAGE_NAME} v${VERSION}`)
|
|
29
|
+
}
|