import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Docs/Composition & Child Elements" />

# Composition & Child Elements

Seven collection elements in the kit accept two input paths — **data** (a JS property array) and **composition** (declarative child markup). Both produce identical rendered output and can be mixed on the same element.

## Two ways to populate a collection

### Data — the JS property array

Set the element's array property from JavaScript. The parent re-renders reactively whenever the reference changes.

```html
<kc-suggestions id="s"></kc-suggestions>

<script type="module">
  import '@kitn.ai/chat/elements';

  document.getElementById('s').suggestions = [
    { label: 'Summarise this', value: 'summarise' },
    { label: 'Translate to French', value: 'translate' },
  ];
</script>
```

### Composition — declarative child elements

Place `<kc-*>` child elements directly in markup. They live in the light DOM but are invisible — the parent's Shadow DOM has no `<slot>` for them, so it reads them via `querySelectorAll` and a `MutationObserver` instead. No JavaScript required.

```html
<kc-suggestions>
  <kc-suggestion value="summarise">Summarise this</kc-suggestion>
  <kc-suggestion value="translate">Translate to French</kc-suggestion>
</kc-suggestions>

<script type="module">
  import '@kitn.ai/chat/elements';
</script>
```

### Merge order

When both are provided, **property items render first** and child elements are appended after. There is no conflict — they simply concatenate.

---

## Choosing the right path

| Situation | Reach for |
|-----------|-----------|
| Data arrives from a fetch, a stream, or application state | **Data (property)** |
| Content is model-driven, user-specific, or changes at runtime | **Data (property)** |
| You're working in React, Vue, Svelte, or Angular with a component model | **Data (property)** |
| Content is static and known at build time | **Composition (children)** |
| You're authoring a CMS template, server-rendered HTML, or a no-build page | **Composition (children)** |
| You want zero JavaScript for the initial population | **Composition (children)** |

---

## Child element reference

Each row below is verified against the element's source in `src/elements/`.

### `<kc-action>`

**Parents:** `<kc-message>` (action bar → `kc-message-action`) and `<kc-prompt-input>` (custom toolbar buttons → `kc-toolbar-action`)

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `id` | `CustomAction.id` | Falls back to the `action` attribute when `id` is absent |
| `action` | `CustomAction.id` | Secondary fallback when `id` is also absent |
| `icon` | `CustomAction.icon` | Optional icon name |
| `tooltip` | `CustomAction.tooltip` | Optional tooltip string |
| *(text content)* | `CustomAction.label` | Falls back to the `label` attribute, then `id` |

**Event fired by parent:** `kc-message-action` → `{ messageId: string, action: string }`

```html
<kc-message>
  <kc-action id="copy" icon="copy" tooltip="Copy to clipboard">Copy</kc-action>
  <kc-action id="retry" icon="refresh">Retry</kc-action>
</kc-message>
```

---

### `<kc-suggestion>`

**Parent:** `<kc-suggestions>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `value` | `Item.value` | Falls back to text content when absent |
| *(text content)* | `Item.label` | The chip label |

**Event fired by parent:** `kc-select` → `{ value: string }`

```html
<kc-suggestions>
  <kc-suggestion value="summarise">Summarise this</kc-suggestion>
  <kc-suggestion value="translate">Translate to French</kc-suggestion>
</kc-suggestions>
```

---

### `<kc-source>`

**Parent:** `<kc-sources>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `href` | `SourceItem.href` | Required — the citation URL |
| `label` | `SourceItem.label` | Trigger label (defaults to the domain) |
| `headline` | `SourceItem.title` | Hover-card headline. Named `headline` (not `title`) because `title` is a reserved HTML global attribute |
| `description` | `SourceItem.description` | Hover-card body text |
| `show-favicon` | `SourceItem.showFavicon` | Boolean — bare attribute or `show-favicon="true"` |
| *(text content)* | *(not read by `kc-sources`)* | Text content on a child `<kc-source>` is not mapped by the parent; use `label`/`headline`/`description` attributes instead |

**No interaction event** — sources open their `href` as links.

```html
<kc-sources>
  <kc-source
    href="https://example.com/page"
    label="Example"
    headline="Example Domain"
    description="An illustrative domain used in documentation."
    show-favicon
  ></kc-source>
</kc-sources>
```

