Files
kandev/apps/web/components/settings/settings-target-provider.tsx
T
2026-08-05 14:07:52 +01:00

69 lines
2.4 KiB
TypeScript

"use client";
import { createContext, useCallback, useContext, useEffect, useRef, type ReactNode } from "react";
import { LOCATION_CHANGE_EVENT } from "@/lib/routing/navigation-event";
import {
SETTINGS_TARGET_REQUEST_EVENT,
createSettingsTargetRegistry,
revealSettingsTarget,
settingsTargetFromHash,
type SettingsTargetRegistry,
type SettingsTargetRequestDetail,
} from "@/lib/settings-discovery/target";
const SettingsTargetContext = createContext<SettingsTargetRegistry | null>(null);
export function SettingsTargetProvider({
children,
revealTarget = revealSettingsTarget,
}: {
children: ReactNode;
revealTarget?: (element: HTMLElement) => void;
}) {
const registryRef = useRef<SettingsTargetRegistry | null>(null);
if (!registryRef.current) registryRef.current = createSettingsTargetRegistry(revealTarget);
const registry = registryRef.current;
useEffect(() => {
const requestHashTarget = () => {
const targetId = settingsTargetFromHash(window.location.hash);
if (targetId) registry.request(targetId);
};
const requestExplicitTarget = (event: Event) => {
const targetId = (event as CustomEvent<SettingsTargetRequestDetail>).detail?.targetId;
if (targetId) registry.request(targetId);
};
window.addEventListener(LOCATION_CHANGE_EVENT, requestHashTarget);
window.addEventListener("hashchange", requestHashTarget);
window.addEventListener("popstate", requestHashTarget);
window.addEventListener(SETTINGS_TARGET_REQUEST_EVENT, requestExplicitTarget);
requestHashTarget();
return () => {
window.removeEventListener(LOCATION_CHANGE_EVENT, requestHashTarget);
window.removeEventListener("hashchange", requestHashTarget);
window.removeEventListener("popstate", requestHashTarget);
window.removeEventListener(SETTINGS_TARGET_REQUEST_EVENT, requestExplicitTarget);
};
}, [registry]);
return (
<SettingsTargetContext.Provider value={registry}>{children}</SettingsTargetContext.Provider>
);
}
export function useSettingsTargetRegistration(targetId?: string) {
const registry = useContext(SettingsTargetContext);
const unregisterRef = useRef<(() => void) | null>(null);
return useCallback(
(element: HTMLElement | null) => {
unregisterRef.current?.();
unregisterRef.current =
element && targetId && registry ? registry.register(targetId, element) : null;
},
[registry, targetId],
);
}