Files
breakpilot-compliance/consent-sdk/src/react/index.tsx
T
Sharang Parnerkar 4ed39d2616 refactor(consent-sdk): split ConsentManager + framework adapters under 500 LOC
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>
2026-04-11 22:25:44 +02:00

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 };