# Chat Composer Drawer

## Usage

Use ChatComposerDrawer in the ChatComposer drawer slot for supplementary content such as attachments, context chips, or previews. Provide `count` only when people should be able to collapse that content.

```ts
import { ChatComposerDrawer } from '@astryx-vue/core/Chat'
```

### Anatomy

- **Root surface**: The drawer surface that composes above the ChatComposer body.
- **Disclosure toggle**: The keyboard- and pointer-operable collapse control rendered when count is provided.
- **Collapsed summary**: The default count Badge and label, or caller-provided visual content, presented while the drawer is collapsed.
- **Content area**: The caller-provided supplementary content; collapsed descendants are unavailable to keyboard and assistive technology.

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Give `label` a concrete plural noun such as “Attachments” so the expand and collapse actions have a useful accessible name. |
| Do | Use controlled `v-model:is-collapsed` with `collapsedChange` when another part of the page owns drawer state; otherwise use `defaultIsCollapsed`. |
| Don't | Put primary composer actions in the drawer; use ChatComposer `footerActions` or `sendActions` so those controls remain available when the drawer is collapsed. |

## Examples

Common configurations, variations, and states.

### ChatComposerDrawer — Attachments

Drawer with two rows: a scrollable carousel of image thumbnails and a row of removable file tokens. Omit count to keep the drawer always expanded.

```vue
<script setup lang="ts">
import { Carousel } from '@astryx-vue/core/Carousel'
import { ChatComposer, ChatComposerDrawer } from '@astryx-vue/core/Chat'
import { HStack, VStack } from '@astryx-vue/core/Stack'
import { Thumbnail } from '@astryx-vue/core/Thumbnail'
import { Token } from '@astryx-vue/core/Token'
import { ref } from 'vue'

const images = ref([
  { id: '1', src: '/template-assets/illustrative-vertical-1.png', alt: 'River through a valley', label: 'valley.jpg' },
  { id: '2', src: '/template-assets/light-working-vertical-1.png', alt: 'Foggy mountain peak', label: 'mountain.jpg' },
  { id: '3', src: '/template-assets/light-working-vertical-2.png', alt: 'Golden retriever puppy', label: 'puppy.jpg' },
  { id: '4', src: '/template-assets/illustrative-horizontal-1.png', alt: 'Bridge at sunset', label: 'bridge.jpg' },
  { id: '5', src: '/template-assets/light-scene-horizontal-1.png', alt: 'Lakeside at dusk', label: 'lakeside.jpg' },
])

const files = ref(['quarterly-report.pdf', 'budget-forecast.xlsx'])

function removeImage(id: string): void {
  images.value = images.value.filter(item => item.id !== id)
}

function remove(name: string): void {
  files.value = files.value.filter(item => item !== name)
}
</script>

<template>
  <ChatComposer>
    <template #drawer>
      <ChatComposerDrawer>
        <VStack :gap="2" width="100%">
          <Carousel :gap="1">
            <Thumbnail
              v-for="image in images"
              :key="image.id"
              :src="image.src"
              :alt="image.alt"
              :label="image.label"
              @remove="removeImage(image.id)"
            />
          </Carousel>
          <HStack :gap="1" wrap="wrap">
            <Token
              v-for="name in files"
              :key="name"
              removable
              :label="name"
              @remove="remove(name)"
            />
          </HStack>
        </VStack>
      </ChatComposerDrawer>
    </template>
  </ChatComposer>
</template>
```

### ChatComposerDrawer — Collapsible

Drawer with many items and a collapse toggle. Pass count to enable the toggle; collapsed state shows a badge with the total count and a label.

```vue
<script setup lang="ts">
import { ChatComposer, ChatComposerDrawer } from '@astryx-vue/core/Chat'
import { Token } from '@astryx-vue/core/Token'
import { ref } from 'vue'

const files = ref([
  'design-spec.pdf',
  'api-schema.json',
  'screenshot.png',
  'meeting-notes.md',
  'test-results.csv',
  'deploy-log.txt',
])

function remove(name: string): void {
  files.value = files.value.filter(item => item !== name)
}
</script>

<template>
  <ChatComposer>
    <template #drawer>
      <ChatComposerDrawer :count="files.length" label="Files">
        <Token
          v-for="name in files"
          :key="name"
          removable
          :label="name"
          @remove="remove(name)"
        />
      </ChatComposerDrawer>
    </template>
  </ChatComposer>
</template>
```

