{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "slot",
  "title": "React Slot",
  "description": "Vue-style slot composition for React",
  "files": [
    {
      "path": "components/slot.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nconst DEFAULT_SLOT = 'DEFAULT';\n\ntype Slot = React.ReactNode;\n\ntype Slots = Record<string, Slot>;\n\ntype SlotName = typeof DEFAULT_SLOT | (string & {});\n\ntype SlotContext = { scope: string };\n\nconst SlotContext = React.createContext<SlotContext | null>(null);\n\nexport function useSlots(caller: string) {\n  const context = React.use(SlotContext);\n\n  if (!context) {\n    throw new Error(`${caller} must be used inside a Slottable`);\n  }\n\n  return context;\n}\n\ntype Slottable = {\n  scope?: string;\n  strictDefault?: boolean;\n  children: React.ReactNode;\n};\n\nexport function Slottable({ children, scope = 'Slottable', strictDefault = true }: Slottable) {\n  const { slots, template, defaultSlot } = React.useMemo(() => {\n    const slots: Slots = {};\n    const defaultSlot: React.ReactNode[] = [];\n\n    let template: TemplateElement | undefined;\n\n    React.Children.forEach(children, child => {\n      if (isSlot(child)) {\n        const { name: slot = DEFAULT_SLOT, children } = child.props;\n\n        if (slot in slots) {\n          throw new Error(`Duplicate slot: ${slot} was found`);\n        }\n\n        slots[slot] = children;\n        return;\n      }\n\n      if (isTemplate(child)) {\n        if (template) {\n          throw new Error(\n            'A duplicate Template was found. Only one direct Template child is allowed',\n          );\n        }\n\n        template = child;\n        return;\n      }\n\n      defaultSlot.push(child);\n    });\n\n    return {\n      slots,\n      template,\n      defaultSlot,\n    };\n  }, [children]);\n\n  if (template == null) {\n    throw new Error(`A single template is required as a direct child of ${scope}`);\n  }\n\n  const [tree, foundDefault] = React.useMemo(\n    () =>\n      traverse(template, {\n        placeholder(placeholder) {\n          const slot = placeholder.props.name ?? DEFAULT_SLOT;\n          const isDefaultSlot = slot === DEFAULT_SLOT;\n          const outlet = isDefaultSlot ? defaultSlot : slots[slot];\n\n          return outlet ?? null;\n        },\n      }),\n    [children],\n  );\n\n  const renderDefault = !strictDefault && !foundDefault;\n\n  const context: SlotContext = { scope };\n\n  return (\n    <SlotContext value={context}>\n      {tree}\n      {renderDefault && defaultSlot}\n    </SlotContext>\n  );\n}\n\ntype Visitors = {\n  placeholder: (element: SlotPlaceholderElement) => React.ReactNode;\n};\n\nfunction traverse(template: TemplateElement, visitors: Visitors) {\n  const { placeholder } = visitors;\n\n  let foundDefault = false;\n\n  const traverseChildren = (children: React.ReactNode): React.ReactNode[] => {\n    return (\n      React.Children.map(children, child => {\n        if (isSlotPlaceholder(child)) {\n          const slot = child.props.name ?? DEFAULT_SLOT;\n          const isDefault = slot === DEFAULT_SLOT;\n          if (isDefault && !foundDefault) foundDefault = true;\n\n          return placeholder(child);\n        }\n\n        if (isParent(child)) {\n          const { children, ...props } = child.props;\n\n          return React.cloneElement(child, props, ...traverseChildren(children));\n        }\n\n        return child;\n      }) ?? []\n    );\n  };\n\n  return [traverseChildren(template.props.children), foundDefault as boolean] as const;\n}\n\nconst TemplateContext = React.createContext(false);\n\ntype TemplateProps = {\n  children: React.ReactNode;\n};\n\nexport function Template({ children }: TemplateProps) {\n  const hasTemplate = React.use(TemplateContext);\n\n  if (hasTemplate) {\n    throw new Error('A duplicate Template was found. Only one direct Template child is allowed');\n  }\n\n  return <TemplateContext.Provider value={true}>{children}</TemplateContext.Provider>;\n}\n\ntype SlotPlaceholderProps = {\n  name?: SlotName;\n  children?: React.ReactNode;\n};\n\nexport function SlotPlaceholder(_props: SlotPlaceholderProps) {\n  return null;\n}\n\ntype SlotProps = {\n  name?: SlotName;\n  children: React.ReactNode;\n};\n\nexport function Slot({ name = DEFAULT_SLOT, children }: SlotProps) {\n  const { scope } = useSlots(`Slot: ${name}`);\n\n  React.useEffect(() => {\n    throw new Error(`Slot: ${name} must be a direct child of ${scope}`);\n  }, [name, scope, children]);\n\n  return null;\n}\n\ntype SlotElement = React.ReactElement<SlotProps>;\n\nfunction isSlot(child: React.ReactNode): child is SlotElement {\n  const isElement = React.isValidElement<typeof Slot>(child);\n  return isElement && child.type === Slot;\n}\n\ntype TemplateElement = React.ReactElement<TemplateProps>;\n\nfunction isTemplate(child: React.ReactNode): child is TemplateElement {\n  const isElement = React.isValidElement<typeof Template>(child);\n  return isElement && child.type === Template;\n}\n\ntype SlotPlaceholderElement = React.ReactElement<SlotPlaceholderProps>;\n\nfunction isSlotPlaceholder(child: React.ReactNode): child is SlotPlaceholderElement {\n  const isElement = React.isValidElement<typeof SlotPlaceholder>(child);\n  return isElement && child.type === SlotPlaceholder;\n}\n\ntype ParentElement = React.ReactElement<Required<React.PropsWithChildren>>;\n\nfunction isParent(child: React.ReactNode): child is ParentElement {\n  const isElement = React.isValidElement<React.PropsWithChildren>(child);\n  return isElement && child.props.children !== undefined;\n}\n\n// TODO: later add ref to the Provider after\n// TODO: support for Fragment refs' is added\n",
      "type": "registry:component",
      "target": "components/ui/slot.tsx"
    }
  ],
  "type": "registry:component"
}