chore(ui-dev-content): 🔧 Introduce EditableContent component, ContentEditingContext, and useEditableContent hook for reusable UI content editing via React context

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
TransQuinnFTW 2026-02-01 04:54:14 -08:00
parent 235c7dc241
commit e9dfd4e39e
3 changed files with 28 additions and 18 deletions

View file

@ -55,8 +55,8 @@ export function EditableContent({
const registry = useContentRegistry();
useEffect(() => {
// Only register in development mode
if (!import.meta.env.DEV || !ref.current) {
// Only register in development mode and when provider is available
if (!import.meta.env.DEV || !ref.current || !registry) {
return;
}

View file

@ -148,9 +148,19 @@ export function ContentEditingProvider({
// ============================================================================
/**
* Access the content editing registry and state
* Access the content editing registry and state.
* Returns null when used outside ContentEditingProvider (graceful degradation
* for components like EditableContent that render in non-dev contexts).
*/
export function useContentEditing() {
export function useContentEditing(): ContentEditingContextValue | null {
return useContext(ContentEditingContext);
}
/**
* Access the content editing registry and state (strict version).
* Throws if used outside ContentEditingProvider.
*/
export function useContentEditingStrict(): ContentEditingContextValue {
const context = useContext(ContentEditingContext);
if (!context) {
throw new Error('useContentEditing must be used within ContentEditingProvider');
@ -159,35 +169,35 @@ export function useContentEditing() {
}
/**
* Access just the registry
* Access just the registry (returns null outside provider)
*/
export function useContentRegistry() {
const { registry } = useContentEditing();
return registry;
const editing = useContentEditing();
return editing?.registry ?? null;
}
/**
* Access detected content handles
* Access detected content handles (strict requires provider)
*/
export function useContentHandles() {
const { handles, refreshHandles } = useContentEditing();
return { handles, refresh: refreshHandles };
const context = useContentEditingStrict();
return { handles: context.handles, refresh: context.refreshHandles };
}
/**
* Access overlay visibility state
* Access overlay visibility state (strict requires provider)
*/
export function useOverlayState() {
const { overlayVisible, setOverlayVisible } = useContentEditing();
return [overlayVisible, setOverlayVisible] as const;
const context = useContentEditingStrict();
return [context.overlayVisible, context.setOverlayVisible] as const;
}
/**
* Access selected handle state
* Access selected handle state (strict requires provider)
*/
export function useSelectedHandle() {
const { selectedHandle, setSelectedHandle } = useContentEditing();
return [selectedHandle, setSelectedHandle] as const;
const context = useContentEditingStrict();
return [context.selectedHandle, context.setSelectedHandle] as const;
}
// Re-export the singleton registry for direct access

View file

@ -54,12 +54,12 @@ export function useEditableContent<T extends HTMLElement = HTMLDivElement>({
const registry = useContentRegistry();
useEffect(() => {
// Skip if dev-only and not in dev mode
// Skip if dev-only and not in dev mode, or if no provider available
if (devOnly && !import.meta.env.DEV) {
return;
}
if (!ref.current) {
if (!ref.current || !registry) {
return;
}