### ChatComposerDrawer — Feedback

Chat composer drawer with a feedback prompt and selectable lettered options. Use for user confirmation workflows that require explicit action before proceeding.

```vue
<script setup lang="ts">
import { Badge } from '@astryx-vue/core/Badge'
import { ChatComposer, ChatComposerDrawer } from '@astryx-vue/core/Chat'
import { List, ListItem } from '@astryx-vue/core/List'
import { VStack } from '@astryx-vue/core/Stack'
import { Text } from '@astryx-vue/core/Text'
import { ref } from 'vue'

const options = [
  { key: 'A', label: 'Yes' },
  { key: 'B', label: 'Yes, and don’t ask again for `git add` commands' },
  { key: 'C', label: 'No, and tell me what to do differently' },
]

const selected = ref<string | null>(null)
</script>

<template>
  <ChatComposer>
    <template #drawer>
      <ChatComposerDrawer :count="1" label="User feedback requested">
        <VStack :gap="1" width="100%">
          <List>
            <ListItem>
              <template #label>
                <Text weight="bold">
                  Do you want to proceed?
                </Text>
              </template>
            </ListItem>
            <ListItem
              v-for="option in options"
              :key="option.key"
              :label="option.label"
              :is-selected="selected === option.key"
              @click="selected = option.key"
            >
              <template #startContent>
                <Badge :variant="selected === option.key ? 'info' : 'neutral'" :label="option.key" />
              </template>
            </ListItem>
          </List>
        </VStack>
      </ChatComposerDrawer>
    </template>
  </ChatComposer>
</template>
```

### ChatComposerDrawer — With Progress

Drawer paired with a context progress bar in the header. Show context window usage when attachments consume part of the available token budget.

```vue
<script setup lang="ts">
import { ChatComposer, ChatComposerDrawer } from '@astryx-vue/core/Chat'
import { Icon } from '@astryx-vue/core/Icon'
import { IconButton } from '@astryx-vue/core/IconButton'
import { ProgressBar } from '@astryx-vue/core/ProgressBar'
import { Token } from '@astryx-vue/core/Token'
import { ref } from 'vue'

const files = ref(['design-spec.pdf', 'api-schema.json', 'screenshot.png'])

function remove(name: string): void {
  files.value = files.value.filter(item => item !== name)
}
</script>

<template>
  <ChatComposer>
    <template #drawer>
      <ChatComposerDrawer :count="files.length" label="Attachments">
        <Token
          v-for="name in files"
          :key="name"
          removable
          :label="name"
          @remove="remove(name)"
        />
      </ChatComposerDrawer>
    </template>
    <template #headerActions>
      <IconButton label="Mention" variant="ghost" size="sm">
        <Icon name="heroicons:at-symbol" />
      </IconButton>
      <IconButton label="Attach" variant="ghost" size="sm">
        <Icon name="heroicons:paper-clip" />
      </IconButton>
    </template>
    <template #headerContext>
      <ProgressBar label="Context usage" :value="42" :width="96" is-label-hidden />
    </template>
  </ChatComposer>
</template>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `count` | `number` | — | Item count. Providing it turns on the collapse toggle and is shown in the collapsed summary. Omit it and the drawer stays open. |
| `label` | `string` | Localized “Items” | Name used in the collapsed summary and in the expand and collapse accessible labels. |
| `v-model:is-collapsed` | `boolean` | — | Controlled collapsed state. Bound, the drawer renders exactly this value and the toggle only asks for a change. |
| `defaultIsCollapsed` | `boolean` | `false` | Initial collapsed state while `isCollapsed` is unbound. |

## Events

| Event | Payload | Description |
| --- | --- | --- |
| `collapsedChange` | `boolean` | The collapsed state the toggle is asking for. `v-model:is-collapsed` listens to it. |

## Slots

| Slot | Description |
| --- | --- |
| (default) | The supplementary content: attachment chips, thumbnails, prompts. Laid out as a wrapping row. |
| `collapsedSummary` | Replaces the count badge and label shown while collapsed. The toggle still owns the disclosure name. |
