diff --git a/src/index.tsx b/src/index.tsx index a14ef6b..e47c885 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -147,8 +147,10 @@ function makeStyle(def: TagDef) { return style } +const ROOT_PROPS_KEY = '_forgeRootProps' + // turns a TagDef into a JSX component -function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record, partName?: string) { +function makeComponent(baseName: string, rootDef: TagDef, partName?: string) { const def = partName ? rootDef.parts?.[partName]! : rootDef const base = def.base ?? 'div' @@ -167,7 +169,7 @@ function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record { + return ({ [ROOT_PROPS_KEY]: rootProps = {}, children, ...props }: { [ROOT_PROPS_KEY]?: Record, children: any, [key: string]: any }) => { const classNames = [makeClassName(baseName, partName)] const allProps = { ...rootProps, ...props } @@ -286,14 +288,28 @@ export function define(nameOrDef: string | TagDef, defIfNamed?: TagDef) { if (styles[name]) throw `${name} is already defined! Must use unique names.` registerStyles(name, def) + // ensure component function identity doesn't change between renders + const components: Record any> = {} + + for (const [partName] of Object.entries(def.parts ?? {})) + components[partName] = makeComponent(name, def, partName) + + const RootComponent = makeComponent(name, def) + return (props: Record) => { - const parts: Record = {} + if (def.render) { + // For custom render, create parts object that injects _forgeRootProps + const parts: Record any> = {} - for (const [part] of Object.entries(def.parts ?? {})) - parts[part] = makeComponent(name, def, props, part) + for (const [partName, Comp] of Object.entries(components)) + parts[partName] = (partProps: any) => - parts.Root = makeComponent(name, def, props) - return def.render?.({ props, parts }) ?? {props.children} + parts.Root = (partProps: any) => + return def.render({ props, parts }) + } + + // Default render + return {props.children} } }