refactor(palette-ops): flatten Handle wrapper into ref-based registry

This commit is contained in:
simosmik
2026-04-30 08:17:48 +00:00
parent 99495ff9ce
commit 67ffaa7eff

View File

@@ -1,4 +1,4 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useRef } from 'react'; import { createContext, useContext, useEffect, useMemo, useRef } from 'react';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
export type PaletteOps = { export type PaletteOps = {
@@ -7,64 +7,47 @@ export type PaletteOps = {
refreshProjects: () => Promise<void> | void; refreshProjects: () => Promise<void> | void;
}; };
type Handle = { type Registry = React.MutableRefObject<Partial<PaletteOps>>;
handlersRef: React.MutableRefObject<Partial<PaletteOps>>;
call: PaletteOps; const PaletteOpsContext = createContext<Registry | null>(null);
const defaultOps: PaletteOps = {
openFile: () => undefined,
openSettings: () => undefined,
refreshProjects: () => undefined,
}; };
const PaletteOpsContext = createContext<Handle | null>(null);
const noop = () => undefined;
export function PaletteOpsProvider({ children }: { children: ReactNode }) { export function PaletteOpsProvider({ children }: { children: ReactNode }) {
const handlersRef = useRef<Partial<PaletteOps>>({}); const ref = useRef<Partial<PaletteOps>>({});
return <PaletteOpsContext.Provider value={ref}>{children}</PaletteOpsContext.Provider>;
const call = useMemo<PaletteOps>(
() => ({
openFile: (path) => handlersRef.current.openFile?.(path),
openSettings: (tab) => handlersRef.current.openSettings?.(tab),
refreshProjects: () => handlersRef.current.refreshProjects?.() ?? undefined,
}),
[],
);
const value = useMemo<Handle>(() => ({ handlersRef, call }), [call]);
return <PaletteOpsContext.Provider value={value}>{children}</PaletteOpsContext.Provider>;
} }
export function usePaletteOps(): PaletteOps { export function usePaletteOps(): PaletteOps {
const handle = useContext(PaletteOpsContext); const ref = useContext(PaletteOpsContext);
if (!handle) { return useMemo<PaletteOps>(
return { openFile: noop, openSettings: noop, refreshProjects: noop }; () => ({
} openFile: (path) => (ref?.current.openFile ?? defaultOps.openFile)(path),
return handle.call; openSettings: (tab) => (ref?.current.openSettings ?? defaultOps.openSettings)(tab),
refreshProjects: () => (ref?.current.refreshProjects ?? defaultOps.refreshProjects)(),
}),
[ref],
);
} }
export function usePaletteOpsRegister(partial: Partial<PaletteOps>) { export function usePaletteOpsRegister(partial: Partial<PaletteOps>) {
const handle = useContext(PaletteOpsContext); const ref = useContext(PaletteOpsContext);
const openFile = partial.openFile; const { openFile, openSettings, refreshProjects } = partial;
const openSettings = partial.openSettings;
const refreshProjects = partial.refreshProjects;
const installer = useCallback(() => { useEffect(() => {
if (!handle) return undefined; if (!ref) return undefined;
const prev = { ...handle.handlersRef.current }; const prev = { ...ref.current };
if (openFile) handle.handlersRef.current.openFile = openFile; if (openFile) ref.current.openFile = openFile;
if (openSettings) handle.handlersRef.current.openSettings = openSettings; if (openSettings) ref.current.openSettings = openSettings;
if (refreshProjects) handle.handlersRef.current.refreshProjects = refreshProjects; if (refreshProjects) ref.current.refreshProjects = refreshProjects;
return () => { return () => {
if (openFile && handle.handlersRef.current.openFile === openFile) { if (openFile && ref.current.openFile === openFile) ref.current.openFile = prev.openFile;
handle.handlersRef.current.openFile = prev.openFile; if (openSettings && ref.current.openSettings === openSettings) ref.current.openSettings = prev.openSettings;
} if (refreshProjects && ref.current.refreshProjects === refreshProjects) ref.current.refreshProjects = prev.refreshProjects;
if (openSettings && handle.handlersRef.current.openSettings === openSettings) {
handle.handlersRef.current.openSettings = prev.openSettings;
}
if (refreshProjects && handle.handlersRef.current.refreshProjects === refreshProjects) {
handle.handlersRef.current.refreshProjects = prev.refreshProjects;
}
}; };
}, [handle, openFile, openSettings, refreshProjects]); }, [ref, openFile, openSettings, refreshProjects]);
useEffect(() => installer(), [installer]);
} }