import type { Meta, StoryObj } from 'storybook-solidjs-vite'; import { createSignal, onMount, type JSX } from 'solid-js'; import './tasks'; import { argTypesFor, specDescription } from '../stories/docs/element-controls'; import type { TasksCardData } from '../components/tasks-card'; import type { CardEvent } from '../primitives/card-contract'; declare module 'solid-js' { // eslint-disable-next-line @typescript-eslint/no-namespace namespace JSX { interface IntrinsicElements { 'kc-tasks': JSX.HTMLAttributes & { heading?: string; 'card-id'?: string; ref?: (el: HTMLElement) => void; }; } } } type TasksEl = HTMLElement & { data?: TasksCardData; resolution?: Record }; function Frame(props: { children: JSX.Element }) { return
{props.children}
; } /** Mounts a , sets `.data`, logs the emitted CardEvent under the render. */ function TasksDemo(props: { def: TasksCardData; cardId: string; heading?: string }) { const [log, setLog] = createSignal([]); let el: TasksEl | undefined; onMount(() => { if (!el) return; el.data = props.def; el.addEventListener('kc-card', (e) => { const detail = (e as CustomEvent).detail; setLog((prev) => [...prev, detail]); }); }); return (
(el = e as TasksEl)} card-id={props.cardId} heading={props.heading} />
          {log().length === 0 ? '// emitted CardEvents appear here' : JSON.stringify(log(), null, 2)}
        
); } const PLAN: TasksCardData = { mode: 'select', selectAll: true, confirmLabel: 'Run selected', tasks: [ { id: 'lint', label: 'Run linter', checked: true }, { id: 'test', label: 'Run unit tests', checked: true }, { id: 'build', label: 'Build production bundle' }, { id: 'deploy', label: 'Deploy to staging', description: 'Reversible; staging only' }, ], }; const REQUIRE_ONE: TasksCardData = { confirmLabel: 'Apply', allowEmpty: false, tasks: [ { id: 'cache', label: 'Clear the CDN cache' }, { id: 'reindex', label: 'Rebuild the search index' }, { id: 'restart', label: 'Restart the workers' }, ], }; const BOUNDED: TasksCardData = { heading: 'Pick up to 2 reviewers', confirmLabel: 'Request review', min: 1, max: 2, tasks: [ { id: 'ana', label: 'Ana' }, { id: 'ben', label: 'Ben' }, { id: 'cat', label: 'Cat' }, { id: 'dan', label: 'Dan' }, ], }; const WITH_DESCRIPTIONS: TasksCardData = { selectAll: true, confirmLabel: 'Run cleanup', tasks: [ { id: 'tmp', label: 'Delete temp files', description: 'Frees ~2.1 GB; safe to remove' }, { id: 'logs', label: 'Rotate logs', description: 'Archives logs older than 30 days' }, { id: 'orphans', label: 'Prune orphaned blobs', description: 'Unreferenced uploads only' }, ], }; const HEADING_MAP: Record = { 'card-plan': 'Approve the plan steps', 'card-require': 'Choose maintenance steps', 'card-bounded': undefined, 'card-desc': 'Storage cleanup', }; const HTML_SNIPPET = (def: TasksCardData, cardId: string) => { const heading = HEADING_MAP[cardId]; return `
`; }; const meta = { title: 'Generative UI/Cards/kc-tasks', tags: ['autodocs'], argTypes: argTypesFor('kc-tasks'), parameters: { layout: 'padded', docs: { description: specDescription('kc-tasks', [ "`` is a **selectable** task/plan list (set via the `data` **property**): checkbox rows + an optional select-all + a confirm button. The user picks a subset, confirms, and the card emits the Card contract's **`submit`** verb up a bubbling **`kc-card`** CustomEvent of `{ kind: 'submit', cardId, data: { selected } }` — the checked ids in **input order**. Toggling rows is local UI state; **only the final confirm emits** (the wire stays quiet, the result atomic).", '**Anatomy:** `` chrome (optional heading) → **select-all row** (shown when `selectAll:true` and ≥2 toggleable tasks; indeterminate state when partially checked) → **task rows** (one per `tasks[]` item: checkbox + label + optional description; disabled when `max` reached or `task.disabled`) → **card footer** (selected-count label + confirm `