renderer.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. "use client";
  2. import { useMemo, type ReactNode } from "react";
  3. import {
  4. Renderer,
  5. type ComponentRegistry,
  6. type Spec,
  7. DataProvider,
  8. VisibilityProvider,
  9. ActionProvider,
  10. } from "@json-render/react";
  11. import { components, Fallback } from "./catalog/components";
  12. import { executeAction } from "./catalog/actions";
  13. // =============================================================================
  14. // PlaygroundRenderer
  15. // =============================================================================
  16. interface PlaygroundRendererProps {
  17. spec: Spec | null;
  18. data?: Record<string, unknown>;
  19. loading?: boolean;
  20. }
  21. // Build registry once - stable reference to prevent input focus loss
  22. function buildRegistry(loading?: boolean): ComponentRegistry {
  23. const registry: ComponentRegistry = {};
  24. for (const [name, Component] of Object.entries(components)) {
  25. registry[name] = (renderProps: {
  26. element: { props: Record<string, unknown>; type: string };
  27. children?: ReactNode;
  28. }) => (
  29. <Component
  30. props={renderProps.element.props as never}
  31. onAction={(a) => executeAction(a.name, a.params)}
  32. loading={loading}
  33. >
  34. {renderProps.children}
  35. </Component>
  36. );
  37. }
  38. return registry;
  39. }
  40. // Fallback component for unknown types
  41. const fallbackRegistry = (renderProps: { element: { type: string } }) => (
  42. <Fallback type={renderProps.element.type} />
  43. );
  44. export function PlaygroundRenderer({
  45. spec,
  46. data,
  47. loading,
  48. }: PlaygroundRendererProps): ReactNode {
  49. // Memoize registry to prevent re-creating on every render
  50. const registry = useMemo(() => buildRegistry(loading), [loading]);
  51. if (!spec) return null;
  52. return (
  53. <DataProvider initialData={data}>
  54. <VisibilityProvider>
  55. <ActionProvider>
  56. <Renderer
  57. spec={spec}
  58. registry={registry}
  59. fallback={fallbackRegistry}
  60. loading={loading}
  61. />
  62. </ActionProvider>
  63. </VisibilityProvider>
  64. </DataProvider>
  65. );
  66. }