{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "collapsible",
  "title": "Collapsible",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/collapsible/Collapsible.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CollapsibleProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n    /** Controlled open state. Bindable (`bind:open`); falls back to internal state seeded by `defaultOpen`. */\n    open?: boolean\n    /** Initial open state when `open` is unbound. */\n    defaultOpen?: boolean\n    /** When `true`, the trigger cannot toggle the content. */\n    disabled?: boolean\n    /** Fires when the open state changes. */\n    onOpenChange?: (open: boolean) => void\n    children?: Snippet<[{ open: boolean }]>\n  }\n\n  /** Hand-rolled collapsible context (replaces reka-ui's CollapsibleRoot state). */\n  export interface CollapsibleContext {\n    readonly isOpen: boolean\n    readonly disabled: boolean\n    readonly contentId: string\n    toggle: () => void\n  }\n</script>\n\n<script lang=\"ts\">\n  import { setContext } from 'svelte'\n\n  let {\n    class: className,\n    open = $bindable(),\n    defaultOpen = false,\n    disabled = false,\n    onOpenChange,\n    children,\n    ...restProps\n  }: CollapsibleProps = $props()\n\n  // Intentional one-shot seed: later `defaultOpen` changes must not clobber user toggles.\n  // svelte-ignore state_referenced_locally\n  let internalOpen = $state(defaultOpen)\n  const isOpen = $derived(open ?? internalOpen)\n\n  const contentId = `collapsible-${Math.random().toString(36).slice(2, 9)}`\n\n  function toggle() {\n    if (disabled) return\n    const next = !isOpen\n    if (open === undefined) internalOpen = next\n    open = next\n    onOpenChange?.(next)\n  }\n\n  setContext<CollapsibleContext>('collapsibleContext', {\n    get isOpen() {\n      return isOpen\n    },\n    get disabled() {\n      return disabled\n    },\n    get contentId() {\n      return contentId\n    },\n    toggle,\n  })\n</script>\n\n<div data-uipkge data-slot=\"collapsible\" data-state={isOpen ? 'open' : 'closed'} class={className} {...restProps}>\n  {@render children?.({ open: isOpen })}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/collapsible/Collapsible.svelte"
    },
    {
      "path": "packages/registry-svelte/components/collapsible/CollapsibleContent.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CollapsibleContentProps extends HTMLAttributes<HTMLDivElement> {\n    /** Keep the content mounted (hidden) when closed instead of unmounting it. */\n    forceMount?: boolean\n    children?: Snippet\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import type { CollapsibleContext } from './Collapsible.svelte'\n\n  let { forceMount = false, children, ...restProps }: CollapsibleContentProps = $props()\n\n  const ctx = getContext<CollapsibleContext | null>('collapsibleContext')\n  const isOpen = $derived(ctx?.isOpen ?? false)\n</script>\n\n{#if forceMount || isOpen}\n  <div\n    data-uipkge\n    data-slot=\"collapsible-content\"\n    id={ctx?.contentId}\n    data-state={isOpen ? 'open' : 'closed'}\n    hidden={!isOpen || undefined}\n    {...restProps}\n  >\n    {@render children?.()}\n  </div>\n{/if}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/collapsible/CollapsibleContent.svelte"
    },
    {
      "path": "packages/registry-svelte/components/collapsible/CollapsibleTrigger.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { Snippet } from 'svelte'\n  import type { HTMLButtonAttributes } from 'svelte/elements'\n\n  export interface CollapsibleTriggerProps extends HTMLButtonAttributes {\n    /**\n     * Render your own element with the trigger's props instead of emitting a\n     * `<button>` — the Svelte counterpart of React's `asChild`. Spread `props`\n     * onto your element.\n     */\n    child?: Snippet<[{ props: Record<string, unknown> }]>\n    /** The rendered `<button>`, via `bind:ref`. Stays null in `child` mode. */\n    ref?: HTMLButtonElement | null\n    children?: Snippet\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import type { MouseEventHandler } from 'svelte/elements'\n  import type { CollapsibleContext } from './Collapsible.svelte'\n\n  let { child, ref = $bindable(null), children, onclick: onclickProp, ...restProps }: CollapsibleTriggerProps =\n    $props()\n\n  const ctx = getContext<CollapsibleContext | null>('collapsibleContext')\n\n  const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n    ctx?.toggle()\n    onclickProp?.(event)\n  }\n\n  // `onclick` is chained (toggle + consumer handler) so `child` buttons work\n  // with a bare `{...props}` spread. It is safe to list before `...restProps`\n  // — the consumer's own `onclick` was destructured out as `onclickProp`.\n  const mergedProps = $derived({\n    'data-uipkge': '',\n    'data-slot': 'collapsible-trigger',\n    'data-state': ctx?.isOpen ? 'open' : 'closed',\n    'aria-expanded': ctx?.isOpen ?? false,\n    'aria-controls': ctx?.contentId,\n    disabled: ctx?.disabled,\n    onclick: handleClick,\n    ...restProps,\n  })\n</script>\n\n{#if child}\n  {@render child({ props: mergedProps })}\n{:else}\n  <button bind:this={ref} type=\"button\" {...mergedProps}>\n    {@render children?.()}\n  </button>\n{/if}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/collapsible/CollapsibleTrigger.svelte"
    },
    {
      "path": "packages/registry-svelte/components/collapsible/index.ts",
      "content": "export { default as Collapsible, type CollapsibleContext, type CollapsibleProps } from './Collapsible.svelte'\nexport { default as CollapsibleContent, type CollapsibleContentProps } from './CollapsibleContent.svelte'\nexport { default as CollapsibleTrigger, type CollapsibleTriggerProps } from './CollapsibleTrigger.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/collapsible/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Headless single-region show/hide primitive with Svelte 5 runes (no headless dependency — open state, ids, and trigger/content wiring are hand-rolled via context). Use it when Accordion is overkill — a single toggle reveals one panel of content. Controlled (bind:open) or uncontrolled (defaultOpen); the trigger supports a child snippet for custom buttons.",
  "categories": [
    "disclosure"
  ]
}