diff --git a/client/src/blocks/BlockRenderer.jsx b/client/src/blocks/BlockRenderer.jsx new file mode 100644 index 0000000..e78e94f --- /dev/null +++ b/client/src/blocks/BlockRenderer.jsx @@ -0,0 +1,26 @@ +// Renders stored blocks via their registry component. Used by the public page +// route, the draft preview, and (recursively) the two_column block. Kept +// separate from the registry so both the renderer and the builder can import it. +// Import the lookup from the registry directly (not ./index) to avoid a cycle: +// index → types/twoColumn → BlockRenderer. The page route/builder import ./index, +// which registers every block before anything renders. +import { getBlock } from './registry.js' + +/** + * Render one block. A block with `visible === false` renders nothing (admins + * hide blocks without deleting them). An unknown type also renders nothing — + * server validation prevents storing one, so this only guards a client/server + * registry skew rather than crashing the whole page. + */ +export default function BlockRenderer({ block }) { + if (!block || block.visible === false) return null + const def = getBlock(block.type) + if (!def || !def.component) return null + const Component = def.component + return +} + +/** Render an ordered array of blocks (array position = display order). */ +export function BlockList({ blocks }) { + return (blocks || []).map((block) => ) +} diff --git a/client/src/blocks/editorKit.jsx b/client/src/blocks/editorKit.jsx new file mode 100644 index 0000000..481fb21 --- /dev/null +++ b/client/src/blocks/editorKit.jsx @@ -0,0 +1,64 @@ +// Shared form controls for block editors, styled with the existing admin design +// system (.field-label / .input / .select). Every block's editor is a +// ({ props, onChange }) component; these keep the seven of them consistent and +// short. onChange always receives the full next props object. + +export function Field({ label, hint, children }) { + return ( + + ) +} + +export function TextField({ label, hint, value, onChange, placeholder, maxLength }) { + return ( + + onChange(e.target.value)} + /> + + ) +} + +export function TextAreaField({ label, hint, value, onChange, placeholder, rows = 4, maxLength }) { + return ( + +