{
  "aliases": [
    "card-selection",
    "selectable-cards",
    "bulk-delete-cards",
    "grid-bulk-actions"
  ],
  "code": "// Live page: /showcase/card-grid-selection (docs/showcase/card-grid-selection.tsx).\n// DataTable.BulkActions reads the table context only when one exists — outside a table pass\n// `count`: it renders nothing at 0, and the localised \"N selected\" + a labelled region above 0.\n// The Checkbox's inline label is the item name: the name is the click target and the\n// accessible name. Do NOT make the whole Card an onClick div.\nimport * as React from \"react\";\nimport { FolderOpen, Trash2 } from \"lucide-react\";\nimport { Card, CardContent, DataTable, EmptyState } from \"@godxjp/ui/data-display\";\nimport { Checkbox } from \"@godxjp/ui/data-entry\";\nimport { AlertDialog, Toaster, toast } from \"@godxjp/ui/feedback\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { Flex, ResponsiveGrid } from \"@godxjp/ui/layout\";\nimport { useTranslation } from \"@godxjp/ui/i18n\";\n\ntype Item = { id: string; name: string };\n\nexport function SelectableCardGrid({ source }: { source: Item[] }) {\n  const { t } = useTranslation();\n  const [items, setItems] = React.useState(source);\n  const [selected, setSelected] = React.useState<Set<string>>(new Set());\n  const [confirmOpen, setConfirmOpen] = React.useState(false);\n  const [confirmCount, setConfirmCount] = React.useState(0); // frozen: selection clears on confirm\n\n  const all = items.length > 0 && items.every((i) => selected.has(i.id));\n  const selectAll: boolean | \"indeterminate\" = all ? true : selected.size > 0 ? \"indeterminate\" : false;\n\n  const deleteSelected = () => {\n    const removed = new Set(selected);\n    setItems((cur) => cur.filter((i) => !removed.has(i.id)));\n    setSelected(new Set());\n    toast(t(\"files.deleted\", { count: removed.size }), {\n      action: {\n        label: t(\"files.undo\"),\n        // restore in the SOURCE order, not appended at the end\n        onClick: () => setItems((cur) => source.filter((i) => removed.has(i.id) || cur.some((c) => c.id === i.id))),\n      },\n    });\n  };\n\n  if (items.length === 0) {\n    return (\n      <EmptyState\n        icon={FolderOpen}\n        variant=\"section\"\n        title={t(\"files.emptyTitle\")}\n        action={<Button variant=\"outline\" onClick={() => setItems(source)}>{t(\"files.reset\")}</Button>}\n      />\n    );\n  }\n\n  return (\n    <Flex direction=\"col\" gap=\"md\">\n      <Flex direction=\"row\" align=\"center\" gap=\"md\" wrap>\n        <Checkbox\n          checked={selectAll}\n          onCheckedChange={(next) => setSelected(next === true ? new Set(items.map((i) => i.id)) : new Set())}\n        >\n          {t(\"files.selectAll\")}\n        </Checkbox>\n        <DataTable.BulkActions count={selected.size}>\n          <Button variant=\"destructive\" size=\"sm\" onClick={() => { setConfirmCount(selected.size); setConfirmOpen(true); }}>\n            <Trash2 aria-hidden=\"true\" />\n            {t(\"files.delete\")}\n          </Button>\n        </DataTable.BulkActions>\n      </Flex>\n      <ResponsiveGrid columns={{ base: 1, sm: 2, lg: 3 }}>\n        {items.map((item) => (\n          <Card key={item.id} className={selected.has(item.id) ? \"border-primary\" : undefined}>\n            <CardContent>\n              <Checkbox\n                checked={selected.has(item.id)}\n                onCheckedChange={(on) =>\n                  setSelected((prev) => {\n                    const next = new Set(prev);\n                    if (on === true) next.add(item.id); else next.delete(item.id);\n                    return next;\n                  })\n                }\n              >\n                {item.name}\n              </Checkbox>\n            </CardContent>\n          </Card>\n        ))}\n      </ResponsiveGrid>\n      <AlertDialog\n        open={confirmOpen}\n        onOpenChange={setConfirmOpen}\n        variant=\"destructive\"\n        title={t(\"files.confirmTitle\", { count: confirmCount })}\n        confirmLabel={t(\"files.delete\")}\n        onConfirm={deleteSelected}\n      />\n      <Toaster />{/* once per app — usually in the root layout, not here */}\n    </Flex>\n  );\n}",
  "name": "card-grid-selection",
  "tagline": "Select cards in a grid and act on them in bulk — Card + Checkbox per item, the real DataTable.BulkActions driven by count outside a table, indeterminate select-all, AlertDialog before delete, toast with Undo after, EmptyState when nothing is left.",
  "tags": [
    "selection",
    "bulk",
    "cards",
    "grid",
    "delete",
    "undo",
    "empty",
    "checkbox"
  ]
}
