| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- "use client";
- import { useMemo, useRef, type ReactNode } from "react";
- import {
- Renderer,
- type ComponentRenderer,
- type Spec,
- DataProvider,
- VisibilityProvider,
- ActionProvider,
- } from "@json-render/react";
- import { registry, Fallback, handlers as createHandlers } from "./registry";
- // =============================================================================
- // DashboardRenderer
- // =============================================================================
- type SetData = (
- updater: (prev: Record<string, unknown>) => Record<string, unknown>,
- ) => void;
- interface DashboardRendererProps {
- spec: Spec | null;
- data?: Record<string, unknown>;
- setData?: SetData;
- onDataChange?: (path: string, value: unknown) => void;
- loading?: boolean;
- }
- // Fallback component for unknown types
- const fallback: ComponentRenderer = ({ element }) => (
- <Fallback type={element.type} />
- );
- export function DashboardRenderer({
- spec,
- data = {},
- setData,
- onDataChange,
- loading,
- }: DashboardRendererProps): ReactNode {
- // Use refs so action handlers always see the latest data/setData
- const dataRef = useRef(data);
- const setDataRef = useRef(setData);
- dataRef.current = data;
- setDataRef.current = setData;
- // Create ActionProvider-compatible handlers using getters so they
- // always read the latest data/setData from refs
- const actionHandlers = useMemo(
- () =>
- createHandlers(
- () => setDataRef.current,
- () => dataRef.current,
- ),
- [],
- );
- if (!spec) return null;
- return (
- <DataProvider initialData={data} onDataChange={onDataChange}>
- <VisibilityProvider>
- <ActionProvider handlers={actionHandlers}>
- <Renderer
- spec={spec}
- registry={registry}
- fallback={fallback}
- loading={loading}
- />
- </ActionProvider>
- </VisibilityProvider>
- </DataProvider>
- );
- }
|