import type { Meta, StoryObj } from 'storybook-solidjs-vite'; import { createSignal, For } from 'solid-js'; import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent, } from './empty'; import { Button } from '../ui/button'; import { Avatar } from '../ui/avatar'; import { PromptSuggestion } from './prompt-suggestion'; import { PromptInput, PromptInputTextarea, PromptInputActions } from './prompt-input'; import { FolderPlus, MessageCircleQuestion, Inbox, Search, Sparkles, FileText, ArrowUp, Plus, Upload, } from 'lucide-solid'; import { componentDescription } from '../stories/docs/element-controls'; /** * Story for the compound `Empty` family. `Empty` is the root container; the * header/media/title/description/content subcomponents compose the layout. The * only real enum prop is `EmptyMedia`'s `variant`, so the controls focus on the * common composition, and the variation stories are compositional showcases. */ const meta = { title: 'Solid (Advanced)/Elements/Empty', component: Empty, tags: ['autodocs'], parameters: { layout: 'padded', docs: { description: componentDescription([ 'A composable empty-state block (modeled on shadcn/ui `Empty`): a centered media tile, title, description, and a content slot for actions or suggestions. Token-driven styling; no border by default.', '**When to use:** when a region has nothing to show yet — an empty list/inbox, no search results, a blank chat, or a drop zone — and you want to guide the user toward a next action.', '**How to use:** compose `Empty > EmptyHeader (EmptyMedia + EmptyTitle + EmptyDescription)` and an optional `EmptyContent` for buttons or prompt suggestions. Set `EmptyMedia` `variant` to `icon` for a muted tile or `default` for a bare slot (avatar/illustration). Add `border border-dashed` via `class` for a card.', '**Placement:** empty lists/sidebars, search-result panes, blank chat launch states, and file drop zones.', ]), controls: { exclude: ['use:eventListener'] }, }, }, argTypes: { title: { control: 'text', description: 'Demo control: the `EmptyTitle` text.', }, description: { control: 'text', description: 'Demo control: the `EmptyDescription` text.', }, mediaVariant: { control: 'select', options: ['default', 'icon'], description: '`EmptyMedia` variant — `icon` is a muted rounded tile, `default` is a bare slot.', table: { defaultValue: { summary: 'default' } }, }, actionLabel: { control: 'text', description: 'Demo control: label of the primary action button.', }, }, render: (args: { title?: string; description?: string; mediaVariant?: 'default' | 'icon'; actionLabel?: string; }) => (
{args.title} {args.description}
), } satisfies Meta; export default meta; type Story = StoryObj; const IMPORT = `import { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, EmptyContent, } from '@kitn.ai/chat';`; const src = (code: string) => ({ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } }, }); /** Interactive playground — edit the title, description, media variant, and action label. */ export const Playground: Story = { args: { title: 'No projects yet', description: 'Get started by creating your first project.', mediaVariant: 'icon', actionLabel: 'Create project', }, ...src(` No projects yet Get started by creating your first project. `), }; /** A single primary action — the canonical empty state. */ export const Default: Story = { render: () => (
No projects yet Get started by creating your first project.
), ...src(` No projects yet Get started by creating your first project. `), }; /** Two actions — a primary plus a secondary (outline). */ export const WithActions: Story = { name: 'With Multiple Actions', render: () => (
Your inbox is empty Import existing items or start from scratch.
), ...src(` Your inbox is empty Import existing items or start from scratch.
`), }; /** The two `EmptyMedia` variants: an icon tile vs. a default (bare) slot * holding an avatar or larger illustration. */ export const MediaVariants: Story = { name: 'Media Variants (icon / default)', render: () => (
variant="icon" Icon sits in a muted rounded tile.
variant="default" Bare slot for an avatar or illustration.
), ...src(` Icon tile Bare slot `), }; /** Suggestions as pills (PromptSuggestion default) in a centered wrap — * best for a handful of short prompts. */ export const SuggestionPills: Story = { name: 'Suggestions — Pills', render: () => (
Start a conversation Pick a prompt or type your own.
{(s) => {s}}
), ...src(` Start a conversation Pick a prompt or type your own.
{(s) => {s}}
`), }; /** Suggestions as a full-width list (PromptSuggestion `block`) — best for * longer, sentence-length prompts. This is the report chat dock's pattern. */ export const SuggestionList: Story = { name: 'Suggestions — List (block)', render: () => (
Hi Jordan Ask me anything about your report. {(s) => {s}}
), ...src(` Hi Jordan Ask me anything about your report. {(s) => {s}} `), }; /** Suggestions organized into labeled groups (mirrors the Prompt Input * Variants → WithSuggestions pattern), inside an empty block. */ export const GroupedSuggestions: Story = { name: 'Suggestions — Grouped', render: () => { const groups = [ { label: 'Get started', items: ['Summarize this document', 'What are the key takeaways?'] }, { label: 'Go deeper', items: ['Compare with similar approaches', 'What are the tradeoffs?'] }, ]; return (
Ask about this document Choose a starting point. {(group) => (
{group.label}
{(item) => {item}}
)}
); }, ...src(` Ask about this document Choose a starting point. {(group) => (/* label + wrapped PromptSuggestions */)} `), }; /** An empty block whose content is an input — the "blank chat" launch state. */ export const WithInput: Story = { name: 'With Prompt Input', render: () => { const [value, setValue] = createSignal(''); return (
How can I help? Ask anything to get started. setValue('')} class="w-full">
); }, ...src(` How can I help? Ask anything to get started. setValue('')}> `), }; /** A description can carry a link; styled underline + primary-on-hover. */ export const WithLink: Story = { name: 'With Link in Description', render: () => (
No results found Try a different search, or browse all items instead.
), ...src(` No results found Try a different search, or browse all items instead. `), }; /** A bordered (dashed) card treatment — add `border border-dashed` via class. */ export const Bordered: Story = { render: () => (
Drop files here Or click to browse from your computer.
), ...src(` Drop files here Or click to browse from your computer. `), };