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 yetGet started by creating your first project.`),
};
/** A single primary action — the canonical empty state. */
export const Default: Story = {
render: () => (
No projects yetGet started by creating your first project.
),
...src(`No projects yetGet 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 emptyImport existing items or start from scratch.
),
...src(`Your inbox is emptyImport 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 tileBare 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 conversationPick a prompt or type your own.
{(s) => {s}}
),
...src(`Start a conversationPick 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 JordanAsk me anything about your report.
{(s) => {s}}
),
...src(`Hi JordanAsk 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 documentChoose a starting point.
{(group) => (
{group.label}
{(item) => {item}}
)}
);
},
...src(`Ask about this documentChoose 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 hereOr click to browse from your computer.
),
...src(`Drop files hereOr click to browse from your computer.`),
};