4ed39d2616
Phase 4: extract config defaults, Google Consent Mode helper, and framework adapter internals into sibling files so every source file is under the hard cap. Public API surface preserved; all 135 tests green, tsup build + tsc typecheck clean. - core/ConsentManager 525 -> 467 LOC (extract config + google helpers) - react/index 511 LOC -> 199 LOC barrel + components/hooks/context - vue/index 511 LOC -> 32 LOC barrel + components/composables/context/plugin - angular/index 509 LOC -> 45 LOC barrel + interface/service/module/templates Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
192 lines
4.6 KiB
TypeScript
192 lines
4.6 KiB
TypeScript
/**
|
|
* Vue consent components: ConsentProvider, ConsentGate, ConsentPlaceholder,
|
|
* ConsentBanner.
|
|
*
|
|
* Phase 4: extracted from vue/index.ts.
|
|
*/
|
|
|
|
import { computed, defineComponent, h, type PropType } from 'vue';
|
|
import type { ConsentCategory, ConsentConfig } from '../types';
|
|
import { provideConsent, useConsent } from './composables';
|
|
|
|
/**
|
|
* ConsentProvider - Wrapper-Komponente.
|
|
*/
|
|
export const ConsentProvider = defineComponent({
|
|
name: 'ConsentProvider',
|
|
props: {
|
|
config: {
|
|
type: Object as PropType<ConsentConfig>,
|
|
required: true,
|
|
},
|
|
},
|
|
setup(props, { slots }) {
|
|
provideConsent(props.config);
|
|
return () => slots.default?.();
|
|
},
|
|
});
|
|
|
|
/**
|
|
* ConsentGate - Zeigt Inhalt nur bei Consent.
|
|
*/
|
|
export const ConsentGate = defineComponent({
|
|
name: 'ConsentGate',
|
|
props: {
|
|
category: {
|
|
type: String as PropType<ConsentCategory>,
|
|
required: true,
|
|
},
|
|
},
|
|
setup(props, { slots }) {
|
|
const { hasConsent, isLoading } = useConsent();
|
|
|
|
return () => {
|
|
if (isLoading.value) {
|
|
return slots.fallback?.() ?? null;
|
|
}
|
|
if (!hasConsent(props.category)) {
|
|
return slots.placeholder?.() ?? null;
|
|
}
|
|
return slots.default?.();
|
|
};
|
|
},
|
|
});
|
|
|
|
/**
|
|
* ConsentPlaceholder - Placeholder fuer blockierten Inhalt.
|
|
*/
|
|
export const ConsentPlaceholder = defineComponent({
|
|
name: 'ConsentPlaceholder',
|
|
props: {
|
|
category: {
|
|
type: String as PropType<ConsentCategory>,
|
|
required: true,
|
|
},
|
|
message: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
buttonText: {
|
|
type: String,
|
|
default: 'Cookie-Einstellungen öffnen',
|
|
},
|
|
},
|
|
setup(props) {
|
|
const { showSettings } = useConsent();
|
|
|
|
const categoryNames: Record<ConsentCategory, string> = {
|
|
essential: 'Essentielle Cookies',
|
|
functional: 'Funktionale Cookies',
|
|
analytics: 'Statistik-Cookies',
|
|
marketing: 'Marketing-Cookies',
|
|
social: 'Social Media-Cookies',
|
|
};
|
|
|
|
const displayMessage = computed(() => {
|
|
return (
|
|
props.message ||
|
|
`Dieser Inhalt erfordert ${categoryNames[props.category]}.`
|
|
);
|
|
});
|
|
|
|
return () =>
|
|
h('div', { class: 'bp-consent-placeholder' }, [
|
|
h('p', displayMessage.value),
|
|
h(
|
|
'button',
|
|
{
|
|
type: 'button',
|
|
onClick: showSettings,
|
|
},
|
|
props.buttonText
|
|
),
|
|
]);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* ConsentBanner - Cookie-Banner Komponente (headless with default UI).
|
|
*/
|
|
export const ConsentBanner = defineComponent({
|
|
name: 'ConsentBanner',
|
|
setup(_, { slots }) {
|
|
const {
|
|
consent,
|
|
isBannerVisible,
|
|
needsConsent,
|
|
acceptAll,
|
|
rejectAll,
|
|
saveSelection,
|
|
showSettings,
|
|
hideBanner,
|
|
} = useConsent();
|
|
|
|
const slotProps = computed(() => ({
|
|
isVisible: isBannerVisible.value,
|
|
consent: consent.value,
|
|
needsConsent: needsConsent.value,
|
|
onAcceptAll: acceptAll,
|
|
onRejectAll: rejectAll,
|
|
onSaveSelection: saveSelection,
|
|
onShowSettings: showSettings,
|
|
onClose: hideBanner,
|
|
}));
|
|
|
|
return () => {
|
|
if (slots.default) {
|
|
return slots.default(slotProps.value);
|
|
}
|
|
if (!isBannerVisible.value) {
|
|
return null;
|
|
}
|
|
return h(
|
|
'div',
|
|
{
|
|
class: 'bp-consent-banner',
|
|
role: 'dialog',
|
|
'aria-modal': 'true',
|
|
'aria-label': 'Cookie-Einstellungen',
|
|
},
|
|
[
|
|
h('div', { class: 'bp-consent-banner-content' }, [
|
|
h('h2', 'Datenschutzeinstellungen'),
|
|
h(
|
|
'p',
|
|
'Wir nutzen Cookies und ähnliche Technologien, um Ihnen ein optimales Nutzererlebnis zu bieten.'
|
|
),
|
|
h('div', { class: 'bp-consent-banner-actions' }, [
|
|
h(
|
|
'button',
|
|
{
|
|
type: 'button',
|
|
class: 'bp-consent-btn bp-consent-btn-reject',
|
|
onClick: rejectAll,
|
|
},
|
|
'Alle ablehnen'
|
|
),
|
|
h(
|
|
'button',
|
|
{
|
|
type: 'button',
|
|
class: 'bp-consent-btn bp-consent-btn-settings',
|
|
onClick: showSettings,
|
|
},
|
|
'Einstellungen'
|
|
),
|
|
h(
|
|
'button',
|
|
{
|
|
type: 'button',
|
|
class: 'bp-consent-btn bp-consent-btn-accept',
|
|
onClick: acceptAll,
|
|
},
|
|
'Alle akzeptieren'
|
|
),
|
|
]),
|
|
]),
|
|
]
|
|
);
|
|
};
|
|
},
|
|
});
|