import type { Meta, StoryObj } from 'storybook-solidjs-vite';
import { createSignal, For } from 'solid-js';
import {
Attachments,
Attachment,
AttachmentPreview,
AttachmentInfo,
AttachmentRemove,
AttachmentHoverCard,
AttachmentHoverCardTrigger,
AttachmentHoverCardContent,
AttachmentEmpty,
} from './attachments';
import type { AttachmentData } from './attachments';
import { componentDescription } from '../stories/docs/element-controls';
const sampleAttachments: AttachmentData[] = [
{
id: '1',
type: 'file',
filename: 'mountain-landscape.jpg',
mediaType: 'image/jpeg',
url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop',
},
{
id: '2',
type: 'file',
filename: 'sunset-beach.png',
mediaType: 'image/png',
url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=400&h=400&fit=crop',
},
{
id: '3',
type: 'file',
filename: 'architecture-report.pdf',
mediaType: 'application/pdf',
},
{
id: '4',
type: 'file',
filename: 'demo-recording.mp4',
mediaType: 'video/mp4',
},
{
id: '5',
type: 'file',
filename: 'podcast-episode.mp3',
mediaType: 'audio/mpeg',
},
{
id: '6',
type: 'source-document',
filename: 'SolidJS Documentation',
title: 'SolidJS Reactivity Guide',
url: 'https://solidjs.com/docs',
},
];
const meta = {
title: 'Solid (Advanced)/Elements/Attachments',
component: Attachments,
tags: ['autodocs'],
parameters: {
layout: 'padded',
docs: {
description: componentDescription([
'A composable container for displaying file and source-document attachments as thumbnails, inline chips, or a vertical list. Built from `Attachments` + per-item `Attachment` with `AttachmentPreview`, `AttachmentInfo`, and `AttachmentRemove` parts.',
'**When to use:** to show files a user attached to a prompt, or documents/sources referenced by a message. Choose `grid` for thumbnails, `inline` for compact chips, `list` for a detailed rows view.',
'**How to use:** set `variant` on `Attachments`, then map your data to `Attachment` (passing each item via `data` and an `onRemove` handler) and compose the preview/info/remove parts inside.',
'**Placement:** prompt input area (pending uploads), message bodies (attached or cited files), and document panels.',
]),
controls: { exclude: ['use:eventListener'] },
},
},
argTypes: {
variant: {
control: 'select',
options: ['grid', 'inline', 'list'],
description: 'Layout of the attachment items.',
table: { defaultValue: { summary: 'grid' } },
},
children: {
control: false,
description: 'The `Attachment` items to render inside the container.',
},
class: {
control: 'text',
description: 'Extra classes for the container element.',
},
},
args: {
variant: 'grid',
},
render: (args) => {
const [items, setItems] = createSignal([...sampleAttachments]);
const remove = (id: string) => setItems((prev) => prev.filter((a) => a.id !== id));
return (
{(item) => (
remove(item.id)}>
)}
);
},
} satisfies Meta;
export default meta;
type Story = StoryObj;
const IMPORT = `import {
Attachments, Attachment, AttachmentPreview, AttachmentInfo, AttachmentRemove,
} from '@kitn.ai/chat';`;
const src = (code: string) => ({
parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
});
/** Interactive playground — switch `variant` to compare grid / inline / list. */
export const Playground: Story = {
...src(`
{(item) => (
remove(item.id)}>
)}
`),
};
export const Grid: Story = {
render: () => {
const [items, setItems] = createSignal([...sampleAttachments]);
const remove = (id: string) => setItems((prev) => prev.filter((a) => a.id !== id));
return (
{(item) => (
remove(item.id)}>
)}
);
},
...src(`
{(item) => (
remove(item.id)}>
)}
`),
};
export const Inline: Story = {
render: () => {
const [items, setItems] = createSignal([...sampleAttachments]);
const remove = (id: string) => setItems((prev) => prev.filter((a) => a.id !== id));
return (
{(item) => (
remove(item.id)}>
)}
);
},
...src(`
{(item) => (
remove(item.id)}>
)}
`),
};
export const List: Story = {
render: () => {
const [items, setItems] = createSignal([...sampleAttachments]);
const remove = (id: string) => setItems((prev) => prev.filter((a) => a.id !== id));
return (
{(item) => (
remove(item.id)}>
)}
);
},
...src(`
{(item) => (
remove(item.id)}>
)}
`),
};
export const WithHoverCard: Story = {
render: () => (
),
...src(`
`),
};
export const Empty: Story = {
render: () => (
),
...src(`
`),
};