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>
200 lines
4.8 KiB
TypeScript
200 lines
4.8 KiB
TypeScript
/**
|
|
* React Integration fuer @breakpilot/consent-sdk
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* import { ConsentProvider, useConsent, ConsentBanner } from '@breakpilot/consent-sdk/react';
|
|
*
|
|
* function App() {
|
|
* return (
|
|
* <ConsentProvider config={config}>
|
|
* <ConsentBanner />
|
|
* <MainContent />
|
|
* </ConsentProvider>
|
|
* );
|
|
* }
|
|
* ```
|
|
*
|
|
* Phase 4 refactor: provider stays here; hooks + components live in sibling
|
|
* files. Context definition is in ./context so hooks and provider can share it
|
|
* without circular imports.
|
|
*/
|
|
|
|
import {
|
|
useEffect,
|
|
useState,
|
|
useCallback,
|
|
useMemo,
|
|
type FC,
|
|
type ReactNode,
|
|
} from 'react';
|
|
import { ConsentManager } from '../core/ConsentManager';
|
|
import type {
|
|
ConsentCategories,
|
|
ConsentCategory,
|
|
ConsentConfig,
|
|
ConsentState,
|
|
} from '../types';
|
|
import { ConsentContext, type ConsentContextValue } from './context';
|
|
import { useConsent, useConsentManager } from './hooks';
|
|
import {
|
|
ConsentBanner,
|
|
ConsentGate,
|
|
ConsentPlaceholder,
|
|
type ConsentBannerRenderProps,
|
|
} from './components';
|
|
|
|
// =============================================================================
|
|
// Provider
|
|
// =============================================================================
|
|
|
|
interface ConsentProviderProps {
|
|
/** SDK-Konfiguration */
|
|
config: ConsentConfig;
|
|
/** Kinder-Komponenten */
|
|
children: ReactNode;
|
|
}
|
|
|
|
/**
|
|
* ConsentProvider - Stellt Consent-Kontext bereit.
|
|
*/
|
|
export const ConsentProvider: FC<ConsentProviderProps> = ({
|
|
config,
|
|
children,
|
|
}) => {
|
|
const [manager, setManager] = useState<ConsentManager | null>(null);
|
|
const [consent, setConsent] = useState<ConsentState | null>(null);
|
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isBannerVisible, setIsBannerVisible] = useState(false);
|
|
|
|
// Manager erstellen und initialisieren
|
|
useEffect(() => {
|
|
const consentManager = new ConsentManager(config);
|
|
setManager(consentManager);
|
|
|
|
// Events abonnieren
|
|
const unsubChange = consentManager.on('change', (newConsent) => {
|
|
setConsent(newConsent);
|
|
});
|
|
|
|
const unsubBannerShow = consentManager.on('banner_show', () => {
|
|
setIsBannerVisible(true);
|
|
});
|
|
|
|
const unsubBannerHide = consentManager.on('banner_hide', () => {
|
|
setIsBannerVisible(false);
|
|
});
|
|
|
|
// Initialisieren
|
|
consentManager
|
|
.init()
|
|
.then(() => {
|
|
setConsent(consentManager.getConsent());
|
|
setIsInitialized(true);
|
|
setIsLoading(false);
|
|
setIsBannerVisible(consentManager.isBannerVisible());
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to initialize ConsentManager:', error);
|
|
setIsLoading(false);
|
|
});
|
|
|
|
// Cleanup
|
|
return () => {
|
|
unsubChange();
|
|
unsubBannerShow();
|
|
unsubBannerHide();
|
|
};
|
|
}, [config]);
|
|
|
|
// Callback-Funktionen
|
|
const hasConsent = useCallback(
|
|
(category: ConsentCategory): boolean => {
|
|
return manager?.hasConsent(category) ?? category === 'essential';
|
|
},
|
|
[manager]
|
|
);
|
|
|
|
const acceptAll = useCallback(async () => {
|
|
await manager?.acceptAll();
|
|
}, [manager]);
|
|
|
|
const rejectAll = useCallback(async () => {
|
|
await manager?.rejectAll();
|
|
}, [manager]);
|
|
|
|
const saveSelection = useCallback(
|
|
async (categories: Partial<ConsentCategories>) => {
|
|
await manager?.setConsent(categories);
|
|
manager?.hideBanner();
|
|
},
|
|
[manager]
|
|
);
|
|
|
|
const showBanner = useCallback(() => {
|
|
manager?.showBanner();
|
|
}, [manager]);
|
|
|
|
const hideBanner = useCallback(() => {
|
|
manager?.hideBanner();
|
|
}, [manager]);
|
|
|
|
const showSettings = useCallback(() => {
|
|
manager?.showSettings();
|
|
}, [manager]);
|
|
|
|
const needsConsent = useMemo(() => {
|
|
return manager?.needsConsent() ?? true;
|
|
}, [manager, consent]);
|
|
|
|
// Context-Wert
|
|
const contextValue = useMemo<ConsentContextValue>(
|
|
() => ({
|
|
manager,
|
|
consent,
|
|
isInitialized,
|
|
isLoading,
|
|
isBannerVisible,
|
|
needsConsent,
|
|
hasConsent,
|
|
acceptAll,
|
|
rejectAll,
|
|
saveSelection,
|
|
showBanner,
|
|
hideBanner,
|
|
showSettings,
|
|
}),
|
|
[
|
|
manager,
|
|
consent,
|
|
isInitialized,
|
|
isLoading,
|
|
isBannerVisible,
|
|
needsConsent,
|
|
hasConsent,
|
|
acceptAll,
|
|
rejectAll,
|
|
saveSelection,
|
|
showBanner,
|
|
hideBanner,
|
|
showSettings,
|
|
]
|
|
);
|
|
|
|
return (
|
|
<ConsentContext.Provider value={contextValue}>
|
|
{children}
|
|
</ConsentContext.Provider>
|
|
);
|
|
};
|
|
|
|
// =============================================================================
|
|
// Re-exports for the public @breakpilot/consent-sdk/react entrypoint
|
|
// =============================================================================
|
|
|
|
export { useConsent, useConsentManager };
|
|
export { ConsentBanner, ConsentGate, ConsentPlaceholder };
|
|
export { ConsentContext };
|
|
export type { ConsentContextValue, ConsentBannerRenderProps };
|