> **Note:** `<kc-source>` also works as a **standalone** element — its own `href`, `label`, `headline`, `description`, and `show-favicon` props drive a single citation chip with a hover-card. The child-element role above is its use *inside* `<kc-sources>`.

---

### `<kc-conversation>`

**Parent:** `<kc-conversations>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `id` | `ConversationSummary.id` | Required — stable conversation identifier |
| `group-id` | `ConversationSummary.groupId` | Optional group bucket (e.g. "today", "yesterday") |
| *(text content)* | `ConversationSummary.title` | The conversation title shown in the list |

Required fields not expressible as HTML attributes (`scope`, `messageCount`, `lastMessageAt`, `updatedAt`) receive safe defaults so the list item is fully functional with just `id` and text.

**Events fired by parent:** `kc-conversation-select` → `{ id: string }`, `kc-new-chat` → `{}`, `kc-toggle-sidebar` → `{}`

```html
<kc-conversations>
  <kc-conversation id="conv-1" group-id="today">Morning standup recap</kc-conversation>
  <kc-conversation id="conv-2" group-id="yesterday">API design review</kc-conversation>
</kc-conversations>
```

---

### `<kc-step>`

**Parent:** `<kc-chain-of-thought>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `label` | `Step.label` | The always-visible step heading (required) |
| *(text content)* | `Step.content` | Optional expandable detail; empty string is treated as `undefined` (no expand affordance) |

**No interaction event** — steps expand/collapse internally.

```html
<kc-chain-of-thought>
  <kc-step label="Understand the request">The user wants a concise summary.</kc-step>
  <kc-step label="Draft a response">Condense the key points into three sentences.</kc-step>
  <kc-step label="Review"></kc-step>
</kc-chain-of-thought>
```

---

### `<kc-model>`

**Parent:** `<kc-model-switcher>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `id` | `ModelOption.id` | Required — the model identifier passed in `kc-model-change` |
| `provider` | `ModelOption.provider` | Optional provider name shown in the switcher UI |
| *(text content)* | `ModelOption.name` | Human-readable model name |

**Event fired by parent:** `kc-model-change` → `{ modelId: string }`

The switcher only renders when more than one model is provided.

```html
<kc-model-switcher>
  <kc-model id="gpt-4o" provider="OpenAI">GPT-4o</kc-model>
  <kc-model id="gpt-4o-mini" provider="OpenAI">GPT-4o mini</kc-model>
  <kc-model id="claude-3-7-sonnet" provider="Anthropic">Claude 3.7 Sonnet</kc-model>
</kc-model-switcher>
```

---

### `<kc-skill>`

**Parent:** `<kc-skills>`

| Attribute | Maps to | Notes |
|-----------|---------|-------|
| `id` | `Skill.id` | Falls back to text content when absent |
| *(text content)* | `Skill.name` | The badge label |

**No interaction event** — skills badges are display-only.

```html
<kc-skills>
  <kc-skill id="web-search">Web Search</kc-skill>
  <kc-skill id="code">Code</kc-skill>
</kc-skills>
```

---

## Side-by-side comparison

The same set of suggestions rendered both ways — identical output, different authoring model.

**Data path:**

```html
<kc-suggestions id="s1"></kc-suggestions>

<script type="module">
  import '@kitn.ai/chat/elements';
  document.getElementById('s1').suggestions = [
    { label: 'Explain quantum computing', value: 'explain-quantum' },
    { label: 'Write a haiku', value: 'haiku' },
    { label: 'Summarise in bullet points', value: 'summarise' },
  ];
</script>
```

**Composition path:**

```html
<kc-suggestions>
  <kc-suggestion value="explain-quantum">Explain quantum computing</kc-suggestion>
  <kc-suggestion value="haiku">Write a haiku</kc-suggestion>
  <kc-suggestion value="summarise">Summarise in bullet points</kc-suggestion>
</kc-suggestions>

<script type="module">
  import '@kitn.ai/chat/elements';
</script>
```

Both fire `kc-select` with the same `value` string. The only difference is where the data lives — in application state or in the HTML.

---

## Customisation

Child elements are **attribute- and text-driven data carriers**. Their visual appearance is entirely controlled by the parent element's props and the kit's design tokens (`--kc-*`, `--color-*`). There is no CSS to pierce through Shadow DOM boundaries and no per-child styling surface — reach for the parent's documented attributes and tokens instead.

See **[Theming](?path=/docs/theming-overview--docs)** for the full token reference.
